KB68 Implement newsletter sending.

Change-Id: I1d56e40d7f35d6be77fde1a1e8519a91bd2dc3b8
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
index fd023d7..346b71f 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
@@ -5,12 +5,14 @@
 import eu.mulk.mulkcms2.benki.accesscontrol.Role;
 import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
 import eu.mulk.mulkcms2.benki.lazychat.LazychatMessage;
+import eu.mulk.mulkcms2.benki.newsletter.Newsletter;
 import eu.mulk.mulkcms2.benki.users.User;
 import eu.mulk.mulkcms2.benki.users.User_;
 import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
 import java.time.LocalDate;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
@@ -70,6 +72,12 @@
   public OffsetDateTime date;
 
   @ManyToOne(fetch = FetchType.LAZY)
+  @JoinColumn(name = "newsletter", referencedColumnName = "id", nullable = true)
+  @CheckForNull
+  @JsonbTransient
+  public Newsletter newsletter;
+
+  @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "owner", referencedColumnName = "id")
   @CheckForNull
   @JsonbTransient
@@ -340,18 +348,22 @@
     }
 
     // Fetch texts (to avoid n+1 selects).
-    var postIds = forwardResults.stream().map(x -> x.id).collect(toList());
+    fetchTexts(forwardResults);
+
+    return new PostPage<>(prevCursor, cursor, nextCursor, forwardResults);
+  }
+
+  public static <T extends Post<?>> void fetchTexts(Collection<T> posts) {
+    var postIds = posts.stream().map(x -> x.id).collect(toList());
 
     if (!postIds.isEmpty()) {
       find("SELECT p FROM Post p LEFT JOIN FETCH p.texts WHERE p.id IN (?1)", postIds).stream()
           .count();
     }
-
-    return new PostPage<>(prevCursor, cursor, nextCursor, forwardResults);
   }
 
   @CheckForNull
-  protected Text getText() {
+  public Text getText() {
     var texts = getTexts();
     if (texts.isEmpty()) {
       return null;