summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2020-04-13 17:01:35 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2020-04-13 17:01:35 +0200
commit06e6c81c77f8098693473e49c11557820541dd15 (patch)
tree90093ef738927496a5c3f48540ed62871b565bbb /src/main/java
parent7f4daccab9dc21cfd95be219e5c8c86545d47125 (diff)
Lazy Chat: Implement editing of messages.
Change-Id: I291201da1fbc7c2b6563f0837f7ce3e2f7f8555c
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java18
-rw-r--r--src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java21
-rw-r--r--src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java4
-rw-r--r--src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java78
-rw-r--r--src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java40
-rw-r--r--src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java30
-rw-r--r--src/main/java/eu/mulk/mulkcms2/benki/users/User.java6
7 files changed, 160 insertions, 37 deletions
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 6298245..349322b 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java
@@ -5,6 +5,7 @@ import eu.mulk.mulkcms2.benki.users.User;
import eu.mulk.mulkcms2.benki.users.UserRole;
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import java.util.Collection;
+import java.util.Objects;
import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
@@ -78,4 +79,21 @@ public class Role extends PanacheEntityBase {
public static Role getWorld() {
return find("from Role r join r.tags tag where tag = 'world'").singleResult();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Role)) {
+ return false;
+ }
+ Role role = (Role) o;
+ return Objects.equals(id, role.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
}
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 f2e3067..cb4c20f 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
@@ -3,10 +3,9 @@ package eu.mulk.mulkcms2.benki.bookmarks;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
-import eu.mulk.mulkcms2.benki.accesscontrol.Role;
+import eu.mulk.mulkcms2.benki.posts.Post;
import eu.mulk.mulkcms2.benki.posts.PostFilter;
import eu.mulk.mulkcms2.benki.posts.PostResource;
-import eu.mulk.mulkcms2.benki.users.User;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.api.ResourcePath;
@@ -21,9 +20,6 @@ import javax.inject.Inject;
import javax.json.JsonObject;
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;
@@ -51,12 +47,10 @@ public class BookmarkResource extends PostResource {
@FormParam("uri") URI uri,
@FormParam("title") @NotEmpty String title,
@FormParam("description") String description,
- @FormParam("visibility") @NotNull @Pattern(regexp = "public|semiprivate|private")
- String visibility)
+ @FormParam("visibility") Post.Visibility visibility)
throws URISyntaxException {
- var userName = identity.getPrincipal().getName();
- var user = User.findByNickname(userName);
+ var user = getCurrentUser();
var bookmark = new Bookmark();
bookmark.uri = uri.toString();
@@ -66,14 +60,7 @@ public class BookmarkResource extends PostResource {
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));
- }
+ assignPostTargets(visibility, user, bookmark);
bookmark.persistAndFlush();
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 918cad7..1e92c38 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
@@ -4,6 +4,7 @@ import eu.mulk.mulkcms2.benki.posts.Post;
import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;
import java.util.Collection;
import javax.annotation.CheckForNull;
+import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@@ -22,9 +23,11 @@ public class LazychatMessage extends Post {
public String format;
@OneToMany(mappedBy = "referrer", fetch = FetchType.LAZY)
+ @JsonbTransient
public Collection<LazychatReference> references;
@Transient
+ @JsonbTransient
public String getContentHtml() {
return new MarkdownConverter().htmlify(content);
}
@@ -43,6 +46,7 @@ public class LazychatMessage extends Post {
@CheckForNull
@Override
+ @JsonbTransient
public String getDescriptionHtml() {
return getContentHtml();
}
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java
index 8a4d2a3..fd672f8 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java
@@ -1,21 +1,24 @@
package eu.mulk.mulkcms2.benki.lazychat;
-import eu.mulk.mulkcms2.benki.accesscontrol.Role;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import eu.mulk.mulkcms2.benki.posts.Post;
import eu.mulk.mulkcms2.benki.posts.PostFilter;
import eu.mulk.mulkcms2.benki.posts.PostResource;
-import eu.mulk.mulkcms2.benki.users.User;
import io.quarkus.security.Authenticated;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
-import java.util.Set;
+import java.util.Objects;
import javax.transaction.Transactional;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
-import javax.ws.rs.BadRequestException;
+import javax.ws.rs.ForbiddenException;
import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
@Path("/lazychat")
@@ -29,13 +32,10 @@ public class LazychatResource extends PostResource {
@Transactional
@Authenticated
public Response postMessage(
- @FormParam("text") String text,
- @FormParam("visibility") @NotNull @Pattern(regexp = "public|semiprivate|private")
- String visibility)
+ @FormParam("text") String text, @FormParam("visibility") Post.Visibility visibility)
throws URISyntaxException {
- var userName = identity.getPrincipal().getName();
- var user = User.findByNickname(userName);
+ var user = getCurrentUser();
var message = new LazychatMessage();
message.content = text;
@@ -43,17 +43,57 @@ public class LazychatResource extends PostResource {
message.owner = user;
message.date = OffsetDateTime.now();
- if (visibility.equals("public")) {
- Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult();
- message.targets = Set.of(world);
- } else if (visibility.equals("semiprivate")) {
- message.targets = Set.copyOf(user.defaultTargets);
- } else if (!visibility.equals("private")) {
- throw new BadRequestException(String.format("invalid visibility “%s”", visibility));
- }
+ assignPostTargets(visibility, user, message);
message.persistAndFlush();
return Response.seeOther(new URI("/lazychat")).build();
}
+
+ @POST
+ @Transactional
+ @Authenticated
+ @Path("/p/{id}/edit")
+ public Response patchMessage(
+ @PathParam("id") int id,
+ @FormParam("text") String text,
+ @FormParam("visibility") Post.Visibility visibility)
+ throws URISyntaxException {
+
+ var user = getCurrentUser();
+
+ var message = getSession().byId(LazychatMessage.class).load(id);
+
+ if (message == null) {
+ throw new NotFoundException();
+ }
+
+ if (!Objects.equals(message.owner.id, user.id)) {
+ throw new ForbiddenException();
+ }
+
+ message.content = text;
+ message.format = "markdown";
+
+ assignPostTargets(visibility, user, message);
+
+ return Response.seeOther(new URI("/lazychat")).build();
+ }
+
+ @GET
+ @Transactional
+ @Produces(APPLICATION_JSON)
+ @Path("/p/{id}")
+ public LazychatMessage getMessage(@PathParam("id") int id) {
+
+ var user = getCurrentUser();
+
+ var message = getSession().byId(LazychatMessage.class).load(id);
+
+ if (!user.canSee(message)) {
+ throw new ForbiddenException();
+ }
+
+ return message;
+ }
}
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 20aec05..356461c 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
@@ -13,6 +13,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.annotation.CheckForNull;
+import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@@ -57,6 +58,7 @@ public abstract class Post extends PanacheEntityBase {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "owner", referencedColumnName = "id")
+ @JsonbTransient
public User owner;
@ManyToMany(fetch = FetchType.LAZY)
@@ -65,6 +67,7 @@ public abstract class Post extends PanacheEntityBase {
schema = "benki",
joinColumns = @JoinColumn(name = "message"),
inverseJoinColumns = @JoinColumn(name = "user"))
+ @JsonbTransient
public Set<User> visibleTo;
@ManyToMany(fetch = FetchType.LAZY)
@@ -73,6 +76,7 @@ public abstract class Post extends PanacheEntityBase {
schema = "benki",
joinColumns = @JoinColumn(name = "message"),
inverseJoinColumns = @JoinColumn(name = "target"))
+ @JsonbTransient
public Set<Role> targets;
public abstract boolean isBookmark();
@@ -88,6 +92,18 @@ public abstract class Post extends PanacheEntityBase {
@CheckForNull
public abstract String getUri();
+ public Visibility getVisibility() {
+ if (targets.isEmpty()) {
+ return Visibility.PRIVATE;
+ } else if (targets.contains(Role.getWorld())) {
+ return Visibility.PUBLIC;
+ } else {
+ // FIXME: There should really be a check whether targets.equals(owner.defaultTargets) here.
+ // Otherwise the actual visibility is DISCRETIONARY.
+ return Visibility.SEMIPRIVATE;
+ }
+ }
+
protected static <T extends Post> CriteriaQuery<T> queryViewable(
Class<T> entityClass,
SecurityIdentity readerIdentity,
@@ -236,4 +252,28 @@ public abstract class Post extends PanacheEntityBase {
return new PostPage<T>(prevCursor, cursor, nextCursor, forwardResults);
}
+
+ public enum Visibility {
+ PUBLIC,
+ SEMIPRIVATE,
+ DISCRETIONARY,
+ PRIVATE,
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Post)) {
+ return false;
+ }
+ Post post = (Post) o;
+ return Objects.equals(id, post.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
}
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 fbe6bf7..a691490 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
@@ -10,6 +10,7 @@ import com.rometools.rome.feed.atom.Link;
import com.rometools.rome.feed.synd.SyndPersonImpl;
import com.rometools.rome.io.FeedException;
import com.rometools.rome.io.WireFeedOutput;
+import eu.mulk.mulkcms2.benki.accesscontrol.Role;
import eu.mulk.mulkcms2.benki.users.User;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateExtension;
@@ -26,6 +27,7 @@ import java.time.temporal.TemporalAccessor;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -33,6 +35,7 @@ import javax.inject.Inject;
import javax.json.spi.JsonProvider;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
+import javax.ws.rs.BadRequestException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -70,7 +73,7 @@ public abstract class PostResource {
@ConfigProperty(name = "mulkcms.tag-base")
String tagBase;
- @PersistenceContext EntityManager entityManager;
+ @PersistenceContext protected EntityManager entityManager;
private final PostFilter postFilter;
private final String pageTitle;
@@ -256,4 +259,29 @@ public abstract class PostResource {
throw new IllegalStateException();
}
}
+
+ protected Session getSession() {
+ return entityManager.unwrap(Session.class);
+ }
+
+ protected static void assignPostTargets(Post.Visibility visibility, User user, Post post) {
+ switch (visibility) {
+ case PUBLIC:
+ post.targets = Set.of(Role.getWorld());
+ break;
+ case SEMIPRIVATE:
+ post.targets = Set.copyOf(user.defaultTargets);
+ break;
+ case PRIVATE:
+ post.targets = Set.of();
+ break;
+ default:
+ throw new BadRequestException(String.format("invalid visibility “%s”", visibility));
+ }
+ }
+
+ protected User getCurrentUser() {
+ var userName = identity.getPrincipal().getName();
+ return User.findByNickname(userName);
+ }
}
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 5879046..ab89baa 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
@@ -5,6 +5,7 @@ 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.posts.Post;
+import eu.mulk.mulkcms2.benki.posts.Post.Visibility;
import eu.mulk.mulkcms2.benki.wiki.WikiPageRevision;
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import java.util.Collection;
@@ -141,4 +142,9 @@ public class User extends PanacheEntityBase {
public static User findByNickname(String nickname) {
return User.find("from BenkiUser u join u.nicknames n where ?1 = n", nickname).singleResult();
}
+
+ public boolean canSee(Post message) {
+ // FIXME: Make this more efficient.
+ return message.getVisibility() == Visibility.PUBLIC || visiblePosts.contains(message);
+ }
}