Merge branch 'release-0.3.5' 0.3.5
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 18 Dec 2010 01:17:50 +0000 (02:17 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 18 Dec 2010 01:17:50 +0000 (02:17 +0100)
14 files changed:
pom.xml
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/CoreListener.java
src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java
src/main/java/net/pterodactylus/sone/core/SoneInserter.java
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java
src/main/resources/i18n/sone.en.properties
src/main/resources/templates/include/viewPost.html
src/main/resources/templates/include/viewReply.html
src/main/resources/templates/notify/lockedSonesNotification.html [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 4024519..8190ea0 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -2,12 +2,12 @@
        <modelVersion>4.0.0</modelVersion>
        <groupId>net.pterodactylus</groupId>
        <artifactId>sone</artifactId>
-       <version>0.3.4</version>
+       <version>0.3.5</version>
        <dependencies>
                <dependency>
                        <groupId>net.pterodactylus</groupId>
                        <artifactId>utils</artifactId>
-                       <version>0.7.2</version>
+                       <version>0.7.4</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
index c99b055..2ef2d57 100644 (file)
@@ -710,7 +710,9 @@ public class Core implements IdentityListener {
         */
        public void lockSone(Sone sone) {
                synchronized (lockedSones) {
-                       lockedSones.add(sone);
+                       if (lockedSones.add(sone)) {
+                               coreListenerManager.fireSoneLocked(sone);
+                       }
                }
        }
 
@@ -723,7 +725,9 @@ public class Core implements IdentityListener {
         */
        public void unlockSone(Sone sone) {
                synchronized (lockedSones) {
-                       lockedSones.remove(sone);
+                       if (lockedSones.remove(sone)) {
+                               coreListenerManager.fireSoneUnlocked(sone);
+                       }
                }
        }
 
@@ -1117,7 +1121,7 @@ public class Core implements IdentityListener {
         * @param sone
         *            The Sone to save
         */
-       public void saveSone(Sone sone) {
+       public synchronized void saveSone(Sone sone) {
                if (!isLocalSone(sone)) {
                        logger.log(Level.FINE, "Tried to save non-local Sone: %s", sone);
                        return;
@@ -1187,6 +1191,7 @@ public class Core implements IdentityListener {
                        }
                        configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null);
 
+                       configuration.save();
                        logger.log(Level.INFO, "Sone %s saved.", sone);
                } catch (ConfigurationException ce1) {
                        logger.log(Level.WARNING, "Could not save Sone: " + sone, ce1);
@@ -1408,7 +1413,7 @@ public class Core implements IdentityListener {
        /**
         * Saves the current options.
         */
-       public void saveConfiguration() {
+       public synchronized void saveConfiguration() {
                /* store the options first. */
                try {
                        configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal());
index 950e890..5d5e715 100644 (file)
@@ -111,4 +111,20 @@ public interface CoreListener extends EventListener {
         */
        public void replyRemoved(Reply reply);
 
+       /**
+        * Notifies a listener when a Sone was locked.
+        *
+        * @param sone
+        *            The Sone that was locked
+        */
+       public void soneLocked(Sone sone);
+
+       /**
+        * Notifies a listener that a Sone was unlocked.
+        *
+        * @param sone
+        *            The Sone that was unlocked
+        */
+       public void soneUnlocked(Sone sone);
+
 }
index 7ba226b..464342c 100644 (file)
@@ -171,4 +171,30 @@ public class CoreListenerManager extends AbstractListenerManager<Core, CoreListe
                }
        }
 
+       /**
+        * Notifies all listeners that the given Sone was locked.
+        *
+        * @see CoreListener#soneLocked(Sone)
+        * @param sone
+        *            The Sone that was locked
+        */
+       void fireSoneLocked(Sone sone) {
+               for (CoreListener coreListener : getListeners()) {
+                       coreListener.soneLocked(sone);
+               }
+       }
+
+       /**
+        * Notifies all listeners that the given Sone was unlocked.
+        *
+        * @see CoreListener#soneUnlocked(Sone)
+        * @param sone
+        *            The Sone that was unlocked
+        */
+       void fireSoneUnlocked(Sone sone) {
+               for (CoreListener coreListener : getListeners()) {
+                       coreListener.soneUnlocked(sone);
+               }
+       }
+
 }
index 41c1f82..fec4e8f 100644 (file)
@@ -260,7 +260,7 @@ public class SoneInserter extends AbstractService {
                        soneProperties.put("posts", new ArrayList<Post>(sone.getPosts()));
                        soneProperties.put("replies", new HashSet<Reply>(sone.getReplies()));
                        soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
-                       soneProperties.put("likeReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
+                       soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
                }
 
                //
index ae7c9d2..5199af6 100644 (file)
@@ -44,7 +44,6 @@ import freenet.pluginmanager.FredPluginL10n;
 import freenet.pluginmanager.FredPluginThreadless;
 import freenet.pluginmanager.FredPluginVersioned;
 import freenet.pluginmanager.PluginRespirator;
-import freenet.pluginmanager.PluginStore;
 
 /**
  * This class interfaces with Freenet. It is the class that is loaded by the
@@ -96,9 +95,6 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
        /** The l10n helper. */
        private PluginL10n l10n;
 
-       /** The plugin store. */
-       private PluginStore pluginStore;
-
        /** The identity manager. */
        private IdentityManager identityManager;
 
@@ -228,14 +224,8 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
 
                        /* stop the identity manager. */
                        identityManager.stop();
-
-                       /* TODO wait for core to stop? */
-                       try {
-                               pluginRespirator.putStore(pluginStore);
-                       } catch (DatabaseDisabledException dde1) {
-                               logger.log(Level.WARNING, "Could not store plugin store, database is disabled.", dde1);
-                       }
-
+               } catch (Throwable t1) {
+                       logger.log(Level.SEVERE, "Error while shutting down!", t1);
                } finally {
                        /* shutdown logger. */
                        Logging.shutdown();
index 9a8bd9b..ccd0d05 100644 (file)
@@ -124,6 +124,21 @@ public class SoneTemplatePage extends TemplatePage {
        }
 
        /**
+        * Returns the currently logged in Sone.
+        *
+        * @param toadletContext
+        *            The toadlet context
+        * @param create
+        *            {@code true} to create a new session if no session exists,
+        *            {@code false} to not create a new session
+        * @return The currently logged in Sone, or {@code null} if no Sone is
+        *         currently logged in
+        */
+       protected Sone getCurrentSone(ToadletContext toadletContext, boolean create) {
+               return webInterface.getCurrentSone(toadletContext, create);
+       }
+
+       /**
         * Sets the currently logged in Sone.
         *
         * @param toadletContext
@@ -192,7 +207,7 @@ public class SoneTemplatePage extends TemplatePage {
        @Override
        public boolean isEnabled(ToadletContext toadletContext) {
                if (requiresLogin()) {
-                       return getCurrentSone(toadletContext) != null;
+                       return getCurrentSone(toadletContext, false) != null;
                }
                return true;
        }
index 95978ce..d3ec1ee 100644 (file)
@@ -23,8 +23,11 @@ import java.io.Reader;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import java.util.logging.Level;
@@ -132,6 +135,12 @@ public class WebInterface implements CoreListener {
        /** The “Sone rescued” notification. */
        private final ListNotification<Sone> sonesRescuedNotification;
 
+       /** Sone locked notification ticker objects. */
+       private final Map<Sone, Object> lockedSonesTickerObjects = Collections.synchronizedMap(new HashMap<Sone, Object>());
+
+       /** The “Sone locked” notification. */
+       private final ListNotification<Sone> lockedSonesNotification;
+
        /**
         * Creates a new web interface.
         *
@@ -177,6 +186,9 @@ public class WebInterface implements CoreListener {
 
                Template sonesRescuedTemplate = templateFactory.createTemplate(createReader("/templates/notify/sonesRescuedNotification.html"));
                sonesRescuedNotification = new ListNotification<Sone>("sones-rescued-notification", "sones", sonesRescuedTemplate);
+
+               Template lockedSonesTemplate = templateFactory.createTemplate(createReader("/templates/notify/lockedSonesNotification.html"));
+               lockedSonesNotification = new ListNotification<Sone>("sones-locked-notification", "sones", lockedSonesTemplate);
        }
 
        //
@@ -234,7 +246,22 @@ public class WebInterface implements CoreListener {
         *         currently logged in
         */
        public Sone getCurrentSone(ToadletContext toadletContext) {
-               return getCurrentSone(getCurrentSession(toadletContext));
+               return getCurrentSone(toadletContext, true);
+       }
+
+       /**
+        * Returns the currently logged in Sone.
+        *
+        * @param toadletContext
+        *            The toadlet context
+        * @param create
+        *            {@code true} to create a new session if no session exists,
+        *            {@code false} to not create a new session
+        * @return The currently logged in Sone, or {@code null} if no Sone is
+        *         currently logged in
+        */
+       public Sone getCurrentSone(ToadletContext toadletContext, boolean create) {
+               return getCurrentSone(getCurrentSession(toadletContext, create));
        }
 
        /**
@@ -585,6 +612,8 @@ public class WebInterface implements CoreListener {
                newPostNotification.add(post);
                if (!hasFirstStartNotification()) {
                        notificationManager.addNotification(newPostNotification);
+               } else {
+                       getCore().markPostKnown(post);
                }
        }
 
@@ -599,6 +628,8 @@ public class WebInterface implements CoreListener {
                newReplyNotification.add(reply);
                if (!hasFirstStartNotification()) {
                        notificationManager.addNotification(newReplyNotification);
+               } else {
+                       getCore().markReplyKnown(reply);
                }
        }
 
@@ -631,7 +662,7 @@ public class WebInterface implements CoreListener {
         */
        @Override
        public void postRemoved(Post post) {
-               /* TODO */
+               newPostNotification.remove(post);
        }
 
        /**
@@ -639,7 +670,34 @@ public class WebInterface implements CoreListener {
         */
        @Override
        public void replyRemoved(Reply reply) {
-               /* TODO */
+               newReplyNotification.remove(reply);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void soneLocked(final Sone sone) {
+               Object tickerObject = Ticker.getInstance().registerEvent(System.currentTimeMillis() + (5 * 60) * 1000, new Runnable() {
+
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               lockedSonesNotification.add(sone);
+                               lockedSonesTickerObjects.remove(sone);
+                               notificationManager.addNotification(lockedSonesNotification);
+                       }
+               }, "Sone Locked Notification");
+               lockedSonesTickerObjects.put(sone, tickerObject);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void soneUnlocked(Sone sone) {
+               lockedSonesNotification.remove(sone);
+               Ticker.getInstance().deregisterEvent(lockedSonesTickerObjects.remove(sone));
        }
 
        /**
index ec0145a..92d5da6 100644 (file)
@@ -20,9 +20,11 @@ package net.pterodactylus.sone.web.ajax;
 import java.io.StringWriter;
 
 import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.json.JsonObject;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.template.TemplateException;
 
@@ -60,8 +62,7 @@ public class GetPostAjaxPage extends JsonPage {
                if (post == null) {
                        return createErrorJsonObject("invalid-post-id");
                }
-               postTemplate.set("currentSone", getCurrentSone(request.getToadletContext()));
-               return createSuccessJsonObject().put("post", createJsonPost(post));
+               return createSuccessJsonObject().put("post", createJsonPost(post, getCurrentSone(request.getToadletContext())));
        }
 
        /**
@@ -82,24 +83,26 @@ public class GetPostAjaxPage extends JsonPage {
         *
         * @param post
         *            The post to create a JSON object from
+        * @param currentSone
+        *            The currently logged in Sone (to store in the template)
         * @return The JSON representation of the post
         */
-       private JsonObject createJsonPost(Post post) {
+       private JsonObject createJsonPost(Post post, Sone currentSone) {
                JsonObject jsonPost = new JsonObject();
                jsonPost.put("id", post.getId());
                jsonPost.put("sone", post.getSone().getId());
                jsonPost.put("recipient", (post.getRecipient() == null) ? null : post.getRecipient().getId());
                jsonPost.put("time", post.getTime());
                StringWriter stringWriter = new StringWriter();
-               synchronized (postTemplate) {
-                       postTemplate.set("post", post);
-                       try {
-                               postTemplate.render(stringWriter);
-                       } catch (TemplateException te1) {
-                               /* TODO - shouldn’t happen. */
-                       } finally {
-                               Closer.close(stringWriter);
-                       }
+               DataProvider dataProvider = postTemplate.createDataProvider();
+               dataProvider.setData("post", post);
+               dataProvider.setData("currentSone", currentSone);
+               try {
+                       postTemplate.render(dataProvider, stringWriter);
+               } catch (TemplateException te1) {
+                       /* TODO - shouldn’t happen. */
+               } finally {
+                       Closer.close(stringWriter);
                }
                jsonPost.put("html", stringWriter.toString());
                return jsonPost;
index 0963b09..e589191 100644 (file)
@@ -20,9 +20,11 @@ package net.pterodactylus.sone.web.ajax;
 import java.io.StringWriter;
 
 import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.json.JsonObject;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.template.TemplateException;
 
@@ -63,8 +65,7 @@ public class GetReplyAjaxPage extends JsonPage {
                if ((reply == null) || (reply.getSone() == null)) {
                        return createErrorJsonObject("invalid-reply-id");
                }
-               replyTemplate.set("currentSone", getCurrentSone(request.getToadletContext()));
-               return createSuccessJsonObject().put("reply", createJsonReply(reply));
+               return createSuccessJsonObject().put("reply", createJsonReply(reply, getCurrentSone(request.getToadletContext())));
        }
 
        /**
@@ -84,24 +85,26 @@ public class GetReplyAjaxPage extends JsonPage {
         *
         * @param reply
         *            The reply to convert
+        * @param currentSone
+        *            The currently logged in Sone (to store in the template)
         * @return The JSON representation of the reply
         */
-       private JsonObject createJsonReply(Reply reply) {
+       private JsonObject createJsonReply(Reply reply, Sone currentSone) {
                JsonObject jsonReply = new JsonObject();
                jsonReply.put("id", reply.getId());
                jsonReply.put("postId", reply.getPost().getId());
                jsonReply.put("soneId", reply.getSone().getId());
                jsonReply.put("time", reply.getTime());
                StringWriter stringWriter = new StringWriter();
-               synchronized (replyTemplate) {
-                       replyTemplate.set("reply", reply);
-                       try {
-                               replyTemplate.render(stringWriter);
-                       } catch (TemplateException te1) {
-                               /* TODO - shouldn’t happen. */
-                       } finally {
-                               Closer.close(stringWriter);
-                       }
+               DataProvider dataProvider = replyTemplate.createDataProvider();
+               dataProvider.setData("reply", reply);
+               dataProvider.setData("currentSone", currentSone);
+               try {
+                       replyTemplate.render(dataProvider, stringWriter);
+               } catch (TemplateException te1) {
+                       /* TODO - shouldn’t happen. */
+               } finally {
+                       Closer.close(stringWriter);
                }
                return jsonReply.put("html", stringWriter.toString());
        }
index 89af2d1..e080e4d 100644 (file)
@@ -186,4 +186,4 @@ Notification.NewReply.Text=New replies have been discovered by the following Son
 Notification.SoneIsBeingRescued.Text=The following Sones are currently being rescued:
 Notification.SoneRescued.Text=The following Sones have been rescued:
 Notification.SoneRescued.Text.RememberToUnlock=Please remember to control the posts and replies you have given and don’t forget to unlock your Sones!
-
+Notification.LockedSones.Text=The following Sones have been locked for more than 5 minutes. Please check if you really want to keep these Sones locked:
index 1d938d1..cd6cc5c 100644 (file)
@@ -19,7 +19,7 @@
                </div>
                <div class="post-status-line status-line">
                        <div class="time"><a href="viewPost.html?post=<% post.id|html>"><% post.time|date format="MMM d, yyyy, HH:mm:ss"></a></div>
-                       <div class="likes<%if post.likes.size|match value=0> hidden<%/if>"><span title="<% post.likes.soneNames|html>">â¬\86<span class="like-count"><% post.likes.size></span></span></div>
+                       <div class="likes<%if post.likes.size|match value=0> hidden<%/if>"><span title="<% post.likes.soneNames|html>">â\86\91<span class="like-count"><% post.likes.size></span></span></div>
                        <%ifnull ! currentSone>
                                <form class="like like-post<%if post.liked> hidden<%/if>" action="like.html" method="post">
                                        <input type="hidden" name="formPassword" value="<% formPassword|html>" />
index d11aa5f..f65a201 100644 (file)
@@ -11,7 +11,7 @@
                </div>
                <div class="reply-status-line status-line">
                        <div class="time"><% reply.time|date format="MMM d, yyyy, HH:mm:ss"></div>
-                       <div class="likes<%if reply.likes.size|match value=0> hidden<%/if>"><span title="<% reply.likes.soneNames|html>">â¬\86<span class="like-count"><% reply.likes.size></span></span></div>
+                       <div class="likes<%if reply.likes.size|match value=0> hidden<%/if>"><span title="<% reply.likes.soneNames|html>">â\86\91<span class="like-count"><% reply.likes.size></span></span></div>
                        <%ifnull ! currentSone>
                                <form class="like like-reply<%if reply.liked> hidden<%/if>" action="like.html" method="post">
                                        <input type="hidden" name="formPassword" value="<% formPassword|html>" />
diff --git a/src/main/resources/templates/notify/lockedSonesNotification.html b/src/main/resources/templates/notify/lockedSonesNotification.html
new file mode 100644 (file)
index 0000000..b2b8b95
--- /dev/null
@@ -0,0 +1,6 @@
+<div class="text">
+       <%= Notification.LockedSones.Text|l10n|html>
+       <%foreach sones sone>
+               <a href="viewSone.html?sone=<% sone.id|html>" title="<% sone.requestUri|html>"><% sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
+       <%/foreach>
+</div>