| package eu.mulk.mulkcms2.benki.posts; |
| |
| 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.users.User; |
| import eu.mulk.mulkcms2.benki.users.User_; |
| import io.quarkus.hibernate.orm.panache.PanacheEntityBase; |
| import io.quarkus.security.identity.SecurityIdentity; |
| import java.time.OffsetDateTime; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| import javax.annotation.CheckForNull; |
| import javax.persistence.Column; |
| import javax.persistence.Entity; |
| import javax.persistence.FetchType; |
| import javax.persistence.GeneratedValue; |
| import javax.persistence.GenerationType; |
| import javax.persistence.Id; |
| import javax.persistence.Inheritance; |
| import javax.persistence.InheritanceType; |
| import javax.persistence.JoinColumn; |
| import javax.persistence.JoinTable; |
| import javax.persistence.ManyToMany; |
| import javax.persistence.ManyToOne; |
| import javax.persistence.SequenceGenerator; |
| import javax.persistence.Table; |
| import javax.persistence.criteria.CriteriaBuilder; |
| import javax.persistence.criteria.CriteriaQuery; |
| import javax.persistence.criteria.From; |
| import javax.persistence.criteria.JoinType; |
| import javax.persistence.criteria.Predicate; |
| import org.hibernate.Session; |
| import org.jboss.logging.Logger; |
| |
| @Entity |
| @Table(name = "posts", schema = "benki") |
| @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) |
| public abstract class Post extends PanacheEntityBase { |
| |
| private static Logger log = Logger.getLogger(Post.class); |
| |
| @Id |
| @SequenceGenerator( |
| allocationSize = 1, |
| sequenceName = "posts_id_seq", |
| name = "posts_id_seq", |
| schema = "benki") |
| @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "posts_id_seq") |
| @Column(name = "id", nullable = false) |
| public Integer id; |
| |
| @Column(name = "date", nullable = true) |
| public OffsetDateTime date; |
| |
| @ManyToOne(fetch = FetchType.LAZY) |
| @JoinColumn(name = "owner", referencedColumnName = "id") |
| public User owner; |
| |
| @ManyToMany(fetch = FetchType.LAZY) |
| @JoinTable( |
| name = "user_visible_posts", |
| schema = "benki", |
| joinColumns = @JoinColumn(name = "message"), |
| inverseJoinColumns = @JoinColumn(name = "user")) |
| public Set<User> visibleTo; |
| |
| @ManyToMany(fetch = FetchType.LAZY) |
| @JoinTable( |
| name = "post_targets", |
| schema = "benki", |
| joinColumns = @JoinColumn(name = "message"), |
| inverseJoinColumns = @JoinColumn(name = "target")) |
| public Set<Role> targets; |
| |
| public abstract boolean isBookmark(); |
| |
| public abstract boolean isLazychatMessage(); |
| |
| protected static <T extends Post> CriteriaQuery<T> queryViewable( |
| Class<T> entityClass, |
| SecurityIdentity readerIdentity, |
| @CheckForNull User owner, |
| @CheckForNull Integer cursor, |
| CriteriaBuilder cb, |
| boolean forward) { |
| CriteriaQuery<T> query = cb.createQuery(entityClass); |
| |
| var conditions = new ArrayList<Predicate>(); |
| |
| From<?, T> post; |
| if (readerIdentity.isAnonymous()) { |
| post = query.from(entityClass); |
| var target = post.join(Post_.targets); |
| conditions.add(cb.equal(target, Role.getWorld())); |
| } else { |
| var userName = readerIdentity.getPrincipal().getName(); |
| var user = User.findByNickname(userName); |
| |
| var root = query.from(User.class); |
| conditions.add(cb.equal(root, user)); |
| if (entityClass.isAssignableFrom(Bookmark.class)) { |
| post = (From<?, T>) root.join(User_.visibleBookmarks); |
| } else if (entityClass.isAssignableFrom(LazychatMessage.class)) { |
| post = (From<?, T>) root.join(User_.visibleLazychatMessages); |
| } else { |
| post = (From<?, T>) root.join(User_.visiblePosts); |
| } |
| } |
| |
| query.select(post); |
| post.fetch(Post_.owner, JoinType.LEFT); |
| |
| if (owner != null) { |
| conditions.add(cb.equal(post.get(Post_.owner), owner)); |
| } |
| |
| if (forward) { |
| query.orderBy(cb.desc(post.get(Post_.id))); |
| } else { |
| query.orderBy(cb.asc(post.get(Post_.id))); |
| } |
| |
| if (cursor != null) { |
| if (forward) { |
| conditions.add(cb.le(post.get(Post_.id), cursor)); |
| } else { |
| conditions.add(cb.gt(post.get(Post_.id), cursor)); |
| } |
| } |
| |
| query.where(conditions.toArray(new Predicate[0])); |
| |
| return query; |
| } |
| |
| public static class PostPage<T extends Post> { |
| public @CheckForNull Integer prevCursor; |
| public @CheckForNull Integer cursor; |
| public @CheckForNull Integer nextCursor; |
| public List<T> posts; |
| |
| private PostPage( |
| @CheckForNull Integer c0, |
| @CheckForNull Integer c1, |
| @CheckForNull Integer c2, |
| List<T> resultList) { |
| this.prevCursor = c0; |
| this.cursor = c1; |
| this.nextCursor = c2; |
| this.posts = resultList; |
| } |
| } |
| |
| public static PostPage<Post> findViewable( |
| PostFilter postFilter, |
| Session session, |
| SecurityIdentity viewer, |
| @CheckForNull User owner, |
| @CheckForNull Integer cursor, |
| @CheckForNull Integer count) { |
| Class<? extends Post> entityClass; |
| switch (postFilter) { |
| case BOOKMARKS_ONLY: |
| entityClass = Bookmark.class; |
| break; |
| case LAZYCHAT_MESSAGES_ONLY: |
| entityClass = LazychatMessage.class; |
| break; |
| default: |
| entityClass = Post.class; |
| } |
| return findViewable(entityClass, session, viewer, owner, cursor, count); |
| } |
| |
| protected static <T extends Post> PostPage<T> findViewable( |
| Class<? extends T> entityClass, |
| Session session, |
| SecurityIdentity viewer, |
| @CheckForNull User owner, |
| @CheckForNull Integer cursor, |
| @CheckForNull Integer count) { |
| |
| if (cursor != null) { |
| Objects.requireNonNull(count); |
| } |
| |
| var cb = session.getCriteriaBuilder(); |
| |
| var forwardCriteria = queryViewable(entityClass, viewer, owner, cursor, cb, true); |
| var forwardQuery = session.createQuery(forwardCriteria); |
| |
| if (count != null) { |
| forwardQuery.setMaxResults(count + 1); |
| } |
| |
| log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString()); |
| |
| @CheckForNull Integer prevCursor = null; |
| @CheckForNull Integer nextCursor = null; |
| |
| if (cursor != null) { |
| // Look backwards as well so we can find the prevCursor. |
| var backwardCriteria = queryViewable(entityClass, viewer, owner, cursor, cb, false); |
| var backwardQuery = session.createQuery(backwardCriteria); |
| backwardQuery.setMaxResults(count); |
| var backwardResults = backwardQuery.getResultList(); |
| if (!backwardResults.isEmpty()) { |
| prevCursor = backwardResults.get(backwardResults.size() - 1).id; |
| } |
| } |
| |
| var forwardResults = (List<T>) forwardQuery.getResultList(); |
| if (count != null) { |
| if (forwardResults.size() == count + 1) { |
| nextCursor = forwardResults.get(count).id; |
| forwardResults.remove((int) count); |
| } |
| } |
| |
| return new PostPage<T>(prevCursor, cursor, nextCursor, forwardResults); |
| } |
| } |