Lafargue: Implement basic viewer.

Change-Id: If24f58aa069a14139454708d02ac40109c2181ef
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 6bbd3a6..503f551 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
@@ -10,6 +10,7 @@
 import javax.persistence.JoinColumn;
 import javax.persistence.Table;
 import javax.persistence.Transient;
+import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;
 
 @Entity
 @Table(name = "bookmarks", schema = "benki")
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 de1e0cc..0afc294 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
@@ -1,5 +1,6 @@
 package eu.mulk.mulkcms2.benki.lazychat;
 
+import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;
 import eu.mulk.mulkcms2.benki.generic.Post;
 import java.util.Collection;
 import javax.persistence.Column;
@@ -7,6 +8,7 @@
 import javax.persistence.FetchType;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 
 @Entity
 @Table(name = "lazychat_messages", schema = "benki")
@@ -20,4 +22,9 @@
 
   @OneToMany(mappedBy = "referrer", fetch = FetchType.LAZY)
   public Collection<LazychatReference> references;
+
+  @Transient
+  public String getContentHtml() {
+    return new MarkdownConverter().htmlify(content);
+  }
 }
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java
new file mode 100644
index 0000000..8be3d4a
--- /dev/null
+++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java
@@ -0,0 +1,78 @@
+package eu.mulk.mulkcms2.benki.lazychat;
+
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+
+import eu.mulk.mulkcms2.benki.accesscontrol.Role;
+import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
+import eu.mulk.mulkcms2.benki.users.User;
+import io.quarkus.panache.common.Sort;
+import io.quarkus.qute.Template;
+import io.quarkus.qute.TemplateExtension;
+import io.quarkus.qute.TemplateInstance;
+import io.quarkus.qute.api.ResourcePath;
+import io.quarkus.security.identity.SecurityIdentity;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.time.temporal.TemporalAccessor;
+import java.util.List;
+import javax.inject.Inject;
+import javax.json.spi.JsonProvider;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import org.jboss.logging.Logger;
+
+@Path("/lazychat")
+public class LazychatResource {
+
+  private static Logger log = Logger.getLogger(LazychatResource.class);
+
+  private static DateTimeFormatter htmlDateFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+
+  private static DateTimeFormatter humanDateFormatter =
+      DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT);
+
+  private static JsonProvider jsonProvider = JsonProvider.provider();
+
+  @ResourcePath("benki/lazychat/lazychatList.html")
+  @Inject
+  Template lazychatList;
+
+  @Inject SecurityIdentity identity;
+
+  @GET
+  @Produces(TEXT_HTML)
+  public TemplateInstance getPage() {
+    List<LazychatMessage> lazychatMessages;
+    if (identity.isAnonymous()) {
+      Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult();
+      lazychatMessages =
+          Bookmark.find(
+                  "select bm from LazychatMessage lm join lm.targets target left join fetch lm.owner where target = ?1",
+                  Sort.by("date").descending(),
+                  world)
+              .list();
+    } else {
+      var userName = identity.getPrincipal().getName();
+      User user =
+          User.find("from BenkiUser u join u.nicknames n where ?1 = n", userName).singleResult();
+      lazychatMessages =
+          Bookmark.find(
+                  "select lm from BenkiUser u inner join u.visibleLazychatMessages lm left join fetch lm.owner where u.id = ?1",
+                  Sort.by("date").descending(),
+                  user.id)
+              .list();
+    }
+    return lazychatList.data("lazychatMessages", lazychatMessages);
+  }
+
+  @TemplateExtension
+  static String humanDateTime(TemporalAccessor x) {
+    return humanDateFormatter.format(x);
+  }
+
+  @TemplateExtension
+  static String htmlDateTime(TemporalAccessor x) {
+    return htmlDateFormatter.format(x);
+  }
+}
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/MarkdownConverter.java b/src/main/java/eu/mulk/mulkcms2/common/markdown/MarkdownConverter.java
similarity index 95%
rename from src/main/java/eu/mulk/mulkcms2/benki/bookmarks/MarkdownConverter.java
rename to src/main/java/eu/mulk/mulkcms2/common/markdown/MarkdownConverter.java
index 1fae62c..68f7a18 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/MarkdownConverter.java
+++ b/src/main/java/eu/mulk/mulkcms2/common/markdown/MarkdownConverter.java
@@ -1,4 +1,4 @@
-package eu.mulk.mulkcms2.benki.bookmarks;
+package eu.mulk.mulkcms2.common.markdown;
 
 import com.vladsch.flexmark.ext.abbreviation.AbbreviationExtension;
 import com.vladsch.flexmark.ext.autolink.AutolinkExtension;
@@ -18,7 +18,7 @@
   private final Parser parser;
   private final HtmlRenderer renderer;
 
-  MarkdownConverter() {
+  public MarkdownConverter() {
     var options = new MutableDataSet();
     options.set(
         Parser.EXTENSIONS,
diff --git a/src/main/resources/META-INF/resources/cms2/base.css b/src/main/resources/META-INF/resources/cms2/base.css
index bb5d200..126be6d 100644
--- a/src/main/resources/META-INF/resources/cms2/base.css
+++ b/src/main/resources/META-INF/resources/cms2/base.css
@@ -145,3 +145,17 @@
   padding: 0.3em;
   background: #fee;
 }
+
+.lazychat-message-info {
+  font-style: italic;
+  font-size: smaller;
+  margin: 0;
+  padding: 0;
+}
+
+article.lazychat-message {
+  margin: 0.5em 0;
+  border: 1px solid lightseagreen;
+  padding: 0.3em;
+  background: #efe;
+}
diff --git a/src/main/resources/templates/benki/lazychat/lazychatList.html b/src/main/resources/templates/benki/lazychat/lazychatList.html
new file mode 100644
index 0000000..ecac7a7
--- /dev/null
+++ b/src/main/resources/templates/benki/lazychat/lazychatList.html
@@ -0,0 +1,32 @@
+{@java.util.List<eu.mulk.mulkcms2.benki.lazychat.LazychatMessage> lazychatMessages}
+
+{#include base.html}
+
+{#title}Benki Lazychat{/title}
+{#siteSection}Lazychat{/siteSection}
+{#lazychatClass}this-page{/lazychatClass}
+
+{#head}{/head}
+
+{#body}
+
+{#for lazychatMessage in lazychatMessages}
+  {#with lazychatMessage}
+    <article class="lazychat-message">
+      <header>
+        <div class="lazychat-message-info">
+          <time datetime="{date.htmlDateTime}">{date.humanDateTime}</time>
+          <span class="lazychat-message-owner">{owner.firstName} {owner.lastName}</span>
+        </div>
+      </header>
+
+      <section class="lazychat-message-content">
+        {contentHtml.raw}
+      </section>
+    </article>
+  {/with}
+{/for}
+
+{/body}
+
+{/include}