blob: 20aec0503625f784e6a8188c47a35d505dc7baaa [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 Benkardd5ae0d52020-03-29 18:57:22 +020082 @CheckForNull
83 public abstract String getTitle();
84
85 @CheckForNull
86 public abstract String getDescriptionHtml();
87
88 @CheckForNull
89 public abstract String getUri();
90
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010091 protected static <T extends Post> CriteriaQuery<T> queryViewable(
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010092 Class<T> entityClass,
93 SecurityIdentity readerIdentity,
94 @CheckForNull User owner,
95 @CheckForNull Integer cursor,
96 CriteriaBuilder cb,
97 boolean forward) {
98 CriteriaQuery<T> query = cb.createQuery(entityClass);
99
100 var conditions = new ArrayList<Predicate>();
101
102 From<?, T> post;
103 if (readerIdentity.isAnonymous()) {
104 post = query.from(entityClass);
105 var target = post.join(Post_.targets);
106 conditions.add(cb.equal(target, Role.getWorld()));
107 } else {
108 var userName = readerIdentity.getPrincipal().getName();
109 var user = User.findByNickname(userName);
110
111 var root = query.from(User.class);
112 conditions.add(cb.equal(root, user));
113 if (entityClass.isAssignableFrom(Bookmark.class)) {
114 post = (From<?, T>) root.join(User_.visibleBookmarks);
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200115 } else if (entityClass.isAssignableFrom(LazychatMessage.class)) {
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100116 post = (From<?, T>) root.join(User_.visibleLazychatMessages);
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200117 } else {
118 post = (From<?, T>) root.join(User_.visiblePosts);
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100119 }
120 }
121
122 query.select(post);
123 post.fetch(Post_.owner, JoinType.LEFT);
124
125 if (owner != null) {
126 conditions.add(cb.equal(post.get(Post_.owner), owner));
127 }
128
129 if (forward) {
130 query.orderBy(cb.desc(post.get(Post_.id)));
131 } else {
132 query.orderBy(cb.asc(post.get(Post_.id)));
133 }
134
135 if (cursor != null) {
136 if (forward) {
137 conditions.add(cb.le(post.get(Post_.id), cursor));
138 } else {
139 conditions.add(cb.gt(post.get(Post_.id), cursor));
140 }
141 }
142
143 query.where(conditions.toArray(new Predicate[0]));
144
145 return query;
146 }
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100147
148 public static class PostPage<T extends Post> {
149 public @CheckForNull Integer prevCursor;
150 public @CheckForNull Integer cursor;
151 public @CheckForNull Integer nextCursor;
152 public List<T> posts;
153
154 private PostPage(
155 @CheckForNull Integer c0,
156 @CheckForNull Integer c1,
157 @CheckForNull Integer c2,
158 List<T> resultList) {
159 this.prevCursor = c0;
160 this.cursor = c1;
161 this.nextCursor = c2;
162 this.posts = resultList;
163 }
164 }
165
Matthias Andreas Benkardd5ae0d52020-03-29 18:57:22 +0200166 public static List<Post> findViewable(
167 PostFilter postFilter, Session session, SecurityIdentity viewer, @CheckForNull User owner) {
168 return findViewable(postFilter, session, viewer, owner, null, null).posts;
169 }
170
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200171 public static PostPage<Post> findViewable(
172 PostFilter postFilter,
173 Session session,
174 SecurityIdentity viewer,
175 @CheckForNull User owner,
176 @CheckForNull Integer cursor,
177 @CheckForNull Integer count) {
178 Class<? extends Post> entityClass;
179 switch (postFilter) {
180 case BOOKMARKS_ONLY:
181 entityClass = Bookmark.class;
182 break;
183 case LAZYCHAT_MESSAGES_ONLY:
184 entityClass = LazychatMessage.class;
185 break;
186 default:
187 entityClass = Post.class;
188 }
189 return findViewable(entityClass, session, viewer, owner, cursor, count);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100190 }
191
192 protected static <T extends Post> PostPage<T> findViewable(
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200193 Class<? extends T> entityClass,
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100194 Session session,
195 SecurityIdentity viewer,
196 @CheckForNull User owner,
197 @CheckForNull Integer cursor,
198 @CheckForNull Integer count) {
199
200 if (cursor != null) {
201 Objects.requireNonNull(count);
202 }
203
204 var cb = session.getCriteriaBuilder();
205
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200206 var forwardCriteria = queryViewable(entityClass, viewer, owner, cursor, cb, true);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100207 var forwardQuery = session.createQuery(forwardCriteria);
208
209 if (count != null) {
210 forwardQuery.setMaxResults(count + 1);
211 }
212
213 log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString());
214
215 @CheckForNull Integer prevCursor = null;
216 @CheckForNull Integer nextCursor = null;
217
218 if (cursor != null) {
219 // Look backwards as well so we can find the prevCursor.
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200220 var backwardCriteria = queryViewable(entityClass, viewer, owner, cursor, cb, false);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100221 var backwardQuery = session.createQuery(backwardCriteria);
222 backwardQuery.setMaxResults(count);
223 var backwardResults = backwardQuery.getResultList();
224 if (!backwardResults.isEmpty()) {
225 prevCursor = backwardResults.get(backwardResults.size() - 1).id;
226 }
227 }
228
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200229 var forwardResults = (List<T>) forwardQuery.getResultList();
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100230 if (count != null) {
231 if (forwardResults.size() == count + 1) {
232 nextCursor = forwardResults.get(count).id;
233 forwardResults.remove((int) count);
234 }
235 }
236
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200237 return new PostPage<T>(prevCursor, cursor, nextCursor, forwardResults);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100238 }
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +0100239}