Wiki: Make editing work.

Change-Id: Ibff52be5c595deba96b3f7642307ba1208eff9cf
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/wiki/WikiResource.java b/src/main/java/eu/mulk/mulkcms2/benki/wiki/WikiResource.java
index b5ece7e..30cae98 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/wiki/WikiResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/wiki/WikiResource.java
@@ -16,6 +16,8 @@
 import java.time.temporal.TemporalAccessor;
 import java.util.Optional;
 import javax.inject.Inject;
+import javax.transaction.Transactional;
+import javax.ws.rs.BadRequestException;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.NotFoundException;
@@ -24,6 +26,8 @@
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import org.jboss.logging.Logger;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
 
 @Path("/wiki")
 public class WikiResource {
@@ -66,10 +70,25 @@
   @POST
   @Path("/{pageName}")
   @Authenticated
+  @Transactional
   public void updatePage(
       @PathParam("pageName") String pageName,
-      @FormParam("title") String title,
-      @FormParam("content") String content) {
+      @FormParam("wiki-title") String title,
+      @FormParam("wiki-content") String content) {
+
+    if (title == null && content == null) {
+      // No changes, nothing to do.
+      return;
+    }
+
+    if (title != null) {
+      // Remove markup.  Reject whitespace.
+      title = Jsoup.clean(title, Whitelist.none());
+      if (!title.matches("\\w+")) {
+        throw new BadRequestException("title does not match \"\\w+\"");
+      }
+    }
+
     var userName = identity.getPrincipal().getName();
 
     Optional<WikiPageRevision> maybeCurrentRevision =
@@ -86,13 +105,13 @@
     var pageRevision =
         new WikiPageRevision(
             OffsetDateTime.now(),
-            title,
-            content,
+            title != null ? title : currentRevision.title,
+            content != null ? content : currentRevision.content,
             "html5",
             currentRevision.page,
-            User.find("name = ?1", userName).singleResult());
+            User.find("from BenkiUser u join u.nicknames n where ?1 = n", userName).singleResult());
 
-    WikiPageRevision.persist(pageRevision);
+    pageRevision.persistAndFlush();
   }
 
   @GET
diff --git a/src/main/resources/templates/benki/wiki/wikiPage.html b/src/main/resources/templates/benki/wiki/wikiPage.html
index 8ca34e7..f9f5214 100644
--- a/src/main/resources/templates/benki/wiki/wikiPage.html
+++ b/src/main/resources/templates/benki/wiki/wikiPage.html
@@ -11,6 +11,26 @@
   window.addEventListener('DOMContentLoaded', function() {
     let editor = ContentTools.EditorApp.get();
     editor.init('*[data-editable]', 'data-name');
+    editor.addEventListener('saved', async function (ev) {
+      let regions = ev.detail().regions;
+      if (Object.getOwnPropertyNames(regions).length === 0) {
+        // Nothing changed.
+        return;
+      }
+
+      this.busy(true);
+
+      let requestParams = new URLSearchParams();
+      for (let name of Object.getOwnPropertyNames(regions)) {
+        requestParams.append(name, regions[name]);
+      }
+
+      var response = await fetch("/wiki/{page.title}", {
+        method: 'POST',
+        body: requestParams
+      });
+      this.busy(false);
+    });
   });
 </script>
 {/head}
@@ -18,11 +38,13 @@
 {#body}
 <article id="wiki-page">
   <header>
-    <h1>{page.title}</h1>
+    <div data-editable data-name="wiki-title">
+      <h1>{page.title}</h1>
+    </div>
   </header>
 
   <main>
-    <div data-editable data-name="main-content">
+    <div data-editable data-name="wiki-content">
       {#with page}{content.raw}{/}
     </div>
   </main>