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;
27 import java.util.TimeZone;
28 import java.util.UUID;
29 import java.util.logging.Logger;
30 import javax.annotation.Nonnull;
31 import javax.annotation.Nullable;
32 import javax.inject.Named;
34 import net.pterodactylus.sone.core.Core;
35 import net.pterodactylus.sone.core.ElementLoader;
36 import net.pterodactylus.sone.core.event.*;
37 import net.pterodactylus.sone.data.Post;
38 import net.pterodactylus.sone.data.PostReply;
39 import net.pterodactylus.sone.data.Sone;
40 import net.pterodactylus.sone.freenet.L10nFilter;
41 import net.pterodactylus.sone.freenet.Translation;
42 import net.pterodactylus.sone.main.Loaders;
43 import net.pterodactylus.sone.main.PluginHomepage;
44 import net.pterodactylus.sone.main.PluginVersion;
45 import net.pterodactylus.sone.main.PluginYear;
46 import net.pterodactylus.sone.main.SonePlugin;
47 import net.pterodactylus.sone.notify.ListNotification;
48 import net.pterodactylus.sone.notify.ListNotificationFilter;
49 import net.pterodactylus.sone.notify.PostVisibilityFilter;
50 import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
51 import net.pterodactylus.sone.template.LinkedElementRenderFilter;
52 import net.pterodactylus.sone.template.ParserFilter;
53 import net.pterodactylus.sone.template.RenderFilter;
54 import net.pterodactylus.sone.template.ShortenFilter;
55 import net.pterodactylus.sone.text.TimeTextConverter;
56 import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage;
57 import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage;
58 import net.pterodactylus.sone.web.ajax.CreateReplyAjaxPage;
59 import net.pterodactylus.sone.web.ajax.DeletePostAjaxPage;
60 import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage;
61 import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage;
62 import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage;
63 import net.pterodactylus.sone.web.ajax.EditAlbumAjaxPage;
64 import net.pterodactylus.sone.web.ajax.EditImageAjaxPage;
65 import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
66 import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
67 import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
68 import net.pterodactylus.sone.web.ajax.GetLinkedElementAjaxPage;
69 import net.pterodactylus.sone.web.ajax.GetNotificationsAjaxPage;
70 import net.pterodactylus.sone.web.ajax.GetPostAjaxPage;
71 import net.pterodactylus.sone.web.ajax.GetReplyAjaxPage;
72 import net.pterodactylus.sone.web.ajax.GetStatusAjaxPage;
73 import net.pterodactylus.sone.web.ajax.GetTimesAjaxPage;
74 import net.pterodactylus.sone.web.ajax.GetTranslationAjaxPage;
75 import net.pterodactylus.sone.web.ajax.LikeAjaxPage;
76 import net.pterodactylus.sone.web.ajax.LockSoneAjaxPage;
77 import net.pterodactylus.sone.web.ajax.MarkAsKnownAjaxPage;
78 import net.pterodactylus.sone.web.ajax.MoveProfileFieldAjaxPage;
79 import net.pterodactylus.sone.web.ajax.UnbookmarkAjaxPage;
80 import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage;
81 import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage;
82 import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
83 import net.pterodactylus.sone.web.page.FreenetRequest;
84 import net.pterodactylus.sone.web.page.TemplateRenderer;
85 import net.pterodactylus.sone.web.pages.*;
86 import net.pterodactylus.util.notify.Notification;
87 import net.pterodactylus.util.notify.NotificationManager;
88 import net.pterodactylus.util.notify.TemplateNotification;
89 import net.pterodactylus.util.template.Template;
90 import net.pterodactylus.util.template.TemplateContextFactory;
91 import net.pterodactylus.util.web.RedirectPage;
92 import net.pterodactylus.util.web.TemplatePage;
94 import freenet.clients.http.SessionManager;
95 import freenet.clients.http.SessionManager.Session;
96 import freenet.clients.http.ToadletContext;
98 import com.codahale.metrics.*;
99 import com.google.common.base.Optional;
100 import com.google.common.collect.ImmutableSet;
101 import com.google.common.eventbus.Subscribe;
102 import com.google.inject.Inject;
105 * Bundles functionality that a web interface of a Freenet plugin needs, e.g.
106 * references to l10n helpers.
108 public class WebInterface implements SessionProvider {
111 private static final Logger logger = getLogger(WebInterface.class.getName());
113 /** The loaders for templates, pages, and classpath providers. */
114 private final Loaders loaders;
116 /** The notification manager. */
117 private final NotificationManager notificationManager;
119 /** The Sone plugin. */
120 private final SonePlugin sonePlugin;
122 /** The form password. */
123 private final String formPassword;
125 /** The template context factory. */
126 private final TemplateContextFactory templateContextFactory;
127 private final TemplateRenderer templateRenderer;
129 /** The parser filter. */
130 private final ParserFilter parserFilter;
131 private final ShortenFilter shortenFilter;
132 private final RenderFilter renderFilter;
134 private final ListNotificationFilter listNotificationFilter;
135 private final PostVisibilityFilter postVisibilityFilter;
136 private final ReplyVisibilityFilter replyVisibilityFilter;
138 private final ElementLoader elementLoader;
139 private final LinkedElementRenderFilter linkedElementRenderFilter;
140 private final TimeTextConverter timeTextConverter = new TimeTextConverter();
141 private final L10nFilter l10nFilter;
143 private final PageToadletRegistry pageToadletRegistry;
144 private final MetricRegistry metricRegistry;
145 private final Translation translation;
147 /** The “new post” notification. */
148 private final ListNotification<Post> newPostNotification;
150 /** The “new reply” notification. */
151 private final ListNotification<PostReply> newReplyNotification;
153 /** The invisible “local post” notification. */
154 private final ListNotification<Post> localPostNotification;
156 /** The invisible “local reply” notification. */
157 private final ListNotification<PostReply> localReplyNotification;
159 /** Notifications for sone inserts. */
160 private final Map<Sone, TemplateNotification> soneInsertNotifications = new HashMap<>();
163 public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter,
164 PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter,
165 ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
166 TemplateRenderer templateRenderer,
167 ParserFilter parserFilter, ShortenFilter shortenFilter,
168 RenderFilter renderFilter,
169 LinkedElementRenderFilter linkedElementRenderFilter,
170 PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter,
171 NotificationManager notificationManager, @Named("newRemotePost") ListNotification<Post> newPostNotification,
172 @Named("newRemotePostReply") ListNotification<PostReply> newReplyNotification,
173 @Named("localPost") ListNotification<Post> localPostNotification,
174 @Named("localReply") ListNotification<PostReply> localReplyNotification) {
175 this.sonePlugin = sonePlugin;
176 this.loaders = loaders;
177 this.listNotificationFilter = listNotificationFilter;
178 this.postVisibilityFilter = postVisibilityFilter;
179 this.replyVisibilityFilter = replyVisibilityFilter;
180 this.elementLoader = elementLoader;
181 this.templateRenderer = templateRenderer;
182 this.parserFilter = parserFilter;
183 this.shortenFilter = shortenFilter;
184 this.renderFilter = renderFilter;
185 this.linkedElementRenderFilter = linkedElementRenderFilter;
186 this.pageToadletRegistry = pageToadletRegistry;
187 this.metricRegistry = metricRegistry;
188 this.l10nFilter = l10nFilter;
189 this.translation = translation;
190 this.notificationManager = notificationManager;
191 this.newPostNotification = newPostNotification;
192 this.newReplyNotification = newReplyNotification;
193 this.localPostNotification = localPostNotification;
194 this.localReplyNotification = localReplyNotification;
195 formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
197 this.templateContextFactory = templateContextFactory;
198 templateContextFactory.addTemplateObject("webInterface", this);
199 templateContextFactory.addTemplateObject("formPassword", formPassword);
207 * Returns the Sone core used by the Sone plugin.
209 * @return The Sone core
212 public Core getCore() {
213 return sonePlugin.core();
217 * Returns the template context factory of the web interface.
219 * @return The template context factory
221 public TemplateContextFactory getTemplateContextFactory() {
222 return templateContextFactory;
225 private Session getCurrentSessionWithoutCreation(ToadletContext toadletContenxt) {
226 return getSessionManager().useSession(toadletContenxt);
229 private Session getOrCreateCurrentSession(ToadletContext toadletContenxt) {
230 Session session = getCurrentSessionWithoutCreation(toadletContenxt);
231 if (session == null) {
232 session = getSessionManager().createSession(UUID.randomUUID().toString(), toadletContenxt);
237 public Sone getCurrentSoneCreatingSession(ToadletContext toadletContext) {
238 Collection<Sone> localSones = getCore().getLocalSones();
239 if (localSones.size() == 1) {
240 return localSones.iterator().next();
242 return getCurrentSone(getOrCreateCurrentSession(toadletContext));
245 public Sone getCurrentSoneWithoutCreatingSession(ToadletContext toadletContext) {
246 Collection<Sone> localSones = getCore().getLocalSones();
247 if (localSones.size() == 1) {
248 return localSones.iterator().next();
250 return getCurrentSone(getCurrentSessionWithoutCreation(toadletContext));
254 * Returns the currently logged in Sone.
258 * @return The currently logged in Sone, or {@code null} if no Sone is
259 * currently logged in
261 private Sone getCurrentSone(Session session) {
262 if (session == null) {
265 String soneId = (String) session.getAttribute("Sone.CurrentSone");
266 if (soneId == null) {
269 return getCore().getLocalSone(soneId);
274 public Sone getCurrentSone(@Nonnull ToadletContext toadletContext, boolean createSession) {
275 return createSession ? getCurrentSoneCreatingSession(toadletContext) : getCurrentSoneWithoutCreatingSession(toadletContext);
279 * Sets the currently logged in Sone.
281 * @param toadletContext
282 * The toadlet context
284 * The Sone to set as currently logged in
287 public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) {
288 Session session = getOrCreateCurrentSession(toadletContext);
290 session.removeAttribute("Sone.CurrentSone");
292 session.setAttribute("Sone.CurrentSone", sone.getId());
297 * Returns the notification manager.
299 * @return The notification manager
301 public NotificationManager getNotifications() {
302 return notificationManager;
306 public Optional<Notification> getNotification(@Nonnull String notificationId) {
307 return Optional.fromNullable(notificationManager.getNotification(notificationId));
311 public Collection<Notification> getNotifications(@Nullable Sone currentSone) {
312 return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone);
315 public Translation getTranslation() {
320 * Returns the session manager of the node.
322 * @return The node’s session manager
324 public SessionManager getSessionManager() {
325 return sonePlugin.pluginRespirator().getSessionManager("Sone");
329 * Returns the node’s form password.
331 * @return The form password
333 public String getFormPassword() {
338 public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
339 Set<Post> allNewPosts = ImmutableSet.<Post> builder()
340 .addAll(newPostNotification.getElements())
341 .addAll(localPostNotification.getElements())
343 return from(allNewPosts).filter(postVisibilityFilter.isVisible(currentSone)).toSet();
347 * Returns the replies that have been announced as new in the
348 * {@link #newReplyNotification}.
350 * @return The new replies
352 public Set<PostReply> getNewReplies() {
353 return ImmutableSet.<PostReply> builder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).build();
357 public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
358 Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
359 .addAll(newReplyNotification.getElements())
360 .addAll(localReplyNotification.getElements())
362 return from(allNewReplies).filter(replyVisibilityFilter.isVisible(currentSone)).toSet();
370 * Returns whether the first start notification is currently displayed.
372 * @return {@code true} if the first-start notification is currently
373 * displayed, {@code false} otherwise
375 private boolean hasFirstStartNotification() {
376 return notificationManager.getNotification("first-start-notification") != null;
384 * Starts the web interface and registers all toadlets.
386 public void start() {
391 * Stops the web interface and unregisters all toadlets.
394 pageToadletRegistry.unregisterToadlets();
402 * Register all toadlets.
404 private void registerToadlets() {
405 Template postTemplate = loaders.loadTemplate("/templates/include/viewPost.html");
406 Template replyTemplate = loaders.loadTemplate("/templates/include/viewReply.html");
407 Template openSearchTemplate = loaders.loadTemplate("/templates/xml/OpenSearch.xml");
409 pageToadletRegistry.addPage(new RedirectPage<FreenetRequest>("", "index.html"));
410 pageToadletRegistry.addPage(new IndexPage(this, loaders, templateRenderer, postVisibilityFilter));
411 pageToadletRegistry.addPage(new NewPage(this, loaders, templateRenderer));
412 pageToadletRegistry.addPage(new CreateSonePage(this, loaders, templateRenderer));
413 pageToadletRegistry.addPage(new KnownSonesPage(this, loaders, templateRenderer));
414 pageToadletRegistry.addPage(new EditProfilePage(this, loaders, templateRenderer));
415 pageToadletRegistry.addPage(new EditProfileFieldPage(this, loaders, templateRenderer));
416 pageToadletRegistry.addPage(new DeleteProfileFieldPage(this, loaders, templateRenderer));
417 pageToadletRegistry.addPage(new CreatePostPage(this, loaders, templateRenderer));
418 pageToadletRegistry.addPage(new CreateReplyPage(this, loaders, templateRenderer));
419 pageToadletRegistry.addPage(new ViewSonePage(this, loaders, templateRenderer));
420 pageToadletRegistry.addPage(new ViewPostPage(this, loaders, templateRenderer));
421 pageToadletRegistry.addPage(new LikePage(this, loaders, templateRenderer));
422 pageToadletRegistry.addPage(new UnlikePage(this, loaders, templateRenderer));
423 pageToadletRegistry.addPage(new DeletePostPage(this, loaders, templateRenderer));
424 pageToadletRegistry.addPage(new DeleteReplyPage(this, loaders, templateRenderer));
425 pageToadletRegistry.addPage(new LockSonePage(this, loaders, templateRenderer));
426 pageToadletRegistry.addPage(new UnlockSonePage(this, loaders, templateRenderer));
427 pageToadletRegistry.addPage(new FollowSonePage(this, loaders, templateRenderer));
428 pageToadletRegistry.addPage(new UnfollowSonePage(this, loaders, templateRenderer));
429 pageToadletRegistry.addPage(new ImageBrowserPage(this, loaders, templateRenderer));
430 pageToadletRegistry.addPage(new CreateAlbumPage(this, loaders, templateRenderer));
431 pageToadletRegistry.addPage(new EditAlbumPage(this, loaders, templateRenderer));
432 pageToadletRegistry.addPage(new DeleteAlbumPage(this, loaders, templateRenderer));
433 pageToadletRegistry.addPage(new UploadImagePage(this, loaders, templateRenderer));
434 pageToadletRegistry.addPage(new EditImagePage(this, loaders, templateRenderer));
435 pageToadletRegistry.addPage(new DeleteImagePage(this, loaders, templateRenderer));
436 pageToadletRegistry.addPage(new MarkAsKnownPage(this, loaders, templateRenderer));
437 pageToadletRegistry.addPage(new BookmarkPage(this, loaders, templateRenderer));
438 pageToadletRegistry.addPage(new UnbookmarkPage(this, loaders, templateRenderer));
439 pageToadletRegistry.addPage(new BookmarksPage(this, loaders, templateRenderer));
440 pageToadletRegistry.addPage(new SearchPage(this, loaders, templateRenderer));
441 pageToadletRegistry.addPage(new DeleteSonePage(this, loaders, templateRenderer));
442 pageToadletRegistry.addPage(new LoginPage(this, loaders, templateRenderer));
443 pageToadletRegistry.addPage(new LogoutPage(this, loaders, templateRenderer));
444 pageToadletRegistry.addPage(new OptionsPage(this, loaders, templateRenderer));
445 pageToadletRegistry.addPage(new RescuePage(this, loaders, templateRenderer));
446 pageToadletRegistry.addPage(new AboutPage(this, loaders, templateRenderer, new PluginVersion(SonePlugin.getPluginVersion()), new PluginYear(sonePlugin.getYear()), new PluginHomepage(sonePlugin.getHomepage())));
447 pageToadletRegistry.addPage(new InvalidPage(this, loaders, templateRenderer));
448 pageToadletRegistry.addPage(new NoPermissionPage(this, loaders, templateRenderer));
449 pageToadletRegistry.addPage(new EmptyImageTitlePage(this, loaders, templateRenderer));
450 pageToadletRegistry.addPage(new EmptyAlbumTitlePage(this, loaders, templateRenderer));
451 pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer));
452 pageToadletRegistry.addPage(new DebugPage(this, loaders, templateRenderer));
453 pageToadletRegistry.addDebugPage(new MetricsPage(this, loaders, templateRenderer, metricRegistry));
454 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css"));
455 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript"));
456 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png"));
457 pageToadletRegistry.addPage(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate));
458 pageToadletRegistry.addPage(new GetImagePage(this));
459 pageToadletRegistry.addPage(new GetTranslationAjaxPage(this));
460 pageToadletRegistry.addPage(new GetStatusAjaxPage(this, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault()));
461 pageToadletRegistry.addPage(new GetNotificationsAjaxPage(this));
462 pageToadletRegistry.addPage(new DismissNotificationAjaxPage(this));
463 pageToadletRegistry.addPage(new CreatePostAjaxPage(this));
464 pageToadletRegistry.addPage(new CreateReplyAjaxPage(this));
465 pageToadletRegistry.addPage(new GetReplyAjaxPage(this, replyTemplate));
466 pageToadletRegistry.addPage(new GetPostAjaxPage(this, postTemplate));
467 pageToadletRegistry.addPage(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter));
468 pageToadletRegistry.addPage(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter, TimeZone.getDefault()));
469 pageToadletRegistry.addPage(new MarkAsKnownAjaxPage(this));
470 pageToadletRegistry.addPage(new DeletePostAjaxPage(this));
471 pageToadletRegistry.addPage(new DeleteReplyAjaxPage(this));
472 pageToadletRegistry.addPage(new LockSoneAjaxPage(this));
473 pageToadletRegistry.addPage(new UnlockSoneAjaxPage(this));
474 pageToadletRegistry.addPage(new FollowSoneAjaxPage(this));
475 pageToadletRegistry.addPage(new UnfollowSoneAjaxPage(this));
476 pageToadletRegistry.addPage(new EditAlbumAjaxPage(this));
477 pageToadletRegistry.addPage(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter));
478 pageToadletRegistry.addPage(new LikeAjaxPage(this));
479 pageToadletRegistry.addPage(new UnlikeAjaxPage(this));
480 pageToadletRegistry.addPage(new GetLikesAjaxPage(this));
481 pageToadletRegistry.addPage(new BookmarkAjaxPage(this));
482 pageToadletRegistry.addPage(new UnbookmarkAjaxPage(this));
483 pageToadletRegistry.addPage(new EditProfileFieldAjaxPage(this));
484 pageToadletRegistry.addPage(new DeleteProfileFieldAjaxPage(this));
485 pageToadletRegistry.addPage(new MoveProfileFieldAjaxPage(this));
487 pageToadletRegistry.registerToadlets();
491 * Returns the Sone insert notification for the given Sone. If no
492 * notification for the given Sone exists, a new notification is created and
496 * The Sone to get the insert notification for
497 * @return The Sone insert notification
499 private TemplateNotification getSoneInsertNotification(Sone sone) {
500 synchronized (soneInsertNotifications) {
501 TemplateNotification templateNotification = soneInsertNotifications.get(sone);
502 if (templateNotification == null) {
503 templateNotification = new TemplateNotification(loaders.loadTemplate("/templates/notify/soneInsertNotification.html"));
504 templateNotification.set("insertSone", sone);
505 soneInsertNotifications.put(sone, templateNotification);
507 return templateNotification;
516 public void markPostKnown(MarkPostKnownEvent markPostKnownEvent) {
517 removePost(markPostKnownEvent.getPost());
521 public void postRemoved(PostRemovedEvent postRemovedEvent) {
522 removePost(postRemovedEvent.getPost());
525 private void removePost(Post post) {
526 newPostNotification.remove(post);
530 * Notifies the web interface that a {@link Sone} is being inserted.
532 * @param soneInsertingEvent
536 public void soneInserting(SoneInsertingEvent soneInsertingEvent) {
537 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertingEvent.getSone());
538 soneInsertNotification.set("soneStatus", "inserting");
539 if (soneInsertingEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
540 notificationManager.addNotification(soneInsertNotification);
545 * Notifies the web interface that a {@link Sone} was inserted.
547 * @param soneInsertedEvent
551 public void soneInserted(SoneInsertedEvent soneInsertedEvent) {
552 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertedEvent.getSone());
553 soneInsertNotification.set("soneStatus", "inserted");
554 soneInsertNotification.set("insertDuration", soneInsertedEvent.getInsertDuration() / 1000);
555 if (soneInsertedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
556 notificationManager.addNotification(soneInsertNotification);
561 * Notifies the web interface that a {@link Sone} insert was aborted.
563 * @param soneInsertAbortedEvent
567 public void soneInsertAborted(SoneInsertAbortedEvent soneInsertAbortedEvent) {
568 TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertAbortedEvent.getSone());
569 soneInsertNotification.set("soneStatus", "insert-aborted");
570 soneInsertNotification.set("insert-error", soneInsertAbortedEvent.getCause());
571 if (soneInsertAbortedEvent.getSone().getOptions().isSoneInsertNotificationEnabled()) {
572 notificationManager.addNotification(soneInsertNotification);
577 public void debugActivated(@Nonnull DebugActivatedEvent debugActivatedEvent) {
578 pageToadletRegistry.activateDebugMode();