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.concurrent.TimeUnit;
33 import java.util.logging.Logger;
34 import javax.annotation.Nonnull;
35 import javax.annotation.Nullable;
36 import javax.inject.Named;
38 import net.pterodactylus.sone.core.Core;
39 import net.pterodactylus.sone.core.ElementLoader;
40 import net.pterodactylus.sone.core.event.*;
41 import net.pterodactylus.sone.data.Post;
42 import net.pterodactylus.sone.data.PostReply;
43 import net.pterodactylus.sone.data.Sone;
44 import net.pterodactylus.sone.freenet.L10nFilter;
45 import net.pterodactylus.sone.freenet.Translation;
46 import net.pterodactylus.sone.main.Loaders;
47 import net.pterodactylus.sone.main.PluginHomepage;
48 import net.pterodactylus.sone.main.PluginVersion;
49 import net.pterodactylus.sone.main.PluginYear;
50 import net.pterodactylus.sone.main.SonePlugin;
51 import net.pterodactylus.sone.notify.ListNotification;
52 import net.pterodactylus.sone.notify.ListNotificationFilter;
53 import net.pterodactylus.sone.notify.PostVisibilityFilter;
54 import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
55 import net.pterodactylus.sone.template.LinkedElementRenderFilter;
56 import net.pterodactylus.sone.template.ParserFilter;
57 import net.pterodactylus.sone.template.RenderFilter;
58 import net.pterodactylus.sone.template.ShortenFilter;
59 import net.pterodactylus.sone.text.Part;
60 import net.pterodactylus.sone.text.SonePart;
61 import net.pterodactylus.sone.text.SoneTextParser;
62 import net.pterodactylus.sone.text.TimeTextConverter;
63 import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage;
64 import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage;
65 import net.pterodactylus.sone.web.ajax.CreateReplyAjaxPage;
66 import net.pterodactylus.sone.web.ajax.DeletePostAjaxPage;
67 import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage;
68 import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage;
69 import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage;
70 import net.pterodactylus.sone.web.ajax.EditAlbumAjaxPage;
71 import net.pterodactylus.sone.web.ajax.EditImageAjaxPage;
72 import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
73 import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
74 import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
75 import net.pterodactylus.sone.web.ajax.GetLinkedElementAjaxPage;
76 import net.pterodactylus.sone.web.ajax.GetNotificationsAjaxPage;
77 import net.pterodactylus.sone.web.ajax.GetPostAjaxPage;
78 import net.pterodactylus.sone.web.ajax.GetReplyAjaxPage;
79 import net.pterodactylus.sone.web.ajax.GetStatusAjaxPage;
80 import net.pterodactylus.sone.web.ajax.GetTimesAjaxPage;
81 import net.pterodactylus.sone.web.ajax.GetTranslationAjaxPage;
82 import net.pterodactylus.sone.web.ajax.LikeAjaxPage;
83 import net.pterodactylus.sone.web.ajax.LockSoneAjaxPage;
84 import net.pterodactylus.sone.web.ajax.MarkAsKnownAjaxPage;
85 import net.pterodactylus.sone.web.ajax.MoveProfileFieldAjaxPage;
86 import net.pterodactylus.sone.web.ajax.UnbookmarkAjaxPage;
87 import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage;
88 import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage;
89 import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
90 import net.pterodactylus.sone.web.page.FreenetRequest;
91 import net.pterodactylus.sone.web.page.TemplateRenderer;
92 import net.pterodactylus.sone.web.pages.*;
93 import net.pterodactylus.util.notify.Notification;
94 import net.pterodactylus.util.notify.NotificationManager;
95 import net.pterodactylus.util.notify.TemplateNotification;
96 import net.pterodactylus.util.template.Template;
97 import net.pterodactylus.util.template.TemplateContextFactory;
98 import net.pterodactylus.util.web.RedirectPage;
99 import net.pterodactylus.util.web.TemplatePage;
101 import freenet.clients.http.SessionManager;
102 import freenet.clients.http.SessionManager.Session;
103 import freenet.clients.http.ToadletContext;
105 import com.codahale.metrics.*;
106 import com.google.common.base.Optional;
107 import com.google.common.collect.Collections2;
108 import com.google.common.collect.ImmutableSet;
109 import com.google.common.eventbus.Subscribe;
110 import com.google.inject.Inject;
113 * Bundles functionality that a web interface of a Freenet plugin needs, e.g.
114 * references to l10n helpers.
116 public class WebInterface implements SessionProvider {
119 private static final Logger logger = getLogger(WebInterface.class.getName());
121 /** The loaders for templates, pages, and classpath providers. */
122 private final Loaders loaders;
124 /** The notification manager. */
125 private final NotificationManager notificationManager;
127 /** The Sone plugin. */
128 private final SonePlugin sonePlugin;
130 /** The form password. */
131 private final String formPassword;
133 /** The template context factory. */
134 private final TemplateContextFactory templateContextFactory;
135 private final TemplateRenderer templateRenderer;
137 /** The Sone text parser. */
138 private final SoneTextParser soneTextParser;
140 /** The parser filter. */
141 private final ParserFilter parserFilter;
142 private final ShortenFilter shortenFilter;
143 private final RenderFilter renderFilter;
145 private final ListNotificationFilter listNotificationFilter;
146 private final PostVisibilityFilter postVisibilityFilter;
147 private final ReplyVisibilityFilter replyVisibilityFilter;
149 private final ElementLoader elementLoader;
150 private final LinkedElementRenderFilter linkedElementRenderFilter;
151 private final TimeTextConverter timeTextConverter = new TimeTextConverter();
152 private final L10nFilter l10nFilter;
154 private final PageToadletRegistry pageToadletRegistry;
155 private final MetricRegistry metricRegistry;
156 private final Translation translation;
158 /** The “new post” notification. */
159 private final ListNotification<Post> newPostNotification;
161 /** The “new reply” notification. */
162 private final ListNotification<PostReply> newReplyNotification;
164 /** The invisible “local post” notification. */
165 private final ListNotification<Post> localPostNotification;
167 /** The invisible “local reply” notification. */
168 private final ListNotification<PostReply> localReplyNotification;
170 /** The “you have been mentioned” notification. */
171 private final ListNotification<Post> mentionNotification;
173 /** Notifications for sone inserts. */
174 private final Map<Sone, TemplateNotification> soneInsertNotifications = new HashMap<>();
176 /** Scheduled executor for time-based notifications. */
177 private final ScheduledExecutorService ticker = Executors.newScheduledThreadPool(1);
180 public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter,
181 PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter,
182 ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
183 TemplateRenderer templateRenderer,
184 ParserFilter parserFilter, ShortenFilter shortenFilter,
185 RenderFilter renderFilter,
186 LinkedElementRenderFilter linkedElementRenderFilter,
187 PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter,
188 NotificationManager notificationManager, @Named("newRemotePost") ListNotification<Post> newPostNotification,
189 @Named("localPost") ListNotification<Post> localPostNotification) {
190 this.sonePlugin = sonePlugin;
191 this.loaders = loaders;
192 this.listNotificationFilter = listNotificationFilter;
193 this.postVisibilityFilter = postVisibilityFilter;
194 this.replyVisibilityFilter = replyVisibilityFilter;
195 this.elementLoader = elementLoader;
196 this.templateRenderer = templateRenderer;
197 this.parserFilter = parserFilter;
198 this.shortenFilter = shortenFilter;
199 this.renderFilter = renderFilter;
200 this.linkedElementRenderFilter = linkedElementRenderFilter;
201 this.pageToadletRegistry = pageToadletRegistry;
202 this.metricRegistry = metricRegistry;
203 this.l10nFilter = l10nFilter;
204 this.translation = translation;
205 this.notificationManager = notificationManager;
206 this.newPostNotification = newPostNotification;
207 this.localPostNotification = localPostNotification;
208 formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
209 soneTextParser = new SoneTextParser(getCore(), getCore());
211 this.templateContextFactory = templateContextFactory;
212 templateContextFactory.addTemplateObject("webInterface", this);
213 templateContextFactory.addTemplateObject("formPassword", formPassword);
215 /* create notifications. */
216 Template newReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
217 newReplyNotification = new ListNotification<>("new-reply-notification", "replies", newReplyNotificationTemplate, false);
219 Template localReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
220 localReplyNotification = new ListNotification<>("local-reply-notification", "replies", localReplyNotificationTemplate, false);
222 Template mentionNotificationTemplate = loaders.loadTemplate("/templates/notify/mentionNotification.html");
223 mentionNotification = new ListNotification<>("mention-notification", "posts", mentionNotificationTemplate, false);
231 * Returns the Sone core used by the Sone plugin.
233 * @return The Sone core
236 public Core getCore() {
237 return sonePlugin.core();
241 * Returns the template context factory of the web interface.
243 * @return The template context factory
245 public TemplateContextFactory getTemplateContextFactory() {
246 return templateContextFactory;
249 private Session getCurrentSessionWithoutCreation(ToadletContext toadletContenxt) {
250 return getSessionManager().useSession(toadletContenxt);
253 private Session getOrCreateCurrentSession(ToadletContext toadletContenxt) {
254 Session session = getCurrentSessionWithoutCreation(toadletContenxt);
255 if (session == null) {
256 session = getSessionManager().createSession(UUID.randomUUID().toString(), toadletContenxt);
261 public Sone getCurrentSoneCreatingSession(ToadletContext toadletContext) {
262 Collection<Sone> localSones = getCore().getLocalSones();
263 if (localSones.size() == 1) {
264 return localSones.iterator().next();
266 return getCurrentSone(getOrCreateCurrentSession(toadletContext));
269 public Sone getCurrentSoneWithoutCreatingSession(ToadletContext toadletContext) {
270 Collection<Sone> localSones = getCore().getLocalSones();
271 if (localSones.size() == 1) {
272 return localSones.iterator().next();
274 return getCurrentSone(getCurrentSessionWithoutCreation(toadletContext));
278 * Returns the currently logged in Sone.
282 * @return The currently logged in Sone, or {@code null} if no Sone is
283 * currently logged in
285 private Sone getCurrentSone(Session session) {
286 if (session == null) {
289 String soneId = (String) session.getAttribute("Sone.CurrentSone");
290 if (soneId == null) {
293 return getCore().getLocalSone(soneId);
298 public Sone getCurrentSone(@Nonnull ToadletContext toadletContext, boolean createSession) {
299 return createSession ? getCurrentSoneCreatingSession(toadletContext) : getCurrentSoneWithoutCreatingSession(toadletContext);
303 * Sets the currently logged in Sone.
305 * @param toadletContext
306 * The toadlet context
308 * The Sone to set as currently logged in
311 public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) {
312 Session session = getOrCreateCurrentSession(toadletContext);
314 session.removeAttribute("Sone.CurrentSone");
316 session.setAttribute("Sone.CurrentSone", sone.getId());
321 * Returns the notification manager.
323 * @return The notification manager
325 public NotificationManager getNotifications() {
326 return notificationManager;
330 public Optional<Notification> getNotification(@Nonnull String notificationId) {
331 return Optional.fromNullable(notificationManager.getNotification(notificationId));
335 public Collection<Notification> getNotifications(@Nullable Sone currentSone) {
336 return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone);
339 public Translation getTranslation() {
344 * Returns the session manager of the node.
346 * @return The node’s session manager
348 public SessionManager getSessionManager() {
349 return sonePlugin.pluginRespirator().getSessionManager("Sone");
353 * Returns the node’s form password.
355 * @return The form password
357 public String getFormPassword() {
362 public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
363 Set<Post> allNewPosts = ImmutableSet.<Post> builder()
364 .addAll(newPostNotification.getElements())
365 .addAll(localPostNotification.getElements())
367 return from(allNewPosts).filter(postVisibilityFilter.isVisible(currentSone)).toSet();
371 * Returns the replies that have been announced as new in the
372 * {@link #newReplyNotification}.
374 * @return The new replies
376 public Set<PostReply> getNewReplies() {
377 return ImmutableSet.<PostReply> builder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).build();
381 public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
382 Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
383 .addAll(newReplyNotification.getElements())
384 .addAll(localReplyNotification.getElements())
386 return from(allNewReplies).filter(replyVisibilityFilter.isVisible(currentSone)).toSet();
394 * Returns whether the first start notification is currently displayed.
396 * @return {@code true} if the first-start notification is currently
397 * displayed, {@code false} otherwise
399 private boolean hasFirstStartNotification() {
400 return notificationManager.getNotification("first-start-notification") != null;
408 * Starts the web interface and registers all toadlets.
410 public void start() {
413 /* notification templates. */
414 Template wotMissingNotificationTemplate = loaders.loadTemplate("/templates/notify/wotMissingNotification.html");
415 final TemplateNotification wotMissingNotification = new TemplateNotification("wot-missing-notification", wotMissingNotificationTemplate);
416 ticker.scheduleAtFixedRate(new Runnable() {
419 @SuppressWarnings("synthetic-access")
421 if (getCore().getIdentityManager().isConnected()) {
422 wotMissingNotification.dismiss();
424 notificationManager.addNotification(wotMissingNotification);
428 }, 15, 15, TimeUnit.SECONDS);
432 * Stops the web interface and unregisters all toadlets.
435 pageToadletRegistry.unregisterToadlets();
436 ticker.shutdownNow();
444 * Register all toadlets.
446 private void registerToadlets() {
447 Template postTemplate = loaders.loadTemplate("/templates/include/viewPost.html");
448 Template replyTemplate = loaders.loadTemplate("/templates/include/viewReply.html");
449 Template openSearchTemplate = loaders.loadTemplate("/templates/xml/OpenSearch.xml");
451 pageToadletRegistry.addPage(new RedirectPage<FreenetRequest>("", "index.html"));
452 pageToadletRegistry.addPage(new IndexPage(this, loaders, templateRenderer, postVisibilityFilter));
453 pageToadletRegistry.addPage(new NewPage(this, loaders, templateRenderer));
454 pageToadletRegistry.addPage(new CreateSonePage(this, loaders, templateRenderer));
455 pageToadletRegistry.addPage(new KnownSonesPage(this, loaders, templateRenderer));
456 pageToadletRegistry.addPage(new EditProfilePage(this, loaders, templateRenderer));
457 pageToadletRegistry.addPage(new EditProfileFieldPage(this, loaders, templateRenderer));
458 pageToadletRegistry.addPage(new DeleteProfileFieldPage(this, loaders, templateRenderer));
459 pageToadletRegistry.addPage(new CreatePostPage(this, loaders, templateRenderer));
460 pageToadletRegistry.addPage(new CreateReplyPage(this, loaders, templateRenderer));
461 pageToadletRegistry.addPage(new ViewSonePage(this, loaders, templateRenderer));
462 pageToadletRegistry.addPage(new ViewPostPage(this, loaders, templateRenderer));
463 pageToadletRegistry.addPage(new LikePage(this, loaders, templateRenderer));
464 pageToadletRegistry.addPage(new UnlikePage(this, loaders, templateRenderer));
465 pageToadletRegistry.addPage(new DeletePostPage(this, loaders, templateRenderer));
466 pageToadletRegistry.addPage(new DeleteReplyPage(this, loaders, templateRenderer));
467 pageToadletRegistry.addPage(new LockSonePage(this, loaders, templateRenderer));
468 pageToadletRegistry.addPage(new UnlockSonePage(this, loaders, templateRenderer));
469 pageToadletRegistry.addPage(new FollowSonePage(this, loaders, templateRenderer));
470 pageToadletRegistry.addPage(new UnfollowSonePage(this, loaders, templateRenderer));
471 pageToadletRegistry.addPage(new ImageBrowserPage(this, loaders, templateRenderer));
472 pageToadletRegistry.addPage(new CreateAlbumPage(this, loaders, templateRenderer));
473 pageToadletRegistry.addPage(new EditAlbumPage(this, loaders, templateRenderer));
474 pageToadletRegistry.addPage(new DeleteAlbumPage(this, loaders, templateRenderer));
475 pageToadletRegistry.addPage(new UploadImagePage(this, loaders, templateRenderer));
476 pageToadletRegistry.addPage(new EditImagePage(this, loaders, templateRenderer));
477 pageToadletRegistry.addPage(new DeleteImagePage(this, loaders, templateRenderer));
478 pageToadletRegistry.addPage(new MarkAsKnownPage(this, loaders, templateRenderer));
479 pageToadletRegistry.addPage(new BookmarkPage(this, loaders, templateRenderer));
480 pageToadletRegistry.addPage(new UnbookmarkPage(this, loaders, templateRenderer));
481 pageToadletRegistry.addPage(new BookmarksPage(this, loaders, templateRenderer));
482 pageToadletRegistry.addPage(new SearchPage(this, loaders, templateRenderer));
483 pageToadletRegistry.addPage(new DeleteSonePage(this, loaders, templateRenderer));
484 pageToadletRegistry.addPage(new LoginPage(this, loaders, templateRenderer));
485 pageToadletRegistry.addPage(new LogoutPage(this, loaders, templateRenderer));
486 pageToadletRegistry.addPage(new OptionsPage(this, loaders, templateRenderer));
487 pageToadletRegistry.addPage(new RescuePage(this, loaders, templateRenderer));
488 pageToadletRegistry.addPage(new AboutPage(this, loaders, templateRenderer, new PluginVersion(SonePlugin.getPluginVersion()), new PluginYear(sonePlugin.getYear()), new PluginHomepage(sonePlugin.getHomepage())));
489 pageToadletRegistry.addPage(new InvalidPage(this, loaders, templateRenderer));
490 pageToadletRegistry.addPage(new NoPermissionPage(this, loaders, templateRenderer));
491 pageToadletRegistry.addPage(new EmptyImageTitlePage(this, loaders, templateRenderer));
492 pageToadletRegistry.addPage(new EmptyAlbumTitlePage(this, loaders, templateRenderer));
493 pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer));
494 pageToadletRegistry.addPage(new DebugPage(this, loaders, templateRenderer));
495 pageToadletRegistry.addDebugPage(new MetricsPage(this, loaders, templateRenderer, metricRegistry));
496 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css"));
497 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript"));
498 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png"));
499 pageToadletRegistry.addPage(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate));
500 pageToadletRegistry.addPage(new GetImagePage(this));
501 pageToadletRegistry.addPage(new GetTranslationAjaxPage(this));
502 pageToadletRegistry.addPage(new GetStatusAjaxPage(this, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault()));
503 pageToadletRegistry.addPage(new GetNotificationsAjaxPage(this));
504 pageToadletRegistry.addPage(new DismissNotificationAjaxPage(this));
505 pageToadletRegistry.addPage(new CreatePostAjaxPage(this));
506 pageToadletRegistry.addPage(new CreateReplyAjaxPage(this));
507 pageToadletRegistry.addPage(new GetReplyAjaxPage(this, replyTemplate));
508 pageToadletRegistry.addPage(new GetPostAjaxPage(this, postTemplate));
509 pageToadletRegistry.addPage(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter));
510 pageToadletRegistry.addPage(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter, TimeZone.getDefault()));
511 pageToadletRegistry.addPage(new MarkAsKnownAjaxPage(this));
512 pageToadletRegistry.addPage(new DeletePostAjaxPage(this));
513 pageToadletRegistry.addPage(new DeleteReplyAjaxPage(this));
514 pageToadletRegistry.addPage(new LockSoneAjaxPage(this));
515 pageToadletRegistry.addPage(new UnlockSoneAjaxPage(this));
516 pageToadletRegistry.addPage(new FollowSoneAjaxPage(this));
517 pageToadletRegistry.addPage(new UnfollowSoneAjaxPage(this));
518 pageToadletRegistry.addPage(new EditAlbumAjaxPage(this));
519 pageToadletRegistry.addPage(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter));
520 pageToadletRegistry.addPage(new LikeAjaxPage(this));
521 pageToadletRegistry.addPage(new UnlikeAjaxPage(this));
522 pageToadletRegistry.addPage(new GetLikesAjaxPage(this));
523 pageToadletRegistry.addPage(new BookmarkAjaxPage(this));
524 pageToadletRegistry.addPage(new UnbookmarkAjaxPage(this));
525 pageToadletRegistry.addPage(new EditProfileFieldAjaxPage(this));
526 pageToadletRegistry.addPage(new DeleteProfileFieldAjaxPage(this));
527 pageToadletRegistry.addPage(new MoveProfileFieldAjaxPage(this));
529 pageToadletRegistry.registerToadlets();
533 * Returns all {@link Sone#isLocal() local Sone}s that are referenced by
534 * {@link SonePart}s in the given text (after parsing it using
535 * {@link SoneTextParser}).
539 * @return All mentioned local Sones
541 private Collection<Sone> getMentionedSones(String text) {
542 /* we need no context to find mentioned Sones. */
543 Set<Sone> mentionedSones = new HashSet<>();
544 for (Part part : soneTextParser.parse(text, null)) {
545 if (part instanceof SonePart) {
546 mentionedSones.add(((SonePart) part).getSone());
549 return Collections2.filter(mentionedSones, Sone.LOCAL_SONE_FILTER);
553 * Returns the Sone insert notification for the given Sone. If no
554 * notification for the given Sone exists, a new notification is created and
558 * The Sone to get the insert notification for
559 * @return The Sone insert notification
561 private TemplateNotification getSoneInsertNotification(Sone sone) {
562 synchronized (soneInsertNotifications) {
563 TemplateNotification templateNotification = soneInsertNotifications.get(sone);
564 if (templateNotification == null) {
565 templateNotification = new TemplateNotification(loaders.loadTemplate("/templates/notify/soneInsertNotification.html"));
566 templateNotification.set("insertSone", sone);
567 soneInsertNotifications.put(sone, templateNotification);
569 return templateNotification;
573 private boolean localSoneMentionedInNewPostOrReply(Post post) {
574 if (!post.getSone().isLocal()) {
575 if (!getMentionedSones(post.getText()).isEmpty() && !post.isKnown()) {
579 for (PostReply postReply : getCore().getReplies(post.getId())) {
580 if (postReply.getSone().isLocal()) {
583 if (!getMentionedSones(postReply.getText()).isEmpty() && !postReply.isKnown()) {
595 * Notifies the web interface that a new {@link Post} was found.
597 * @param newPostFoundEvent
601 public void newPostFound(NewPostFoundEvent newPostFoundEvent) {
602 Post post = newPostFoundEvent.getPost();
603 boolean isLocal = post.getSone().isLocal();
604 if (!hasFirstStartNotification()) {
605 if (!getMentionedSones(post.getText()).isEmpty() && !isLocal) {
606 mentionNotification.add(post);
607 notificationManager.addNotification(mentionNotification);
613 * Notifies the web interface that a new {@link PostReply} was found.
615 * @param newPostReplyFoundEvent
619 public void newReplyFound(NewPostReplyFoundEvent newPostReplyFoundEvent) {
620 PostReply reply = newPostReplyFoundEvent.getPostReply();
621 boolean isLocal = reply.getSone().isLocal();
623 localReplyNotification.add(reply);
625 newReplyNotification.add(reply);
627 if (!hasFirstStartNotification()) {
628 notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification);
629 if (reply.getPost().isPresent() && localSoneMentionedInNewPostOrReply(reply.getPost().get())) {
630 mentionNotification.add(reply.getPost().get());
631 notificationManager.addNotification(mentionNotification);
634 getCore().markReplyKnown(reply);
639 public void markPostKnown(MarkPostKnownEvent markPostKnownEvent) {
640 removePost(markPostKnownEvent.getPost());
644 public void markReplyKnown(MarkPostReplyKnownEvent markPostReplyKnownEvent) {
645 removeReply(markPostReplyKnownEvent.getPostReply());
649 public void postRemoved(PostRemovedEvent postRemovedEvent) {
650 removePost(postRemovedEvent.getPost());
653 private void removePost(Post post) {
654 newPostNotification.remove(post);
655 if (!localSoneMentionedInNewPostOrReply(post)) {
656 mentionNotification.remove(post);
661 public void replyRemoved(PostReplyRemovedEvent postReplyRemovedEvent) {
662 removeReply(postReplyRemovedEvent.getPostReply());
665 private void removeReply(PostReply reply) {
666 newReplyNotification.remove(reply);
667 localReplyNotification.remove(reply);
668 if (reply.getPost().isPresent() && !localSoneMentionedInNewPostOrReply(reply.getPost().get())) {
669 mentionNotification.remove(reply.getPost().get());
674 * Notifies the web interface that a {@link Sone} is being inserted.
676 * @param soneInsertingEvent
680 public void soneInserting(SoneInsertingEvent soneInsertingEvent) {
681 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertingEvent.getSone());
682 soneInsertNotification.set("soneStatus", "inserting");
683 if (soneInsertingEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
684 notificationManager.addNotification(soneInsertNotification);
689 * Notifies the web interface that a {@link Sone} was inserted.
691 * @param soneInsertedEvent
695 public void soneInserted(SoneInsertedEvent soneInsertedEvent) {
696 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertedEvent.getSone());
697 soneInsertNotification.set("soneStatus", "inserted");
698 soneInsertNotification.set("insertDuration", soneInsertedEvent.getInsertDuration() / 1000);
699 if (soneInsertedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
700 notificationManager.addNotification(soneInsertNotification);
705 * Notifies the web interface that a {@link Sone} insert was aborted.
707 * @param soneInsertAbortedEvent
711 public void soneInsertAborted(SoneInsertAbortedEvent soneInsertAbortedEvent) {
712 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertAbortedEvent.getSone());
713 soneInsertNotification.set("soneStatus", "insert-aborted");
714 soneInsertNotification.set("insert-error", soneInsertAbortedEvent.getCause());
715 if (soneInsertAbortedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
716 notificationManager.addNotification(soneInsertNotification);
721 public void debugActivated(@Nonnull DebugActivatedEvent debugActivatedEvent) {
722 pageToadletRegistry.activateDebugMode();