Browse Source

Merge branch 'main' into lang_de

lang_de
Stephan Richter 8 months ago
parent
commit
e35c31e74c
  1. 97
      src/main/java/de/srsoftware/widerhall/Configuration.java
  2. 151
      src/main/java/de/srsoftware/widerhall/Util.java
  3. 14
      src/main/java/de/srsoftware/widerhall/data/Database.java
  4. 91
      src/main/java/de/srsoftware/widerhall/data/ListMember.java
  5. 38
      src/main/java/de/srsoftware/widerhall/data/MailingList.java
  6. 17
      src/main/java/de/srsoftware/widerhall/data/Post.java
  7. 2
      src/main/java/de/srsoftware/widerhall/data/User.java
  8. 130
      src/main/java/de/srsoftware/widerhall/mail/ImapClient.java
  9. 42
      src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java
  10. 88
      src/main/java/de/srsoftware/widerhall/web/Rest.java
  11. 8
      src/main/java/de/srsoftware/widerhall/web/TemplateServlet.java
  12. 126
      src/main/java/de/srsoftware/widerhall/web/Web.java
  13. 103
      src/main/resources/Application.de.translation

97
src/main/java/de/srsoftware/widerhall/Configuration.java

@ -17,6 +17,45 @@ public class Configuration { @@ -17,6 +17,45 @@ public class Configuration {
private JSONObject data;
private File file;
public File archiveDir() {
var locations = locations();
if (!locations.containsKey(ARCHIVE)) locations.put(ARCHIVE, String.join(File.separator,baseDir(),"archive"));
return new File((String) locations.get(ARCHIVE));
}
public String baseDir() {
var locations = locations();
if (!locations.containsKey(BASE)) locations.put(BASE,System.getProperty("user.dir"));
return (String) locations.get(BASE);
}
public String baseUrl() {
if (!data.containsKey(BASE_URL)) data.put(BASE_URL,"http://localhost");
return (String) data.get(BASE_URL);
}
public File configFile() {
var locations = locations();
if (!locations.containsKey(CONFIG)) locations.put(CONFIG, String.join(File.separator,baseDir(),"config","config.json"));
return new File((String) locations.get(CONFIG));
}
public File dbFile() {
var locations = locations();
if (!locations.containsKey(DB)) locations.put(DB, String.join(File.separator,baseDir(),"db","db.sqlite3"));
return new File((String) locations.get(DB));
}
public Configuration dbFile(File dbFile){
var locations = locations();
locations.put(DB,dbFile.toString());
return this;
}
public File file() {
return file;
}
public static Configuration instance() {
if (singleton == null) singleton = new Configuration().setDefaults();
return singleton;
@ -37,18 +76,24 @@ public class Configuration { @@ -37,18 +76,24 @@ public class Configuration {
var newVals = (JSONObject) new JSONParser().parse(Files.readString(file.toPath()));
data.putAll(newVals);
} catch (ParseException | IOException e){
LOG.warn("Konnte Konfiguration nicht aus {} laden:",file,e);
LOG.warn("Was not able to load configuration from {}:",file,e);
}
return this;
}
private JSONObject locations() {
Object o = data.get(LOCATIONS);
if (!(o instanceof JSONObject)) data.put(LOCATIONS,o = new JSONObject());
return (JSONObject) o;
}
public Configuration save(File file) throws IOException {
this.file = file;
return save();
}
public Configuration save() throws IOException {
if (file == null) throw new NullPointerException("Konnte Konfiguration nicht speichern: Datei ist null!");
if (file == null) throw new NullPointerException("Cannot save configuration: file is null!");
file.getParentFile().mkdirs();
Files.writeString(file.toPath(),data.toJSONString());
return this;
@ -64,57 +109,9 @@ public class Configuration { @@ -64,57 +109,9 @@ public class Configuration {
return this;
}
private JSONObject locations() {
Object o = data.get(LOCATIONS);
if (!(o instanceof JSONObject)) data.put(LOCATIONS,o = new JSONObject());
return (JSONObject) o;
}
public String baseDir() {
var locations = locations();
if (!locations.containsKey(BASE)) locations.put(BASE,System.getProperty("user.dir"));
return (String) locations.get(BASE);
}
public File configFile() {
var locations = locations();
if (!locations.containsKey(CONFIG)) locations.put(CONFIG, String.join(File.separator,baseDir(),"config","config.json"));
return new File((String) locations.get(CONFIG));
}
public File dbFile() {
var locations = locations();
if (!locations.containsKey(DB)) locations.put(DB, String.join(File.separator,baseDir(),"db","db.sqlite3"));
return new File((String) locations.get(DB));
}
public Configuration dbFile(File dbFile){
var locations = locations();
locations.put(DB,dbFile.toString());
return this;
}
public File archiveDir() {
var locations = locations();
if (!locations.containsKey(ARCHIVE)) locations.put(ARCHIVE, String.join(File.separator,baseDir(),"archive"));
return new File((String) locations.get(ARCHIVE));
}
public int serverPort() {
if (!data.containsKey(PORT)) data.put(PORT,80L);
var o = data.get(PORT);
return (int) (long) o;
}
public String baseUrl() {
if (!data.containsKey(BASE_URL)) data.put(BASE_URL,"http://localhost");
return (String) data.get(BASE_URL);
}
public File file() {
return file;
}
}

151
src/main/java/de/srsoftware/widerhall/Util.java

@ -28,19 +28,33 @@ public class Util { @@ -28,19 +28,33 @@ public class Util {
private static final MessageDigest SHA256 = getSha256();
private static final String EMAIL_PATTERN = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+(?:\\.[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+)*@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$";
public static String urlEncode(Map<String, Object> data) {
String params = data.entrySet()
.stream()
.map(entry -> encode(entry.getKey()) + "=" + encode(entry.getValue()))
.collect(Collectors.joining("&"));
return params;
public static String dropEmail(String tx) {
return tx.replaceAll( "[.\\-\\w]+@[.\\-\\w]+", "[email_removed]");
}
private static String encode(Object value) {
return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8);
}
public static boolean getCheckbox(HttpServletRequest req, String key) {
return "on".equals(req.getParameter(key));
}
public static MailingList getMailingList(HttpServletRequest req) {
var listEmail = req.getParameter(LIST);
if (listEmail == null || listEmail.isBlank()) return null;
return MailingList.load(listEmail);
}
public static <T> T getNullable(ResultSet rs, String colName) throws SQLException {
final T val = (T) rs.getObject(colName);
return rs.wasNull() ? null : val;
}
public static String getPath(HttpServletRequest req) {
var path = req.getPathInfo();
return path == null ? INDEX : path.substring(1);
}
public static MessageDigest getSha256() {
try {
return MessageDigest.getInstance("SHA-256");
@ -50,9 +64,43 @@ public class Util { @@ -50,9 +64,43 @@ public class Util {
}
}
public static String sha256(String s) {
byte[] bytes = SHA256.digest(s.getBytes(StandardCharsets.UTF_8));
return hex(bytes);
/**
* Return the primary text content of the message.
*/
public static String getText(Part p) throws MessagingException, IOException {
// https://javaee.github.io/javamail/FAQ
if (p.isMimeType("text/*")) return (String)p.getContent();
if (p.isMimeType("multipart/alternative")) {
// prefer html text over plain text
Multipart mp = (Multipart)p.getContent();
String text = null;
for (int i = 0; i < mp.getCount(); i++) {
Part bp = mp.getBodyPart(i);
if (bp.isMimeType("text/plain")) {
if (text == null) text = getText(bp);
continue;
} else if (bp.isMimeType("text/html")) {
String s = getText(bp);
if (s != null) return s;
} else {
return getText(bp);
}
}
return text;
} else if (p.isMimeType("multipart/*")) {
Multipart mp = (Multipart)p.getContent();
for (int i = 0; i < mp.getCount(); i++) {
String s = getText(mp.getBodyPart(i));
if (s != null) return s;
}
}
return null;
}
public static User getUser(HttpServletRequest req) {
var o = req.getSession().getAttribute(USER);
return o instanceof User ? (User) o : null;
}
private static String hex(byte[] bytes) {
@ -67,14 +115,15 @@ public class Util { @@ -67,14 +115,15 @@ public class Util {
return (char)(upper < 10 ? '0'+upper : 'A'+upper-10)+""+(char)(lower < 10 ? '0'+lower : 'A'+lower-10);
}
public static String t(String tx, Object ... fills){
return Translation.get(Application.class,tx,fills);
}
public static boolean isEmail(String email) {
return email.matches(EMAIL_PATTERN);
}
public static String sha256(String s) {
byte[] bytes = SHA256.digest(s.getBytes(StandardCharsets.UTF_8));
return hex(bytes);
}
public static boolean simplePassword(String pass) {
if (pass.length() < 6) return true;
if (pass.length() < 8){
@ -98,75 +147,23 @@ public class Util { @@ -98,75 +147,23 @@ public class Util {
return false;
}
public static int unset(int value, int...flags) {
for (int flag : flags){
if ((value & flag) > 0) value ^= flag;
}
return value;
}
public static User getUser(HttpServletRequest req) {
var o = req.getSession().getAttribute(USER);
return o instanceof User ? (User) o : null;
}
public static String getPath(HttpServletRequest req) {
var path = req.getPathInfo();
return path == null ? INDEX : path.substring(1);
}
public static MailingList getMailingList(HttpServletRequest req) {
var listEmail = req.getParameter(LIST);
if (listEmail == null || listEmail.isBlank()) return null;
return MailingList.load(listEmail);
}
public static boolean getCheckbox(HttpServletRequest req, String key) {
return "on".equals(req.getParameter(key));
public static String t(String tx, Object ... fills){
return Translation.get(Application.class,tx,fills);
}
/**
* Return the primary text content of the message.
*/
public static String getText(Part p) throws MessagingException, IOException {
// https://javaee.github.io/javamail/FAQ
if (p.isMimeType("text/*")) return (String)p.getContent();
if (p.isMimeType("multipart/alternative")) {
// prefer html text over plain text
Multipart mp = (Multipart)p.getContent();
String text = null;
for (int i = 0; i < mp.getCount(); i++) {
Part bp = mp.getBodyPart(i);
if (bp.isMimeType("text/plain")) {
if (text == null) text = getText(bp);
continue;
} else if (bp.isMimeType("text/html")) {
String s = getText(bp);
if (s != null) return s;
} else {
return getText(bp);
}
}
return text;
} else if (p.isMimeType("multipart/*")) {
Multipart mp = (Multipart)p.getContent();
for (int i = 0; i < mp.getCount(); i++) {
String s = getText(mp.getBodyPart(i));
if (s != null) return s;
}
public static String urlEncode(Map<String, Object> data) {
String params = data.entrySet()
.stream()
.map(entry -> encode(entry.getKey()) + "=" + encode(entry.getValue()))
.collect(Collectors.joining("&"));
return params;
}
return null;
}
public static String dropEmail(String tx) {
return tx.replaceAll( "[.\\-\\w]+@[.\\-\\w]+", "[email_removed]");
public static int unset(int value, int...flags) {
for (int flag : flags){
if ((value & flag) > 0) value ^= flag;
}
public static <T> T getNullable(ResultSet rs, String colName) throws SQLException {
final T val = (T) rs.getObject(colName);
return rs.wasNull() ? null : val;
return value;
}
}

14
src/main/java/de/srsoftware/widerhall/data/Database.java

@ -46,13 +46,13 @@ public class Database { @@ -46,13 +46,13 @@ public class Database {
* @throws SQLException
*/
public ResultSet exec() throws SQLException {
LOG.debug("Führe {} aus",this);
LOG.debug("Executing {}",this);
try {
var stmt = conn.prepareStatement(sql);
for (int i = 0; i < args.size(); i++) stmt.setObject(i+1, args.get(i));
return stmt.executeQuery();
} catch (SQLException sqle) { // add sql to the exception
throw new SQLException(t("Query '{}' fehlgeschlagen:",this),sqle);
throw new SQLException(t("Query '{}' failed:",this),sqle);
}
}
@ -61,13 +61,13 @@ public class Database { @@ -61,13 +61,13 @@ public class Database {
* @throws SQLException
*/
public void run() throws SQLException {
LOG.debug("Führe {} aus",this);
LOG.debug("Running {}",this);
try {
var stmt = conn.prepareStatement(sql);
for (int i = 0; i < args.size(); i++) stmt.setObject(i+1, args.get(i));
stmt.execute();
} catch (SQLException sqle) {
throw new SQLException(t("Query '{}' fehlgeschlagen:",this),sqle);
throw new SQLException(t("Query '{}' failed:",this),sqle);
}
}
@ -193,8 +193,6 @@ public class Database { @@ -193,8 +193,6 @@ public class Database {
return new CompiledRequest(sql.toString(),args);
}
public Request groupBy(String column) {
groupBy = column;
return this;
@ -354,7 +352,7 @@ public class Database { @@ -354,7 +352,7 @@ public class Database {
Configuration config = Configuration.instance();
var dbFile = config.dbFile();
String url = "jdbc:sqlite:"+dbFile;
LOG.debug("Öffne {}",url);
LOG.debug("Opening {}",url);
dbFile.getParentFile().mkdirs();
try {
singleton = new Database(DriverManager.getConnection(url));
@ -410,7 +408,7 @@ public class Database { @@ -410,7 +408,7 @@ public class Database {
rs.close();
return val > 0;
} catch (SQLException e) {
throw new SQLException(t("Konnte Existenz der Tabelle {} nicht prüfen!",tbName),e);
throw new SQLException(t("Was not able to check existence of table {}!",tbName),e);
}
}

91
src/main/java/de/srsoftware/widerhall/data/ListMember.java

@ -50,22 +50,22 @@ public class ListMember { @@ -50,22 +50,22 @@ public class ListMember {
}
public String addNewModerator(String userEmail) {
if (!isAllowedToEditMods()) return t("Es ist dir nicht gestattet, neue Moderatoren für {} zu ernennen!",list.email());
if (!isAllowedToEditMods()) return t("You are not allowed to nominate new mods for {}",list.email());
User moderator = null;
try {
moderator = User.load(userEmail);
} catch (SQLException e) {
LOG.warn("Laden des Nutzers zu {} fehlgeschlagen",userEmail,e);
return t("Laden des Nutzers zu {} fehlgeschlagen",userEmail);
LOG.warn("Failed to load user for {}",userEmail,e);
return t("Failed to load user for {}",userEmail);
}
if (moderator == null) return t("Kein solcher Nutzer: {}",userEmail);
if (moderator == null) return t("No such user: {}",userEmail);
ListMember member = null;
try {
member = ListMember.load(list,moderator);
} catch (SQLException e) {
LOG.warn("Laden der Mitgliedschaft zu {}/{} fehlgeschlagen",moderator.email(),list.email(),e);
return t("Laden der Mitgliedschaft zu {}/{} fehlgeschlagen",moderator.email(),list.email());
LOG.warn("Failed to load list member for {}/{}",moderator.email(),list.email(),e);
return t("Failed to load list member for {}/{}",moderator.email(),list.email());
}
try {
if (member == null) {
@ -74,8 +74,8 @@ public class ListMember { @@ -74,8 +74,8 @@ public class ListMember {
member.setState(Util.unset(member.state|STATE_MODERATOR,STATE_AWAITING_CONFIRMATION));
}
} catch (SQLException e) {
LOG.warn("Ernennen von {} zum Moderator von {} fehlgeschlagen",moderator.email(),list.email(),e);
return t("Ernennen von {} zum Moderator von {} fehlgeschlagen",moderator.email(),list.email());
LOG.warn("Failed to make {} a moderator of {}",moderator.email(),list.email(),e);
return t("Failed to make {} a moderator of {}",moderator.email(),list.email());
}
return null;
}
@ -112,20 +112,6 @@ public class ListMember { @@ -112,20 +112,6 @@ public class ListMember {
return null;
}
public void sendConfirmationMail(ST template) throws SQLException, MessagingException {
var subject = t("[{}] Abonnement abgeschlossen!",list.name());
var data = new HashMap<String,Object>();
data.put(USER,user.safeMap());
data.put(LIST,list.minimalMap());
data.put(URL,Configuration.instance().baseUrl()+"/web/index");
if (list.isOpenForSubscribers()) data.put("open_list",true);
var text = template.add("data",data).render();
try {
list.smtp().send(list.email(),list.name(),user.email(),subject,text);
} catch (UnsupportedEncodingException e) {
LOG.warn("Failed to send list subscription confirmation!",e);
}
}
/**
* Create a new list member entry in the database.
* If the member has the state AWAITING_CONFIRMATION, a token is assigned with the member, too.
@ -160,44 +146,44 @@ public class ListMember { @@ -160,44 +146,44 @@ public class ListMember {
}
public String dropMember(String userEmail) {
if (!isModerator()) return t("Es ist dir nicht erlaubt, Mitglieder von {} zu entfernen.",list.email());
if (!isModerator()) return t("You are not allowed to remove members of {}",list.email());
User user = null;
try {
user = User.load(userEmail);
} catch (SQLException e) {
LOG.warn("Laden des Nutzers zu {} fehlgeschlagen",userEmail,e);
return t("Laden des Nutzers zu {} fehlgeschlagen",userEmail);
LOG.warn("Failed to load user for {}",userEmail,e);
return t("Failed to load user for {}",userEmail);
}
if (user == null) return t("Kein solcher Nutzer: {}",userEmail);
if (user == null) return t("No such user: {}",userEmail);
ListMember member = null;
try {
member = ListMember.load(list,user);
} catch (SQLException e) {
LOG.warn("Laden der Mitgliedschaft zu {}/{} fehlgeschlagen",user.email(),list.email(),e);
return t("Laden der Mitgliedschaft zu {}/{} fehlgeschlagen",user.email(),list.email());
LOG.warn("Failed to load list member for {}/{}",user.email(),list.email(),e);
return t("Failed to load list member for {}/{}",user.email(),list.email());
}
if (member == null) return t("{} it nicht Mitglied von {}",user.email(),list.email());
if (member.isOwner()) return t("Du kannst den Listen-Besitzer nicht entfernen!");
if (member == null) return t("{} is no member of {}",user.email(),list.email());
if (member.isOwner()) return t("You are not allowed to remvoe the list owner!");
try {
member.unsubscribe();
} catch (SQLException e) {
LOG.warn("Abbestellen von {} / {} nicht fehlgeschlagen",user.email(),list.email(),e);
return t("Abbestellen von {} / {} nicht fehlgeschlagen",user.email(),list.email());
LOG.warn("Failed to un-subscribe {} from {}",user.email(),list.email(),e);
return t("Failed to un-subscribe {} from {}",user.email(),list.email());
}
return null;
}
public String dropModerator(String userEmail) {
if (!isAllowedToEditMods()) return t("Es ist dir nicht gestattet, die Moderatoren von {} zu verändern",list.email());
if (!isAllowedToEditMods()) return t("You are not allowed to edit mods of {}",list.email());
User moderator = null;
try {
moderator = User.load(userEmail);
} catch (SQLException e) {
LOG.warn("Laden des Nutzers zu {} fehlgeschlagen",userEmail,e);
return t("Laden des Nutzers zu {} fehlgeschlagen",userEmail);
LOG.warn("Failed to load user for {}",userEmail,e);
return t("Failed to load user for {}",userEmail);
}
if (moderator == null) return t("No such user: {}",userEmail);
@ -205,8 +191,8 @@ public class ListMember { @@ -205,8 +191,8 @@ public class ListMember {
try {
member = ListMember.load(list,moderator);
} catch (SQLException e) {
LOG.warn("Laden der Mitgliedschaft zu {}/{} fehlgeschlagen",moderator.email(),list.email(),e);
return t("Laden der Mitgliedschaft zu {}/{} fehlgeschlagen",moderator.email(),list.email());
LOG.warn("Failed to load list member for {}/{}",moderator.email(),list.email(),e);
return t("Failed to load list member for {}/{}",moderator.email(),list.email());
}
try {
if (member == null) {
@ -215,8 +201,8 @@ public class ListMember { @@ -215,8 +201,8 @@ public class ListMember {
member.setState(Util.unset(member.state,STATE_MODERATOR));
}
} catch (SQLException e) {
LOG.warn("Ernennen von {} zum normalen Abonnenten von {} fehlgeschlagen",moderator.email(),list.email(),e);
return t("Ernennen von {} zum normalen Abonnenten von {} fehlgeschlagen",moderator.email(),list.email());
LOG.warn("Failed to make {} a subscriber of {}",moderator.email(),list.email(),e);
return t("Failed to make {} a subscriber of {}",moderator.email(),list.email());
}
return null;
}
@ -283,7 +269,7 @@ public class ListMember { @@ -283,7 +269,7 @@ public class ListMember {
var rs = request.compile().exec();
while (rs.next()) listEmails.add(rs.getString(LIST_EMAIL));
} catch (SQLException e) {
LOG.warn("Sammeln der Listen von {} fehlgeschlagen: ",user.email(),e);
LOG.warn("Collecting lists of {} failed: ",user.email(),e);
}
var lists = MailingList.loadAll(listEmails);
var result = new HashSet<ListMember>();
@ -309,7 +295,7 @@ public class ListMember { @@ -309,7 +295,7 @@ public class ListMember {
var rs = request.compile().exec();
while (rs.next()) list.add(rs.getString(LIST_EMAIL));
} catch (SQLException e) {
LOG.warn("Sammeln der Listen von {} fehlgeschlagen: ",user.email(),e);
LOG.warn("Collecting lists of {} failed: ",user.email(),e);
}
return list;
}
@ -374,6 +360,21 @@ public class ListMember { @@ -374,6 +360,21 @@ public class ListMember {
return this;
}
public void sendConfirmationMail(ST template) throws SQLException, MessagingException {
var subject = t("[{}] Subscription complete!",list.name());
var data = new HashMap<String,Object>();
data.put(USER,user.safeMap());
data.put(LIST,list.minimalMap());
data.put(URL,Configuration.instance().baseUrl()+"/web/index");
if (list.isOpenForSubscribers()) data.put("open_list",true);
var text = template.add("data",data).render();
try {
list.smtp().send(list.email(),list.name(),user.email(),subject,text);
} catch (UnsupportedEncodingException e) {
LOG.warn("Failed to send list subscription confirmation!",e);
}
}
public ListMember setState(int newState) throws SQLException {
Database.open()
.update(TABLE_NAME)
@ -391,10 +392,10 @@ public class ListMember { @@ -391,10 +392,10 @@ public class ListMember {
*/
public String stateText() {
var words = new ArrayList<String>();
if (isSubscriber()) words.add("Abonniert");
if (isOwner()) words.add("Besitzer");
if (isAwaiting()) words.add("erwartet Bestätigung");
if (isModerator()) words.add("Moderator");
if (isAwaiting()) words.add("awaiting confirmation");
if (isModerator()) words.add("moderator");
if (isOwner()) words.add("owner");
if (isSubscriber()) words.add("subscriber");
return String.join(", ",words);
}

38
src/main/java/de/srsoftware/widerhall/data/MailingList.java

@ -270,7 +270,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -270,7 +270,7 @@ public class MailingList implements MessageHandler, ProblemListener {
var member = ListMember.load(this,user);
return member.hasState(ListMember.STATE_OWNER|ListMember.STATE_SUBSCRIBER); // owners may subscribe their own mailing lists
} catch (SQLException e) {
LOG.warn("Konnte Listen-Mitglied nicht laden: ",e);
LOG.warn("Was not able to load ListMember: ",e);
return false;
}
}
@ -303,7 +303,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -303,7 +303,7 @@ public class MailingList implements MessageHandler, ProblemListener {
ml.lastError = rs.getString(LAST_ERROR);
}
} catch (SQLException e) {
LOG.debug("Konnte Mailing-Liste nicht laden: ",e);
LOG.debug("Failed to load MailingList: ",e);
}
return ml;
}
@ -325,7 +325,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -325,7 +325,7 @@ public class MailingList implements MessageHandler, ProblemListener {
.compile().exec();
while (rs.next()) list.add(MailingList.from(rs));
} catch (SQLException e) {
LOG.debug("Konnte Mailing-Listen nicht laden: ",e);
LOG.debug("Failed to load MailingLists: ",e);
}
return list;
}
@ -336,7 +336,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -336,7 +336,7 @@ public class MailingList implements MessageHandler, ProblemListener {
try {
if (ListMember.load(this,user).isModerator()) return true;
} catch (SQLException e) {
LOG.debug("Fehler beim Laden des Listenmitglieds für ({}, {})",user.email(),email());
LOG.debug("Error loading list member for ({}, {})",user.email(),email());
}
return false;
}
@ -346,7 +346,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -346,7 +346,7 @@ public class MailingList implements MessageHandler, ProblemListener {
try {
if (ListMember.load(this,user).isOwner()) return true;
} catch (SQLException e) {
LOG.debug("Fehler beim Laden des Listenmitglieds für ({}, {})",user.email(),email());
LOG.debug("Error loading list member for ({}, {})",user.email(),email());
}
return false;
}
@ -360,7 +360,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -360,7 +360,7 @@ public class MailingList implements MessageHandler, ProblemListener {
try {
if (ListMember.load(this,user).isOwner()) return true;
} catch (SQLException e) {
LOG.debug("Fehler beim Laden des Listenmitglieds für ({}, {})",user.email(),email());
LOG.debug("Error loading list member for ({}, {})",user.email(),email());
}
return false;
}
@ -422,7 +422,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -422,7 +422,7 @@ public class MailingList implements MessageHandler, ProblemListener {
@Override
public void onMessageReceived(Message message) throws MessagingException {
LOG.info("Nachricht empfangen: {}",message.getFrom());
LOG.info("Message received: {}",message.getFrom());
String subject = message.getSubject();
try {
@ -487,7 +487,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -487,7 +487,7 @@ public class MailingList implements MessageHandler, ProblemListener {
while (rs.next()) list.add(MailingList.from(rs));
rs.close();
} catch (SQLException e) {
LOG.warn("Auflisten der Mailinglisten fehlgeschlagen: ", e);
LOG.warn("Listing mailing lists failed: ", e);
}
return list;
}
@ -574,13 +574,13 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -574,13 +574,13 @@ public class MailingList implements MessageHandler, ProblemListener {
.map(ListMember::user)
.map(User::email)
.collect(Collectors.joining(", "));
var subject = t("Liste '{}' erfordert Aufmerksamkeit!",name());
var text = t("Diese Liste hat eine E-Mail von {} empfangen. Der Absender ist nicht Mitglied der Liste.\nDie Email wurde in den '{}'-Ordner verschoben.\nSie können die Nachricht manuell weiterleiten oder verwerfen.",senderEmail,RETAINED_FOLDER);
var subject = t("List '{}' requires attention!",name());
var text = t("This list received an email from {}, who is not member of the list.\nThe email has been moved to the '{}' folder.\nYou may manually forward this message or drop it.",senderEmail,RETAINED_FOLDER);
smtp.send(email(), name(), receivers,subject,text);
subject = t("Ihre Nachricht an {} wurde zurückgewiesen!",email());
text = t("Sie haben versucht, eine Nachricht an die Liste '{}' zu senden. Das wurde verweigert, da Sie kein Mitglied der Liste (mit entsprechenden Berechtigungen) sind.\n",name());
if (hasState(STATE_PUBLIC)) text += t("Sie können zu {} gehen und die Liste abonnieren. Versuchen Sie es danach erneut.",Configuration.instance().baseUrl());
subject = t("Your message to {} was rejected!",email());
text = t("You have tried to send a message to the list '{}', which failed. This is because you are not a (privileged) member of this list.\n",name());
if (hasState(STATE_PUBLIC)) text += t("You may go to {} and subscribe to the list, then try again.",Configuration.instance().baseUrl());
smtp.send(email(), name(), senderEmail,subject,text);
} catch (SQLException e){
LOG.error("Failed to load list of owners of mailing list. Retention notification was not sent to owners of {}",email(),e);
@ -646,7 +646,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -646,7 +646,7 @@ public class MailingList implements MessageHandler, ProblemListener {
rs.close();
return result;
} catch (SQLException e) {
LOG.warn("Lesen der abbonnierbaren Mailinglisten von {} fehlgeschlagen.",user,e);
LOG.warn("Failed to read subscribable mailinglists for {}",user,e);
return Set.of();
}
}
@ -671,11 +671,11 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -671,11 +671,11 @@ public class MailingList implements MessageHandler, ProblemListener {
try {
var config = Configuration.instance();
var url = new StringBuilder(config.baseUrl()).append("/web/confirm?token=").append(member.token()).toString();
var subject = t("[{}] Bitte bestätigen Sie Ihr Listen-Abonnement",name());
var subject = t("[{}] Please confirm your list subscription",name());
var text = template.add(URL,url).add(LIST_NAME,name()).render();
smtp.send(email(),name(),user.email(),subject,text);
} catch (UnsupportedEncodingException e) {
throw new MessagingException(t("Senden der Email an {} fehlgeschlagen",user.email()),e);
throw new MessagingException(t("Failed to send email to {}",user.email()),e);
}
}
@ -711,8 +711,8 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -711,8 +711,8 @@ public class MailingList implements MessageHandler, ProblemListener {
* @throws UnsupportedEncodingException
*/
public void test(User user) throws MessagingException, UnsupportedEncodingException {
var subject = t("[{}] Test-Mail",name());
var text = "Wenn Sie diese Nachricht empfangen haben, sind die SMTP-Einstellungen Ihrer Mailing-Liste korrekt.";
var subject = t("[{}]: test mail",name());
var text = "If you received this mail, the SMTP settings of your mailing list are correct.";
smtp.login().send(email(),name(),user.email(),subject,text);
}
@ -720,7 +720,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -720,7 +720,7 @@ public class MailingList implements MessageHandler, ProblemListener {
try {
return Arrays.asList(InternetAddress.parse(email)).stream();
} catch (AddressException e) {
LOG.debug("Parsen von {} fehlgeschlagen",email,e);
LOG.debug("Was not able to parse {}",email,e);
return new ArrayList<InternetAddress>().stream();
}
}

17
src/main/java/de/srsoftware/widerhall/data/Post.java

@ -96,6 +96,23 @@ public class Post { @@ -96,6 +96,23 @@ public class Post {
return new File(filename);
}
public static ArrayList<Post> find(MailingList list, String month) throws SQLException {
var rs = Database.open()
.select(TABLE_NAME,"*","strftime('%Y-%m',date/1000,'unixepoch') as month")
.where(LIST,list.email())
.where(MONTH,month)
.sort(DATE)
.compile()
.exec();
try {
var result = new ArrayList<Post>();
while (rs.next()) result.add(Post.from(rs));
return result;
} finally {
rs.close();
}
}
public static ArrayList<Post> find(MailingList list, String month, List<String> allowedSenders) throws SQLException {
var query = Database.open()
.select(TABLE_NAME,"*","strftime('%Y-%m',date/1000,'unixepoch') as month")

2
src/main/java/de/srsoftware/widerhall/data/User.java

@ -268,7 +268,7 @@ public class User { @@ -268,7 +268,7 @@ public class User {
* @return
*/
public Map<String,String> safeMap(){
return Map.of(NAME,name,EMAIL,email,PERMISSIONS,permissionList(),PASSWORD,hashedPassword() == null ? "nein" : "ja");
return Map.of(NAME,name,EMAIL,email,PERMISSIONS,permissionList(),PASSWORD,hashedPassword() == null ? "no" : "yes");
}
/**

130
src/main/java/de/srsoftware/widerhall/mail/ImapClient.java

@ -33,87 +33,92 @@ public class ImapClient { @@ -33,87 +33,92 @@ public class ImapClient {
return this;
}
public ListeningThread doStop() {
stopped = true;
return this;
}
public ListeningThread dropListeners() {
listeners.clear();
return this;
}
@Override
public void run() {
while (!stopped) {
try {
sleep(5000);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
private void handle(Message message) throws MessagingException {
LOG.debug("Handling {}",message.getSubject());
for (MessageHandler listener : listeners) listener.onMessageReceived(message);
}
try {
openInbox();
} catch (MessagingException e){
LOG.warn("Verbindung-Problem:",e);
problemListener.onImapException(e);
private void handleMessages() throws MessagingException {
LOG.debug("Reading email of {}:",username);
if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE);
for (Message message : inbox.getMessages()){
if (message.isSet(Flags.Flag.SEEN)) continue;
handle(message);
Folder folder = message.getFolder();
if (!folder.isOpen()) folder.open(Folder.READ_WRITE);
message.setFlag(Flags.Flag.SEEN,true);
}
}
private Properties imapProps() {
Properties props = new Properties();
props.put(Constants.PROTOCOL,Constants.IMAPS);
return props;
}
private void openInbox() throws MessagingException {
LOG.debug("Verbinden und Einloggen…");
LOG.debug("Connecting and logging in…");
Properties props = imapProps();
session = Session.getInstance(props);
Store store = session.getStore(Constants.IMAPS);
store.connect(host,username,password);
LOG.debug("Verbunden. Öffne {}:",folderName);
LOG.debug("Connected, opening {}:",folderName);
inbox = (IMAPFolder)store.getFolder(folderName);
inbox.open(IMAPFolder.READ_WRITE);
while (!stopped){
handleMessages();
problemListener.clearProblems();
LOG.debug("Warte.");
LOG.debug("Idling.");
inbox.idle(true);
}
}
private void handleMessages() throws MessagingException {
LOG.debug("Reading email of {}:",username);
if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE);
for (Message message : inbox.getMessages()){
if (message.isSet(Flags.Flag.SEEN)) continue;
handle(message);
Folder folder = message.getFolder();
if (!folder.isOpen()) folder.open(Folder.READ_WRITE);
message.setFlag(Flags.Flag.SEEN,true);
}
@Override
public void run() {
while (!stopped) {
try {
sleep(5000);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
private void handle(Message message) throws MessagingException {
LOG.debug("Verarbeite {}",message.getSubject());
for (MessageHandler listener : listeners) listener.onMessageReceived(message);
try {
openInbox();
} catch (MessagingException e){
LOG.warn("Connection problem:",e);
problemListener.onImapException(e);
}
private Properties imapProps() {
Properties props = new Properties();
props.put(Constants.PROTOCOL,Constants.IMAPS);
return props;
}
public ListeningThread doStop() {
stopped = true;
return this;
}
}
private class Heartbeat extends Thread{
private static final Logger LOG = LoggerFactory.getLogger(Heartbeat.class);
private boolean stopped = false;
public Heartbeat doStop() {
stopped = true;
return this;
}
@Override
public void run() {
while (!stopped){
try {
sleep(300034);
if (inbox != null) continue;
LOG.debug("Sende NOOP");
LOG.debug("sending NOOP");
inbox.doCommand(protocol -> {
protocol.simpleCommand("NOOP",null);
return null;
@ -123,11 +128,6 @@ public class ImapClient { @@ -123,11 +128,6 @@ public class ImapClient {
}
}
}
public Heartbeat doStop() {
stopped = true;
return this;
}
}
public ImapClient(String host, int port, String username, String password, String folderName,ProblemListener listener) {
@ -148,7 +148,7 @@ public class ImapClient { @@ -148,7 +148,7 @@ public class ImapClient {
public void dropMailsOlderThan(Integer holdTime) throws MessagingException {
var now = new Date();
if (holdTime == null) return;
LOG.info("Removing mails older than {} days:",holdTime);
LOG.debug("Removing mails older than {} days:",holdTime);
if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE);
for (Message message : inbox.getMessages()){
Date receivedDate = message.getReceivedDate();
@ -165,25 +165,13 @@ public class ImapClient { @@ -165,25 +165,13 @@ public class ImapClient {
inbox.expunge();
}
public String host(){
return host;
}
public String username(){
return username;
}
public String password(){
return password;
public String folderName(){
return folderName;
}
public int port(){
return port;
}
public String folderName(){
return folderName;
public String host(){
return host;
}
public ImapClient move(Message message, String destinationFolder) throws MessagingException {
@ -200,14 +188,22 @@ public class ImapClient { @@ -200,14 +188,22 @@ public class ImapClient {
return this;
}
public String password(){
return password;
}
public int port(){
return port;
}
public ImapClient start() {
stop();
LOG.info("Erzeuge ListeningThread für {}…",username);
LOG.info("Creating ListeningThread for {}…",username);
(listeningThread = new ListeningThread()).start();
LOG.info("Erzeuge Heartbeat für {}…",username);
LOG.info("Creating Heartbeat for {}…",username);
(heartbeat = new Heartbeat()).start();
return this;
@ -215,7 +211,7 @@ public class ImapClient { @@ -215,7 +211,7 @@ public class ImapClient {
public ImapClient stop(){
if (listeningThread != null) {
LOG.info("Stoppe alten ListeningThread für {}…",username);
LOG.info("Stopping old ListeningThread for {}…",username);
listeningThread.dropListeners().doStop();
listeningThread = null;
}
@ -225,4 +221,8 @@ public class ImapClient { @@ -225,4 +221,8 @@ public class ImapClient {
}
return this;
}
public String username(){
return username;
}
}

42
src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java

@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory; @@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory;
import javax.mail.*;
import javax.mail.internet.*;
import javax.ws.rs.HEAD;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;
@ -63,7 +62,7 @@ public class SmtpClient { @@ -63,7 +62,7 @@ public class SmtpClient {
MimeMultipart multipart = new MimeMultipart();
if (forwardAsAttachment){
MimeBodyPart bodyPart = new MimeBodyPart();
bodyPart.setText("Die weitergeleitete Nachricht findest du im Anhang dieser E-Mail!\n");
bodyPart.setText("Find the forwarded message in the attachment(s)!\n");
multipart.addBodyPart(bodyPart);
// create another body part to contain the message to be forwarded
@ -82,6 +81,9 @@ public class SmtpClient { @@ -82,6 +81,9 @@ public class SmtpClient {
send(forward);
}
public String host() {
return host;
}
public SmtpClient login(){
if (session == null) {
@ -93,11 +95,25 @@ public class SmtpClient { @@ -93,11 +95,25 @@ public class SmtpClient {
props.put(ENVELOPE_FROM,from);
session = Session.getInstance(props);
LOG.debug("Neue Session erzeugt: {}", session);
LOG.debug("Created new session: {}", session);
}
return this;
}
public String password() {
return password;
}
public int port() {
return port;
}
public void send(Message message) throws MessagingException {
LOG.debug("Versende Mail…");
Transport.send(message,username,password);
LOG.debug("…versendet");
}
public void send(String senderAdress, String senderName, String receivers, String subject, String content) throws MessagingException, UnsupportedEncodingException {
login();
MimeMessage message = new MimeMessage(session);
@ -114,27 +130,7 @@ public class SmtpClient { @@ -114,27 +130,7 @@ public class SmtpClient {
send(message);
}
public void send(Message message) throws MessagingException {
LOG.debug("Versende Mail…");
Transport.send(message,username,password);
LOG.debug("…versendet");
}
public String host() {
return host;
}
public int port() {
return port;
}
public String username() {
return username;
}
public String password() {
return password;
}
}

88
src/main/java/de/srsoftware/widerhall/web/Rest.java

@ -15,7 +15,6 @@ import javax.servlet.http.HttpServletRequest; @@ -15,7 +15,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
import java.time.Month;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -48,22 +47,21 @@ public class Rest extends HttpServlet { @@ -48,22 +47,21 @@ public class Rest extends HttpServlet {
private static final String SUCCESS = "success";
private Map addPermission(String userEmail, String permissions) {
if (userEmail == null || userEmail.isBlank()) return Map.of(ERROR,"E-Mail-Adresse des Listenmitglieds nicht angegeben!");
if (userEmail == null || userEmail.isBlank()) return Map.of(ERROR,"missing user email address!");
try {
int perm = Integer.parseInt(permissions);
var user = User.loadAll(List.of(userEmail)).stream().findAny().orElse(null);
if (user == null) return Map.of(ERROR,t("Laden des Nutzers für die Adresse {} fehlgeschlagen",userEmail));
if (user == null) return Map.of(ERROR,t("Failed to load user for address {}",userEmail));
user.addPermission(perm);
} catch (NumberFormatException nfe){
return Map.of(ERROR,"Keine gültigen Berechtigungen übergeben!");
return Map.of(ERROR,"no valid permissions provided!");
} catch (SQLException e) {
LOG.debug("Laden des Nutzers für die Adresse {} fehlgeschlagen",userEmail,e);
return Map.of(ERROR,t("Laden des Nutzers für die Adresse {} fehlgeschlagen",userEmail));
LOG.debug("Failed to load user for address {}",userEmail,e);
return Map.of(ERROR,t("Failed to load user for address {}",userEmail));
}
return Map.of(SUCCESS,"Nutzer-Berechtigungen aktualisiert");
return Map.of(SUCCESS,"Updated user permissions");
}
private Map<String,Object> archive(HttpServletRequest req, User user) throws SQLException {
var list = Util.getMailingList(req);
if (list == null) throw new IllegalArgumentException(t("You are trying to access a non-existing list!"));
@ -100,7 +98,7 @@ public class Rest extends HttpServlet { @@ -100,7 +98,7 @@ public class Rest extends HttpServlet {
} catch (SQLException e) {
LOG.warn("Was not able to load listmember for {}/{}",list.email(),user.email(),e);
}
if (!allowed) return Map.of(ERROR,"Es ist dir nicht gestattet, diese Liste zu löschen!");
if (!allowed) return Map.of(ERROR,"You are not allowed to remove this list!");
try {
list.hide(true).enable(false).openForGuests(false).openForSubscribers(false);
list.members().forEach(listMember -> {
@ -114,7 +112,7 @@ public class Rest extends HttpServlet { @@ -114,7 +112,7 @@ public class Rest extends HttpServlet {
} catch (Exception e) {
LOG.debug("Disabling and hiding of {} failed",list.email(),e);
}
return Map.of(SUCCESS,t("Liste {} deaktiviert, Abonnement gesperrt, Liste de-publiziert. Mitglieder wurden entfernt.",list.email()));
return Map.of(SUCCESS,t("List {} disabled, closed for subscribers and hidden. Members have been removed.",list.email()));
}
@Override
@ -139,30 +137,30 @@ public class Rest extends HttpServlet { @@ -139,30 +137,30 @@ public class Rest extends HttpServlet {
}
private Map dropPermission(String userEmail, String permissions) {
if (userEmail == null || userEmail.isBlank()) return Map.of(ERROR,"Nutzer-Emailadresse fehlt!");
if (userEmail == null || userEmail.isBlank()) return Map.of(ERROR,"missing user email address!");
try {
int perm = Integer.parseInt(permissions);
var user = User.loadAll(List.of(userEmail)).stream().findAny().orElse(null);
if (user == null) return Map.of(ERROR,t("Laden des Nutzers für die Adresse {} fehlgeschlagen",userEmail));
if (user == null) return Map.of(ERROR,t("Failed to load user for address {}",userEmail));
user.dropPermission(perm);
} catch (NumberFormatException nfe){
return Map.of(ERROR,"Keine gültigen Berechtigungen übergeben!");
return Map.of(ERROR,"no valid permissions provided!");
} catch (SQLException e) {
LOG.debug("Laden des Nutzers für die Adresse {} fehlgeschlagen",userEmail,e);
return Map.of(ERROR,t("Laden des Nutzers für die Adresse {} fehlgeschlagen",userEmail));
LOG.debug("Failed to load user for address {}",userEmail,e);
return Map.of(ERROR,t("Failed to load user for address {}",userEmail));
}
return Map.of(SUCCESS,"Nutzer-Berechtigungen aktualisiert");
return Map.of(SUCCESS,t("Updated user permissions"));
}
private Map enableList(MailingList list, User user, boolean enable) {
if (list == null) return Map.of(ERROR,"Keine Listen-Email übertragen!");
if (!list.mayBeAlteredBy(user)) Map.of(ERROR,t("Du bist nicht berechtigt, '{}' zu bearbeiten!",list.email()));
if (list == null) return Map.of(ERROR,"no list email provided!");
if (!list.mayBeAlteredBy(user)) Map.of(ERROR,t("You are not allowed to edit '{}'",list.email()));
try {
list.enable(enable);
return Map.of(SUCCESS,t("Mailing-Liste '{}' wurde {}!",list.email(),enable ? "aktiviert" : "inaktiviert"));
return Map.of(SUCCESS,t("Mailing list '{}' was {}!",list.email(),enable ? "enabled" : "disabled"));
} catch (SQLException e) {
LOG.error("Aktivieren/Inaktivieren der Mailing-Liste fehlgeschlagen: ",e);
return Map.of(ERROR,t("Aktualisieren der Liste '{}' fehlgeschlagen",list.email()));
LOG.error("Failed to enable/disable mailing list: ",e);
return Map.of(ERROR,t("Failed to update list '{}'",list.email()));
}
}
@ -182,8 +180,8 @@ public class Rest extends HttpServlet { @@ -182,8 +180,8 @@ public class Rest extends HttpServlet {
try {
json.put("users", (user.hashPermission(User.PERMISSION_ADMIN) ? User.loadAll() : List.of(user)).stream().map(User::safeMap).toList());
} catch (SQLException e) {
LOG.debug("Laden der Nutzerliste fehlgeschlagen:",e);
json.put(ERROR,"Laden der Nutzerliste fehlgeschlagen");
LOG.debug("Failed to load user list:",e);
json.put(ERROR,"failed to load user list");
}
break;
case LIST_MODERATED:
@ -193,7 +191,7 @@ public class Rest extends HttpServlet { @@ -193,7 +191,7 @@ public class Rest extends HttpServlet {
json.put("lists", MailingList.subscribable(user).stream().map(MailingList::minimalMap).toList());
break;
default:
json.put(ERROR,t("Kein Handler für den Pfad '{}'!",path));
json.put(ERROR,t("No handler for path '{}'!",path));
break;
}
} else {
@ -205,7 +203,7 @@ public class Rest extends HttpServlet { @@ -205,7 +203,7 @@ public class Rest extends HttpServlet {
json.put("lists", MailingList.subscribable().stream().map(MailingList::minimalMap).toList());
break;
default:
json.put(ERROR,"Nicht eingeloggt!");
json.put(ERROR,"Not logged in!");
}
}
try {
@ -213,7 +211,7 @@ public class Rest extends HttpServlet { @@ -213,7 +211,7 @@ public class Rest extends HttpServlet {
resp.getWriter().println(json.toJSONString());
return null;
} catch (IOException e) {
return t("Konnte Anfrage nicht verarbeiten: {}",e.getMessage());
return t("Failed to handle request: {}",e.getMessage());
}
}
@ -272,38 +270,38 @@ public class Rest extends HttpServlet { @@ -272,38 +270,38 @@ public class Rest extends HttpServlet {
case USER_ADD_PERMISSION:
if (user.hashPermission(User.PERMISSION_ADMIN)){
json.putAll(addPermission(userEmail,permissions));
} else json.put(ERROR,"Sie haben nicht die Berechtigung, um Berechtigungen zu ändern!");
} else json.put(ERROR,"You are not allowed to alter user permissions!");
break;
case USER_DROP_PERMISSION:
if (user.hashPermission(User.PERMISSION_ADMIN)){
json.putAll(dropPermission(userEmail,permissions));
} else json.put(ERROR,"Sie haben nicht die Berechtigung, um Berechtigungen zu ändern!");
} else json.put(ERROR,"You are not allowed to alter user permissions!");
break;
default:
json.put(ERROR,t("Kein Handler für den Pfad '{}'!",path));
json.put(ERROR,t("No handler for path '{}'!",path));
break;
}
} else {
json.put(ERROR,"Nicht eingeloggt!");
json.put(ERROR,"Not logged in!");
}
try {
resp.setContentType("application/json");
resp.getWriter().println(json.toJSONString());
return null;
} catch (IOException e) {
return t("Konnte Anfrage nicht verarbeiten: {}",e.getMessage());
return t("Failed to handle request: {}",e.getMessage());
}
}
private Map<String, String> hideList(MailingList list, User user, boolean hide) {
if (list == null) return Map.of(ERROR,"Keine Listen-Email übertragen!");
if (list == null) return Map.of(ERROR,"no list email provided!");
if (!list.mayBeAlteredBy(user)) Map.of(ERROR,t("You are not allowed to edit '{}'",list.email()));
try {
list.hide(hide);
return Map.of(SUCCESS,t("Mailing-Liste '{}' wurde {}!",list.email(),hide ? "versteckt" : "veröffentlicht"));
return Map.of(SUCCESS,t("Mailing list '{}' was {}!",list.email(),hide ? "hidden" : "made public"));
} catch (SQLException e) {
LOG.error("Veröffentlichen/Verstekcen der Mailing-Liste fehlgeschlagen: ",e);
return Map.of(ERROR,t("Aktualisieren der Liste '{}' fehlgeschlagen",list.email()));
LOG.error("Failed to (un)hide mailing list: ",e);
return Map.of(ERROR,t("Failed to update list '{}'",list.email()));
}
}
@ -323,7 +321,7 @@ public class Rest extends HttpServlet { @@ -323,7 +321,7 @@ public class Rest extends HttpServlet {
}
private Map listDetail(MailingList list, User user) {
if (list == null) return Map.of(ERROR,"Keine Listen-Email übertragen!");
if (list == null) return Map.of(ERROR,"no list email provided!");
var map = new HashMap<>();
if (list.hasState(MailingList.STATE_FORWARD_FROM)) map.put(KEY_FORWARD_FROM,true);
if (list.hasState(MailingList.STATE_FORWARD_ATTACHED)) map.put(KEY_FORWARD_ATTACHED,true);
@ -368,8 +366,8 @@ public class Rest extends HttpServlet { @@ -368,8 +366,8 @@ public class Rest extends HttpServlet {
}
private Map<String, Object> listMembers(MailingList list, User user) {
if (list == null) return Map.of(ERROR,"Keine Listen-Email übertragen!");
if (!list.membersMayBeListedBy(user)) Map.of(ERROR,t("Es ist dir nicht gestattet, die Mitglieder von '{}' aufzulisten",list.email()));
if (list == null) return Map.of(ERROR,"no list email provided!");
if (!list.membersMayBeListedBy(user)) Map.of(ERROR,t("You are not allowed to list members of '{}'",list.email()));
try {
var members = list.members()
.sorted((m1,m2)->m1.user().name().compareTo(m2.user().name()))
@ -377,20 +375,20 @@ public class Rest extends HttpServlet { @@ -377,20 +375,20 @@ public class Rest extends HttpServlet {
.toList();
return Map.of(MEMBERS,members,LIST,list.minimalMap());
} catch (SQLException e) {
LOG.error("Laden der Mitglieder-Liste fehlgeschlagen: ",e);
return Map.of(ERROR,t("Laden der Mitglieder-Liste von '{}' fehlgeschlagen!",list.email()));
LOG.error("Failed to load member list: ",e);
return Map.of(ERROR,t("Failed to load member list '{}'",list.email()));
}
}
private Map testList(MailingList list, User user) {
if (list == null) return Map.of(ERROR,"Keine Listen-Email übertragen!");
if (!list.mayBeTestedBy(user)) Map.of(ERROR,t("Es ist dir nicht gestattet, '{}' zu testen",list.email()));
if (list == null) return Map.of(ERROR,"no list email provided!");
if (!list.mayBeTestedBy(user)) Map.of(ERROR,t("You are not allowed to test '{}'",list.email()));
try {
list.test(user);
return Map.of(SUCCESS,t("Test-Email an {} versendet",user.email()));
return Map.of(SUCCESS,t("Sent test email to {}",user.email()));
} catch (Exception e) {
LOG.warn("Senden der Test-Email fehlgeschlagen",e);
return Map.of(ERROR,t("Senden der Test-Email an {} fehlgeschlagen",user.email()));
LOG.warn("Failed to send test email",e);
return Map.of(ERROR,t("Failed to send test email to {}",user.email()));
}
}
}

8
src/main/java/de/srsoftware/widerhall/web/TemplateServlet.java

@ -31,14 +31,14 @@ public abstract class TemplateServlet extends HttpServlet { @@ -31,14 +31,14 @@ public abstract class TemplateServlet extends HttpServlet {
protected String loadFile(String filename, HttpServletResponse resp) {
var path = String.join(File.separator,baseDir,"static",filename);
var file = new File(path);
if (!file.exists()) return t("Datei {} existiert nicht!",filename);
if (!file.exists()) return t("File {} does not exist!",filename);
try {
var content = Files.readAllBytes(file.toPath());
var out = resp.getOutputStream();
out.write(content);
out.flush();
} catch (IOException e) {
return t("Ladend der Datei '{}' fehlgeschlagen!",filename);
return t("Failed to load file '{}'!",filename);
}
return null;
}
@ -52,10 +52,10 @@ public abstract class TemplateServlet extends HttpServlet { @@ -52,10 +52,10 @@ public abstract class TemplateServlet extends HttpServlet {
resp.getWriter().println(template.render());
return null;
} catch (IOException e) {
return t("Laden der Vorlage '{}' fehlgeschlagen",path);
return t("Failed to load template '{}'",path);
}
}
return t("Keine Vorlage für den Pfad '{}' vorhanden!",path);
return t("No template for path '{}'!",path);
}
protected void loadTemplates() {

126
src/main/java/de/srsoftware/widerhall/web/Web.java

@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory; @@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.HEAD;
import java.io.IOException;
import java.nio.file.Files;
import java.security.InvalidKeyException;
@ -57,7 +56,7 @@ public class Web extends TemplateServlet { @@ -57,7 +56,7 @@ public class Web extends TemplateServlet {
data.put(USER, user.safeMap());
if (!user.hashPermission(User.PERMISSION_CREATE_LISTS)){
data.put(ERROR,t("Ihnen ist es nicht gestattet, neue Mailinglisten anzulegen!"));
data.put(ERROR,t("You are not allowed to create new mailing lists!"));
return loadTemplate(ADMIN,data,resp);
}
@ -89,17 +88,17 @@ public class Web extends TemplateServlet { @@ -89,17 +88,17 @@ public class Web extends TemplateServlet {
data.put(SMTP_PORT, smtpPort);
if (name == null || name.isBlank() || email == null || email.isBlank()) {
data.put(ERROR, "Name und Adresse der Liste sind notwendige Felder!");
data.put(ERROR, "List name and address are required!");
return loadTemplate(ADD_LIST, data, resp);
}
if (!Util.isEmail(email)) {
data.put(ERROR, t("Listen-E-Mail-Adresse ({}) ist keine gültige E-Mail-Adresse!", email));
data.put(ERROR, t("List email ({}) is not a valid email address!", email));
return loadTemplate(ADD_LIST, data, resp);
}
if (imapHost == null || imapHost.isBlank() || imapUser == null || imapUser.isBlank() || imapPass == null || imapPass.isBlank()) {
data.put(ERROR, "IMAP-Zugangsdaten sind erforderlich!");
data.put(ERROR, "IMAP credentials are required!");
return loadTemplate(ADD_LIST, data, resp);
}
@ -108,12 +107,12 @@ public class Web extends TemplateServlet { @@ -108,12 +107,12 @@ public class Web extends TemplateServlet {
imapPort = Integer.parseInt(req.getParameter(IMAP_PORT));
data.put(IMAP_PORT, imapPort);
} catch (NumberFormatException nfe) {
data.put(ERROR, t("'{}' ist keine gültige Port-Nummer!", req.getParameter(IMAP_PORT)));
data.put(ERROR, t("'{}' is not a proper port number!", req.getParameter(IMAP_PORT)));
return loadTemplate(ADD_LIST, data, resp);
}
if (smtpHost == null || smtpHost.isBlank() || smtpUser == null || smtpUser.isBlank() || smtpPass == null || smtpPass.isBlank()) {
data.put(ERROR, "SMTP-Zugangsdaten sind erforderlich!");
data.put(ERROR, "SMTP credentials are required!");
return loadTemplate(ADD_LIST, data, resp);
}
@ -121,7 +120,7 @@ public class Web extends TemplateServlet { @@ -121,7 +120,7 @@ public class Web extends TemplateServlet {
smtpPort = Integer.parseInt(req.getParameter(SMTP_PORT));
data.put(SMTP_PORT, smtpPort);
} catch (NumberFormatException nfe) {
data.put(ERROR, t("'{}' ist keine gültige Port-Nummer!", req.getParameter(SMTP_PORT)));
data.put(ERROR, t("'{}' is not a proper port number!", req.getParameter(SMTP_PORT)));
return loadTemplate(ADD_LIST, data, resp);
}
@ -130,7 +129,7 @@ public class Web extends TemplateServlet { @@ -130,7 +129,7 @@ public class Web extends TemplateServlet {
ListMember.create(list, user, ListMember.STATE_OWNER);
return redirectTo(ADMIN, resp);
} catch (SQLException e) {
return t("Erzeugen der Liste '{}' fehlgeschlagen: {}", name, e.getMessage());
return t("Failed to create list '{}': {}", name, e.getMessage());
}
}
@ -154,17 +153,17 @@ public class Web extends TemplateServlet { @@ -154,17 +153,17 @@ public class Web extends TemplateServlet {
private String confirm(HttpServletRequest req, HttpServletResponse resp) {
try {
var token = req.getParameter(TOKEN);
if (token== null || token.isBlank()) return t("Ungültiger oder fehlender Token!");
if (token== null || token.isBlank()) return t("Invalid or missing token!");
var listMember = ListMember.confirm(token);
if (listMember != null) {
listMember.sendConfirmationMail(getTemplate("confirmation_mail"));
return loadTemplate(INDEX,Map.of(USER,listMember.user().safeMap(),NOTES,"Listen-Mitgliedschaft bestätigt!"),resp);
return loadTemplate(INDEX,Map.of(USER,listMember.user().safeMap(),NOTES,"Confirmed list subscription!"),resp);
}
return t("Nutzer oder Token unbekannt");
return t("Unknown user or token");
} catch (Exception e) {
LOG.debug("Bestätigung des Listen-Abonnements fehlgeschlagen:",e);
return t("Bestätigung des Listen-Abonnements fehlgeschlagen!");
LOG.debug("Failed to confirm list membership:",e);
return t("Confirmation of list membership failed!");
}
}
@ -221,17 +220,17 @@ public class Web extends TemplateServlet { @@ -221,17 +220,17 @@ public class Web extends TemplateServlet {
data.put(SMTP_PORT, smtpPort);
if (name == null || name.isBlank() || email == null || email.isBlank()) {
data.put(ERROR, "Listen-Name und -adresse sind erforderlich!");
data.put(ERROR, "List name and address are required!");
return loadTemplate(EDIT_LIST, data, resp);
}
if (!Util.isEmail(email)) {
data.put(ERROR, t("Listen-E-Mail ({}) ist keine gültige Mailadresse!", email));
data.put(ERROR, t("List email ({}) is not a valid email address!", email));
return loadTemplate(EDIT_LIST, data, resp);
}
if (imapHost == null || imapHost.isBlank() || imapUser == null || imapUser.isBlank() || imapPass == null || imapPass.isBlank()) {
data.put(ERROR, "IMAP-Zugangsdaten sind erforderlich!");
data.put(ERROR, "IMAP credentials are required!");
return loadTemplate(EDIT_LIST, data, resp);
}
@ -240,12 +239,12 @@ public class Web extends TemplateServlet { @@ -240,12 +239,12 @@ public class Web extends TemplateServlet {
imapPort = Integer.parseInt(req.getParameter(IMAP_PORT));
data.put(IMAP_PORT, imapPort);
} catch (NumberFormatException nfe) {
data.put(ERROR, t("'{}' ist keine gültige Port-Nummer!", req.getParameter(IMAP_PORT)));
data.put(ERROR, t("'{}' is not a proper port number!", req.getParameter(IMAP_PORT)));
return loadTemplate(EDIT_LIST, data, resp);
}
if (smtpHost == null || smtpHost.isBlank() || smtpUser == null || smtpUser.isBlank() || smtpPass == null || smtpPass.isBlank()) {
data.put(ERROR, "SMTP-Zugangsdaten sind erforderlich!");
data.put(ERROR, "SMTP credentials are required!");
return loadTemplate(EDIT_LIST, data, resp);
}
@ -253,7 +252,7 @@ public class Web extends TemplateServlet { @@ -253,7 +252,7 @@ public class Web extends TemplateServlet {
smtpPort = Integer.parseInt(req.getParameter(SMTP_PORT));
data.put(SMTP_PORT, smtpPort);
} catch (NumberFormatException nfe) {
data.put(ERROR, t("'{}' ist keine gültige Port-Nummer!", req.getParameter(SMTP_PORT)));
data.put(ERROR, t("'{}' is not a proper port number!", req.getParameter(SMTP_PORT)));
return loadTemplate(EDIT_LIST, data, resp);
}
@ -261,7 +260,7 @@ public class Web extends TemplateServlet { @@ -261,7 +260,7 @@ public class Web extends TemplateServlet {
return loadTemplate(ADMIN,data,resp);
} catch (SQLException e) {
LOG.warn("Editing list {} by {} failed",list.email(),user.email(),e);
return t("Bearbeiten der Liste {} durch {} fehlgeschlagen",list.email(),user.email());
return t("Editing list {} by {} failed",list.email(),user.email());
}
}
@ -295,7 +294,7 @@ public class Web extends TemplateServlet { @@ -295,7 +294,7 @@ public class Web extends TemplateServlet {
return post(req,resp);
case RELOAD:
loadTemplates();
data.put(NOTES,t("Vorlagen wurden neu geladen"));
data.put(NOTES,t("Templates have been reloaded"));
path = INDEX;
case CSS:
case INDEX:
@ -305,16 +304,16 @@ public class Web extends TemplateServlet { @@ -305,16 +304,16 @@ public class Web extends TemplateServlet {
data.put(LIST,list.email());
return loadTemplate(path, data, resp);
}
return t("Es ist ihnen nicht gestattet, '{}' zu abonnieren!",list.email());
return t("You are not allowed to subscribe to '{}'!",list.email());
case "js":
resp.setContentType("text/javascript");
return loadTemplate(path,data,resp);
case LOGIN:
try {
if (User.noUsers()) return loadTemplate(REGISTER, Map.of(NOTES,t("Nutzer-Datenbank ist leer. Admin-Nutzer wird hiermit angelegt:")), resp);
if (User.noUsers()) return loadTemplate(REGISTER, Map.of(NOTES,t("User database is empty. Create admin user first:")), resp);
return loadTemplate(path,null,resp);
} catch (SQLException e) {
return "Fehler beim Lesen der Datenbank!";
return "Error reading user database!";
}
case LOGOUT:
req.getSession().invalidate();
@ -347,8 +346,8 @@ public class Web extends TemplateServlet { @@ -347,8 +346,8 @@ public class Web extends TemplateServlet {
private String handleLogin(HttpServletRequest req, HttpServletResponse resp) {
var email = req.getParameter("email");
var pass = req.getParameter("pass");
if (email == null || pass == null) return loadTemplate("login", Map.of("error",t("Nutzername oder Passwort fehlen!")), resp);
if (!Util.isEmail(email)) return loadTemplate("login", Map.of("error",t("'{}' ist keine gültige Mailadresse!",email)), resp);
if (email == null || pass == null) return loadTemplate("login", Map.of("error",t("Missing username or password!")), resp);
if (!Util.isEmail(email)) return loadTemplate("login", Map.of("error",t("'{}' is not a valid email address!",email)), resp);
try {
var user = User.loadUser(email,pass);
req.getSession().setAttribute("user",user);
@ -356,10 +355,10 @@ public class Web extends TemplateServlet { @@ -356,10 +355,10 @@ public class Web extends TemplateServlet {
resp.sendRedirect(String.join("/",WEB_ROOT,"admin"));
} catch (Exception e) {
try {
LOG.warn("Static.handleLogin fehlgeschlagen:",e);
LOG.warn("Static.handleLogin failed:",e);
Thread.sleep(10000);
} finally {
return loadTemplate("login", Map.of(ERROR,t("Ungültiger Nutzername oder ungültiges Passwort!"),EMAIL,email), resp);
return loadTemplate("login", Map.of(ERROR,t("Invalid username/password"),EMAIL,email), resp);
}
}
return null;
@ -385,7 +384,7 @@ public class Web extends TemplateServlet { @@ -385,7 +384,7 @@ public class Web extends TemplateServlet {
return unsubscribe(req,resp);
}
return t("Kein Handler für den Pfad '{}'!",path);
return t("No handler for path {}!",path);
}
private String inspect(HttpServletRequest req, HttpServletResponse resp) {
@ -398,12 +397,12 @@ public class Web extends TemplateServlet { @@ -398,12 +397,12 @@ public class Web extends TemplateServlet {
var list = Util.getMailingList(req);
if (list == null) {
error = true;
data.put(ERROR, t("Keine gültige MailingListe übermittelt!"));
data.put(ERROR, t("No valid mailing list provided!"));
} else data.put(LIST, list.email());
if (!error && !list.mayBeAlteredBy(user)) {
error = true;
data.put(ERROR,t("Es ist Ihnen nicht gestattet, die Einselltungen dieser Mailingliste zu verändern!"));
data.put(ERROR,t("You are not alter settings of this list!"));
}
if (!error){
@ -417,10 +416,10 @@ public class Web extends TemplateServlet { @@ -417,10 +416,10 @@ public class Web extends TemplateServlet {
.openForSubscribers(Util.getCheckbox(req,KEY_OPEN_FOR_SUBSCRIBERS))
.archive(Util.getCheckbox(req,KEY_ARCHIVE))
.deleteMessages(Util.getCheckbox(req,KEY_DELETE_MESSAGES),req.getParameter(HOLD_TIME));
data.put(NOTES,t("Mailing-Liste aktualisiert!"));
data.put(NOTES,t("Sucessfully updated MailingList!"));
} catch (SQLException e){
LOG.warn("Aktualisierung der Mailing-Liste fehlgeschlagen:",e);
data.put(ERROR,t("Aktualisierung der Mailing-Liste fehlgeschlagen!"));
LOG.warn("Failed to update MailingList:",e);
data.put(ERROR,t("Failed to update MailingList!"));
}
}
@ -439,7 +438,7 @@ public class Web extends TemplateServlet { @@ -439,7 +438,7 @@ public class Web extends TemplateServlet {
return loadTemplate("post",map,resp);
} catch (SQLException | IOException e) {
LOG.debug("Failed to load post from file!",e);
return t("Laden der Nachricht aus Datei fehlgeschlagen!");
return t("Failed to load post from file!");
}
}
@ -448,7 +447,7 @@ public class Web extends TemplateServlet { @@ -448,7 +447,7 @@ public class Web extends TemplateServlet {
resp.sendRedirect(String.join("/",WEB_ROOT,page));
return null;
} catch (IOException e) {
return t("Weiterleitung nach {} fehlgeschlagen: {}", page, e.getMessage());
return t("Was not able to redirect to {} page: {}", page, e.getMessage());
}
}
@ -464,15 +463,15 @@ public class Web extends TemplateServlet { @@ -464,15 +463,15 @@ public class Web extends TemplateServlet {
if (email == null || email.isBlank() ||
name == null || name.isBlank() ||
pass == null || pass.isBlank() ||
pass_repeat == null || pass_repeat.isBlank()) return loadTemplate(REGISTER,Map.of(ERROR,"Bitte alle Felder ausfüllen!",NAME,name,EMAIL,email),resp);
if (!pass.equals(pass_repeat)) return loadTemplate(REGISTER,Map.of(ERROR,"Passworte stimmen nicht überein!",NAME,name,EMAIL,email),resp);
if (Util.simplePassword(pass)) return loadTemplate(REGISTER,Map.of(ERROR,"Passwort zu kurz oder zu einfach!",NAME,name,EMAIL,email),resp);
pass_repeat == null || pass_repeat.isBlank()) return loadTemplate(REGISTER,Map.of(ERROR,"Fill all fields, please!",NAME,name,EMAIL,email),resp);
if (!pass.equals(pass_repeat)) return loadTemplate(REGISTER,Map.of(ERROR,"Passwords do not match!",NAME,name,EMAIL,email),resp);
if (Util.simplePassword(pass)) return loadTemplate(REGISTER,Map.of(ERROR,"Password to short or to simple!",NAME,name,EMAIL,email),resp);
var firstUser = false;
try {
firstUser = User.noUsers();
} catch (SQLException e) {
return t("Fehler beim Zugriff auf die Nutzer-Datenbank: {}",e.getMessage());
return t("Failed to access user database: {}",e.getMessage());
}
@ -482,8 +481,8 @@ public class Web extends TemplateServlet { @@ -482,8 +481,8 @@ public class Web extends TemplateServlet {
req.getSession().setAttribute("user",user);
return redirectTo(INDEX,resp);
} catch (SQLException e) {
LOG.warn("Erzeugen des neuen Nutzers fehlgeschlagen:",e);
return t("Erzeugen des neuen Nutzers fehlgeschlagen: {}",e.getMessage());
LOG.warn("Failed to create new user:",e);
return t("Failed to create new user: {}",e.getMessage());
}
}
@ -500,12 +499,12 @@ public class Web extends TemplateServlet { @@ -500,12 +499,12 @@ public class Web extends TemplateServlet {
if (list == null){
data.put(ERROR,"Formular-Daten enthalten keine Liste!");
data.put(ERROR,"No list provided by form data!");
return loadTemplate(SUBSCRIBE,data,resp);
}
if (name == null || name.isBlank() || email == null || email.isBlank()){
data.put(ERROR,"Name und E-Mail-Adresse sind für das Abonnieren der Mailingliste erforderlich!");
data.put(ERROR,"Name and email are required fields for list subscription!");
return loadTemplate(SUBSCRIBE,data,resp);
}
if (pass != null && pass.isBlank()) pass = null;
@ -522,36 +521,36 @@ public class Web extends TemplateServlet { @@ -522,36 +521,36 @@ public class Web extends TemplateServlet {
// success → subscribe
} catch (InvalidKeyException | SQLException e) {
// invalid credentials
data.put(ERROR,t("'{}' gibt es schon in der Datenbank, hat dort aber ein anderes Passwort!",email));
data.put(ERROR,t("'{}' already in database, but with different password!",email));
return loadTemplate(SUBSCRIBE,data,resp);
}
}
data.put(USER,user.safeMap());
if (!list.isOpenFor(user)){
data.put(ERROR,t("Ihnen ist es nicht gestattet, '{}' zu abonnieren!",list.email()));
data.put(ERROR,t("You are not allowed to join {}!",list.email()));
return loadTemplate(SUBSCRIBE,data,resp);
}
try {
list.requestSubscription(user,skipConfirmation,getTemplate("subscribe_mail"));
if (skipConfirmation) {
data.put(NOTES, t("'{}' hat die Mailingliste '{}' erfolgreich abonniert.", user.email(), list.email()));
data.put(NOTES, t("Successfully subscribed '{}' to '{}'.", user.email(), list.email()));
} else {
data.put(NOTES, t("Bestätigungs-Email wurde an '{} versendet.", user.email()));
data.put(NOTES, t("Sent confirmation mail to '{}.", user.email()));
}
return loadTemplate(INDEX,data,resp);
} catch (SQLException sqle) {
LOG.debug("Abonnieren der Liste fehlgeschlagen: ",sqle);
LOG.debug("List subscription failed: ",sqle);
var cause = getCausingException(sqle);
int code = cause.getErrorCode();
if (code == PRIMARY_KEY_CONSTRAINT) {// user already exists
data.put(ERROR,t("Sie haben diese Liste bereits abonniert!",sqle.getMessage()));
} else data.put(ERROR,t("Abonnieren der Liste fehlgeschlagen: {}",sqle.getMessage()));
data.put(ERROR,t("You already are member of this list!",sqle.getMessage()));
} else data.put(ERROR,t("Subscription failed: {}",sqle.getMessage()));
return loadTemplate(SUBSCRIBE,data,resp);
} catch (MessagingException e) {
LOG.warn("Senden der Bestätigungs-Email fehlgeschlagen:",e);
data.put(ERROR,t("Senden der Bestätigungs-Email fehlgeschlagen: {}",e.getMessage()));
LOG.warn("Failed to send request confirmation email:",e);
data.put(ERROR,t("Failed to send request confirmation email: {}",e.getMessage()));
return loadTemplate(SUBSCRIBE,data,resp);
}
}
@ -566,12 +565,12 @@ public class Web extends TemplateServlet { @@ -566,12 +565,12 @@ public class Web extends TemplateServlet {
data.put(EMAIL,email);
if (user != null) data.put(USER,user.safeMap());
if (list == null){
data.put(ERROR,"Keine Mailin-Liste in den Formular-Daten übermittelt!!");
data.put(ERROR,"No list provided by form data!");
return loadTemplate(UNSUBSCRIBE,data,resp);
} else data.put(LIST,list.email());
if (user == null) {
if (email == null || email.isBlank()) {
data.put(ERROR, "Für das Abbestellen ist eine E-Mail-Adresse erforderlich!");
data.put(ERROR, "Email is required for list un-subscription!");
return loadTemplate(UNSUBSCRIBE, data, resp);
}
var pass = req.getParameter(PASSWORD);
@ -582,7 +581,7 @@ public class Web extends TemplateServlet { @@ -582,7 +581,7 @@ public class Web extends TemplateServlet {
req.getSession().setAttribute(USER,user);
data.put(USER,user.safeMap());
} catch (InvalidKeyException | SQLException e) {
data.put(ERROR,"Ungültige E-Mail-/Passwort-Kombination!");
data.put(ERROR,"Invalid email/password combination!");
return loadTemplate(UNSUBSCRIBE,data,resp);
}
}
@ -591,23 +590,22 @@ public class Web extends TemplateServlet { @@ -591,23 +590,22 @@ public class Web extends TemplateServlet {
try {
member = ListMember.load(list,user);
} catch (SQLException e) {
LOG.debug("Laden des Listenmitglieds für {}/{} fehlgeschlagen",user.email(),list.email(),e);
data.put(ERROR, t("Laden des Listenmitglieds für {}/{} fehlgeschlagen",user.email(),list.email()));
LOG.debug("Failed to load list member for {}/{}",user.email(),list.email(),e);
data.put(ERROR, t("Failed to load list member for {}/{}",user.email(),list.email()));
return loadTemplate(UNSUBSCRIBE,data,resp);
}
if (member == null){
data.put(ERROR, t("{} ist kein Mitglied von {}",user.email(),list.email()));
data.put(ERROR, t("{} is no member of {}",user.email(),list.email()));
return loadTemplate(UNSUBSCRIBE,data,resp);
}
// if we get here, we should have a valid member object
try {
member.unsubscribe();
data.put(NOTES,t("'{}' erfolgreich abbestellt.",list.email()));
data.put(NOTES,t("Sucessfully un-subscribed from '{}'.",list.email()));
return loadTemplate(INDEX,data,resp);
} catch (SQLException e) {
LOG.warn("Es ist ein Problem beim Entfernen von {} aus der Liste {} aufgetreten:",user.email(),list.email(),e);
data.put(ERROR,"Abbestellen der Mailin-Liste fehlgeschlagen!");
LOG.warn("Problem during unscubsription of {} from {}:",user.email(),list.email(),e);
data.put(ERROR,"Failed to unsubscribe!");
return loadTemplate(UNSUBSCRIBE,data,resp);
}

103
src/main/resources/Application.de.translation

@ -1,9 +1,112 @@ @@ -1,9 +1,112 @@
[{}] Please confirm your list subscription : [{}] Bitte bestätigen Sie Ihr Listen-Abonnement
[{}] Subscription complete! : [{}] Abonnement abgeschlossen!
[{}]\: test mail : [{}] Test-Mail
'{}' already in database, but with different password! : '{}' gibt es schon in der Datenbank, hat dort aber ein anderes Passwort!
{} is no member of {} : {} ist nicht Mitglied von {}!
{} is not a member of {} : {} ist nicht Mitglied von {}!
'{}' is not a proper port number! : '{}' ist keine gültige Port-Nummer!
'{}' is not a valid email address! : '{}' ist keine gültige Mailadresse!
{} is now a moderator of {} : {} ist nun ein Moderator der Liste {}
archive : Archiv
awaiting confirmation : erwartet Bestätigung
Confirmation of list membership failed! : Bestätigung des Listen-Abonnements fehlgeschlagen!
Confirmed list subscription! : Listen-Mitgliedschaft bestätigt!
enabled : aktiviert
disabled : deaktiviert
Editing list {} by {} failed : Bearbeiten der Liste {} durch {} fehlgeschlagen!
Email is required for list un-subscription! : Für das Abbestellen ist eine E-Mail-Adresse erforderlich!
Error reading user database! : Fehler beim Lesen der Nutzer-Datenbank!
Failed to access user database\: {} : Fehler beim Zugriff auf die Nutzer-Datenbank: {}
Failed to create list '{}'\: {} : Erzeugen der Liste '{}' fehlgeschlagen: {}
Failed to create new user\: {} : Erzeugen des neuen Nutzers fehlgeschlagen: {}
Failed to handle request\: {} : Konnte Anfrage nicht verarbeiten: {}
Failed to load file '{}'! : Laden der Datei '{}' fehlgeschlagen!
Failed to load list member for {}/{} : Laden der Mitgliedschaft zu {}/{} fehlgeschlagen!
Failed to load member list '{}' : Laden der Mitglieder-Liste von '{}' fehlgeschlagen!
Failed to load post from file! : Laden der Nachricht aus Datei fehlgeschlagen!
Failed to load template '{}' : Laden der Vorlage '{}' fehlgeschlagen!
Failed to load user for {} : Laden des Nutzers zu {} fehlgeschlagen!
Failed to load user for address {} : Laden des Nutzers für die Adresse {} fehlgeschlagen!
Failed to make {} a moderator of {} : Ernennen von {} zum Moderator von {} fehlgeschlagen!
Failed to make {} a subscriber of {} : Ernennen von {} zum regulären Abonnenten von {} fehlgeschlagen!
Failed to send email to {} : Senden der Email an {} fehlgeschlagen!
Failed to send request confirmation email\: {} : Senden der Bestätigungs-Email fehlgeschlagen: {}
Failed to send test email to {} : Senden der Test-Email an {} fehlgeschlagen!
Failed to (un)hide mailing list\: : Veröffentlichen/Verstekcen der Mailing-Liste fehlgeschlagen:
Failed to un-subscribe {} from {} : Abbestellen von {} / {} fehlgeschlagen!
Failed to unsubscribe! : Abbestellen der Mailin-Liste fehlgeschlagen!
Failed to update list '{}' : Aktualisieren der Liste '{}' fehlgeschlagen!
Failed to update MailingList! : Aktualisierung der Mailing-Liste fehlgeschlagen!
Fill all fields, please! : Bitte alle Felder ausfüllen!
File {} does not exist! : Datei {} existiert nicht!
Find the forwarded message in the attachment(s)!\n : Die weitergeleitete Nachricht findest du im Anhang dieser E-Mail!\n
forward_attached : als Anhang weiterleiten
hidden : versteckt
hide_receivers : Empfänger verbergen
If you received this mail, the SMTP settings of your mailing list are correct. : Wenn Sie diese Nachricht empfangen haben, sind die SMTP-Einstellungen Ihrer Mailing-Liste korrekt.
IMAP credentials are required! : IMAP-Zugangsdaten sind erforderlich!
Invalid or missing token : Ungültiger oder fehlender Token!
Invalid username/password : Ungültiger Nutzername oder ungültiges Passwort!
Invalid email/password combination! : Ungültige E-Mail-/Passwort-Kombination!
List '{}' requires attention! : Liste '{}' erfordert Aufmerksamkeit!
List {} disabled, closed for subscribers and hidden. Members have been removed. : Liste {} deaktiviert, Abonnement gesperrt, Liste de-publiziert. Mitglieder wurden entfernt.
List email ({}) is not a valid email address! : Listen-E-Mail-Adresse ({}) ist keine gültige E-Mail-Adresse!
List name and address are required! : Name und Adresse der Liste sind notwendige Felder!
made public : veröffentlicht
Mailing list '{}' was {}! : Mailing-Liste '{}' wurde {}!
missing user email address! : E-Mail-Adresse des Listenmitglieds nicht angegeben!
Missing username or password! : Nutzername oder Passwort fehlen!
moderator : Moderator
Name and email are required fields for list subscription! : Name und E-Mail-Adresse sind für das Abonnieren der Mailingliste erforderlich!
No handler for path {}! : Kein Handler für den Pfad '{}'!
no list email provided! : Keine Listen-Email übertragen!
No list provided by form data! : Formular-Daten enthalten keine Liste!
No such user\: {} : Kein solcher Nutzer: {}
No template for path '{}'! : Keine Vorlage für den Pfad '{}' vorhanden!
No valid mailing list provided! : Keine gültige MailingListe übermittelt!
no valid permissions provided! : Keine gültigen Berechtigungen übergeben!
Not logged in! : Nicht eingeloggt!
open_for_guests : Gästbeiträge erlaubt
open_for_subscribers : Selbstregistrierung
original_from : ursprünglicher Absender
owner : Besitzer
Passwords do not match! : Passworte stimmen nicht überein!
Password to short or to simple! : Passwort zu kurz oder zu einfach!
Problem during unscubsription of {} from {}\: : Es ist ein Problem beim Austragen von {} aus der Liste {} aufgetreten:
public : öffentlich
Query '{}' failed\: : Query '{}' fehlgeschlagen:
reply_to_list : Antwort an Liste
Sent confirmation mail to '{}. : Bestätigungs-Email wurde an '{} versendet.
SMTP credentials are required! : SMTP-Zugangsdaten sind erforderlich!
subscriber : Abonniert
Subscription failed\: {} : Abonnieren der Liste fehlgeschlagen: {}
Sucessfully updated MailingList! : Mailing-Liste aktualisiert!
Successfully subscribed '{}' to '{}'. : '{}' hat die Mailingliste '{}' erfolgreich abonniert.
Sucessfully un-subscribed from '{}'. : '{}' erfolgreich abbestellt.
Templates have been reloaded : Vorlagen wurden neu geladen!
The mailing list you are trying to view does not exist! : Die Mailingliste, auf die Sie zugreifen wollen, gibt es nicht!
This list received an email from {}, who is not member of the list.\nThe email has been moved to the '{}' folder.\nYou may manually forward this message or drop it. : Diese Liste hat eine E-Mail von {} empfangen. Der Absender ist nicht Mitglied der Liste.\nDie Email wurde in den '{}'-Ordner verschoben.\nSie können die Nachricht manuell weiterleiten oder verwerfen.
Unknown user or token : Nutzer oder Token unbekannt!
Updated user permissions : Nutzer-Berechtigungen aktualisiert
User database is empty. Create admin user first\: : Nutzer-Datenbank ist leer. Admin-Nutzer wird hiermit angelegt:
Was not able to check existence of table {}! : Konnte Existenz der Tabelle {} nicht prüfen!
Was not able to redirect to {} page\: {} : Weiterleitung nach {} fehlgeschlagen: {}
You already are member of this list! : Sie haben diese Liste bereits abonniert!
You are not allowed to access the archive of this list! : Du hast keine Berechtigung, das Archiv dieser Liste anzusehen!
You are not allowed to alter user permissions! : Sie haben nicht die Berechtigung, um Berechtigungen zu ändern!
You are not allowed to create new mailing lists! : Ihnen ist es nicht gestattet, neue Mailinglisten anzulegen!
You are not allowed to edit '{}' : Du bist nicht berechtigt, '{}' zu bearbeiten!
You are not allowed to edit mods of {} : Es ist dir nicht gestattet, die Moderatoren von {} zu verändern!
You are not allowed to join {}! : Ihnen ist es nicht gestattet, '{}' zu abonnieren!
You are not allowed to list members of '{}' : Es ist dir nicht gestattet, die Mitglieder von '{}' aufzulisten!
You are not allowed to nominate new mods for {} : Es ist dir nicht gestattet, neue Moderatoren für {} zu ernennen!
You are not allowed to remove members of {} : Es ist dir nicht erlaubt, Mitglieder von {} zu entfernen!
You are not allowed to remove the list owner! : Du kannst den Listen-Besitzer nicht entfernen!
You are not allowed to remove this list! : Es ist dir nicht gestattet, diese Liste zu löschen!
You are not allowed to subscribe to '{}'! : Es ist ihnen nicht gestattet, '{}' zu abonnieren!
You are not allowed to test '{}' : Es ist dir nicht gestattet, '{}' zu testen
You are not alter settings of this list! : Es ist Ihnen nicht gestattet, die Einselltungen dieser Mailingliste zu verändern!
You are trying to access a non-existing list! : Du versuchst auf eine nicht existierende Liste zuzugreifen!
You have tried to send a message to the list '{}', which failed. This is because you are not a (privileged) member of this list.\n : Sie haben versucht, eine Nachricht an die Liste '{}' zu senden. Das wurde verweigert, da Sie kein Mitglied der Liste (mit entsprechenden Berechtigungen) sind.\n
You may go to {} and subscribe to the list, then try again. : Sie können zu {} gehen und die Liste abonnieren. Versuchen Sie es danach erneut.
Your message to {} was rejected! : Ihre Nachricht an {} wurde zurückgewiesen!

Loading…
Cancel
Save