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<LectureSummary> lectures, LocalImageVersion newVersion) {
if (!hasMailConfig())
return;
for (LectureSummary lecture : lectures) {
List<UserInfo> 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<LectureSummary> lectures, LocalImageVersion newVersion) {
if (!hasMailConfig())
return;
for (LectureSummary lecture : lectures) {
List<UserInfo> 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<LectureSummary> lectures) {
if (!hasMailConfig())
return;
for (LectureSummary lecture : lectures) {
List<UserInfo> 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<UserInfo> 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<UserInfo> 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<UserInfo> 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<UserInfo> getUserToMail(LectureSummary lecture) {
Map<String, LecturePermissions> users;
try {
users = DbLecturePermissions.getForLecture(lecture.lectureId, false);
} catch (SQLException e) {
users = new HashMap<>();
}
users.put(lecture.ownerId, new LecturePermissions(true, true));
List<UserInfo> list = new ArrayList<>(users.size());
for (Entry<String, LecturePermissions> 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<UserInfo> getUserToMail(ImageDetailsRead image) {
Map<String, ImagePermissions> 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<UserInfo> list = new ArrayList<>(users.size());
for (Entry<String, ImagePermissions> 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();
}
}