blob: 898eeb440294d53cb75de21f7ce2bfa08d40a1e0 [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 Benkard3d399f32020-03-22 07:23:07 +010078 protected static <T extends Post> CriteriaQuery<T> queryViewable(
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010079 Class<T> entityClass,
80 SecurityIdentity readerIdentity,
81 @CheckForNull User owner,
82 @CheckForNull Integer cursor,
83 CriteriaBuilder cb,
84 boolean forward) {
85 CriteriaQuery<T> query = cb.createQuery(entityClass);
86
87 var conditions = new ArrayList<Predicate>();
88
89 From<?, T> post;
90 if (readerIdentity.isAnonymous()) {
91 post = query.from(entityClass);
92 var target = post.join(Post_.targets);
93 conditions.add(cb.equal(target, Role.getWorld()));
94 } else {
95 var userName = readerIdentity.getPrincipal().getName();
96 var user = User.findByNickname(userName);
97
98 var root = query.from(User.class);
99 conditions.add(cb.equal(root, user));
100 if (entityClass.isAssignableFrom(Bookmark.class)) {
101 post = (From<?, T>) root.join(User_.visibleBookmarks);
102 } else {
103 assert entityClass.isAssignableFrom(LazychatMessage.class) : entityClass;
104 post = (From<?, T>) root.join(User_.visibleLazychatMessages);
105 }
106 }
107
108 query.select(post);
109 post.fetch(Post_.owner, JoinType.LEFT);
110
111 if (owner != null) {
112 conditions.add(cb.equal(post.get(Post_.owner), owner));
113 }
114
115 if (forward) {
116 query.orderBy(cb.desc(post.get(Post_.id)));
117 } else {
118 query.orderBy(cb.asc(post.get(Post_.id)));
119 }
120
121 if (cursor != null) {
122 if (forward) {
123 conditions.add(cb.le(post.get(Post_.id), cursor));
124 } else {
125 conditions.add(cb.gt(post.get(Post_.id), cursor));
126 }
127 }
128
129 query.where(conditions.toArray(new Predicate[0]));
130
131 return query;
132 }
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100133
134 public static class PostPage<T extends Post> {
135 public @CheckForNull Integer prevCursor;
136 public @CheckForNull Integer cursor;
137 public @CheckForNull Integer nextCursor;
138 public List<T> posts;
139
140 private PostPage(
141 @CheckForNull Integer c0,
142 @CheckForNull Integer c1,
143 @CheckForNull Integer c2,
144 List<T> resultList) {
145 this.prevCursor = c0;
146 this.cursor = c1;
147 this.nextCursor = c2;
148 this.posts = resultList;
149 }
150 }
151
152 protected static <T extends Post> List<T> findViewable(
153 Class<T> entityClass, Session session, SecurityIdentity viewer, @CheckForNull User owner) {
154 return findViewable(entityClass, session, viewer, owner, null, null).posts;
155 }
156
157 protected static <T extends Post> PostPage<T> findViewable(
158 Class<T> entityClass,
159 Session session,
160 SecurityIdentity viewer,
161 @CheckForNull User owner,
162 @CheckForNull Integer cursor,
163 @CheckForNull Integer count) {
164
165 if (cursor != null) {
166 Objects.requireNonNull(count);
167 }
168
169 var cb = session.getCriteriaBuilder();
170
171 var forwardCriteria = Bookmark.queryViewable(entityClass, viewer, owner, cursor, cb, true);
172 var forwardQuery = session.createQuery(forwardCriteria);
173
174 if (count != null) {
175 forwardQuery.setMaxResults(count + 1);
176 }
177
178 log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString());
179
180 @CheckForNull Integer prevCursor = null;
181 @CheckForNull Integer nextCursor = null;
182
183 if (cursor != null) {
184 // Look backwards as well so we can find the prevCursor.
185 var backwardCriteria = Bookmark.queryViewable(entityClass, viewer, owner, cursor, cb, false);
186 var backwardQuery = session.createQuery(backwardCriteria);
187 backwardQuery.setMaxResults(count);
188 var backwardResults = backwardQuery.getResultList();
189 if (!backwardResults.isEmpty()) {
190 prevCursor = backwardResults.get(backwardResults.size() - 1).id;
191 }
192 }
193
194 var forwardResults = forwardQuery.getResultList();
195 if (count != null) {
196 if (forwardResults.size() == count + 1) {
197 nextCursor = forwardResults.get(count).id;
198 forwardResults.remove((int) count);
199 }
200 }
201
202 return new PostPage(prevCursor, cursor, nextCursor, forwardResults);
203 }
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +0100204}