blob: f783ba031bd9c262dba5d46472a6ab456c638704 [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 Benkardd5498fc2020-08-23 21:51:00 +02003import static java.util.stream.Collectors.toList;
4
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +01005import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType;
Matthias Andreas Benkard2d4f92e2020-02-09 16:15:07 +01006import eu.mulk.mulkcms2.benki.accesscontrol.Role;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +01007import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
8import eu.mulk.mulkcms2.benki.lazychat.LazychatMessage;
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +01009import eu.mulk.mulkcms2.benki.newsletter.Newsletter;
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010010import eu.mulk.mulkcms2.benki.users.User;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010011import eu.mulk.mulkcms2.benki.users.User_;
Matthias Andreas Benkard35cb1592020-01-24 11:05:20 +010012import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
Matthias Andreas Benkard1c2a8a72020-04-26 06:09:57 +020013import java.time.LocalDate;
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010014import java.time.OffsetDateTime;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010015import java.util.ArrayList;
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010016import java.util.Collection;
Matthias Andreas Benkard1c2a8a72020-04-26 06:09:57 +020017import java.util.Comparator;
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +020018import java.util.HashMap;
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010019import java.util.List;
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +020020import java.util.Map;
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010021import java.util.Objects;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010022import java.util.Set;
Matthias Andreas Benkard1c2a8a72020-04-26 06:09:57 +020023import java.util.TimeZone;
24import java.util.stream.Collectors;
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +020025import java.util.stream.Stream;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010026import javax.annotation.CheckForNull;
Matthias Andreas Benkard6cfe16b2020-04-18 15:36:04 +020027import javax.annotation.Nullable;
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +020028import javax.json.bind.annotation.JsonbTransient;
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +020029import javax.persistence.CascadeType;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010030import javax.persistence.Column;
31import javax.persistence.Entity;
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +010032import javax.persistence.EnumType;
33import javax.persistence.Enumerated;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010034import javax.persistence.FetchType;
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010035import javax.persistence.GeneratedValue;
36import javax.persistence.GenerationType;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010037import javax.persistence.Id;
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010038import javax.persistence.Inheritance;
39import javax.persistence.InheritanceType;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010040import javax.persistence.JoinColumn;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +010041import javax.persistence.JoinTable;
42import javax.persistence.ManyToMany;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010043import javax.persistence.ManyToOne;
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +020044import javax.persistence.MapKey;
45import javax.persistence.OneToMany;
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010046import javax.persistence.SequenceGenerator;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010047import javax.persistence.Table;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +010048import javax.persistence.criteria.CriteriaBuilder;
49import javax.persistence.criteria.CriteriaQuery;
50import javax.persistence.criteria.From;
51import javax.persistence.criteria.JoinType;
52import javax.persistence.criteria.Predicate;
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010053import org.hibernate.Session;
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +010054import org.hibernate.annotations.Type;
55import org.hibernate.annotations.TypeDef;
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010056import org.jboss.logging.Logger;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010057
58@Entity
Matthias Andreas Benkard57c9a8a2020-01-24 19:09:38 +010059@Table(name = "posts", schema = "benki")
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010060@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +010061@TypeDef(name = "pg_enum", typeClass = PostgreSQLEnumType.class)
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +020062public abstract class Post<Text extends PostText<?>> extends PanacheEntityBase {
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010063
Matthias Andreas Benkard593765d2020-04-18 20:44:07 +020064 private static final Logger log = Logger.getLogger(Post.class);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +010065
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +010066 public enum Scope {
67 top_level,
68 comment
69 }
70
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010071 @Id
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010072 @SequenceGenerator(
73 allocationSize = 1,
74 sequenceName = "posts_id_seq",
75 name = "posts_id_seq",
76 schema = "benki")
77 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "posts_id_seq")
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010078 @Column(name = "id", nullable = false)
Matthias Andreas Benkard0246c3e2020-01-27 05:39:08 +010079 public Integer id;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010080
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010081 @Column(name = "date", nullable = true)
Matthias Andreas Benkard1e7674c2020-04-18 20:28:51 +020082 @CheckForNull
Matthias Andreas Benkardd9b95882020-01-24 11:42:49 +010083 public OffsetDateTime date;
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010084
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +010085 @Column(nullable = false)
86 @Enumerated(EnumType.STRING)
87 @Type(type = "pg_enum")
88 public Scope scope = Scope.top_level;
89
Matthias Andreas Benkardaa754802020-01-24 11:55:26 +010090 @ManyToOne(fetch = FetchType.LAZY)
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010091 @JoinColumn(name = "newsletter", referencedColumnName = "id", nullable = true)
92 @CheckForNull
93 @JsonbTransient
94 public Newsletter newsletter;
95
96 @ManyToOne(fetch = FetchType.LAZY)
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +010097 @JoinColumn(name = "owner", referencedColumnName = "id")
Matthias Andreas Benkardcf0fe882020-04-19 18:33:37 +020098 @CheckForNull
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +020099 @JsonbTransient
Matthias Andreas Benkard35cb1592020-01-24 11:05:20 +0100100 public User owner;
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +0100101
102 @ManyToMany(fetch = FetchType.LAZY)
103 @JoinTable(
104 name = "user_visible_posts",
Matthias Andreas Benkard553de3e2020-01-27 05:33:15 +0100105 schema = "benki",
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +0100106 joinColumns = @JoinColumn(name = "message"),
107 inverseJoinColumns = @JoinColumn(name = "user"))
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +0200108 @JsonbTransient
Matthias Andreas Benkardf9c74272020-01-24 11:51:35 +0100109 public Set<User> visibleTo;
Matthias Andreas Benkard2d4f92e2020-02-09 16:15:07 +0100110
111 @ManyToMany(fetch = FetchType.LAZY)
112 @JoinTable(
113 name = "post_targets",
114 schema = "benki",
115 joinColumns = @JoinColumn(name = "message"),
116 inverseJoinColumns = @JoinColumn(name = "target"))
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +0200117 @JsonbTransient
Matthias Andreas Benkard2d4f92e2020-02-09 16:15:07 +0100118 public Set<Role> targets;
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100119
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200120 @OneToMany(
121 mappedBy = "post",
122 fetch = FetchType.LAZY,
123 cascade = CascadeType.ALL,
124 targetEntity = PostText.class)
125 @MapKey(name = "language")
126 public Map<String, Text> texts = new HashMap<>();
127
128 public Map<String, Text> getTexts() {
129 return texts;
130 }
131
Matthias Andreas Benkard371164a2020-03-23 06:21:25 +0100132 public abstract boolean isBookmark();
133
134 public abstract boolean isLazychatMessage();
135
Matthias Andreas Benkardd5ae0d52020-03-29 18:57:22 +0200136 @CheckForNull
137 public abstract String getTitle();
138
139 @CheckForNull
Matthias Andreas Benkardd5ae0d52020-03-29 18:57:22 +0200140 public abstract String getUri();
141
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +0200142 public Visibility getVisibility() {
143 if (targets.isEmpty()) {
144 return Visibility.PRIVATE;
145 } else if (targets.contains(Role.getWorld())) {
146 return Visibility.PUBLIC;
147 } else {
148 // FIXME: There should really be a check whether targets.equals(owner.defaultTargets) here.
149 // Otherwise the actual visibility is DISCRETIONARY.
150 return Visibility.SEMIPRIVATE;
151 }
152 }
153
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100154 protected static <T extends Post> CriteriaQuery<T> queryViewable(
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100155 Class<T> entityClass,
Matthias Andreas Benkardcf0fe882020-04-19 18:33:37 +0200156 @CheckForNull User reader,
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100157 @CheckForNull User owner,
158 @CheckForNull Integer cursor,
159 CriteriaBuilder cb,
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200160 boolean forward,
161 @CheckForNull String searchQuery) {
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100162 CriteriaQuery<T> query = cb.createQuery(entityClass);
163
164 var conditions = new ArrayList<Predicate>();
165
166 From<?, T> post;
Matthias Andreas Benkardcf0fe882020-04-19 18:33:37 +0200167 if (reader == null) {
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100168 post = query.from(entityClass);
169 var target = post.join(Post_.targets);
170 conditions.add(cb.equal(target, Role.getWorld()));
171 } else {
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100172 var root = query.from(User.class);
Matthias Andreas Benkardcf0fe882020-04-19 18:33:37 +0200173 conditions.add(cb.equal(root, reader));
Matthias Andreas Benkardca4d7942020-04-18 14:13:41 +0200174 if (entityClass.isAssignableFrom(Post.class)) {
175 post = (From<?, T>) root.join(User_.visiblePosts);
176 } else if (entityClass.isAssignableFrom(Bookmark.class)) {
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100177 post = (From<?, T>) root.join(User_.visibleBookmarks);
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200178 } else if (entityClass.isAssignableFrom(LazychatMessage.class)) {
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100179 post = (From<?, T>) root.join(User_.visibleLazychatMessages);
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200180 } else {
Matthias Andreas Benkardca4d7942020-04-18 14:13:41 +0200181 throw new IllegalArgumentException();
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100182 }
183 }
184
185 query.select(post);
186 post.fetch(Post_.owner, JoinType.LEFT);
187
188 if (owner != null) {
189 conditions.add(cb.equal(post.get(Post_.owner), owner));
190 }
191
192 if (forward) {
193 query.orderBy(cb.desc(post.get(Post_.id)));
194 } else {
195 query.orderBy(cb.asc(post.get(Post_.id)));
196 }
197
198 if (cursor != null) {
199 if (forward) {
200 conditions.add(cb.le(post.get(Post_.id), cursor));
201 } else {
202 conditions.add(cb.gt(post.get(Post_.id), cursor));
203 }
204 }
205
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200206 if (searchQuery != null && !searchQuery.isBlank()) {
207 var postTexts = post.join(Post_.texts);
208 var localizedSearches =
209 Stream.of("de", "en")
210 .map(
211 language ->
212 cb.isTrue(
213 cb.function(
214 "post_matches_websearch",
215 Boolean.class,
216 postTexts.get(PostText_.searchTerms),
217 cb.literal(language),
218 cb.literal(searchQuery))))
219 .toArray(n -> new Predicate[n]);
220 conditions.add(cb.or(localizedSearches));
221 }
222
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +0100223 conditions.add(cb.equal(post.get(Post_.scope), Scope.top_level));
224
Matthias Andreas Benkardf5999552020-03-22 06:52:06 +0100225 query.where(conditions.toArray(new Predicate[0]));
226
227 return query;
228 }
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100229
Matthias Andreas Benkard6cfe16b2020-04-18 15:36:04 +0200230 public final boolean isVisibleTo(@Nullable User user) {
231 // FIXME: Make this more efficient.
232 return getVisibility() == Visibility.PUBLIC || (user != null && visibleTo.contains(user));
233 }
234
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200235 @CheckForNull
236 public final String getDescriptionHtml() {
237 var text = getText();
238 if (text == null) {
239 return null;
240 }
241 return text.getDescriptionHtml();
242 }
243
Matthias Andreas Benkard67c60672021-01-30 14:43:39 +0100244 public final boolean isTopLevel() {
245 return scope == Scope.top_level;
246 }
247
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200248 public static class PostPage<T extends Post<? extends PostText>> {
Matthias Andreas Benkard593765d2020-04-18 20:44:07 +0200249 public @CheckForNull final Integer prevCursor;
250 public @CheckForNull final Integer cursor;
251 public @CheckForNull final Integer nextCursor;
252 public final List<T> posts;
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100253
Matthias Andreas Benkard1c2a8a72020-04-26 06:09:57 +0200254 private static final TimeZone timeZone = TimeZone.getDefault();
255
256 public PostPage(
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100257 @CheckForNull Integer c0,
258 @CheckForNull Integer c1,
259 @CheckForNull Integer c2,
260 List<T> resultList) {
261 this.prevCursor = c0;
262 this.cursor = c1;
263 this.nextCursor = c2;
264 this.posts = resultList;
265 }
Matthias Andreas Benkard1c2a8a72020-04-26 06:09:57 +0200266
Matthias Andreas Benkard60c08922020-06-13 19:22:25 +0200267 public void cacheDescriptions() {
268 days().forEach(Day::cacheDescriptions);
269 }
270
Matthias Andreas Benkard1c2a8a72020-04-26 06:09:57 +0200271 public class Day {
272 public final @CheckForNull LocalDate date;
273 public final List<T> posts;
274
275 private Day(LocalDate date, List<T> posts) {
276 this.date = date;
277 this.posts = posts;
278 }
Matthias Andreas Benkard60c08922020-06-13 19:22:25 +0200279
280 public void cacheDescriptions() {
281 for (var post : posts) {
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200282 post.getTexts().values().forEach(PostText::getDescriptionHtml);
Matthias Andreas Benkard60c08922020-06-13 19:22:25 +0200283 }
284 }
Matthias Andreas Benkard1c2a8a72020-04-26 06:09:57 +0200285 }
286
287 public List<Day> days() {
288 return posts.stream()
289 .collect(Collectors.groupingBy(post -> post.date.toLocalDate()))
290 .entrySet()
291 .stream()
292 .map(x -> new Day(x.getKey(), x.getValue()))
293 .sorted(Comparator.comparing((Day day) -> day.date).reversed())
294 .collect(Collectors.toUnmodifiableList());
295 }
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100296 }
297
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200298 public static PostPage<Post<? extends PostText>> findViewable(
Matthias Andreas Benkardcf0fe882020-04-19 18:33:37 +0200299 PostFilter postFilter, Session session, @CheckForNull User viewer, @CheckForNull User owner) {
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200300 return findViewable(postFilter, session, viewer, owner, null, null, null);
Matthias Andreas Benkardd5ae0d52020-03-29 18:57:22 +0200301 }
302
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200303 public static PostPage<Post<? extends PostText>> findViewable(
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200304 PostFilter postFilter,
305 Session session,
Matthias Andreas Benkardcf0fe882020-04-19 18:33:37 +0200306 @CheckForNull User viewer,
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200307 @CheckForNull User owner,
308 @CheckForNull Integer cursor,
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200309 @CheckForNull Integer count,
310 @CheckForNull String searchQuery) {
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200311 Class<? extends Post> entityClass;
312 switch (postFilter) {
313 case BOOKMARKS_ONLY:
314 entityClass = Bookmark.class;
315 break;
316 case LAZYCHAT_MESSAGES_ONLY:
317 entityClass = LazychatMessage.class;
318 break;
319 default:
320 entityClass = Post.class;
321 }
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200322 return findViewable(entityClass, session, viewer, owner, cursor, count, searchQuery);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100323 }
324
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200325 protected static <T extends Post<? extends PostText>> PostPage<T> findViewable(
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200326 Class<? extends T> entityClass,
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100327 Session session,
Matthias Andreas Benkardcf0fe882020-04-19 18:33:37 +0200328 @CheckForNull User viewer,
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100329 @CheckForNull User owner,
330 @CheckForNull Integer cursor,
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200331 @CheckForNull Integer count,
332 @CheckForNull String searchQuery) {
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100333
334 if (cursor != null) {
335 Objects.requireNonNull(count);
336 }
337
338 var cb = session.getCriteriaBuilder();
339
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200340 var forwardCriteria = queryViewable(entityClass, viewer, owner, cursor, cb, true, searchQuery);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100341 var forwardQuery = session.createQuery(forwardCriteria);
342
343 if (count != null) {
344 forwardQuery.setMaxResults(count + 1);
345 }
346
347 log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString());
348
349 @CheckForNull Integer prevCursor = null;
350 @CheckForNull Integer nextCursor = null;
351
352 if (cursor != null) {
353 // Look backwards as well so we can find the prevCursor.
Matthias Andreas Benkard8563a3c2020-09-16 17:57:24 +0200354 var backwardCriteria =
355 queryViewable(entityClass, viewer, owner, cursor, cb, false, searchQuery);
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100356 var backwardQuery = session.createQuery(backwardCriteria);
357 backwardQuery.setMaxResults(count);
358 var backwardResults = backwardQuery.getResultList();
359 if (!backwardResults.isEmpty()) {
360 prevCursor = backwardResults.get(backwardResults.size() - 1).id;
361 }
362 }
363
Matthias Andreas Benkard4940b292020-03-29 18:41:07 +0200364 var forwardResults = (List<T>) forwardQuery.getResultList();
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100365 if (count != null) {
366 if (forwardResults.size() == count + 1) {
367 nextCursor = forwardResults.get(count).id;
368 forwardResults.remove((int) count);
369 }
370 }
371
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200372 // Fetch texts (to avoid n+1 selects).
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +0100373 fetchTexts(forwardResults);
374
375 return new PostPage<>(prevCursor, cursor, nextCursor, forwardResults);
376 }
377
378 public static <T extends Post<?>> void fetchTexts(Collection<T> posts) {
379 var postIds = posts.stream().map(x -> x.id).collect(toList());
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200380
381 if (!postIds.isEmpty()) {
382 find("SELECT p FROM Post p LEFT JOIN FETCH p.texts WHERE p.id IN (?1)", postIds).stream()
383 .count();
384 }
Matthias Andreas Benkard3d399f32020-03-22 07:23:07 +0100385 }
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +0200386
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200387 @CheckForNull
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +0100388 public Text getText() {
Matthias Andreas Benkardd5498fc2020-08-23 21:51:00 +0200389 var texts = getTexts();
390 if (texts.isEmpty()) {
391 return null;
392 } else if (texts.containsKey("")) {
393 return texts.get("");
394 } else if (texts.containsKey("en")) {
395 return texts.get("en");
396 } else {
397 return texts.values().stream().findAny().get();
398 }
399 }
400
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +0200401 public enum Visibility {
402 PUBLIC,
403 SEMIPRIVATE,
404 DISCRETIONARY,
405 PRIVATE,
406 }
407
408 @Override
409 public boolean equals(Object o) {
410 if (this == o) {
411 return true;
412 }
413 if (!(o instanceof Post)) {
414 return false;
415 }
Matthias Andreas Benkardde46b232021-02-06 07:51:57 +0100416 Post<?> post = (Post<?>) o;
Matthias Andreas Benkard06e6c812020-04-13 17:01:35 +0200417 return Objects.equals(id, post.id);
418 }
419
420 @Override
421 public int hashCode() {
422 return Objects.hash(id);
423 }
Matthias Andreas Benkard734879e2020-01-24 10:47:37 +0100424}