diff --git a/java/com.sap.sailing.server/resources/com/sap/sailing/server/notification/impl/notification-mail-template.html b/java/com.sap.sailing.server/resources/com/sap/sailing/server/notification/impl/notification-mail-template.html new file mode 100644 index 00000000000..e26d3e760bb --- /dev/null +++ b/java/com.sap.sailing.server/resources/com/sap/sailing/server/notification/impl/notification-mail-template.html @@ -0,0 +1,27 @@ + + + + ${title} + + + + ${body} + + \ No newline at end of file diff --git a/java/com.sap.sailing.server/resources/stringmessages/StringMessages.properties b/java/com.sap.sailing.server/resources/stringmessages/StringMessages.properties index 0a380024413..62d6ddca390 100644 --- a/java/com.sap.sailing.server/resources/stringmessages/StringMessages.properties +++ b/java/com.sap.sailing.server/resources/stringmessages/StringMessages.properties @@ -2,23 +2,29 @@ raceInRegattaOfEvent=race {0} in regatta {1} of event {2} raceInFleetInRegattaOfEvent={0} ({3}) in regatta {1} of event {2} leaderboardOfEvent=leaderboard {0} of event {1} +leaderboardShowResultsLinkTitle=Show results +racesOverviewLinkTitle=Show races overview +raceboardShowRaceLinkTitle=Show race +raceboardRaceAnalysisLinkTitle=Show race analysis boatClassRaceFinishedSubject=Race for boat class {0} has finished - new results available -# 0: boatClassName, 1: raceDescription, 2: link -boatClassRaceFinishedBody=New results for boat class {0} in {1} are available. See: {2} +# 0: boatClassName, 1: raceDescription +boatClassRaceFinishedBody=New results for boat class {0} in {1} are available. boatClassScoreCorrectionSubject=New results are available for boat class {0} -# 0: boatClassName, 1: leaderboardDescription, 2: link -boatClassScoreCorrectionBody=New results for boat class {0} in {1} are available. See: {2} +# 0: boatClassName, 1: leaderboardDescription +boatClassScoreCorrectionBody=New results for boat class {0} in {1} are available. competitorPassesFinishSubject=Competitor {0} passed finish line in a race -# 0: competitorName, 1: raceDescription, 2: link -competitorPassesFinishBody=Competitor {0} passed finish line in {1}. See: {2} +# 0: competitorName, 1: raceDescription +competitorPassesFinishBody=Competitor {0} passed finish line in {1}. competitorScoreCorrectionSubject=New results are available for competitor {0} -# 0: competitorName, 1: leaderboardDescription, 2: link -competitorScoreCorrectionBody=New results for competitor {0} in {1} are available. See: {2} +# 0: competitorName, 1: leaderboardDescription +competitorScoreCorrectionBody=New results for competitor {0} in {1} are available. boatClassUpcomingRaceSubject=A race for boat class {0} will start soon -# 0: boatClassName, 1: raceDescription, 2: time, 3: link -boatClassUpcomingRaceBody=A race for boat class {0} in {1} will start approximately at {2}. See: {3} +# 0: boatClassName, 1: raceDescription, 2: time +boatClassUpcomingRaceBody=A race for boat class {0} in {1} will start approximately at {2}. + + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/NotificationMailTemplate.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/NotificationMailTemplate.java new file mode 100644 index 00000000000..be88f351ec9 --- /dev/null +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/NotificationMailTemplate.java @@ -0,0 +1,42 @@ +package com.sap.sailing.server.notification.impl; + +import java.util.Arrays; + +import com.sap.sse.common.Util.Pair; + +public class NotificationMailTemplate { + + private final String subject; + private final String title; + private final String text; + private final Iterable> labelsAndLinkUrls; + + @SafeVarargs + public NotificationMailTemplate(String subject, String text, Pair... labelsAndLinkUrls) { + this(subject, null, text, labelsAndLinkUrls); + } + + @SafeVarargs + public NotificationMailTemplate(String subject, String title, String text, Pair... labelsAndLinkUrls) { + this.subject = subject; + this.title = title; + this.text = text; + this.labelsAndLinkUrls = Arrays.asList(labelsAndLinkUrls); + } + + public String getSubject() { + return subject; + } + + public String getTitle() { + return title; + } + + public String getText() { + return text; + } + + public Iterable> getLabelsAndLinkUrls() { + return labelsAndLinkUrls; + } +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/NotificationSetNotification.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/NotificationSetNotification.java index 8d051707277..91976427a2f 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/NotificationSetNotification.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/NotificationSetNotification.java @@ -1,5 +1,9 @@ package com.sap.sailing.server.notification.impl; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; @@ -9,12 +13,31 @@ import javax.mail.Multipart; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; +import com.sap.sse.common.Util.Pair; import com.sap.sse.mail.MailService; import com.sap.sse.mail.queue.MailNotification; import com.sap.sse.security.PreferenceObjectBasedNotificationSet; public abstract class NotificationSetNotification implements MailNotification { private static final Logger logger = Logger.getLogger(NotificationSetNotification.class.getName()); + + private static final String TEMPLATE_FILE = "notification-mail-template.html"; + private static final String TEMPLATE = loadTemplateFile(); + + private static String loadTemplateFile() { + StringBuilder content = new StringBuilder(); + try (BufferedReader in = new BufferedReader(new InputStreamReader( + NotificationSetNotification.class.getResourceAsStream(TEMPLATE_FILE), StandardCharsets.UTF_8))) { + String line; + while ((line = in.readLine()) != null) { + content.append(line).append("\n"); + } + } catch (IOException exc) { + logger.log(Level.SEVERE, "Error while loading notification mail template!", exc); + } + return content.toString(); + } + private final T objectToNotifyAbout; private final PreferenceObjectBasedNotificationSet associatedNotificationSet; @@ -33,17 +56,30 @@ public abstract class NotificationSetNotification implements MailNotification Multipart multipart = new MimeMultipart(); BodyPart bodyPart = new MimeBodyPart(); try { - bodyPart.setContent(constructBody(objectToNotifyAbout, locale), "text/html"); + NotificationMailTemplate mailTemplate = getMailTemplate(objectToNotifyAbout, locale); + bodyPart.setContent(getMailContent(mailTemplate), "text/html"); multipart.addBodyPart(bodyPart); - mailService.sendMail(user.getEmail(), constructSubject(objectToNotifyAbout, locale), multipart); + mailService.sendMail(user.getEmail(), mailTemplate.getSubject(), multipart); } catch (Exception e) { logger.log(Level.SEVERE, "Could not send mail notification for \"" + objectToNotifyAbout + "\" to user \" + user + \"", e); } }); } + + private String getMailContent(NotificationMailTemplate notificationMailTemplate) { + StringBuilder bodyContent = new StringBuilder(); + if (notificationMailTemplate.getTitle() != null) { + bodyContent.append("

").append(notificationMailTemplate.getTitle()).append("

"); + } + bodyContent.append("

").append(notificationMailTemplate.getText()).append("

"); + for (Pair link : notificationMailTemplate.getLabelsAndLinkUrls()) { + bodyContent.append(""); + bodyContent.append(link.getA()).append(""); + } + return TEMPLATE.replace("${title}", notificationMailTemplate.getSubject()).replace("${body}", bodyContent.toString()); + } + + protected abstract NotificationMailTemplate getMailTemplate(T objectToNotifyAbout, Locale locale); - protected abstract String constructSubject(T objectToNotifyAbout, Locale locale); - - protected abstract String constructBody(T objectToNotifyAbout, Locale locale); } \ No newline at end of file diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java index 2f60ce3462a..c42fcd7673d 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java @@ -173,19 +173,44 @@ public class SailingNotificationServiceImpl implements Stoppable, SailingNotific return messages.get(locale, "leaderboardOfEvent", leaderboardDisplayName, eventName); } - - private String createRaceBoardLink(TrackedRace trackedRace, Leaderboard leaderboard, Event event, - LeaderboardGroup leaderboardGroup) { - RegattaAndRaceIdentifier raceIdentifier = trackedRace.getRaceIdentifier(); - String link = getBaseURL(event).toString() + "/gwt/RaceBoard.html?eventId=" + event.getId() + "&leaderboardName=" + leaderboard.getName() - + "&leaderboardGroupName=" + leaderboardGroup.getName() + "&raceName=" - + raceIdentifier.getRaceName() + "&showMapControls=true&viewShowNavigationPanel=true®attaName=" - + raceIdentifier.getRegattaName(); - return link; + + private Pair createRaceBoardShowRaceLink(TrackedRace trackedRace, Leaderboard leaderboard, + Event event, LeaderboardGroup leaderboardGroup, Locale locale) { + return createRaceBoardLink(trackedRace, leaderboard, event, leaderboardGroup, "PLAYER", + "raceboardShowRaceLinkTitle", locale); } - private String createHomeRacesListLink(Leaderboard leaderboard, Event event) { - return createHomeRegattaLink("races", leaderboard, event); + private Pair createRaceBoardRaceAnalysisLink(TrackedRace trackedRace, Leaderboard leaderboard, + Event event, LeaderboardGroup leaderboardGroup, Locale locale) { + return createRaceBoardLink(trackedRace, leaderboard, event, leaderboardGroup, "FULL_ANALYSIS", + "raceboardRaceAnalysisLinkTitle", locale); + } + + private Pair createRaceBoardLink(TrackedRace trackedRace, Leaderboard leaderboard, Event event, + LeaderboardGroup leaderboardGroup, String raceboardMode, String labelMessageKey, Locale locale) { + RegattaAndRaceIdentifier raceIdentifier = trackedRace.getRaceIdentifier(); + String link = getBaseURL(event).toString() + "/gwt/RaceBoard.html?locale=" + locale.toLanguageTag() + + "&eventId=" + event.getId() + "&leaderboardName=" + leaderboard.getName() + + "&leaderboardGroupName=" + leaderboardGroup.getName() + "&raceName=" + + raceIdentifier.getRaceName() + "&showMapControls=true&viewShowNavigationPanel=true®attaName=" + + raceIdentifier.getRegattaName() + "&mode=" + raceboardMode; + return new Pair(messages.get(locale, labelMessageKey), link); + } + + private Pair createHomeRacesListLink(Leaderboard leaderboard, Event event, Locale locale) { + return createHomeRegattaLink("races", "racesOverviewLinkTitle", leaderboard, event, locale); + } + + private Pair createHomeLeaderboardLink(Leaderboard leaderboard, Event event, + Locale locale) { + return createHomeRegattaLink("leaderboard", "leaderboardShowResultsLinkTitle", leaderboard, event, locale); + } + + private Pair createHomeRegattaLink(String tab, String labelMessageKey, Leaderboard leaderboard, + Event event, Locale locale) { + String link = getBaseURL(event).toString() + "/gwt/Home.html?locale=" + locale.toLanguageTag() + + "#/regatta/" + tab + "/:eventId=" + event.getId() + "®attaId=" + leaderboard.getName(); + return new Pair(messages.get(locale, labelMessageKey), link); } /** @@ -201,34 +226,21 @@ public class SailingNotificationServiceImpl implements Stoppable, SailingNotific } return result; } - - private String createHomeLeaderboardLink(Leaderboard leaderboard, Event event) { - return createHomeRegattaLink("leaderboard", leaderboard, event); - } - - private String createHomeRegattaLink(String tab, Leaderboard leaderboard, Event event) { - String link = getBaseURL(event).toString() + "/gwt/Home.html#/regatta/" + tab + "/:eventId=" + event.getId() + "®attaId=" - + leaderboard.getName(); - return link; - } @Override public void notifyUserOnBoatClassRaceChangesStateToFinished(BoatClass boatClass, TrackedRace trackedRace, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet) { doWithEvent(leaderboard, (event, leaderboardGroup) -> { - String link = createRaceBoardLink(trackedRace, leaderboard, event, leaderboardGroup); - mailQueue.addNotification(new NotificationSetNotification(boatClass, boatClassResults) { + @Override - protected String constructSubject(BoatClass objectToNotifyAbout, Locale locale) { - return messages.get(locale, "boatClassRaceFinishedSubject", boatClass.getDisplayName()); - } - - @Override - protected String constructBody(BoatClass objectToNotifyAbout, Locale locale) { + protected NotificationMailTemplate getMailTemplate(BoatClass objectToNotifyAbout, Locale locale) { String raceDescription = calculateRaceDescription(locale, event, leaderboard, raceColumn, fleet); - return messages.get(locale, "boatClassRaceFinishedBody", boatClass.getDisplayName(), - raceDescription, encodeLink(link)); + return new NotificationMailTemplate( + messages.get(locale, "boatClassRaceFinishedSubject", boatClass.getDisplayName()), + messages.get(locale, "boatClassRaceFinishedBody", boatClass.getDisplayName(), raceDescription), + createRaceBoardShowRaceLink(trackedRace, leaderboard, event, leaderboardGroup, locale), + createRaceBoardRaceAnalysisLink(trackedRace, leaderboard, event, leaderboardGroup, locale)); } }); }); @@ -238,19 +250,15 @@ public class SailingNotificationServiceImpl implements Stoppable, SailingNotific public void notifyUserOnBoatClassWhenScoreCorrectionsAreAvailable(BoatClass boatClass, Leaderboard leaderboard) { // TODO don't send notifications when a notification for the same boatClass / leaderboard has already been sent shortly before doWithEvent(leaderboard, (event, leaderboardGroup) -> { - String link = createHomeLeaderboardLink(leaderboard, event); mailQueue.addNotification(new NotificationSetNotification(boatClass, boatClassResults) { @Override - protected String constructSubject(BoatClass objectToNotifyAbout, Locale locale) { - return messages.get(locale, "boatClassScoreCorrectionSubject", boatClass.getDisplayName()); - } - - @Override - protected String constructBody(BoatClass objectToNotifyAbout, Locale locale) { + protected NotificationMailTemplate getMailTemplate(BoatClass objectToNotifyAbout, Locale locale) { String leaderboardDescription = calculateLeaderboardDescription(locale, event, leaderboard); - return messages.get(locale, "boatClassScoreCorrectionBody", boatClass.getDisplayName(), - leaderboardDescription, encodeLink(link)); + return new NotificationMailTemplate( + messages.get(locale, "boatClassScoreCorrectionSubject", boatClass.getDisplayName()), + messages.get(locale, "boatClassScoreCorrectionBody", boatClass.getDisplayName(), leaderboardDescription), + createHomeLeaderboardLink(leaderboard, event, locale)); } }); }); @@ -259,49 +267,37 @@ public class SailingNotificationServiceImpl implements Stoppable, SailingNotific public void notifyUserOnBoatClassUpcomingRace(BoatClass boatClass, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, TimePoint when) { doWithEvent(leaderboard, (event, leaderboardGroup) -> { - String link = createHomeRacesListLink(leaderboard, event); mailQueue.addNotification(new NotificationSetNotification(boatClass, boatClassUpcomingRace) { @Override - protected String constructSubject(BoatClass objectToNotifyAbout, Locale locale) { - return messages.get(locale, "boatClassUpcomingRaceSubject", boatClass.getDisplayName()); - } - - @Override - protected String constructBody(BoatClass objectToNotifyAbout, Locale locale) { + protected NotificationMailTemplate getMailTemplate(BoatClass objectToNotifyAbout, Locale locale) { String raceDescription = calculateRaceDescription(locale, event, leaderboard, raceColumn, fleet); // TODO properly format time in the user's locale String time = when.toString(); - return messages.get(locale, "boatClassUpcomingRaceBody", boatClass.getDisplayName(), - raceDescription, time, encodeLink(link)); + return new NotificationMailTemplate( + messages.get(locale, "boatClassUpcomingRaceSubject", boatClass.getDisplayName()), + messages.get(locale, "boatClassUpcomingRaceBody", boatClass.getDisplayName(), + raceDescription, time), + createHomeRacesListLink(leaderboard, event, locale)); } }); }); } - - private String encodeLink(String link) { - String encodedLink = ""+link.replace("&", "&")+""; - return encodedLink; - } - @Override public void notifyUserOnCompetitorPassesFinish(Competitor competitor, TrackedRace trackedRace, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet) { doWithEvent(leaderboard, (event, leaderboardGroup) -> { - String link = createRaceBoardLink(trackedRace, leaderboard, event, leaderboardGroup); mailQueue.addNotification(new NotificationSetNotification(competitor, competitorResults) { @Override - protected String constructSubject(Competitor objectToNotifyAbout, Locale locale) { - return messages.get(locale, "competitorPassesFinishSubject", competitor.getName()); - } - - @Override - protected String constructBody(Competitor objectToNotifyAbout, Locale locale) { + protected NotificationMailTemplate getMailTemplate(Competitor objectToNotifyAbout, Locale locale) { String raceDescription = calculateRaceDescription(locale, event, leaderboard, raceColumn, fleet); - return messages.get(locale, "competitorPassesFinishBody", competitor.getName(), raceDescription, - encodeLink(link)); + return new NotificationMailTemplate( + messages.get(locale, "competitorPassesFinishSubject", competitor.getName()), + messages.get(locale, "competitorPassesFinishBody", competitor.getName(), raceDescription), + createRaceBoardShowRaceLink(trackedRace, leaderboard, event, leaderboardGroup, locale), + createRaceBoardRaceAnalysisLink(trackedRace, leaderboard, event, leaderboardGroup, locale)); } }); }); @@ -310,19 +306,16 @@ public class SailingNotificationServiceImpl implements Stoppable, SailingNotific @Override public void notifyUserOnCompetitorScoreCorrections(Competitor competitor, Leaderboard leaderboard) { doWithEvent(leaderboard, (event, leaderboardGroup) -> { - String link = createHomeLeaderboardLink(leaderboard, event); mailQueue.addNotification(new NotificationSetNotification(competitor, competitorResults) { @Override - protected String constructSubject(Competitor objectToNotifyAbout, Locale locale) { - return messages.get(locale, "boatClassScoreCorrectionSubject", competitor.getName()); - } - - @Override - protected String constructBody(Competitor objectToNotifyAbout, Locale locale) { + protected NotificationMailTemplate getMailTemplate(Competitor objectToNotifyAbout, Locale locale) { String leaderboardDescription = calculateLeaderboardDescription(locale, event, leaderboard); - return messages.get(locale, "boatClassScoreCorrectionBody", competitor.getName(), - leaderboardDescription, encodeLink(link)); + return new NotificationMailTemplate( + messages.get(locale, "boatClassScoreCorrectionSubject", competitor.getName()), + messages.get(locale, "boatClassScoreCorrectionBody", competitor.getName(), leaderboardDescription), + createHomeLeaderboardLink(leaderboard, event, locale), + createHomeLeaderboardLink(leaderboard, event, locale)); } }); });