blob: 7d75bb41ef185dd184f5e95b195c8c974a27ebf9 [file] [log] [blame]
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +01001package eu.mulk.mulkcms2.benki.generic;
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);
106 } else {
107 assert entityClass.isAssignableFrom(LazychatMessage.class) : entityClass;
108 post = (From<?, T>) root.join(User_.visibleLazychatMessages);
109 }
110 }
111
112 query.select(post);
113 post.fetch(Post_.owner, JoinType.LEFT);
114
115 if (owner != null) {
116 conditions.add(cb.equal(post.get(Post_.owner), owner));
117 }
118
119 if (forward) {
120 query.orderBy(cb.desc(post.get(Post_.id)));
121 } else {
122 query.orderBy(cb.asc(post.get(Post_.id)));
123 }
124
125 if (cursor != null) {
126 if (forward) {
127 conditions.add(cb.le(post.get(Post_.id), cursor));
128 } else {
129 conditions.add(cb.gt(post.get(Post_.id), cursor));
130 }
131 }
132
133 query.where(conditions.toArray(new Predicate[0]));
134
135 return query;
136 }
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100137
138 public static class PostPage<T extends Post> {
139 public @CheckForNull Integer prevCursor;
140 public @CheckForNull Integer cursor;
141 public @CheckForNull Integer nextCursor;
142 public List<T> posts;
143
144 private PostPage(
145 @CheckForNull Integer c0,
146 @CheckForNull Integer c1,
147 @CheckForNull Integer c2,
148 List<T> resultList) {
149 this.prevCursor = c0;
150 this.cursor = c1;
151 this.nextCursor = c2;
152 this.posts = resultList;
153 }
154 }
155
156 protected static <T extends Post> List<T> findViewable(
157 Class<T> entityClass, Session session, SecurityIdentity viewer, @CheckForNull User owner) {
158 return findViewable(entityClass, session, viewer, owner, null, null).posts;
159 }
160
161 protected static <T extends Post> PostPage<T> findViewable(
162 Class<T> entityClass,
163 Session session,
164 SecurityIdentity viewer,
165 @CheckForNull User owner,
166 @CheckForNull Integer cursor,
167 @CheckForNull Integer count) {
168
169 if (cursor != null) {
170 Objects.requireNonNull(count);
171 }
172
173 var cb = session.getCriteriaBuilder();
174
175 var forwardCriteria = Bookmark.queryViewable(entityClass, viewer, owner, cursor, cb, true);
176 var forwardQuery = session.createQuery(forwardCriteria);
177
178 if (count != null) {
179 forwardQuery.setMaxResults(count + 1);
180 }
181
182 log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString());
183
184 @CheckForNull Integer prevCursor = null;
185 @CheckForNull Integer nextCursor = null;
186
187 if (cursor != null) {
188 // Look backwards as well so we can find the prevCursor.
189 var backwardCriteria = Bookmark.queryViewable(entityClass, viewer, owner, cursor, cb, false);
190 var backwardQuery = session.createQuery(backwardCriteria);
191 backwardQuery.setMaxResults(count);
192 var backwardResults = backwardQuery.getResultList();
193 if (!backwardResults.isEmpty()) {
194 prevCursor = backwardResults.get(backwardResults.size() - 1).id;
195 }
196 }
197
198 var forwardResults = forwardQuery.getResultList();
199 if (count != null) {
200 if (forwardResults.size() == count + 1) {
201 nextCursor = forwardResults.get(count).id;
202 forwardResults.remove((int) count);
203 }
204 }
205
206 return new PostPage(prevCursor, cursor, nextCursor, forwardResults);
207 }
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +0100208}