blob: fc9ba782fec0752846f1a87f0cc93020c402fab5 [file] [log] [blame]
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +02001package eu.mulk.mulkcms2.benki.posts;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +01002
Matthias Andreas Benkard2d4f92e2020-02-09 16:15:07 +01003import eu.mulk.mulkcms2.benki.accesscontrol.Role;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +01004import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
5import eu.mulk.mulkcms2.benki.lazychat.LazychatMessage;
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +01006import eu.mulk.mulkcms2.benki.users.User;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +01007import eu.mulk.mulkcms2.benki.users.User_;
Matthias Andreas Benkard35cb1592020-01-24 11:05:20 +01008import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +01009import io.quarkus.security.identity.SecurityIdentity;
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010010import java.time.OffsetDateTime;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010011import java.util.ArrayList;
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010012import java.util.List;
13import java.util.Objects;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010014import java.util.Set;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010015import javax.annotation.CheckForNull;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010016import javax.persistence.Column;
17import javax.persistence.Entity;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010018import javax.persistence.FetchType;
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010019import javax.persistence.GeneratedValue;
20import javax.persistence.GenerationType;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010021import javax.persistence.Id;
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010022import javax.persistence.Inheritance;
23import javax.persistence.InheritanceType;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010024import javax.persistence.JoinColumn;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010025import javax.persistence.JoinTable;
26import javax.persistence.ManyToMany;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010027import javax.persistence.ManyToOne;
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010028import javax.persistence.SequenceGenerator;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010029import javax.persistence.Table;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010030import javax.persistence.criteria.CriteriaBuilder;
31import javax.persistence.criteria.CriteriaQuery;
32import javax.persistence.criteria.From;
33import javax.persistence.criteria.JoinType;
34import javax.persistence.criteria.Predicate;
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010035import org.hibernate.Session;
36import org.jboss.logging.Logger;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010037
38@Entity
Matthias Andreas Benkard57c9a8a2020-01-24 19:09:38 +010039@Table(name = "posts", schema = "benki")
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010040@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
41public abstract class Post extends PanacheEntityBase {
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010042
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010043 private static Logger log = Logger.getLogger(Post.class);
44
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010045 @Id
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010046 @SequenceGenerator(
47 allocationSize = 1,
48 sequenceName = "posts_id_seq",
49 name = "posts_id_seq",
50 schema = "benki")
51 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "posts_id_seq")
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010052 @Column(name = "id", nullable = false)
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010053 public Integer id;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010054
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010055 @Column(name = "date", nullable = true)
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010056 public OffsetDateTime date;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010057
Matthias Andreas Benkardaa754802020-01-24 11:55:26 +010058 @ManyToOne(fetch = FetchType.LAZY)
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010059 @JoinColumn(name = "owner", referencedColumnName = "id")
Matthias Andreas Benkard35cb1592020-01-24 11:05:20 +010060 public User owner;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010061
62 @ManyToMany(fetch = FetchType.LAZY)
63 @JoinTable(
64 name = "user_visible_posts",
Matthias Andreas Benkard553de3e2020-01-27 05:33:15 +010065 schema = "benki",
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010066 joinColumns = @JoinColumn(name = "message"),
67 inverseJoinColumns = @JoinColumn(name = "user"))
68 public Set<User> visibleTo;
Matthias Andreas Benkard2d4f92e2020-02-09 16:15:07 +010069
70 @ManyToMany(fetch = FetchType.LAZY)
71 @JoinTable(
72 name = "post_targets",
73 schema = "benki",
74 joinColumns = @JoinColumn(name = "message"),
75 inverseJoinColumns = @JoinColumn(name = "target"))
76 public Set<Role> targets;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010077
Matthias Andreas Benkard371164a2020-03-23 06:21:25 +010078 public abstract boolean isBookmark();
79
80 public abstract boolean isLazychatMessage();
81
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010082 protected static <T extends Post> CriteriaQuery<T> queryViewable(
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010083 Class<T> entityClass,
84 SecurityIdentity readerIdentity,
85 @CheckForNull User owner,
86 @CheckForNull Integer cursor,
87 CriteriaBuilder cb,
88 boolean forward) {
89 CriteriaQuery<T> query = cb.createQuery(entityClass);
90
91 var conditions = new ArrayList<Predicate>();
92
93 From<?, T> post;
94 if (readerIdentity.isAnonymous()) {
95 post = query.from(entityClass);
96 var target = post.join(Post_.targets);
97 conditions.add(cb.equal(target, Role.getWorld()));
98 } else {
99 var userName = readerIdentity.getPrincipal().getName();
100 var user = User.findByNickname(userName);
101
102 var root = query.from(User.class);
103 conditions.add(cb.equal(root, user));
104 if (entityClass.isAssignableFrom(Bookmark.class)) {
105 post = (From<?, T>) root.join(User_.visibleBookmarks);
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200106 } else if (entityClass.isAssignableFrom(LazychatMessage.class)) {
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100107 post = (From<?, T>) root.join(User_.visibleLazychatMessages);
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200108 } else {
109 post = (From<?, T>) root.join(User_.visiblePosts);
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100110 }
111 }
112
113 query.select(post);
114 post.fetch(Post_.owner, JoinType.LEFT);
115
116 if (owner != null) {
117 conditions.add(cb.equal(post.get(Post_.owner), owner));
118 }
119
120 if (forward) {
121 query.orderBy(cb.desc(post.get(Post_.id)));
122 } else {
123 query.orderBy(cb.asc(post.get(Post_.id)));
124 }
125
126 if (cursor != null) {
127 if (forward) {
128 conditions.add(cb.le(post.get(Post_.id), cursor));
129 } else {
130 conditions.add(cb.gt(post.get(Post_.id), cursor));
131 }
132 }
133
134 query.where(conditions.toArray(new Predicate[0]));
135
136 return query;
137 }
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100138
139 public static class PostPage<T extends Post> {
140 public @CheckForNull Integer prevCursor;
141 public @CheckForNull Integer cursor;
142 public @CheckForNull Integer nextCursor;
143 public List<T> posts;
144
145 private PostPage(
146 @CheckForNull Integer c0,
147 @CheckForNull Integer c1,
148 @CheckForNull Integer c2,
149 List<T> resultList) {
150 this.prevCursor = c0;
151 this.cursor = c1;
152 this.nextCursor = c2;
153 this.posts = resultList;
154 }
155 }
156
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200157 public static PostPage<Post> findViewable(
158 PostFilter postFilter,
159 Session session,
160 SecurityIdentity viewer,
161 @CheckForNull User owner,
162 @CheckForNull Integer cursor,
163 @CheckForNull Integer count) {
164 Class<? extends Post> entityClass;
165 switch (postFilter) {
166 case BOOKMARKS_ONLY:
167 entityClass = Bookmark.class;
168 break;
169 case LAZYCHAT_MESSAGES_ONLY:
170 entityClass = LazychatMessage.class;
171 break;
172 default:
173 entityClass = Post.class;
174 }
175 return findViewable(entityClass, session, viewer, owner, cursor, count);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100176 }
177
178 protected static <T extends Post> PostPage<T> findViewable(
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200179 Class<? extends T> entityClass,
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100180 Session session,
181 SecurityIdentity viewer,
182 @CheckForNull User owner,
183 @CheckForNull Integer cursor,
184 @CheckForNull Integer count) {
185
186 if (cursor != null) {
187 Objects.requireNonNull(count);
188 }
189
190 var cb = session.getCriteriaBuilder();
191
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200192 var forwardCriteria = queryViewable(entityClass, viewer, owner, cursor, cb, true);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100193 var forwardQuery = session.createQuery(forwardCriteria);
194
195 if (count != null) {
196 forwardQuery.setMaxResults(count + 1);
197 }
198
199 log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString());
200
201 @CheckForNull Integer prevCursor = null;
202 @CheckForNull Integer nextCursor = null;
203
204 if (cursor != null) {
205 // Look backwards as well so we can find the prevCursor.
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200206 var backwardCriteria = queryViewable(entityClass, viewer, owner, cursor, cb, false);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100207 var backwardQuery = session.createQuery(backwardCriteria);
208 backwardQuery.setMaxResults(count);
209 var backwardResults = backwardQuery.getResultList();
210 if (!backwardResults.isEmpty()) {
211 prevCursor = backwardResults.get(backwardResults.size() - 1).id;
212 }
213 }
214
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200215 var forwardResults = (List<T>) forwardQuery.getResultList();
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100216 if (count != null) {
217 if (forwardResults.size() == count + 1) {
218 nextCursor = forwardResults.get(count).id;
219 forwardResults.remove((int) count);
220 }
221 }
222
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200223 return new PostPage<T>(prevCursor, cursor, nextCursor, forwardResults);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100224 }
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +0100225}