KB66 Add comment notification email.

Change-Id: I27feecfe2d4309397b116552856227eacb7e9600
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
index 5a38262..d1e5775 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
@@ -21,6 +21,7 @@
 import eu.mulk.mulkcms2.benki.posts.Post.PostPage;
 import eu.mulk.mulkcms2.benki.posts.Post.Scope;
 import eu.mulk.mulkcms2.benki.users.User;
+import io.quarkus.mailer.MailTemplate.MailTemplateInstance;
 import io.quarkus.qute.CheckedTemplate;
 import io.quarkus.qute.TemplateExtension;
 import io.quarkus.qute.TemplateInstance;
@@ -45,6 +46,9 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -96,7 +100,10 @@
   @ConfigProperty(name = "mulkcms.posts.default-max-results")
   int defaultMaxResults;
 
-  @CheckedTemplate(basePath = "benki/posts")
+  @ConfigProperty(name = "quarkus.mailer.from")
+  String mailSenderAddress;
+
+  @CheckedTemplate
   static class Templates {
 
     public static native TemplateInstance postList(
@@ -112,6 +119,9 @@
         @CheckForNull Integer nextCursor,
         @CheckForNull Integer pageSize,
         @CheckForNull String searchQuery);
+
+    public static native MailTemplateInstance commentNotificationMail(
+        int postId, LazychatMessage comment);
   }
 
   @Inject protected SecurityIdentity identity;
@@ -303,7 +313,7 @@
       @PathParam("id") int postId,
       @FormParam("message") @NotEmpty String message,
       @FormParam("hashcash-salt") long hashcashSalt)
-      throws NoSuchAlgorithmException {
+      throws NoSuchAlgorithmException, ExecutionException, InterruptedException, TimeoutException {
     var hashcashDigest = MessageDigest.getInstance(hashcashDigestAlgorithm);
     hashcashDigest.update("Hashcash-Salt: ".getBytes(UTF_8));
     hashcashDigest.update(String.valueOf(hashcashSalt).getBytes(UTF_8));
@@ -334,6 +344,17 @@
     assignPostTargets(post.getVisibility(), post.owner, comment);
     comment.persist();
 
+    var admins = User.findAdmins();
+
+    var mailText = Templates.commentNotificationMail(postId, comment);
+    var sendJob =
+        mailText
+            .subject(String.format("MulkCMS comment #%d", comment.id))
+            .to(mailSenderAddress)
+            .bcc(admins.stream().map(x -> x.email).toArray(String[]::new))
+            .send();
+    sendJob.subscribe().asCompletionStage().get(10000, TimeUnit.SECONDS);
+
     return Response.seeOther(UriBuilder.fromUri("/posts/{id}").build(postId)).build();
   }
 
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
index 04a5cd4..99ab91f 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
@@ -8,6 +8,7 @@
 import eu.mulk.mulkcms2.benki.wiki.WikiPageRevision;
 import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import javax.annotation.CheckForNull;
@@ -162,6 +163,11 @@
         .singleResult();
   }
 
+  public static List<User> findAdmins() {
+    return find("select distinct u from BenkiUser u left join u.effectiveRoles r left join r.tags t where t = 'admin' or u.status = 'admin'")
+        .list();
+  }
+
   public final boolean canSee(Post message) {
     return message.isVisibleTo(this);
   }
diff --git a/src/main/resources/templates/PostResource/commentNotificationMail.txt b/src/main/resources/templates/PostResource/commentNotificationMail.txt
new file mode 100644
index 0000000..a226106
--- /dev/null
+++ b/src/main/resources/templates/PostResource/commentNotificationMail.txt
@@ -0,0 +1,9 @@
+{@int postId}
+{@eu.mulk.mulkcms2.benki.lazychat.LazychatMessage comment}
+New Comment
+===========
+
+Link: <https://matthias.benkard.de/posts/{postId}#comment-{comment.id}>
+{#if comment.owner != null}Author: {comment.owner.firstName}{/if}
+
+{comment.text.content}
diff --git a/src/main/resources/templates/benki/posts/postList.html b/src/main/resources/templates/PostResource/postList.html
similarity index 100%
rename from src/main/resources/templates/benki/posts/postList.html
rename to src/main/resources/templates/PostResource/postList.html