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.logging.Logger;
31 import javax.annotation.Nonnull;
32 import javax.annotation.Nullable;
33 import javax.inject.Named;
35 import net.pterodactylus.sone.core.Core;
36 import net.pterodactylus.sone.core.ElementLoader;
37 import net.pterodactylus.sone.core.event.*;
38 import net.pterodactylus.sone.data.Post;
39 import net.pterodactylus.sone.data.PostReply;
40 import net.pterodactylus.sone.data.Sone;
41 import net.pterodactylus.sone.freenet.L10nFilter;
42 import net.pterodactylus.sone.freenet.Translation;
43 import net.pterodactylus.sone.main.Loaders;
44 import net.pterodactylus.sone.main.PluginHomepage;
45 import net.pterodactylus.sone.main.PluginVersion;
46 import net.pterodactylus.sone.main.PluginYear;
47 import net.pterodactylus.sone.main.SonePlugin;
48 import net.pterodactylus.sone.notify.ListNotification;
49 import net.pterodactylus.sone.notify.ListNotificationFilter;
50 import net.pterodactylus.sone.notify.PostVisibilityFilter;
51 import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
52 import net.pterodactylus.sone.template.LinkedElementRenderFilter;
53 import net.pterodactylus.sone.template.ParserFilter;
54 import net.pterodactylus.sone.template.RenderFilter;
55 import net.pterodactylus.sone.template.ShortenFilter;
56 import net.pterodactylus.sone.text.Part;
57 import net.pterodactylus.sone.text.SonePart;
58 import net.pterodactylus.sone.text.SoneTextParser;
59 import net.pterodactylus.sone.text.TimeTextConverter;
60 import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage;
61 import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage;
62 import net.pterodactylus.sone.web.ajax.CreateReplyAjaxPage;
63 import net.pterodactylus.sone.web.ajax.DeletePostAjaxPage;
64 import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage;
65 import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage;
66 import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage;
67 import net.pterodactylus.sone.web.ajax.EditAlbumAjaxPage;
68 import net.pterodactylus.sone.web.ajax.EditImageAjaxPage;
69 import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
70 import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
71 import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
72 import net.pterodactylus.sone.web.ajax.GetLinkedElementAjaxPage;
73 import net.pterodactylus.sone.web.ajax.GetNotificationsAjaxPage;
74 import net.pterodactylus.sone.web.ajax.GetPostAjaxPage;
75 import net.pterodactylus.sone.web.ajax.GetReplyAjaxPage;
76 import net.pterodactylus.sone.web.ajax.GetStatusAjaxPage;
77 import net.pterodactylus.sone.web.ajax.GetTimesAjaxPage;
78 import net.pterodactylus.sone.web.ajax.GetTranslationAjaxPage;
79 import net.pterodactylus.sone.web.ajax.LikeAjaxPage;
80 import net.pterodactylus.sone.web.ajax.LockSoneAjaxPage;
81 import net.pterodactylus.sone.web.ajax.MarkAsKnownAjaxPage;
82 import net.pterodactylus.sone.web.ajax.MoveProfileFieldAjaxPage;
83 import net.pterodactylus.sone.web.ajax.UnbookmarkAjaxPage;
84 import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage;
85 import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage;
86 import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
87 import net.pterodactylus.sone.web.page.FreenetRequest;
88 import net.pterodactylus.sone.web.page.TemplateRenderer;
89 import net.pterodactylus.sone.web.pages.*;
90 import net.pterodactylus.util.notify.Notification;
91 import net.pterodactylus.util.notify.NotificationManager;
92 import net.pterodactylus.util.notify.TemplateNotification;
93 import net.pterodactylus.util.template.Template;
94 import net.pterodactylus.util.template.TemplateContextFactory;
95 import net.pterodactylus.util.web.RedirectPage;
96 import net.pterodactylus.util.web.TemplatePage;
98 import freenet.clients.http.SessionManager;
99 import freenet.clients.http.SessionManager.Session;
100 import freenet.clients.http.ToadletContext;
102 import com.codahale.metrics.*;
103 import com.google.common.base.Optional;
104 import com.google.common.collect.Collections2;
105 import com.google.common.collect.ImmutableSet;
106 import com.google.common.eventbus.Subscribe;
107 import com.google.inject.Inject;
110 * Bundles functionality that a web interface of a Freenet plugin needs, e.g.
111 * references to l10n helpers.
113 public class WebInterface implements SessionProvider {
116 private static final Logger logger = getLogger(WebInterface.class.getName());
118 /** The loaders for templates, pages, and classpath providers. */
119 private final Loaders loaders;
121 /** The notification manager. */
122 private final NotificationManager notificationManager;
124 /** The Sone plugin. */
125 private final SonePlugin sonePlugin;
127 /** The form password. */
128 private final String formPassword;
130 /** The template context factory. */
131 private final TemplateContextFactory templateContextFactory;
132 private final TemplateRenderer templateRenderer;
134 /** The Sone text parser. */
135 private final SoneTextParser soneTextParser;
137 /** The parser filter. */
138 private final ParserFilter parserFilter;
139 private final ShortenFilter shortenFilter;
140 private final RenderFilter renderFilter;
142 private final ListNotificationFilter listNotificationFilter;
143 private final PostVisibilityFilter postVisibilityFilter;
144 private final ReplyVisibilityFilter replyVisibilityFilter;
146 private final ElementLoader elementLoader;
147 private final LinkedElementRenderFilter linkedElementRenderFilter;
148 private final TimeTextConverter timeTextConverter = new TimeTextConverter();
149 private final L10nFilter l10nFilter;
151 private final PageToadletRegistry pageToadletRegistry;
152 private final MetricRegistry metricRegistry;
153 private final Translation translation;
155 /** The “new post” notification. */
156 private final ListNotification<Post> newPostNotification;
158 /** The “new reply” notification. */
159 private final ListNotification<PostReply> newReplyNotification;
161 /** The invisible “local post” notification. */
162 private final ListNotification<Post> localPostNotification;
164 /** The invisible “local reply” notification. */
165 private final ListNotification<PostReply> localReplyNotification;
167 /** The “you have been mentioned” notification. */
168 private final ListNotification<Post> mentionNotification;
170 /** Notifications for sone inserts. */
171 private final Map<Sone, TemplateNotification> soneInsertNotifications = new HashMap<>();
174 public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter,
175 PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter,
176 ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
177 TemplateRenderer templateRenderer,
178 ParserFilter parserFilter, ShortenFilter shortenFilter,
179 RenderFilter renderFilter,
180 LinkedElementRenderFilter linkedElementRenderFilter,
181 PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter,
182 NotificationManager notificationManager, @Named("newRemotePost") ListNotification<Post> newPostNotification,
183 @Named("localPost") ListNotification<Post> localPostNotification) {
184 this.sonePlugin = sonePlugin;
185 this.loaders = loaders;
186 this.listNotificationFilter = listNotificationFilter;
187 this.postVisibilityFilter = postVisibilityFilter;
188 this.replyVisibilityFilter = replyVisibilityFilter;
189 this.elementLoader = elementLoader;
190 this.templateRenderer = templateRenderer;
191 this.parserFilter = parserFilter;
192 this.shortenFilter = shortenFilter;
193 this.renderFilter = renderFilter;
194 this.linkedElementRenderFilter = linkedElementRenderFilter;
195 this.pageToadletRegistry = pageToadletRegistry;
196 this.metricRegistry = metricRegistry;
197 this.l10nFilter = l10nFilter;
198 this.translation = translation;
199 this.notificationManager = notificationManager;
200 this.newPostNotification = newPostNotification;
201 this.localPostNotification = localPostNotification;
202 formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
203 soneTextParser = new SoneTextParser(getCore(), getCore());
205 this.templateContextFactory = templateContextFactory;
206 templateContextFactory.addTemplateObject("webInterface", this);
207 templateContextFactory.addTemplateObject("formPassword", formPassword);
209 /* create notifications. */
210 Template newReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
211 newReplyNotification = new ListNotification<>("new-reply-notification", "replies", newReplyNotificationTemplate, false);
213 Template localReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
214 localReplyNotification = new ListNotification<>("local-reply-notification", "replies", localReplyNotificationTemplate, false);
216 Template mentionNotificationTemplate = loaders.loadTemplate("/templates/notify/mentionNotification.html");
217 mentionNotification = new ListNotification<>("mention-notification", "posts", mentionNotificationTemplate, false);
225 * Returns the Sone core used by the Sone plugin.
227 * @return The Sone core
230 public Core getCore() {
231 return sonePlugin.core();
235 * Returns the template context factory of the web interface.
237 * @return The template context factory
239 public TemplateContextFactory getTemplateContextFactory() {
240 return templateContextFactory;
243 private Session getCurrentSessionWithoutCreation(ToadletContext toadletContenxt) {
244 return getSessionManager().useSession(toadletContenxt);
247 private Session getOrCreateCurrentSession(ToadletContext toadletContenxt) {
248 Session session = getCurrentSessionWithoutCreation(toadletContenxt);
249 if (session == null) {
250 session = getSessionManager().createSession(UUID.randomUUID().toString(), toadletContenxt);
255 public Sone getCurrentSoneCreatingSession(ToadletContext toadletContext) {
256 Collection<Sone> localSones = getCore().getLocalSones();
257 if (localSones.size() == 1) {
258 return localSones.iterator().next();
260 return getCurrentSone(getOrCreateCurrentSession(toadletContext));
263 public Sone getCurrentSoneWithoutCreatingSession(ToadletContext toadletContext) {
264 Collection<Sone> localSones = getCore().getLocalSones();
265 if (localSones.size() == 1) {
266 return localSones.iterator().next();
268 return getCurrentSone(getCurrentSessionWithoutCreation(toadletContext));
272 * Returns the currently logged in Sone.
276 * @return The currently logged in Sone, or {@code null} if no Sone is
277 * currently logged in
279 private Sone getCurrentSone(Session session) {
280 if (session == null) {
283 String soneId = (String) session.getAttribute("Sone.CurrentSone");
284 if (soneId == null) {
287 return getCore().getLocalSone(soneId);
292 public Sone getCurrentSone(@Nonnull ToadletContext toadletContext, boolean createSession) {
293 return createSession ? getCurrentSoneCreatingSession(toadletContext) : getCurrentSoneWithoutCreatingSession(toadletContext);
297 * Sets the currently logged in Sone.
299 * @param toadletContext
300 * The toadlet context
302 * The Sone to set as currently logged in
305 public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) {
306 Session session = getOrCreateCurrentSession(toadletContext);
308 session.removeAttribute("Sone.CurrentSone");
310 session.setAttribute("Sone.CurrentSone", sone.getId());
315 * Returns the notification manager.
317 * @return The notification manager
319 public NotificationManager getNotifications() {
320 return notificationManager;
324 public Optional<Notification> getNotification(@Nonnull String notificationId) {
325 return Optional.fromNullable(notificationManager.getNotification(notificationId));
329 public Collection<Notification> getNotifications(@Nullable Sone currentSone) {
330 return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone);
333 public Translation getTranslation() {
338 * Returns the session manager of the node.
340 * @return The node’s session manager
342 public SessionManager getSessionManager() {
343 return sonePlugin.pluginRespirator().getSessionManager("Sone");
347 * Returns the node’s form password.
349 * @return The form password
351 public String getFormPassword() {
356 public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
357 Set<Post> allNewPosts = ImmutableSet.<Post> builder()
358 .addAll(newPostNotification.getElements())
359 .addAll(localPostNotification.getElements())
361 return from(allNewPosts).filter(postVisibilityFilter.isVisible(currentSone)).toSet();
365 * Returns the replies that have been announced as new in the
366 * {@link #newReplyNotification}.
368 * @return The new replies
370 public Set<PostReply> getNewReplies() {
371 return ImmutableSet.<PostReply> builder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).build();
375 public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
376 Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
377 .addAll(newReplyNotification.getElements())
378 .addAll(localReplyNotification.getElements())
380 return from(allNewReplies).filter(replyVisibilityFilter.isVisible(currentSone)).toSet();
388 * Returns whether the first start notification is currently displayed.
390 * @return {@code true} if the first-start notification is currently
391 * displayed, {@code false} otherwise
393 private boolean hasFirstStartNotification() {
394 return notificationManager.getNotification("first-start-notification") != null;
402 * Starts the web interface and registers all toadlets.
404 public void start() {
409 * Stops the web interface and unregisters all toadlets.
412 pageToadletRegistry.unregisterToadlets();
420 * Register all toadlets.
422 private void registerToadlets() {
423 Template postTemplate = loaders.loadTemplate("/templates/include/viewPost.html");
424 Template replyTemplate = loaders.loadTemplate("/templates/include/viewReply.html");
425 Template openSearchTemplate = loaders.loadTemplate("/templates/xml/OpenSearch.xml");
427 pageToadletRegistry.addPage(new RedirectPage<FreenetRequest>("", "index.html"));
428 pageToadletRegistry.addPage(new IndexPage(this, loaders, templateRenderer, postVisibilityFilter));
429 pageToadletRegistry.addPage(new NewPage(this, loaders, templateRenderer));
430 pageToadletRegistry.addPage(new CreateSonePage(this, loaders, templateRenderer));
431 pageToadletRegistry.addPage(new KnownSonesPage(this, loaders, templateRenderer));
432 pageToadletRegistry.addPage(new EditProfilePage(this, loaders, templateRenderer));
433 pageToadletRegistry.addPage(new EditProfileFieldPage(this, loaders, templateRenderer));
434 pageToadletRegistry.addPage(new DeleteProfileFieldPage(this, loaders, templateRenderer));
435 pageToadletRegistry.addPage(new CreatePostPage(this, loaders, templateRenderer));
436 pageToadletRegistry.addPage(new CreateReplyPage(this, loaders, templateRenderer));
437 pageToadletRegistry.addPage(new ViewSonePage(this, loaders, templateRenderer));
438 pageToadletRegistry.addPage(new ViewPostPage(this, loaders, templateRenderer));
439 pageToadletRegistry.addPage(new LikePage(this, loaders, templateRenderer));
440 pageToadletRegistry.addPage(new UnlikePage(this, loaders, templateRenderer));
441 pageToadletRegistry.addPage(new DeletePostPage(this, loaders, templateRenderer));
442 pageToadletRegistry.addPage(new DeleteReplyPage(this, loaders, templateRenderer));
443 pageToadletRegistry.addPage(new LockSonePage(this, loaders, templateRenderer));
444 pageToadletRegistry.addPage(new UnlockSonePage(this, loaders, templateRenderer));
445 pageToadletRegistry.addPage(new FollowSonePage(this, loaders, templateRenderer));
446 pageToadletRegistry.addPage(new UnfollowSonePage(this, loaders, templateRenderer));
447 pageToadletRegistry.addPage(new ImageBrowserPage(this, loaders, templateRenderer));
448 pageToadletRegistry.addPage(new CreateAlbumPage(this, loaders, templateRenderer));
449 pageToadletRegistry.addPage(new EditAlbumPage(this, loaders, templateRenderer));
450 pageToadletRegistry.addPage(new DeleteAlbumPage(this, loaders, templateRenderer));
451 pageToadletRegistry.addPage(new UploadImagePage(this, loaders, templateRenderer));
452 pageToadletRegistry.addPage(new EditImagePage(this, loaders, templateRenderer));
453 pageToadletRegistry.addPage(new DeleteImagePage(this, loaders, templateRenderer));
454 pageToadletRegistry.addPage(new MarkAsKnownPage(this, loaders, templateRenderer));
455 pageToadletRegistry.addPage(new BookmarkPage(this, loaders, templateRenderer));
456 pageToadletRegistry.addPage(new UnbookmarkPage(this, loaders, templateRenderer));
457 pageToadletRegistry.addPage(new BookmarksPage(this, loaders, templateRenderer));
458 pageToadletRegistry.addPage(new SearchPage(this, loaders, templateRenderer));
459 pageToadletRegistry.addPage(new DeleteSonePage(this, loaders, templateRenderer));
460 pageToadletRegistry.addPage(new LoginPage(this, loaders, templateRenderer));
461 pageToadletRegistry.addPage(new LogoutPage(this, loaders, templateRenderer));
462 pageToadletRegistry.addPage(new OptionsPage(this, loaders, templateRenderer));
463 pageToadletRegistry.addPage(new RescuePage(this, loaders, templateRenderer));
464 pageToadletRegistry.addPage(new AboutPage(this, loaders, templateRenderer, new PluginVersion(SonePlugin.getPluginVersion()), new PluginYear(sonePlugin.getYear()), new PluginHomepage(sonePlugin.getHomepage())));
465 pageToadletRegistry.addPage(new InvalidPage(this, loaders, templateRenderer));
466 pageToadletRegistry.addPage(new NoPermissionPage(this, loaders, templateRenderer));
467 pageToadletRegistry.addPage(new EmptyImageTitlePage(this, loaders, templateRenderer));
468 pageToadletRegistry.addPage(new EmptyAlbumTitlePage(this, loaders, templateRenderer));
469 pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer));
470 pageToadletRegistry.addPage(new DebugPage(this, loaders, templateRenderer));
471 pageToadletRegistry.addDebugPage(new MetricsPage(this, loaders, templateRenderer, metricRegistry));
472 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css"));
473 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript"));
474 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png"));
475 pageToadletRegistry.addPage(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate));
476 pageToadletRegistry.addPage(new GetImagePage(this));
477 pageToadletRegistry.addPage(new GetTranslationAjaxPage(this));
478 pageToadletRegistry.addPage(new GetStatusAjaxPage(this, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault()));
479 pageToadletRegistry.addPage(new GetNotificationsAjaxPage(this));
480 pageToadletRegistry.addPage(new DismissNotificationAjaxPage(this));
481 pageToadletRegistry.addPage(new CreatePostAjaxPage(this));
482 pageToadletRegistry.addPage(new CreateReplyAjaxPage(this));
483 pageToadletRegistry.addPage(new GetReplyAjaxPage(this, replyTemplate));
484 pageToadletRegistry.addPage(new GetPostAjaxPage(this, postTemplate));
485 pageToadletRegistry.addPage(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter));
486 pageToadletRegistry.addPage(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter, TimeZone.getDefault()));
487 pageToadletRegistry.addPage(new MarkAsKnownAjaxPage(this));
488 pageToadletRegistry.addPage(new DeletePostAjaxPage(this));
489 pageToadletRegistry.addPage(new DeleteReplyAjaxPage(this));
490 pageToadletRegistry.addPage(new LockSoneAjaxPage(this));
491 pageToadletRegistry.addPage(new UnlockSoneAjaxPage(this));
492 pageToadletRegistry.addPage(new FollowSoneAjaxPage(this));
493 pageToadletRegistry.addPage(new UnfollowSoneAjaxPage(this));
494 pageToadletRegistry.addPage(new EditAlbumAjaxPage(this));
495 pageToadletRegistry.addPage(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter));
496 pageToadletRegistry.addPage(new LikeAjaxPage(this));
497 pageToadletRegistry.addPage(new UnlikeAjaxPage(this));
498 pageToadletRegistry.addPage(new GetLikesAjaxPage(this));
499 pageToadletRegistry.addPage(new BookmarkAjaxPage(this));
500 pageToadletRegistry.addPage(new UnbookmarkAjaxPage(this));
501 pageToadletRegistry.addPage(new EditProfileFieldAjaxPage(this));
502 pageToadletRegistry.addPage(new DeleteProfileFieldAjaxPage(this));
503 pageToadletRegistry.addPage(new MoveProfileFieldAjaxPage(this));
505 pageToadletRegistry.registerToadlets();
509 * Returns all {@link Sone#isLocal() local Sone}s that are referenced by
510 * {@link SonePart}s in the given text (after parsing it using
511 * {@link SoneTextParser}).
515 * @return All mentioned local Sones
517 private Collection<Sone> getMentionedSones(String text) {
518 /* we need no context to find mentioned Sones. */
519 Set<Sone> mentionedSones = new HashSet<>();
520 for (Part part : soneTextParser.parse(text, null)) {
521 if (part instanceof SonePart) {
522 mentionedSones.add(((SonePart) part).getSone());
525 return Collections2.filter(mentionedSones, Sone.LOCAL_SONE_FILTER);
529 * Returns the Sone insert notification for the given Sone. If no
530 * notification for the given Sone exists, a new notification is created and
534 * The Sone to get the insert notification for
535 * @return The Sone insert notification
537 private TemplateNotification getSoneInsertNotification(Sone sone) {
538 synchronized (soneInsertNotifications) {
539 TemplateNotification templateNotification = soneInsertNotifications.get(sone);
540 if (templateNotification == null) {
541 templateNotification = new TemplateNotification(loaders.loadTemplate("/templates/notify/soneInsertNotification.html"));
542 templateNotification.set("insertSone", sone);
543 soneInsertNotifications.put(sone, templateNotification);
545 return templateNotification;
549 private boolean localSoneMentionedInNewPostOrReply(Post post) {
550 if (!post.getSone().isLocal()) {
551 if (!getMentionedSones(post.getText()).isEmpty() && !post.isKnown()) {
555 for (PostReply postReply : getCore().getReplies(post.getId())) {
556 if (postReply.getSone().isLocal()) {
559 if (!getMentionedSones(postReply.getText()).isEmpty() && !postReply.isKnown()) {
571 * Notifies the web interface that a new {@link Post} was found.
573 * @param newPostFoundEvent
577 public void newPostFound(NewPostFoundEvent newPostFoundEvent) {
578 Post post = newPostFoundEvent.getPost();
579 boolean isLocal = post.getSone().isLocal();
580 if (!hasFirstStartNotification()) {
581 if (!getMentionedSones(post.getText()).isEmpty() && !isLocal) {
582 mentionNotification.add(post);
583 notificationManager.addNotification(mentionNotification);
589 * Notifies the web interface that a new {@link PostReply} was found.
591 * @param newPostReplyFoundEvent
595 public void newReplyFound(NewPostReplyFoundEvent newPostReplyFoundEvent) {
596 PostReply reply = newPostReplyFoundEvent.getPostReply();
597 boolean isLocal = reply.getSone().isLocal();
599 localReplyNotification.add(reply);
601 newReplyNotification.add(reply);
603 if (!hasFirstStartNotification()) {
604 notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification);
605 if (reply.getPost().isPresent() && localSoneMentionedInNewPostOrReply(reply.getPost().get())) {
606 mentionNotification.add(reply.getPost().get());
607 notificationManager.addNotification(mentionNotification);
610 getCore().markReplyKnown(reply);
615 public void markPostKnown(MarkPostKnownEvent markPostKnownEvent) {
616 removePost(markPostKnownEvent.getPost());
620 public void markReplyKnown(MarkPostReplyKnownEvent markPostReplyKnownEvent) {
621 removeReply(markPostReplyKnownEvent.getPostReply());
625 public void postRemoved(PostRemovedEvent postRemovedEvent) {
626 removePost(postRemovedEvent.getPost());
629 private void removePost(Post post) {
630 newPostNotification.remove(post);
631 if (!localSoneMentionedInNewPostOrReply(post)) {
632 mentionNotification.remove(post);
637 public void replyRemoved(PostReplyRemovedEvent postReplyRemovedEvent) {
638 removeReply(postReplyRemovedEvent.getPostReply());
641 private void removeReply(PostReply reply) {
642 newReplyNotification.remove(reply);
643 localReplyNotification.remove(reply);
644 if (reply.getPost().isPresent() && !localSoneMentionedInNewPostOrReply(reply.getPost().get())) {
645 mentionNotification.remove(reply.getPost().get());
650 * Notifies the web interface that a {@link Sone} is being inserted.
652 * @param soneInsertingEvent
656 public void soneInserting(SoneInsertingEvent soneInsertingEvent) {
657 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertingEvent.getSone());
658 soneInsertNotification.set("soneStatus", "inserting");
659 if (soneInsertingEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
660 notificationManager.addNotification(soneInsertNotification);
665 * Notifies the web interface that a {@link Sone} was inserted.
667 * @param soneInsertedEvent
671 public void soneInserted(SoneInsertedEvent soneInsertedEvent) {
672 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertedEvent.getSone());
673 soneInsertNotification.set("soneStatus", "inserted");
674 soneInsertNotification.set("insertDuration", soneInsertedEvent.getInsertDuration() / 1000);
675 if (soneInsertedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
676 notificationManager.addNotification(soneInsertNotification);
681 * Notifies the web interface that a {@link Sone} insert was aborted.
683 * @param soneInsertAbortedEvent
687 public void soneInsertAborted(SoneInsertAbortedEvent soneInsertAbortedEvent) {
688 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertAbortedEvent.getSone());
689 soneInsertNotification.set("soneStatus", "insert-aborted");
690 soneInsertNotification.set("insert-error", soneInsertAbortedEvent.getCause());
691 if (soneInsertAbortedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
692 notificationManager.addNotification(soneInsertNotification);
697 public void debugActivated(@Nonnull DebugActivatedEvent debugActivatedEvent) {
698 pageToadletRegistry.activateDebugMode();