package org.openslx.bwlp.sat.mail; import java.sql.SQLException; import java.text.BreakIterator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import org.apache.log4j.Logger; import org.openslx.bwlp.sat.database.mappers.DbConfiguration; import org.openslx.bwlp.sat.database.mappers.DbImage; import org.openslx.bwlp.sat.database.mappers.DbImagePermissions; import org.openslx.bwlp.sat.database.mappers.DbLecturePermissions; import org.openslx.bwlp.sat.database.mappers.DbUser; import org.openslx.bwlp.sat.database.mappers.DbUser.User; import org.openslx.bwlp.sat.database.models.LocalImageVersion; import org.openslx.bwlp.sat.mail.MailQueue.MailConfig; import org.openslx.bwlp.sat.util.Formatter; import org.openslx.bwlp.sat.util.Util; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.bwlp.thrift.iface.ImagePermissions; import org.openslx.bwlp.thrift.iface.ImageVersionDetails; import org.openslx.bwlp.thrift.iface.LecturePermissions; import org.openslx.bwlp.thrift.iface.LectureSummary; import org.openslx.bwlp.thrift.iface.TNotFoundException; import org.openslx.bwlp.thrift.iface.UserInfo; public class MailGenerator { // TODO Hard-Coded template mess. Strings and messages changed a lot while testing :( // This is best we could do for Sept. release private static final Logger LOGGER = Logger.getLogger(MailGenerator.class); /** * Called when an image has been updated, and linked lectures will be moved * to the new image version. * * @param lectures List of affected lectures * @param newVersion version id of new image */ public static void lectureAutoUpdate(List lectures, LocalImageVersion newVersion) { if (!hasMailConfig()) return; for (LectureSummary lecture : lectures) { List relevantUsers = getUserToMail(lecture); String message = "Zur Veranstaltung '" + lecture.lectureName + "' gehörige VM wurde aktualisiert."; for (UserInfo user : relevantUsers) { if (newVersion.uploaderId.equals(user.userId)) // Don't notice about changes by user continue; MailQueue.queue(new Mail(user, wordWrap(message))); } } } /** * Called when a lecture is downgraded, or a lecture is updated and it * doesn't have auto-updates enabled. * * @param lectures list of affected lectures * @param newVersion the new version being switched to */ public static void lectureForcedUpdate(List lectures, LocalImageVersion newVersion) { if (!hasMailConfig()) return; for (LectureSummary lecture : lectures) { List relevantUsers = getUserToMail(lecture); String message = "Die verlinkte VM zur Veranstaltung '" + lecture.lectureName + "' wurde gelöscht oder ist beschädigt," + " daher verweist sie jetzt auf die VM-Version vom " + Formatter.date(newVersion.createTime) + "." + " Bitte überprüfen Sie ggf., ob diese VM-Version für Ihren Kurs geeignet ist."; for (UserInfo user : relevantUsers) { MailQueue.queue(new Mail(user, wordWrap(message))); } } } public static void lectureDeactivated(List lectures) { if (!hasMailConfig()) return; for (LectureSummary lecture : lectures) { List relevantUsers = getUserToMail(lecture); String message = "Die Veranstaltung '" + lecture.lectureName + "' musste deaktiviert werden," + " da die verknüpfte VM gelöscht oder beschädigt wurde. Bitte überprüfen" + " Sie die Veranstaltung und ändern Sie ggf. die Verlinkung," + " damit die Veranstaltung wieder verwendbar ist."; for (UserInfo user : relevantUsers) { MailQueue.queue(new Mail(user, wordWrap(message))); } } } public static void versionDeleted(String imageBaseId, LocalImageVersion oldLocal, LocalImageVersion newLocal) { if (!hasMailConfig()) return; ImageDetailsRead image; try { image = DbImage.getImageDetails(null, imageBaseId); } catch (TNotFoundException | SQLException e) { LOGGER.warn("Version Deleted for image=" + imageBaseId + " failed", e); return; } ImageVersionDetails oldVersion = null; ImageVersionDetails newVersion = null; for (ImageVersionDetails version : image.versions) { if (oldLocal != null && version.versionId.equals(oldLocal.imageVersionId)) { oldVersion = version; } if (newLocal != null && version.versionId.equals(newLocal.imageVersionId)) { newVersion = version; } } if (oldVersion == newVersion) return; String message = "Virtuelle Maschine '" + image.imageName + "':"; if (newVersion == null) { message += " Die letzte verbliebene Version der VM wurde gelöscht; VM deaktiviert."; } else { String uploaderName; try { User uploader = DbUser.getCached(newVersion.uploaderId); uploaderName = Formatter.userFullName(uploader.ui) + " <" + uploader.ui.eMail + ">"; } catch (TNotFoundException | SQLException e) { uploaderName = "(unbekannt)"; } message += "\n Neueste Version ist jetzt vom " + Formatter.date(newVersion.createTime) + " (erstellt von " + uploaderName + ")"; if (oldVersion != null) { message += "\n Vorherige Verson war vom " + Formatter.date(oldVersion.createTime); } } List relevantUsers = getUserToMail(image); for (UserInfo user : relevantUsers) { MailQueue.queue(new Mail(user, wordWrap(message))); } } public static void sendDeletionReminder(LocalImageVersion version, int days, boolean mailForced) { if (!hasMailConfig()) return; ImageDetailsRead image; try { image = DbImage.getImageDetails(null, version.imageBaseId); } catch (TNotFoundException | SQLException e) { LOGGER.warn("Could not get image details for image version " + version.imageVersionId); return; } String message; if (image.latestVersionId == null || image.latestVersionId.equals(version.imageVersionId)) { message = "Die aktuellste Version der VM '" + image.imageName + "' läuft in " + days + " Tag(en) ab. Bitte aktualisieren Sie die VM, da verknüpfte" + " Veranstaltungen sonst deaktiviert werden."; } else if (mailForced) { message = "Eine alte Version der VM '" + image.imageName + "' läuft in " + days + " Tag(en) ab (Version vom " + Formatter.date(version.createTime) + ")." + " Eine aktuellere Version ist vorhanden, diese Nachricht dient nur der Information."; } else { return; } List relevantUsers = getUserToMail(image); for (UserInfo user : relevantUsers) { MailQueue.queue(new Mail(user, wordWrap(message))); } } public static void sendEndDateRemainder(LectureSummary lecture, int days) { if (!hasMailConfig()) return; List relevantUsers = getUserToMail(lecture); String message = "Die Veranstaltung '" + lecture.lectureName + "' läuft in " + days + " Tag(en) ab."; for (UserInfo user : relevantUsers) { MailQueue.queue(new Mail(user, wordWrap(message))); } } public static boolean isValidMailConfig(MailConfig conf) { return conf != null && conf.port != 0 && !Util.isEmptyString(conf.host) && !Util.isEmptyString(conf.senderAddress); } private static boolean hasMailConfig() { MailConfig conf; try { conf = DbConfiguration.getMailConfig(); } catch (SQLException e) { return false; } return isValidMailConfig(conf); } private static List getUserToMail(LectureSummary lecture) { Map users; try { users = DbLecturePermissions.getForLecture(lecture.lectureId, false); } catch (SQLException e) { users = new HashMap<>(); } users.put(lecture.ownerId, new LecturePermissions(true, true)); List list = new ArrayList<>(users.size()); for (Entry entry : users.entrySet()) { LecturePermissions perms = entry.getValue(); if (!perms.admin && !perms.edit) continue; User user; try { user = DbUser.getCached(entry.getKey()); } catch (TNotFoundException e) { LOGGER.warn("UserID " + entry.getKey() + " unknown"); continue; } catch (SQLException e) { continue; // Logging happened in DbUser } if (user.local.emailNotifications) { list.add(user.ui); } } return list; } private static List getUserToMail(ImageDetailsRead image) { Map users; try { users = DbImagePermissions.getForImageBase(image.imageBaseId, false); } catch (SQLException e) { users = new HashMap<>(); } users.put(image.ownerId, new ImagePermissions(true, true, true, true)); List list = new ArrayList<>(users.size()); for (Entry entry : users.entrySet()) { ImagePermissions perms = entry.getValue(); if (!perms.admin && !perms.edit) continue; User user; try { user = DbUser.getCached(entry.getKey()); } catch (TNotFoundException e) { LOGGER.warn("UserID " + entry.getKey() + " unknown"); continue; } catch (SQLException e) { continue; // Logging happened in DbUser } if (user.local.emailNotifications) { list.add(user.ui); } } return list; } private static String wordWrap(String input) { return wordWrap(input, 76, "\n ", Locale.GERMAN); } private static String wordWrap(String input, int width, String nlString, Locale locale) { if (input == null) { return ""; } else if (width < 5) { return input; } else if (width >= input.length()) { return input; } StringBuilder buf = new StringBuilder(input); boolean endOfLine = false; int lineStart = 0; for (int i = 0; i < buf.length(); i++) { if (buf.charAt(i) == '\n') { lineStart = i + 1; endOfLine = true; } // handle splitting at width character if (i > lineStart + width - 1) { if (endOfLine) { buf.insert(i, nlString); lineStart = i + nlString.length(); endOfLine = false; } else { int limit = i - lineStart - 1; BreakIterator breaks = BreakIterator.getLineInstance(locale); breaks.setText(buf.substring(lineStart, i)); int end = breaks.last(); // if the last character in the search string isn't a space, // we can't split on it (looks bad). Search for a previous // break character if (end == limit + 1) { if (!Character.isWhitespace(buf.charAt(lineStart + end))) { end = breaks.preceding(end - 1); } } // if the last character is a space, replace it with a \n if (end != BreakIterator.DONE && end == limit + 1) { buf.replace(lineStart + end, lineStart + end + 1, nlString.substring(0, 1)); buf.insert(lineStart + end + 1, nlString.substring(1)); lineStart = lineStart + end + nlString.length() - 1; } // otherwise, just insert a \n else if (end != BreakIterator.DONE && end != 0) { buf.insert(lineStart + end, nlString); lineStart = lineStart + end + nlString.length(); } else { buf.insert(i, nlString); lineStart = i + nlString.length(); } } } } return buf.toString(); } }