🔀 Merge branch 'release/v82'
[Sone.git] / src / main / java / net / pterodactylus / sone / web / WebInterface.java
1 /*
2  * Sone - WebInterface.java - Copyright Â© 2010–2020 David Roden
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.pterodactylus.sone.web;
19
20 import static com.google.common.collect.FluentIterable.from;
21 import static java.util.logging.Logger.getLogger;
22
23 import java.util.Collection;
24 import java.util.Set;
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;
30
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;
89
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;
96
97 /**
98  * Bundles functionality that a web interface of a Freenet plugin needs, e.g.
99  * references to l10n helpers.
100  */
101 public class WebInterface implements SessionProvider {
102
103         /** The logger. */
104         private static final Logger logger = getLogger(WebInterface.class.getName());
105
106         /** The loaders for templates, pages, and classpath providers. */
107         private final Loaders loaders;
108
109         /** The notification manager. */
110         private final NotificationManager notificationManager;
111
112         /** The Sone plugin. */
113         private final SonePlugin sonePlugin;
114
115         /** The form password. */
116         private final String formPassword;
117
118         /** The template context factory. */
119         private final TemplateContextFactory templateContextFactory;
120         private final TemplateRenderer templateRenderer;
121
122         /** The parser filter. */
123         private final ParserFilter parserFilter;
124         private final ShortenFilter shortenFilter;
125         private final RenderFilter renderFilter;
126
127         private final ListNotificationFilter listNotificationFilter;
128         private final PostVisibilityFilter postVisibilityFilter;
129         private final ReplyVisibilityFilter replyVisibilityFilter;
130
131         private final ElementLoader elementLoader;
132         private final LinkedElementRenderFilter linkedElementRenderFilter;
133         private final TimeTextConverter timeTextConverter = new TimeTextConverter();
134         private final L10nFilter l10nFilter;
135
136         private final PageToadletRegistry pageToadletRegistry;
137         private final MetricRegistry metricRegistry;
138         private final Translation translation;
139         private final SessionProvider sessionProvider;
140
141         /** The â€śnew post” notification. */
142         private final ListNotification<Post> newPostNotification;
143
144         /** The â€śnew reply” notification. */
145         private final ListNotification<PostReply> newReplyNotification;
146
147         /** The invisible â€ślocal post” notification. */
148         private final ListNotification<Post> localPostNotification;
149
150         /** The invisible â€ślocal reply” notification. */
151         private final ListNotification<PostReply> localReplyNotification;
152
153         @Inject
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();
189
190                 this.templateContextFactory = templateContextFactory;
191                 templateContextFactory.addTemplateObject("webInterface", this);
192                 templateContextFactory.addTemplateObject("formPassword", formPassword);
193         }
194
195         //
196         // ACCESSORS
197         //
198
199         /**
200          * Returns the Sone core used by the Sone plugin.
201          *
202          * @return The Sone core
203          */
204         @Nonnull
205         public Core getCore() {
206                 return sonePlugin.core();
207         }
208
209         /**
210          * Returns the template context factory of the web interface.
211          *
212          * @return The template context factory
213          */
214         public TemplateContextFactory getTemplateContextFactory() {
215                 return templateContextFactory;
216         }
217
218         @Nullable
219         @Override
220         public Sone getCurrentSone(@Nonnull ToadletContext toadletContext) {
221                 return sessionProvider.getCurrentSone(toadletContext);
222         }
223
224         @Override
225         public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) {
226                 sessionProvider.setCurrentSone(toadletContext, sone);
227         }
228
229         /**
230          * Returns the notification manager.
231          *
232          * @return The notification manager
233          */
234         public NotificationManager getNotifications() {
235                 return notificationManager;
236         }
237
238         @Nonnull
239         public Optional<Notification> getNotification(@Nonnull String notificationId) {
240                 return Optional.fromNullable(notificationManager.getNotification(notificationId));
241         }
242
243         @Nonnull
244         public Collection<Notification> getNotifications(@Nullable Sone currentSone) {
245                 return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone);
246         }
247
248         public Translation getTranslation() {
249                 return translation;
250         }
251
252         /**
253          * Returns the node’s form password.
254          *
255          * @return The form password
256          */
257         public String getFormPassword() {
258                 return formPassword;
259         }
260
261         @Nonnull
262         public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
263                 Set<Post> allNewPosts = ImmutableSet.<Post> builder()
264                                 .addAll(newPostNotification.getElements())
265                                 .addAll(localPostNotification.getElements())
266                                 .build();
267                 return from(allNewPosts).filter(postVisibilityFilter.isVisible(currentSone)).toSet();
268         }
269
270         @Nonnull
271         public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
272                 Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
273                                 .addAll(newReplyNotification.getElements())
274                                 .addAll(localReplyNotification.getElements())
275                                 .build();
276                 return from(allNewReplies).filter(replyVisibilityFilter.isVisible(currentSone)).toSet();
277         }
278
279         //
280         // ACTIONS
281         //
282
283         /**
284          * Starts the web interface and registers all toadlets.
285          */
286         public void start() {
287                 registerToadlets();
288         }
289
290         /**
291          * Stops the web interface and unregisters all toadlets.
292          */
293         public void stop() {
294                 pageToadletRegistry.unregisterToadlets();
295         }
296
297         //
298         // PRIVATE METHODS
299         //
300
301         /**
302          * Register all toadlets.
303          */
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");
308
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));
386
387                 pageToadletRegistry.registerToadlets();
388         }
389
390         @Subscribe
391         public void debugActivated(@Nonnull DebugActivatedEvent debugActivatedEvent) {
392                 pageToadletRegistry.activateDebugMode();
393         }
394
395 }