Book Marx: Implement bookmark submission.
Change-Id: Ieb1fef8565ed0e17de9590d5207ba11ebfe6f177
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java b/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java
index 7c0db71..4bfba8e 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java
@@ -2,7 +2,6 @@
import eu.mulk.mulkcms2.benki.generic.PostTarget;
import eu.mulk.mulkcms2.benki.users.User;
-import eu.mulk.mulkcms2.benki.users.UserDefaultTarget;
import eu.mulk.mulkcms2.benki.users.UserRole;
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import java.util.Collection;
@@ -59,8 +58,8 @@
@ManyToMany(mappedBy = "effectiveSubroles", fetch = FetchType.LAZY)
public Set<Role> effectiveSuperroles;
- @OneToMany(mappedBy = "target", fetch = FetchType.LAZY)
- public Collection<UserDefaultTarget> usersUsedByAsDefaultTarget;
+ @ManyToMany(mappedBy = "defaultTargets", fetch = FetchType.LAZY)
+ public Set<User> usersUsedByAsDefaultTarget;
@OneToMany(mappedBy = "role", fetch = FetchType.LAZY)
public Collection<UserRole> directUsers;
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
index 45c7087..463551c 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
@@ -10,15 +10,27 @@
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.api.ResourcePath;
import io.quarkus.security.identity.SecurityIdentity;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
import java.util.List;
+import java.util.Set;
import javax.inject.Inject;
import javax.json.spi.JsonProvider;
+import javax.transaction.Transactional;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
+import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
import org.jboss.logging.Logger;
@Path("/bookmarks")
@@ -41,7 +53,7 @@
@GET
@Produces(TEXT_HTML)
- public TemplateInstance getPage() {
+ public TemplateInstance getIndex() {
List<Bookmark> bookmarks;
if (identity.isAnonymous()) {
Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult();
@@ -62,7 +74,44 @@
user.id)
.list();
}
- return bookmarkList.data("bookmarks", bookmarks);
+ return bookmarkList
+ .data("bookmarks", bookmarks)
+ .data("authenticated", !identity.isAnonymous());
+ }
+
+ @POST
+ @Transactional
+ public Response postBookmark(
+ @FormParam("uri") URI uri,
+ @FormParam("title") @NotEmpty String title,
+ @FormParam("description") String description,
+ @FormParam("visibility") @NotNull @Pattern(regexp = "public|semiprivate|private") String visibility)
+ throws URISyntaxException {
+
+ var userName = identity.getPrincipal().getName();
+ User user =
+ User.find("from BenkiUser u join u.nicknames n where ?1 = n", userName).singleResult();
+
+ var bookmark = new Bookmark();
+ bookmark.uri = uri.toString();
+ bookmark.title = title;
+ bookmark.tags = Set.of();
+ bookmark.description = description != null ? description : "";
+ bookmark.owner = user;
+ bookmark.date = OffsetDateTime.now();
+
+ if (visibility.equals("public")) {
+ Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult();
+ bookmark.targets = Set.of(world);
+ } else if (visibility.equals("semiprivate")) {
+ bookmark.targets = Set.copyOf(user.defaultTargets);
+ } else if (!visibility.equals("private")) {
+ throw new BadRequestException(String.format("invalid visibility “%s”", visibility));
+ }
+
+ bookmark.persistAndFlush();
+
+ return Response.seeOther(new URI("/bookmarks")).build();
}
@TemplateExtension
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 31c13dd..1a0dae6 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
@@ -68,8 +68,13 @@
@OneToMany(mappedBy = "owner", fetch = FetchType.LAZY)
public Collection<Post> posts;
- @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
- public Collection<UserDefaultTarget> defaultTargets;
+ @ManyToMany(fetch = FetchType.LAZY)
+ @JoinTable(
+ name = "user_default_target",
+ schema = "benki",
+ joinColumns = @JoinColumn(name = "user"),
+ inverseJoinColumns = @JoinColumn(name = "target"))
+ public Set<Role> defaultTargets;
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/users/UserDefaultTarget.java b/src/main/java/eu/mulk/mulkcms2/benki/users/UserDefaultTarget.java
deleted file mode 100644
index 94efef1..0000000
--- a/src/main/java/eu/mulk/mulkcms2/benki/users/UserDefaultTarget.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package eu.mulk.mulkcms2.benki.users;
-
-import eu.mulk.mulkcms2.benki.accesscontrol.Role;
-import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.Id;
-import javax.persistence.IdClass;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
-import javax.persistence.Table;
-
-@Entity
-@Table(name = "user_default_target", schema = "benki")
-@IdClass(UserDefaultTargetPK.class)
-public class UserDefaultTarget extends PanacheEntityBase {
-
- @Id
- @Column(name = "user", nullable = false)
- public int userId;
-
- @Id
- @Column(name = "target", nullable = false)
- public int targetId;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user", referencedColumnName = "id", nullable = false)
- public User user;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "target", referencedColumnName = "id", nullable = false)
- public Role target;
-}
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/users/UserDefaultTargetPK.java b/src/main/java/eu/mulk/mulkcms2/benki/users/UserDefaultTargetPK.java
deleted file mode 100644
index 4a41e7b..0000000
--- a/src/main/java/eu/mulk/mulkcms2/benki/users/UserDefaultTargetPK.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package eu.mulk.mulkcms2.benki.users;
-
-import java.io.Serializable;
-import javax.persistence.Column;
-import javax.persistence.Id;
-
-public class UserDefaultTargetPK implements Serializable {
-
- private int userId;
- private int targetId;
-
- @Column(name = "user", nullable = false)
- @Id
- public int getUserId() {
- return userId;
- }
-
- public void setUserId(int userId) {
- this.userId = userId;
- }
-
- @Column(name = "target", nullable = false)
- @Id
- public int getTargetId() {
- return targetId;
- }
-
- public void setTargetId(int targetId) {
- this.targetId = targetId;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- UserDefaultTargetPK that = (UserDefaultTargetPK) o;
-
- if (userId != that.userId) {
- return false;
- }
- if (targetId != that.targetId) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = userId;
- result = 31 * result + targetId;
- return result;
- }
-}
diff --git a/src/main/resources/META-INF/resources/cms2/base.css b/src/main/resources/META-INF/resources/cms2/base.css
index e04e5d9..735b297 100644
--- a/src/main/resources/META-INF/resources/cms2/base.css
+++ b/src/main/resources/META-INF/resources/cms2/base.css
@@ -159,3 +159,7 @@
padding: 0.3em;
background: #f0f8f0;
}
+
+#bookmark-submission textarea {
+ min-width: calc(100% - 12em);
+}
diff --git a/src/main/resources/templates/benki/bookmarks/bookmarkList.html b/src/main/resources/templates/benki/bookmarks/bookmarkList.html
index 0d392c8..9d4c706 100644
--- a/src/main/resources/templates/benki/bookmarks/bookmarkList.html
+++ b/src/main/resources/templates/benki/bookmarks/bookmarkList.html
@@ -1,4 +1,5 @@
{@java.util.List<eu.mulk.mulkcms2.benki.bookmarks.Bookmark> bookmarks}
+{@java.lang.Boolean authenticated}
{#include base.html}
@@ -10,6 +11,44 @@
{#body}
+{#if authenticated}
+ <section id="bookmark-submission">
+ <form class="pure-form pure-form-aligned" method="post">
+ <fieldset>
+ <legend>Submit Bookmark</legend>
+
+ <div class="pure-control-group">
+ <label for="title-input">Title:</label>
+ <input name="title" id="title-input" type="text" placeholder="Title" required/>
+ </div>
+
+ <div class="pure-control-group">
+ <label for="uri-input">URI:</label>
+ <input name="uri" id="uri-input" type="text" placeholder="URI" required/>
+ </div>
+
+ <div class="pure-control-group">
+ <label for="description-input">Description:</label>
+ <textarea name="description" id="description-input" placeholder="Description"></textarea>
+ </div>
+
+ <div class="pure-control-group">
+ <label for="visibility-input">Visibility:</label>
+ <select id="visibility-input" name="visibility" required>
+ <option value="public">Public</option>
+ <option value="semiprivate" selected>Semiprivate</option>
+ <option value="private">Private</option>
+ </select>
+ </div>
+
+ <div class="pure-controls">
+ <button type="submit" class="pure-button pure-button-primary">Submit Bookmark</button>
+ </div>
+ </fieldset>
+ </form>
+ </section>
+{/if}
+
{#for bookmark in bookmarks}
{#with bookmark}
<article class="bookmark">