2 * Sone - WebInterface.java - Copyright © 2010–2019 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.sone.web;
20 import static com.google.common.collect.FluentIterable.from;
21 import static java.util.logging.Logger.getLogger;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
28 import java.util.TimeZone;
29 import java.util.UUID;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.ScheduledExecutorService;
32 import java.util.logging.Logger;
33 import javax.annotation.Nonnull;
34 import javax.annotation.Nullable;
35 import javax.inject.Named;
37 import net.pterodactylus.sone.core.Core;
38 import net.pterodactylus.sone.core.ElementLoader;
39 import net.pterodactylus.sone.core.event.*;
40 import net.pterodactylus.sone.data.Post;
41 import net.pterodactylus.sone.data.PostReply;
42 import net.pterodactylus.sone.data.Sone;
43 import net.pterodactylus.sone.freenet.L10nFilter;
44 import net.pterodactylus.sone.freenet.Translation;
45 import net.pterodactylus.sone.main.Loaders;
46 import net.pterodactylus.sone.main.PluginHomepage;
47 import net.pterodactylus.sone.main.PluginVersion;
48 import net.pterodactylus.sone.main.PluginYear;
49 import net.pterodactylus.sone.main.SonePlugin;
50 import net.pterodactylus.sone.notify.ListNotification;
51 import net.pterodactylus.sone.notify.ListNotificationFilter;
52 import net.pterodactylus.sone.notify.PostVisibilityFilter;
53 import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
54 import net.pterodactylus.sone.template.LinkedElementRenderFilter;
55 import net.pterodactylus.sone.template.ParserFilter;
56 import net.pterodactylus.sone.template.RenderFilter;
57 import net.pterodactylus.sone.template.ShortenFilter;
58 import net.pterodactylus.sone.text.Part;
59 import net.pterodactylus.sone.text.SonePart;
60 import net.pterodactylus.sone.text.SoneTextParser;
61 import net.pterodactylus.sone.text.TimeTextConverter;
62 import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage;
63 import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage;
64 import net.pterodactylus.sone.web.ajax.CreateReplyAjaxPage;
65 import net.pterodactylus.sone.web.ajax.DeletePostAjaxPage;
66 import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage;
67 import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage;
68 import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage;
69 import net.pterodactylus.sone.web.ajax.EditAlbumAjaxPage;
70 import net.pterodactylus.sone.web.ajax.EditImageAjaxPage;
71 import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
72 import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
73 import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
74 import net.pterodactylus.sone.web.ajax.GetLinkedElementAjaxPage;
75 import net.pterodactylus.sone.web.ajax.GetNotificationsAjaxPage;
76 import net.pterodactylus.sone.web.ajax.GetPostAjaxPage;
77 import net.pterodactylus.sone.web.ajax.GetReplyAjaxPage;
78 import net.pterodactylus.sone.web.ajax.GetStatusAjaxPage;
79 import net.pterodactylus.sone.web.ajax.GetTimesAjaxPage;
80 import net.pterodactylus.sone.web.ajax.GetTranslationAjaxPage;
81 import net.pterodactylus.sone.web.ajax.LikeAjaxPage;
82 import net.pterodactylus.sone.web.ajax.LockSoneAjaxPage;
83 import net.pterodactylus.sone.web.ajax.MarkAsKnownAjaxPage;
84 import net.pterodactylus.sone.web.ajax.MoveProfileFieldAjaxPage;
85 import net.pterodactylus.sone.web.ajax.UnbookmarkAjaxPage;
86 import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage;
87 import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage;
88 import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
89 import net.pterodactylus.sone.web.page.FreenetRequest;
90 import net.pterodactylus.sone.web.page.TemplateRenderer;
91 import net.pterodactylus.sone.web.pages.*;
92 import net.pterodactylus.util.notify.Notification;
93 import net.pterodactylus.util.notify.NotificationManager;
94 import net.pterodactylus.util.notify.TemplateNotification;
95 import net.pterodactylus.util.template.Template;
96 import net.pterodactylus.util.template.TemplateContextFactory;
97 import net.pterodactylus.util.web.RedirectPage;
98 import net.pterodactylus.util.web.TemplatePage;
100 import freenet.clients.http.SessionManager;
101 import freenet.clients.http.SessionManager.Session;
102 import freenet.clients.http.ToadletContext;
104 import com.codahale.metrics.*;
105 import com.google.common.base.Optional;
106 import com.google.common.collect.Collections2;
107 import com.google.common.collect.ImmutableSet;
108 import com.google.common.eventbus.Subscribe;
109 import com.google.inject.Inject;
112 * Bundles functionality that a web interface of a Freenet plugin needs, e.g.
113 * references to l10n helpers.
115 public class WebInterface implements SessionProvider {
118 private static final Logger logger = getLogger(WebInterface.class.getName());
120 /** The loaders for templates, pages, and classpath providers. */
121 private final Loaders loaders;
123 /** The notification manager. */
124 private final NotificationManager notificationManager;
126 /** The Sone plugin. */
127 private final SonePlugin sonePlugin;
129 /** The form password. */
130 private final String formPassword;
132 /** The template context factory. */
133 private final TemplateContextFactory templateContextFactory;
134 private final TemplateRenderer templateRenderer;
136 /** The Sone text parser. */
137 private final SoneTextParser soneTextParser;
139 /** The parser filter. */
140 private final ParserFilter parserFilter;
141 private final ShortenFilter shortenFilter;
142 private final RenderFilter renderFilter;
144 private final ListNotificationFilter listNotificationFilter;
145 private final PostVisibilityFilter postVisibilityFilter;
146 private final ReplyVisibilityFilter replyVisibilityFilter;
148 private final ElementLoader elementLoader;
149 private final LinkedElementRenderFilter linkedElementRenderFilter;
150 private final TimeTextConverter timeTextConverter = new TimeTextConverter();
151 private final L10nFilter l10nFilter;
153 private final PageToadletRegistry pageToadletRegistry;
154 private final MetricRegistry metricRegistry;
155 private final Translation translation;
157 /** The “new post” notification. */
158 private final ListNotification<Post> newPostNotification;
160 /** The “new reply” notification. */
161 private final ListNotification<PostReply> newReplyNotification;
163 /** The invisible “local post” notification. */
164 private final ListNotification<Post> localPostNotification;
166 /** The invisible “local reply” notification. */
167 private final ListNotification<PostReply> localReplyNotification;
169 /** The “you have been mentioned” notification. */
170 private final ListNotification<Post> mentionNotification;
172 /** Notifications for sone inserts. */
173 private final Map<Sone, TemplateNotification> soneInsertNotifications = new HashMap<>();
175 /** Scheduled executor for time-based notifications. */
176 private final ScheduledExecutorService ticker = Executors.newScheduledThreadPool(1);
179 public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter,
180 PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter,
181 ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
182 TemplateRenderer templateRenderer,
183 ParserFilter parserFilter, ShortenFilter shortenFilter,
184 RenderFilter renderFilter,
185 LinkedElementRenderFilter linkedElementRenderFilter,
186 PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter,
187 NotificationManager notificationManager, @Named("newRemotePost") ListNotification<Post> newPostNotification,
188 @Named("localPost") ListNotification<Post> localPostNotification) {
189 this.sonePlugin = sonePlugin;
190 this.loaders = loaders;
191 this.listNotificationFilter = listNotificationFilter;
192 this.postVisibilityFilter = postVisibilityFilter;
193 this.replyVisibilityFilter = replyVisibilityFilter;
194 this.elementLoader = elementLoader;
195 this.templateRenderer = templateRenderer;
196 this.parserFilter = parserFilter;
197 this.shortenFilter = shortenFilter;
198 this.renderFilter = renderFilter;
199 this.linkedElementRenderFilter = linkedElementRenderFilter;
200 this.pageToadletRegistry = pageToadletRegistry;
201 this.metricRegistry = metricRegistry;
202 this.l10nFilter = l10nFilter;
203 this.translation = translation;
204 this.notificationManager = notificationManager;
205 this.newPostNotification = newPostNotification;
206 this.localPostNotification = localPostNotification;
207 formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
208 soneTextParser = new SoneTextParser(getCore(), getCore());
210 this.templateContextFactory = templateContextFactory;
211 templateContextFactory.addTemplateObject("webInterface", this);
212 templateContextFactory.addTemplateObject("formPassword", formPassword);
214 /* create notifications. */
215 Template newReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
216 newReplyNotification = new ListNotification<>("new-reply-notification", "replies", newReplyNotificationTemplate, false);
218 Template localReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
219 localReplyNotification = new ListNotification<>("local-reply-notification", "replies", localReplyNotificationTemplate, false);
221 Template mentionNotificationTemplate = loaders.loadTemplate("/templates/notify/mentionNotification.html");
222 mentionNotification = new ListNotification<>("mention-notification", "posts", mentionNotificationTemplate, false);
230 * Returns the Sone core used by the Sone plugin.
232 * @return The Sone core
235 public Core getCore() {
236 return sonePlugin.core();
240 * Returns the template context factory of the web interface.
242 * @return The template context factory
244 public TemplateContextFactory getTemplateContextFactory() {
245 return templateContextFactory;
248 private Session getCurrentSessionWithoutCreation(ToadletContext toadletContenxt) {
249 return getSessionManager().useSession(toadletContenxt);
252 private Session getOrCreateCurrentSession(ToadletContext toadletContenxt) {
253 Session session = getCurrentSessionWithoutCreation(toadletContenxt);
254 if (session == null) {
255 session = getSessionManager().createSession(UUID.randomUUID().toString(), toadletContenxt);
260 public Sone getCurrentSoneCreatingSession(ToadletContext toadletContext) {
261 Collection<Sone> localSones = getCore().getLocalSones();
262 if (localSones.size() == 1) {
263 return localSones.iterator().next();
265 return getCurrentSone(getOrCreateCurrentSession(toadletContext));
268 public Sone getCurrentSoneWithoutCreatingSession(ToadletContext toadletContext) {
269 Collection<Sone> localSones = getCore().getLocalSones();
270 if (localSones.size() == 1) {
271 return localSones.iterator().next();
273 return getCurrentSone(getCurrentSessionWithoutCreation(toadletContext));
277 * Returns the currently logged in Sone.
281 * @return The currently logged in Sone, or {@code null} if no Sone is
282 * currently logged in
284 private Sone getCurrentSone(Session session) {
285 if (session == null) {
288 String soneId = (String) session.getAttribute("Sone.CurrentSone");
289 if (soneId == null) {
292 return getCore().getLocalSone(soneId);
297 public Sone getCurrentSone(@Nonnull ToadletContext toadletContext, boolean createSession) {
298 return createSession ? getCurrentSoneCreatingSession(toadletContext) : getCurrentSoneWithoutCreatingSession(toadletContext);
302 * Sets the currently logged in Sone.
304 * @param toadletContext
305 * The toadlet context
307 * The Sone to set as currently logged in
310 public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) {
311 Session session = getOrCreateCurrentSession(toadletContext);
313 session.removeAttribute("Sone.CurrentSone");
315 session.setAttribute("Sone.CurrentSone", sone.getId());
320 * Returns the notification manager.
322 * @return The notification manager
324 public NotificationManager getNotifications() {
325 return notificationManager;
329 public Optional<Notification> getNotification(@Nonnull String notificationId) {
330 return Optional.fromNullable(notificationManager.getNotification(notificationId));
334 public Collection<Notification> getNotifications(@Nullable Sone currentSone) {
335 return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone);
338 public Translation getTranslation() {
343 * Returns the session manager of the node.
345 * @return The node’s session manager
347 public SessionManager getSessionManager() {
348 return sonePlugin.pluginRespirator().getSessionManager("Sone");
352 * Returns the node’s form password.
354 * @return The form password
356 public String getFormPassword() {
361 public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
362 Set<Post> allNewPosts = ImmutableSet.<Post> builder()
363 .addAll(newPostNotification.getElements())
364 .addAll(localPostNotification.getElements())
366 return from(allNewPosts).filter(postVisibilityFilter.isVisible(currentSone)).toSet();
370 * Returns the replies that have been announced as new in the
371 * {@link #newReplyNotification}.
373 * @return The new replies
375 public Set<PostReply> getNewReplies() {
376 return ImmutableSet.<PostReply> builder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).build();
380 public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
381 Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
382 .addAll(newReplyNotification.getElements())
383 .addAll(localReplyNotification.getElements())
385 return from(allNewReplies).filter(replyVisibilityFilter.isVisible(currentSone)).toSet();
393 * Returns whether the first start notification is currently displayed.
395 * @return {@code true} if the first-start notification is currently
396 * displayed, {@code false} otherwise
398 private boolean hasFirstStartNotification() {
399 return notificationManager.getNotification("first-start-notification") != null;
407 * Starts the web interface and registers all toadlets.
409 public void start() {
414 * Stops the web interface and unregisters all toadlets.
417 pageToadletRegistry.unregisterToadlets();
418 ticker.shutdownNow();
426 * Register all toadlets.
428 private void registerToadlets() {
429 Template postTemplate = loaders.loadTemplate("/templates/include/viewPost.html");
430 Template replyTemplate = loaders.loadTemplate("/templates/include/viewReply.html");
431 Template openSearchTemplate = loaders.loadTemplate("/templates/xml/OpenSearch.xml");
433 pageToadletRegistry.addPage(new RedirectPage<FreenetRequest>("", "index.html"));
434 pageToadletRegistry.addPage(new IndexPage(this, loaders, templateRenderer, postVisibilityFilter));
435 pageToadletRegistry.addPage(new NewPage(this, loaders, templateRenderer));
436 pageToadletRegistry.addPage(new CreateSonePage(this, loaders, templateRenderer));
437 pageToadletRegistry.addPage(new KnownSonesPage(this, loaders, templateRenderer));
438 pageToadletRegistry.addPage(new EditProfilePage(this, loaders, templateRenderer));
439 pageToadletRegistry.addPage(new EditProfileFieldPage(this, loaders, templateRenderer));
440 pageToadletRegistry.addPage(new DeleteProfileFieldPage(this, loaders, templateRenderer));
441 pageToadletRegistry.addPage(new CreatePostPage(this, loaders, templateRenderer));
442 pageToadletRegistry.addPage(new CreateReplyPage(this, loaders, templateRenderer));
443 pageToadletRegistry.addPage(new ViewSonePage(this, loaders, templateRenderer));
444 pageToadletRegistry.addPage(new ViewPostPage(this, loaders, templateRenderer));
445 pageToadletRegistry.addPage(new LikePage(this, loaders, templateRenderer));
446 pageToadletRegistry.addPage(new UnlikePage(this, loaders, templateRenderer));
447 pageToadletRegistry.addPage(new DeletePostPage(this, loaders, templateRenderer));
448 pageToadletRegistry.addPage(new DeleteReplyPage(this, loaders, templateRenderer));
449 pageToadletRegistry.addPage(new LockSonePage(this, loaders, templateRenderer));
450 pageToadletRegistry.addPage(new UnlockSonePage(this, loaders, templateRenderer));
451 pageToadletRegistry.addPage(new FollowSonePage(this, loaders, templateRenderer));
452 pageToadletRegistry.addPage(new UnfollowSonePage(this, loaders, templateRenderer));
453 pageToadletRegistry.addPage(new ImageBrowserPage(this, loaders, templateRenderer));
454 pageToadletRegistry.addPage(new CreateAlbumPage(this, loaders, templateRenderer));
455 pageToadletRegistry.addPage(new EditAlbumPage(this, loaders, templateRenderer));
456 pageToadletRegistry.addPage(new DeleteAlbumPage(this, loaders, templateRenderer));
457 pageToadletRegistry.addPage(new UploadImagePage(this, loaders, templateRenderer));
458 pageToadletRegistry.addPage(new EditImagePage(this, loaders, templateRenderer));
459 pageToadletRegistry.addPage(new DeleteImagePage(this, loaders, templateRenderer));
460 pageToadletRegistry.addPage(new MarkAsKnownPage(this, loaders, templateRenderer));
461 pageToadletRegistry.addPage(new BookmarkPage(this, loaders, templateRenderer));
462 pageToadletRegistry.addPage(new UnbookmarkPage(this, loaders, templateRenderer));
463 pageToadletRegistry.addPage(new BookmarksPage(this, loaders, templateRenderer));
464 pageToadletRegistry.addPage(new SearchPage(this, loaders, templateRenderer));
465 pageToadletRegistry.addPage(new DeleteSonePage(this, loaders, templateRenderer));
466 pageToadletRegistry.addPage(new LoginPage(this, loaders, templateRenderer));
467 pageToadletRegistry.addPage(new LogoutPage(this, loaders, templateRenderer));
468 pageToadletRegistry.addPage(new OptionsPage(this, loaders, templateRenderer));
469 pageToadletRegistry.addPage(new RescuePage(this, loaders, templateRenderer));
470 pageToadletRegistry.addPage(new AboutPage(this, loaders, templateRenderer, new PluginVersion(SonePlugin.getPluginVersion()), new PluginYear(sonePlugin.getYear()), new PluginHomepage(sonePlugin.getHomepage())));
471 pageToadletRegistry.addPage(new InvalidPage(this, loaders, templateRenderer));
472 pageToadletRegistry.addPage(new NoPermissionPage(this, loaders, templateRenderer));
473 pageToadletRegistry.addPage(new EmptyImageTitlePage(this, loaders, templateRenderer));
474 pageToadletRegistry.addPage(new EmptyAlbumTitlePage(this, loaders, templateRenderer));
475 pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer));
476 pageToadletRegistry.addPage(new DebugPage(this, loaders, templateRenderer));
477 pageToadletRegistry.addDebugPage(new MetricsPage(this, loaders, templateRenderer, metricRegistry));
478 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css"));
479 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript"));
480 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png"));
481 pageToadletRegistry.addPage(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate));
482 pageToadletRegistry.addPage(new GetImagePage(this));
483 pageToadletRegistry.addPage(new GetTranslationAjaxPage(this));
484 pageToadletRegistry.addPage(new GetStatusAjaxPage(this, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault()));
485 pageToadletRegistry.addPage(new GetNotificationsAjaxPage(this));
486 pageToadletRegistry.addPage(new DismissNotificationAjaxPage(this));
487 pageToadletRegistry.addPage(new CreatePostAjaxPage(this));
488 pageToadletRegistry.addPage(new CreateReplyAjaxPage(this));
489 pageToadletRegistry.addPage(new GetReplyAjaxPage(this, replyTemplate));
490 pageToadletRegistry.addPage(new GetPostAjaxPage(this, postTemplate));
491 pageToadletRegistry.addPage(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter));
492 pageToadletRegistry.addPage(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter, TimeZone.getDefault()));
493 pageToadletRegistry.addPage(new MarkAsKnownAjaxPage(this));
494 pageToadletRegistry.addPage(new DeletePostAjaxPage(this));
495 pageToadletRegistry.addPage(new DeleteReplyAjaxPage(this));
496 pageToadletRegistry.addPage(new LockSoneAjaxPage(this));
497 pageToadletRegistry.addPage(new UnlockSoneAjaxPage(this));
498 pageToadletRegistry.addPage(new FollowSoneAjaxPage(this));
499 pageToadletRegistry.addPage(new UnfollowSoneAjaxPage(this));
500 pageToadletRegistry.addPage(new EditAlbumAjaxPage(this));
501 pageToadletRegistry.addPage(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter));
502 pageToadletRegistry.addPage(new LikeAjaxPage(this));
503 pageToadletRegistry.addPage(new UnlikeAjaxPage(this));
504 pageToadletRegistry.addPage(new GetLikesAjaxPage(this));
505 pageToadletRegistry.addPage(new BookmarkAjaxPage(this));
506 pageToadletRegistry.addPage(new UnbookmarkAjaxPage(this));
507 pageToadletRegistry.addPage(new EditProfileFieldAjaxPage(this));
508 pageToadletRegistry.addPage(new DeleteProfileFieldAjaxPage(this));
509 pageToadletRegistry.addPage(new MoveProfileFieldAjaxPage(this));
511 pageToadletRegistry.registerToadlets();
515 * Returns all {@link Sone#isLocal() local Sone}s that are referenced by
516 * {@link SonePart}s in the given text (after parsing it using
517 * {@link SoneTextParser}).
521 * @return All mentioned local Sones
523 private Collection<Sone> getMentionedSones(String text) {
524 /* we need no context to find mentioned Sones. */
525 Set<Sone> mentionedSones = new HashSet<>();
526 for (Part part : soneTextParser.parse(text, null)) {
527 if (part instanceof SonePart) {
528 mentionedSones.add(((SonePart) part).getSone());
531 return Collections2.filter(mentionedSones, Sone.LOCAL_SONE_FILTER);
535 * Returns the Sone insert notification for the given Sone. If no
536 * notification for the given Sone exists, a new notification is created and
540 * The Sone to get the insert notification for
541 * @return The Sone insert notification
543 private TemplateNotification getSoneInsertNotification(Sone sone) {
544 synchronized (soneInsertNotifications) {
545 TemplateNotification templateNotification = soneInsertNotifications.get(sone);
546 if (templateNotification == null) {
547 templateNotification = new TemplateNotification(loaders.loadTemplate("/templates/notify/soneInsertNotification.html"));
548 templateNotification.set("insertSone", sone);
549 soneInsertNotifications.put(sone, templateNotification);
551 return templateNotification;
555 private boolean localSoneMentionedInNewPostOrReply(Post post) {
556 if (!post.getSone().isLocal()) {
557 if (!getMentionedSones(post.getText()).isEmpty() && !post.isKnown()) {
561 for (PostReply postReply : getCore().getReplies(post.getId())) {
562 if (postReply.getSone().isLocal()) {
565 if (!getMentionedSones(postReply.getText()).isEmpty() && !postReply.isKnown()) {
577 * Notifies the web interface that a new {@link Post} was found.
579 * @param newPostFoundEvent
583 public void newPostFound(NewPostFoundEvent newPostFoundEvent) {
584 Post post = newPostFoundEvent.getPost();
585 boolean isLocal = post.getSone().isLocal();
586 if (!hasFirstStartNotification()) {
587 if (!getMentionedSones(post.getText()).isEmpty() && !isLocal) {
588 mentionNotification.add(post);
589 notificationManager.addNotification(mentionNotification);
595 * Notifies the web interface that a new {@link PostReply} was found.
597 * @param newPostReplyFoundEvent
601 public void newReplyFound(NewPostReplyFoundEvent newPostReplyFoundEvent) {
602 PostReply reply = newPostReplyFoundEvent.getPostReply();
603 boolean isLocal = reply.getSone().isLocal();
605 localReplyNotification.add(reply);
607 newReplyNotification.add(reply);
609 if (!hasFirstStartNotification()) {
610 notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification);
611 if (reply.getPost().isPresent() && localSoneMentionedInNewPostOrReply(reply.getPost().get())) {
612 mentionNotification.add(reply.getPost().get());
613 notificationManager.addNotification(mentionNotification);
616 getCore().markReplyKnown(reply);
621 public void markPostKnown(MarkPostKnownEvent markPostKnownEvent) {
622 removePost(markPostKnownEvent.getPost());
626 public void markReplyKnown(MarkPostReplyKnownEvent markPostReplyKnownEvent) {
627 removeReply(markPostReplyKnownEvent.getPostReply());
631 public void postRemoved(PostRemovedEvent postRemovedEvent) {
632 removePost(postRemovedEvent.getPost());
635 private void removePost(Post post) {
636 newPostNotification.remove(post);
637 if (!localSoneMentionedInNewPostOrReply(post)) {
638 mentionNotification.remove(post);
643 public void replyRemoved(PostReplyRemovedEvent postReplyRemovedEvent) {
644 removeReply(postReplyRemovedEvent.getPostReply());
647 private void removeReply(PostReply reply) {
648 newReplyNotification.remove(reply);
649 localReplyNotification.remove(reply);
650 if (reply.getPost().isPresent() && !localSoneMentionedInNewPostOrReply(reply.getPost().get())) {
651 mentionNotification.remove(reply.getPost().get());
656 * Notifies the web interface that a {@link Sone} is being inserted.
658 * @param soneInsertingEvent
662 public void soneInserting(SoneInsertingEvent soneInsertingEvent) {
663 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertingEvent.getSone());
664 soneInsertNotification.set("soneStatus", "inserting");
665 if (soneInsertingEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
666 notificationManager.addNotification(soneInsertNotification);
671 * Notifies the web interface that a {@link Sone} was inserted.
673 * @param soneInsertedEvent
677 public void soneInserted(SoneInsertedEvent soneInsertedEvent) {
678 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertedEvent.getSone());
679 soneInsertNotification.set("soneStatus", "inserted");
680 soneInsertNotification.set("insertDuration", soneInsertedEvent.getInsertDuration() / 1000);
681 if (soneInsertedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
682 notificationManager.addNotification(soneInsertNotification);
687 * Notifies the web interface that a {@link Sone} insert was aborted.
689 * @param soneInsertAbortedEvent
693 public void soneInsertAborted(SoneInsertAbortedEvent soneInsertAbortedEvent) {
694 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertAbortedEvent.getSone());
695 soneInsertNotification.set("soneStatus", "insert-aborted");
696 soneInsertNotification.set("insert-error", soneInsertAbortedEvent.getCause());
697 if (soneInsertAbortedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
698 notificationManager.addNotification(soneInsertNotification);
703 public void debugActivated(@Nonnull DebugActivatedEvent debugActivatedEvent) {
704 pageToadletRegistry.activateDebugMode();