Implement news feed for Lazy Chat.

Change-Id: I3621b77da5277f38c5f50fab24ff7bea580f11cd
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
index 736740a..6f5ffc8 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
@@ -1,10 +1,7 @@
 package eu.mulk.mulkcms2.benki.bookmarks;
 
 import eu.mulk.mulkcms2.benki.posts.Post;
-import eu.mulk.mulkcms2.benki.users.User;
 import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;
-import io.quarkus.security.identity.SecurityIdentity;
-import java.util.List;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.persistence.CollectionTable;
@@ -15,7 +12,6 @@
 import javax.persistence.JoinColumn;
 import javax.persistence.Table;
 import javax.persistence.Transient;
-import org.hibernate.Session;
 
 @Entity
 @Table(name = "bookmarks", schema = "benki")
@@ -43,18 +39,16 @@
     return new MarkdownConverter().htmlify(description);
   }
 
-  public static List<Bookmark> findViewable(
-      Session session, SecurityIdentity viewer, @CheckForNull User owner) {
-    return findViewable(Bookmark.class, session, viewer, owner, null, null).posts;
+  @CheckForNull
+  @Override
+  public String getUri() {
+    return null;
   }
 
-  public static PostPage<Bookmark> findViewable(
-      Session session,
-      SecurityIdentity viewer,
-      @CheckForNull User owner,
-      @CheckForNull Integer cursor,
-      @CheckForNull Integer count) {
-    return findViewable(Bookmark.class, session, viewer, owner, cursor, count);
+  @CheckForNull
+  @Override
+  public String getTitle() {
+    return null;
   }
 
   @Override
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
index 5e00c60..918cad7 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
@@ -1,11 +1,8 @@
 package eu.mulk.mulkcms2.benki.lazychat;
 
 import eu.mulk.mulkcms2.benki.posts.Post;
-import eu.mulk.mulkcms2.benki.users.User;
 import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;
-import io.quarkus.security.identity.SecurityIdentity;
 import java.util.Collection;
-import java.util.List;
 import javax.annotation.CheckForNull;
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -13,7 +10,6 @@
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.persistence.Transient;
-import org.hibernate.Session;
 
 @Entity
 @Table(name = "lazychat_messages", schema = "benki")
@@ -33,18 +29,22 @@
     return new MarkdownConverter().htmlify(content);
   }
 
-  public static List<LazychatMessage> findViewable(
-      Session session, SecurityIdentity viewer, @CheckForNull User owner) {
-    return findViewable(LazychatMessage.class, session, viewer, owner, null, null).posts;
+  @CheckForNull
+  @Override
+  public String getUri() {
+    return null;
   }
 
-  public static PostPage<LazychatMessage> findViewable(
-      Session session,
-      SecurityIdentity viewer,
-      @CheckForNull User owner,
-      @CheckForNull Integer cursor,
-      @CheckForNull Integer count) {
-    return findViewable(LazychatMessage.class, session, viewer, owner, cursor, count);
+  @CheckForNull
+  @Override
+  public String getTitle() {
+    return null;
+  }
+
+  @CheckForNull
+  @Override
+  public String getDescriptionHtml() {
+    return getContentHtml();
   }
 
   @Override
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 fc9ba78..20aec05 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
@@ -79,6 +79,15 @@
 
   public abstract boolean isLazychatMessage();
 
+  @CheckForNull
+  public abstract String getTitle();
+
+  @CheckForNull
+  public abstract String getDescriptionHtml();
+
+  @CheckForNull
+  public abstract String getUri();
+
   protected static <T extends Post> CriteriaQuery<T> queryViewable(
       Class<T> entityClass,
       SecurityIdentity readerIdentity,
@@ -154,6 +163,11 @@
     }
   }
 
+  public static List<Post> findViewable(
+      PostFilter postFilter, Session session, SecurityIdentity viewer, @CheckForNull User owner) {
+    return findViewable(postFilter, session, viewer, owner, null, null).posts;
+  }
+
   public static PostPage<Post> findViewable(
       PostFilter postFilter,
       Session session,
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 e08aaf1..fbe6bf7 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
@@ -10,7 +10,6 @@
 import com.rometools.rome.feed.synd.SyndPersonImpl;
 import com.rometools.rome.io.FeedException;
 import com.rometools.rome.io.WireFeedOutput;
-import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
 import eu.mulk.mulkcms2.benki.users.User;
 import io.quarkus.qute.Template;
 import io.quarkus.qute.TemplateExtension;
@@ -148,21 +147,22 @@
   }
 
   private String makeFeed(@Nullable User owner, @Nullable String ownerName) throws FeedException {
-    var bookmarks = Bookmark.findViewable(entityManager.unwrap(Session.class), identity, owner);
+    var posts = Post.findViewable(postFilter, entityManager.unwrap(Session.class), identity, owner);
     var feed = new Feed("atom_1.0");
 
     var feedSubId = owner == null ? "" : String.format("/%d", owner.id);
 
-    feed.setTitle("Book Marx");
+    feed.setTitle(String.format("Benki → %s", pageTitle));
     feed.setId(
         String.format(
-            "tag:%s,2019:marx%s:%s",
+            "tag:%s,2019:%s:%s:%s",
             tagBase,
+            pageTitle,
             feedSubId,
             identity.isAnonymous() ? "world" : identity.getPrincipal().getName()));
     feed.setUpdated(
         Date.from(
-            bookmarks.stream()
+            posts.stream()
                 .map(x -> x.date)
                 .max(Comparator.comparing(x -> x))
                 .orElse(OffsetDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC))
@@ -181,33 +181,39 @@
     feed.setAlternateLinks(List.of(htmlAltLink));
 
     feed.setEntries(
-        bookmarks.stream()
+        posts.stream()
             .map(
-                bookmark -> {
+                post -> {
                   var entry = new Entry();
 
-                  entry.setId(String.format("tag:%s,2012:/marx/%d", tagBase, bookmark.id));
-                  entry.setPublished(Date.from(bookmark.date.toInstant()));
-                  entry.setUpdated(Date.from(bookmark.date.toInstant()));
+                  entry.setId(String.format("tag:%s,2012:/marx/%d", tagBase, post.id));
+                  entry.setPublished(Date.from(post.date.toInstant()));
+                  entry.setUpdated(Date.from(post.date.toInstant()));
 
                   var author = new SyndPersonImpl();
-                  author.setName(bookmark.owner.getFirstAndLastName());
+                  author.setName(post.owner.getFirstAndLastName());
                   entry.setAuthors(List.of(author));
 
-                  var title = new Content();
-                  title.setType("text");
-                  title.setValue(bookmark.title);
-                  entry.setTitleEx(title);
+                  if (post.getTitle() != null) {
+                    var title = new Content();
+                    title.setType("text");
+                    title.setValue(post.getTitle());
+                    entry.setTitleEx(title);
+                  }
 
-                  var summary = new Content();
-                  summary.setType("html");
-                  summary.setValue(bookmark.getDescriptionHtml());
-                  entry.setSummary(summary);
+                  if (post.getDescriptionHtml() != null) {
+                    var summary = new Content();
+                    summary.setType("html");
+                    summary.setValue(post.getDescriptionHtml());
+                    entry.setSummary(summary);
+                  }
 
-                  var link = new Link();
-                  link.setHref(bookmark.uri);
-                  link.setRel("alternate");
-                  entry.setAlternateLinks(List.of(link));
+                  if (post.getUri() != null) {
+                    var link = new Link();
+                    link.setHref(post.getUri());
+                    link.setRel("alternate");
+                    entry.setAlternateLinks(List.of(link));
+                  }
 
                   return entry;
                 })