2 * Sone - WebInterface.java - Copyright © 2010–2020 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 java.util.logging.Logger.getLogger;
21 import static java.util.stream.Collectors.toSet;
23 import java.util.Collection;
25 import java.util.TimeZone;
26 import java.util.logging.Logger;
27 import javax.annotation.Nonnull;
28 import javax.annotation.Nullable;
29 import javax.inject.Named;
31 import net.pterodactylus.sone.core.Core;
32 import net.pterodactylus.sone.core.ElementLoader;
33 import net.pterodactylus.sone.core.event.*;
34 import net.pterodactylus.sone.data.Post;
35 import net.pterodactylus.sone.data.PostReply;
36 import net.pterodactylus.sone.data.Sone;
37 import net.pterodactylus.sone.freenet.L10nFilter;
38 import net.pterodactylus.sone.freenet.Translation;
39 import net.pterodactylus.sone.main.Loaders;
40 import net.pterodactylus.sone.main.PluginHomepage;
41 import net.pterodactylus.sone.main.PluginVersion;
42 import net.pterodactylus.sone.main.PluginYear;
43 import net.pterodactylus.sone.main.SonePlugin;
44 import net.pterodactylus.sone.notify.ListNotification;
45 import net.pterodactylus.sone.notify.ListNotificationFilter;
46 import net.pterodactylus.sone.notify.PostVisibilityFilter;
47 import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
48 import net.pterodactylus.sone.template.LinkedElementRenderFilter;
49 import net.pterodactylus.sone.template.ParserFilter;
50 import net.pterodactylus.sone.template.RenderFilter;
51 import net.pterodactylus.sone.template.ShortenFilter;
52 import net.pterodactylus.sone.text.TimeTextConverter;
53 import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage;
54 import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage;
55 import net.pterodactylus.sone.web.ajax.CreateReplyAjaxPage;
56 import net.pterodactylus.sone.web.ajax.DeletePostAjaxPage;
57 import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage;
58 import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage;
59 import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage;
60 import net.pterodactylus.sone.web.ajax.EditAlbumAjaxPage;
61 import net.pterodactylus.sone.web.ajax.EditImageAjaxPage;
62 import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
63 import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
64 import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
65 import net.pterodactylus.sone.web.ajax.GetLinkedElementAjaxPage;
66 import net.pterodactylus.sone.web.ajax.GetNotificationsAjaxPage;
67 import net.pterodactylus.sone.web.ajax.GetPostAjaxPage;
68 import net.pterodactylus.sone.web.ajax.GetReplyAjaxPage;
69 import net.pterodactylus.sone.web.ajax.GetStatusAjaxPage;
70 import net.pterodactylus.sone.web.ajax.GetTimesAjaxPage;
71 import net.pterodactylus.sone.web.ajax.GetTranslationAjaxPage;
72 import net.pterodactylus.sone.web.ajax.LikeAjaxPage;
73 import net.pterodactylus.sone.web.ajax.LockSoneAjaxPage;
74 import net.pterodactylus.sone.web.ajax.MarkAsKnownAjaxPage;
75 import net.pterodactylus.sone.web.ajax.MoveProfileFieldAjaxPage;
76 import net.pterodactylus.sone.web.ajax.UnbookmarkAjaxPage;
77 import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage;
78 import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage;
79 import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
80 import net.pterodactylus.sone.web.page.FreenetRequest;
81 import net.pterodactylus.sone.web.page.TemplateRenderer;
82 import net.pterodactylus.sone.web.pages.*;
83 import net.pterodactylus.util.notify.Notification;
84 import net.pterodactylus.util.notify.NotificationManager;
85 import net.pterodactylus.util.template.Template;
86 import net.pterodactylus.util.template.TemplateContextFactory;
87 import net.pterodactylus.util.web.RedirectPage;
88 import net.pterodactylus.util.web.TemplatePage;
90 import com.codahale.metrics.*;
91 import com.google.common.base.Optional;
92 import com.google.common.collect.ImmutableSet;
93 import com.google.common.eventbus.Subscribe;
94 import com.google.inject.Inject;
95 import freenet.clients.http.ToadletContext;
98 * Bundles functionality that a web interface of a Freenet plugin needs, e.g.
99 * references to l10n helpers.
101 public class WebInterface implements SessionProvider {
104 private static final Logger logger = getLogger(WebInterface.class.getName());
106 /** The loaders for templates, pages, and classpath providers. */
107 private final Loaders loaders;
109 /** The notification manager. */
110 private final NotificationManager notificationManager;
112 /** The Sone plugin. */
113 private final SonePlugin sonePlugin;
115 /** The form password. */
116 private final String formPassword;
118 /** The template context factory. */
119 private final TemplateContextFactory templateContextFactory;
120 private final TemplateRenderer templateRenderer;
122 /** The parser filter. */
123 private final ParserFilter parserFilter;
124 private final ShortenFilter shortenFilter;
125 private final RenderFilter renderFilter;
127 private final ListNotificationFilter listNotificationFilter;
128 private final PostVisibilityFilter postVisibilityFilter;
129 private final ReplyVisibilityFilter replyVisibilityFilter;
131 private final ElementLoader elementLoader;
132 private final LinkedElementRenderFilter linkedElementRenderFilter;
133 private final TimeTextConverter timeTextConverter = new TimeTextConverter();
134 private final L10nFilter l10nFilter;
136 private final PageToadletRegistry pageToadletRegistry;
137 private final MetricRegistry metricRegistry;
138 private final Translation translation;
139 private final SessionProvider sessionProvider;
141 /** The “new post” notification. */
142 private final ListNotification<Post> newPostNotification;
144 /** The “new reply” notification. */
145 private final ListNotification<PostReply> newReplyNotification;
147 /** The invisible “local post” notification. */
148 private final ListNotification<Post> localPostNotification;
150 /** The invisible “local reply” notification. */
151 private final ListNotification<PostReply> localReplyNotification;
154 public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter,
155 PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter,
156 ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
157 TemplateRenderer templateRenderer,
158 ParserFilter parserFilter, ShortenFilter shortenFilter,
159 RenderFilter renderFilter,
160 LinkedElementRenderFilter linkedElementRenderFilter,
161 PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter,
162 NotificationManager notificationManager, SessionProvider sessionProvider,
163 @Named("newRemotePost") ListNotification<Post> newPostNotification,
164 @Named("newRemotePostReply") ListNotification<PostReply> newReplyNotification,
165 @Named("localPost") ListNotification<Post> localPostNotification,
166 @Named("localReply") ListNotification<PostReply> localReplyNotification) {
167 this.sonePlugin = sonePlugin;
168 this.loaders = loaders;
169 this.listNotificationFilter = listNotificationFilter;
170 this.postVisibilityFilter = postVisibilityFilter;
171 this.replyVisibilityFilter = replyVisibilityFilter;
172 this.elementLoader = elementLoader;
173 this.templateRenderer = templateRenderer;
174 this.parserFilter = parserFilter;
175 this.shortenFilter = shortenFilter;
176 this.renderFilter = renderFilter;
177 this.linkedElementRenderFilter = linkedElementRenderFilter;
178 this.pageToadletRegistry = pageToadletRegistry;
179 this.metricRegistry = metricRegistry;
180 this.l10nFilter = l10nFilter;
181 this.translation = translation;
182 this.notificationManager = notificationManager;
183 this.sessionProvider = sessionProvider;
184 this.newPostNotification = newPostNotification;
185 this.newReplyNotification = newReplyNotification;
186 this.localPostNotification = localPostNotification;
187 this.localReplyNotification = localReplyNotification;
188 formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
190 this.templateContextFactory = templateContextFactory;
191 templateContextFactory.addTemplateObject("webInterface", this);
192 templateContextFactory.addTemplateObject("formPassword", formPassword);
200 * Returns the Sone core used by the Sone plugin.
202 * @return The Sone core
205 public Core getCore() {
206 return sonePlugin.core();
210 * Returns the template context factory of the web interface.
212 * @return The template context factory
214 public TemplateContextFactory getTemplateContextFactory() {
215 return templateContextFactory;
220 public Sone getCurrentSone(@Nonnull ToadletContext toadletContext) {
221 return sessionProvider.getCurrentSone(toadletContext);
225 public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) {
226 sessionProvider.setCurrentSone(toadletContext, sone);
230 * Returns the notification manager.
232 * @return The notification manager
234 public NotificationManager getNotifications() {
235 return notificationManager;
239 public Optional<Notification> getNotification(@Nonnull String notificationId) {
240 return Optional.fromNullable(notificationManager.getNotification(notificationId));
244 public Collection<Notification> getNotifications(@Nullable Sone currentSone) {
245 return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone);
248 public Translation getTranslation() {
253 * Returns the node’s form password.
255 * @return The form password
257 public String getFormPassword() {
262 public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
263 Set<Post> allNewPosts = ImmutableSet.<Post> builder()
264 .addAll(newPostNotification.getElements())
265 .addAll(localPostNotification.getElements())
267 return allNewPosts.stream().filter(postVisibilityFilter.isVisible(currentSone)).collect(toSet());
271 public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
272 Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
273 .addAll(newReplyNotification.getElements())
274 .addAll(localReplyNotification.getElements())
276 return allNewReplies.stream().filter(replyVisibilityFilter.isVisible(currentSone)).collect(toSet());
284 * Starts the web interface and registers all toadlets.
286 public void start() {
291 * Stops the web interface and unregisters all toadlets.
294 pageToadletRegistry.unregisterToadlets();
302 * Register all toadlets.
304 private void registerToadlets() {
305 Template postTemplate = loaders.loadTemplate("/templates/include/viewPost.html");
306 Template replyTemplate = loaders.loadTemplate("/templates/include/viewReply.html");
307 Template openSearchTemplate = loaders.loadTemplate("/templates/xml/OpenSearch.xml");
309 pageToadletRegistry.addPage(new RedirectPage<FreenetRequest>("", "index.html"));
310 pageToadletRegistry.addPage(new IndexPage(this, loaders, templateRenderer, postVisibilityFilter));
311 pageToadletRegistry.addPage(new NewPage(this, loaders, templateRenderer));
312 pageToadletRegistry.addPage(new CreateSonePage(this, loaders, templateRenderer));
313 pageToadletRegistry.addPage(new KnownSonesPage(this, loaders, templateRenderer));
314 pageToadletRegistry.addPage(new EditProfilePage(this, loaders, templateRenderer));
315 pageToadletRegistry.addPage(new EditProfileFieldPage(this, loaders, templateRenderer));
316 pageToadletRegistry.addPage(new DeleteProfileFieldPage(this, loaders, templateRenderer));
317 pageToadletRegistry.addPage(new CreatePostPage(this, loaders, templateRenderer));
318 pageToadletRegistry.addPage(new CreateReplyPage(this, loaders, templateRenderer));
319 pageToadletRegistry.addPage(new ViewSonePage(this, loaders, templateRenderer));
320 pageToadletRegistry.addPage(new ViewPostPage(this, loaders, templateRenderer));
321 pageToadletRegistry.addPage(new LikePage(this, loaders, templateRenderer));
322 pageToadletRegistry.addPage(new UnlikePage(this, loaders, templateRenderer));
323 pageToadletRegistry.addPage(new DeletePostPage(this, loaders, templateRenderer));
324 pageToadletRegistry.addPage(new DeleteReplyPage(this, loaders, templateRenderer));
325 pageToadletRegistry.addPage(new LockSonePage(this, loaders, templateRenderer));
326 pageToadletRegistry.addPage(new UnlockSonePage(this, loaders, templateRenderer));
327 pageToadletRegistry.addPage(new FollowSonePage(this, loaders, templateRenderer));
328 pageToadletRegistry.addPage(new UnfollowSonePage(this, loaders, templateRenderer));
329 pageToadletRegistry.addPage(new ImageBrowserPage(this, loaders, templateRenderer));
330 pageToadletRegistry.addPage(new CreateAlbumPage(this, loaders, templateRenderer));
331 pageToadletRegistry.addPage(new EditAlbumPage(this, loaders, templateRenderer));
332 pageToadletRegistry.addPage(new DeleteAlbumPage(this, loaders, templateRenderer));
333 pageToadletRegistry.addPage(new UploadImagePage(this, loaders, templateRenderer));
334 pageToadletRegistry.addPage(new EditImagePage(this, loaders, templateRenderer));
335 pageToadletRegistry.addPage(new DeleteImagePage(this, loaders, templateRenderer));
336 pageToadletRegistry.addPage(new MarkAsKnownPage(this, loaders, templateRenderer));
337 pageToadletRegistry.addPage(new BookmarkPage(this, loaders, templateRenderer));
338 pageToadletRegistry.addPage(new UnbookmarkPage(this, loaders, templateRenderer));
339 pageToadletRegistry.addPage(new BookmarksPage(this, loaders, templateRenderer));
340 pageToadletRegistry.addPage(new SearchPage(this, loaders, templateRenderer));
341 pageToadletRegistry.addPage(new DeleteSonePage(this, loaders, templateRenderer));
342 pageToadletRegistry.addPage(new LoginPage(this, loaders, templateRenderer));
343 pageToadletRegistry.addPage(new LogoutPage(this, loaders, templateRenderer));
344 pageToadletRegistry.addPage(new OptionsPage(this, loaders, templateRenderer));
345 pageToadletRegistry.addPage(new RescuePage(this, loaders, templateRenderer));
346 pageToadletRegistry.addPage(new AboutPage(this, loaders, templateRenderer, new PluginVersion(SonePlugin.getPluginVersion()), new PluginYear(sonePlugin.getYear()), new PluginHomepage(sonePlugin.getHomepage())));
347 pageToadletRegistry.addPage(new InvalidPage(this, loaders, templateRenderer));
348 pageToadletRegistry.addPage(new NoPermissionPage(this, loaders, templateRenderer));
349 pageToadletRegistry.addPage(new EmptyImageTitlePage(this, loaders, templateRenderer));
350 pageToadletRegistry.addPage(new EmptyAlbumTitlePage(this, loaders, templateRenderer));
351 pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer));
352 pageToadletRegistry.addPage(new DebugPage(this, loaders, templateRenderer));
353 pageToadletRegistry.addDebugPage(new MetricsPage(this, loaders, templateRenderer, metricRegistry));
354 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css"));
355 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript"));
356 pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png"));
357 pageToadletRegistry.addPage(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate));
358 pageToadletRegistry.addPage(new GetImagePage(this));
359 pageToadletRegistry.addPage(new GetTranslationAjaxPage(this));
360 pageToadletRegistry.addPage(new GetStatusAjaxPage(this, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault()));
361 pageToadletRegistry.addPage(new GetNotificationsAjaxPage(this));
362 pageToadletRegistry.addPage(new DismissNotificationAjaxPage(this));
363 pageToadletRegistry.addPage(new CreatePostAjaxPage(this));
364 pageToadletRegistry.addPage(new CreateReplyAjaxPage(this));
365 pageToadletRegistry.addPage(new GetReplyAjaxPage(this, replyTemplate));
366 pageToadletRegistry.addPage(new GetPostAjaxPage(this, postTemplate));
367 pageToadletRegistry.addPage(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter));
368 pageToadletRegistry.addPage(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter, TimeZone.getDefault()));
369 pageToadletRegistry.addPage(new MarkAsKnownAjaxPage(this));
370 pageToadletRegistry.addPage(new DeletePostAjaxPage(this));
371 pageToadletRegistry.addPage(new DeleteReplyAjaxPage(this));
372 pageToadletRegistry.addPage(new LockSoneAjaxPage(this));
373 pageToadletRegistry.addPage(new UnlockSoneAjaxPage(this));
374 pageToadletRegistry.addPage(new FollowSoneAjaxPage(this));
375 pageToadletRegistry.addPage(new UnfollowSoneAjaxPage(this));
376 pageToadletRegistry.addPage(new EditAlbumAjaxPage(this));
377 pageToadletRegistry.addPage(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter));
378 pageToadletRegistry.addPage(new LikeAjaxPage(this));
379 pageToadletRegistry.addPage(new UnlikeAjaxPage(this));
380 pageToadletRegistry.addPage(new GetLikesAjaxPage(this));
381 pageToadletRegistry.addPage(new BookmarkAjaxPage(this));
382 pageToadletRegistry.addPage(new UnbookmarkAjaxPage(this));
383 pageToadletRegistry.addPage(new EditProfileFieldAjaxPage(this));
384 pageToadletRegistry.addPage(new DeleteProfileFieldAjaxPage(this));
385 pageToadletRegistry.addPage(new MoveProfileFieldAjaxPage(this));
387 pageToadletRegistry.registerToadlets();
391 public void debugActivated(@Nonnull DebugActivatedEvent debugActivatedEvent) {
392 pageToadletRegistry.activateDebugMode();