Only show Sone insert notifications if they are enable for the Sone in question.
[Sone.git] / src / main / java / net / pterodactylus / sone / web / WebInterface.java
index b4fffd9..054ed53 100644 (file)
@@ -17,6 +17,7 @@
 
 package net.pterodactylus.sone.web;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -58,6 +59,9 @@ import net.pterodactylus.sone.template.SoneAccessor;
 import net.pterodactylus.sone.template.SubstringFilter;
 import net.pterodactylus.sone.template.TrustAccessor;
 import net.pterodactylus.sone.template.UnknownDateFilter;
+import net.pterodactylus.sone.text.Part;
+import net.pterodactylus.sone.text.SonePart;
+import net.pterodactylus.sone.text.SoneTextParser;
 import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage;
 import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage;
 import net.pterodactylus.sone.web.ajax.CreateReplyAjaxPage;
@@ -96,6 +100,8 @@ import net.pterodactylus.util.cache.CacheItem;
 import net.pterodactylus.util.cache.DefaultCacheItem;
 import net.pterodactylus.util.cache.MemoryCache;
 import net.pterodactylus.util.cache.ValueRetriever;
+import net.pterodactylus.util.collection.SetBuilder;
+import net.pterodactylus.util.filter.Filters;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.notify.Notification;
 import net.pterodactylus.util.notify.NotificationManager;
@@ -151,6 +157,9 @@ public class WebInterface implements CoreListener {
        /** The template context factory. */
        private final TemplateContextFactory templateContextFactory;
 
+       /** The Sone text parser. */
+       private final SoneTextParser soneTextParser;
+
        /** The “new Sone” notification. */
        private final ListNotification<Sone> newSoneNotification;
 
@@ -160,11 +169,17 @@ public class WebInterface implements CoreListener {
        /** The “new reply” notification. */
        private final ListNotification<Reply> newReplyNotification;
 
-       /** The “rescuing Sone” notification. */
-       private final ListNotification<Sone> rescuingSonesNotification;
+       /** The invisible “local post” notification. */
+       private final ListNotification<Post> localPostNotification;
+
+       /** The invisible “local reply” notification. */
+       private final ListNotification<Reply> localReplyNotification;
 
-       /** The “Sone rescued” notification. */
-       private final ListNotification<Sone> sonesRescuedNotification;
+       /** The “you have been mentioned” notification. */
+       private final ListNotification<Post> mentionNotification;
+
+       /** Notifications for sone inserts. */
+       private final Map<Sone, TemplateNotification> soneInsertNotifications = new HashMap<Sone, TemplateNotification>();
 
        /** Sone locked notification ticker objects. */
        private final Map<Sone, Object> lockedSonesTickerObjects = Collections.synchronizedMap(new HashMap<Sone, Object>());
@@ -185,6 +200,7 @@ public class WebInterface implements CoreListener {
        public WebInterface(SonePlugin sonePlugin) {
                this.sonePlugin = sonePlugin;
                formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
+               soneTextParser = new SoneTextParser(getCore(), getCore());
 
                templateContextFactory = new TemplateContextFactory();
                templateContextFactory.addAccessor(Object.class, new ReflectionAccessor());
@@ -206,7 +222,7 @@ public class WebInterface implements CoreListener {
                templateContextFactory.addFilter("match", new MatchFilter());
                templateContextFactory.addFilter("css", new CssClassNameFilter());
                templateContextFactory.addFilter("js", new JavascriptFilter());
-               templateContextFactory.addFilter("parse", new ParserFilter(getCore(), templateContextFactory));
+               templateContextFactory.addFilter("parse", new ParserFilter(getCore(), templateContextFactory, soneTextParser));
                templateContextFactory.addFilter("unknown", new UnknownDateFilter(getL10n(), "View.Sone.Text.UnknownDate"));
                templateContextFactory.addFilter("format", new FormatFilter());
                templateContextFactory.addFilter("sort", new CollectionSortFilter());
@@ -224,14 +240,17 @@ public class WebInterface implements CoreListener {
                Template newPostNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/newPostNotification.html"));
                newPostNotification = new ListNotification<Post>("new-post-notification", "posts", newPostNotificationTemplate, false);
 
+               Template localPostNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/newPostNotification.html"));
+               localPostNotification = new ListNotification<Post>("local-post-notification", "posts", localPostNotificationTemplate, false);
+
                Template newReplyNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/newReplyNotification.html"));
                newReplyNotification = new ListNotification<Reply>("new-reply-notification", "replies", newReplyNotificationTemplate, false);
 
-               Template rescuingSonesTemplate = TemplateParser.parse(createReader("/templates/notify/rescuingSonesNotification.html"));
-               rescuingSonesNotification = new ListNotification<Sone>("sones-being-rescued-notification", "sones", rescuingSonesTemplate);
+               Template localReplyNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/newReplyNotification.html"));
+               localReplyNotification = new ListNotification<Reply>("local-reply-notification", "replies", localReplyNotificationTemplate, false);
 
-               Template sonesRescuedTemplate = TemplateParser.parse(createReader("/templates/notify/sonesRescuedNotification.html"));
-               sonesRescuedNotification = new ListNotification<Sone>("sones-rescued-notification", "sones", sonesRescuedTemplate);
+               Template mentionNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/mentionNotification.html"));
+               mentionNotification = new ListNotification<Post>("mention-notification", "posts", mentionNotificationTemplate, false);
 
                Template lockedSonesTemplate = TemplateParser.parse(createReader("/templates/notify/lockedSonesNotification.html"));
                lockedSonesNotification = new ListNotification<Sone>("sones-locked-notification", "sones", lockedSonesTemplate);
@@ -405,7 +424,7 @@ public class WebInterface implements CoreListener {
         * @return The new posts
         */
        public Set<Post> getNewPosts() {
-               return new HashSet<Post>(newPostNotification.getElements());
+               return new SetBuilder<Post>().addAll(newPostNotification.getElements()).addAll(localPostNotification.getElements()).get();
        }
 
        /**
@@ -415,7 +434,7 @@ public class WebInterface implements CoreListener {
         * @return The new replies
         */
        public Set<Reply> getNewReplies() {
-               return new HashSet<Reply>(newReplyNotification.getElements());
+               return new SetBuilder<Reply>().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).get();
        }
 
        /**
@@ -540,6 +559,7 @@ public class WebInterface implements CoreListener {
                Template deleteSoneTemplate = TemplateParser.parse(createReader("/templates/deleteSone.html"));
                Template noPermissionTemplate = TemplateParser.parse(createReader("/templates/noPermission.html"));
                Template optionsTemplate = TemplateParser.parse(createReader("/templates/options.html"));
+               Template rescueTemplate = TemplateParser.parse(createReader("/templates/rescue.html"));
                Template aboutTemplate = TemplateParser.parse(createReader("/templates/about.html"));
                Template invalidTemplate = TemplateParser.parse(createReader("/templates/invalid.html"));
                Template postTemplate = TemplateParser.parse(createReader("/templates/include/viewPost.html"));
@@ -578,6 +598,7 @@ public class WebInterface implements CoreListener {
                pageToadlets.add(pageToadletFactory.createPageToadlet(new LoginPage(loginTemplate, this), "Login"));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new LogoutPage(emptyTemplate, this), "Logout"));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new OptionsPage(optionsTemplate, this), "Options"));
+               pageToadlets.add(pageToadletFactory.createPageToadlet(new RescuePage(rescueTemplate, this), "Rescue"));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new AboutPage(aboutTemplate, this, SonePlugin.VERSION), "About"));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("noPermission.html", noPermissionTemplate, "Page.NoPermission.Title", this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new DismissNotificationPage(emptyTemplate, this)));
@@ -649,34 +670,59 @@ public class WebInterface implements CoreListener {
                try {
                        return new InputStreamReader(getClass().getResourceAsStream(resourceName), "UTF-8");
                } catch (UnsupportedEncodingException uee1) {
-                       System.out.println("  fail.");
                        return null;
                }
        }
 
-       //
-       // CORELISTENER METHODS
-       //
-
        /**
-        * {@inheritDoc}
+        * Returns all {@link Core#isLocalSone(Sone) local Sone}s that are
+        * referenced by {@link SonePart}s in the given text (after parsing it using
+        * {@link SoneTextParser}).
+        *
+        * @param text
+        *            The text to parse
+        * @return All mentioned local Sones
         */
-       @Override
-       public void rescuingSone(Sone sone) {
-               rescuingSonesNotification.add(sone);
-               notificationManager.addNotification(rescuingSonesNotification);
+       private Set<Sone> getMentionedSones(String text) {
+               /* we need no context to find mentioned Sones. */
+               Set<Sone> mentionedSones = new HashSet<Sone>();
+               try {
+                       for (Part part : soneTextParser.parse(null, new StringReader(text))) {
+                               if (part instanceof SonePart) {
+                                       mentionedSones.add(((SonePart) part).getSone());
+                               }
+                       }
+               } catch (IOException ioe1) {
+                       logger.log(Level.WARNING, "Could not parse post text: " + text, ioe1);
+               }
+               return Filters.filteredSet(mentionedSones, Sone.LOCAL_SONE_FILTER);
        }
 
        /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void rescuedSone(Sone sone) {
-               rescuingSonesNotification.remove(sone);
-               sonesRescuedNotification.add(sone);
-               notificationManager.addNotification(sonesRescuedNotification);
+        * Returns the Sone insert notification for the given Sone. If no
+        * notification for the given Sone exists, a new notification is created and
+        * cached.
+        *
+        * @param sone
+        *            The Sone to get the insert notification for
+        * @return The Sone insert notification
+        */
+       private TemplateNotification getSoneInsertNotification(Sone sone) {
+               synchronized (soneInsertNotifications) {
+                       TemplateNotification templateNotification = soneInsertNotifications.get(sone);
+                       if (templateNotification == null) {
+                               templateNotification = new TemplateNotification(TemplateParser.parse(createReader("/templates/notify/soneInsertNotification.html")));
+                               templateNotification.set("insertSone", sone);
+                               soneInsertNotifications.put(sone, templateNotification);
+                       }
+                       return templateNotification;
+               }
        }
 
+       //
+       // CORELISTENER METHODS
+       //
+
        /**
         * {@inheritDoc}
         */
@@ -693,9 +739,18 @@ public class WebInterface implements CoreListener {
         */
        @Override
        public void newPostFound(Post post) {
-               newPostNotification.add(post);
+               boolean isLocal = getCore().isLocalSone(post.getSone());
+               if (isLocal) {
+                       localPostNotification.add(post);
+               } else {
+                       newPostNotification.add(post);
+               }
                if (!hasFirstStartNotification()) {
-                       notificationManager.addNotification(newPostNotification);
+                       notificationManager.addNotification(isLocal ? localPostNotification : newPostNotification);
+                       if (!getMentionedSones(post.getText()).isEmpty() && !isLocal) {
+                               mentionNotification.add(post);
+                               notificationManager.addNotification(mentionNotification);
+                       }
                } else {
                        getCore().markPostKnown(post);
                }
@@ -706,12 +761,18 @@ public class WebInterface implements CoreListener {
         */
        @Override
        public void newReplyFound(Reply reply) {
-               if (reply.getPost().getSone() == null) {
-                       return;
+               boolean isLocal = getCore().isLocalSone(reply.getSone());
+               if (isLocal) {
+                       localReplyNotification.add(reply);
+               } else {
+                       newReplyNotification.add(reply);
                }
-               newReplyNotification.add(reply);
                if (!hasFirstStartNotification()) {
-                       notificationManager.addNotification(newReplyNotification);
+                       notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification);
+                       if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal) {
+                               mentionNotification.add(reply.getPost());
+                               notificationManager.addNotification(mentionNotification);
+                       }
                } else {
                        getCore().markReplyKnown(reply);
                }
@@ -731,6 +792,8 @@ public class WebInterface implements CoreListener {
        @Override
        public void markPostKnown(Post post) {
                newPostNotification.remove(post);
+               localPostNotification.remove(post);
+               mentionNotification.remove(post);
        }
 
        /**
@@ -739,6 +802,16 @@ public class WebInterface implements CoreListener {
        @Override
        public void markReplyKnown(Reply reply) {
                newReplyNotification.remove(reply);
+               localReplyNotification.remove(reply);
+               mentionNotification.remove(reply.getPost());
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void soneRemoved(Sone sone) {
+               newSoneNotification.remove(sone);
        }
 
        /**
@@ -747,6 +820,7 @@ public class WebInterface implements CoreListener {
        @Override
        public void postRemoved(Post post) {
                newPostNotification.remove(post);
+               localPostNotification.remove(post);
        }
 
        /**
@@ -755,6 +829,7 @@ public class WebInterface implements CoreListener {
        @Override
        public void replyRemoved(Reply reply) {
                newReplyNotification.remove(reply);
+               localReplyNotification.remove(reply);
        }
 
        /**
@@ -788,6 +863,44 @@ public class WebInterface implements CoreListener {
         * {@inheritDoc}
         */
        @Override
+       public void soneInserting(Sone sone) {
+               TemplateNotification soneInsertNotification = getSoneInsertNotification(sone);
+               soneInsertNotification.set("soneStatus", "inserting");
+               if (sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) {
+                       notificationManager.addNotification(soneInsertNotification);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void soneInserted(Sone sone, long insertDuration) {
+               TemplateNotification soneInsertNotification = getSoneInsertNotification(sone);
+               soneInsertNotification.set("soneStatus", "inserted");
+               soneInsertNotification.set("insertDuration", insertDuration / 1000);
+               if (sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) {
+                       notificationManager.addNotification(soneInsertNotification);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void soneInsertAborted(Sone sone, Throwable cause) {
+               TemplateNotification soneInsertNotification = getSoneInsertNotification(sone);
+               soneInsertNotification.set("soneStatus", "insert-aborted");
+               soneInsertNotification.set("insert-error", cause);
+               if (sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) {
+                       notificationManager.addNotification(soneInsertNotification);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
        public void updateFound(Version version, long releaseTime, long latestEdition) {
                newVersionNotification.getTemplateContext().set("latestVersion", version);
                newVersionNotification.getTemplateContext().set("latestEdition", latestEdition);