Merge branch 'release-0.3.7' 0.3.7
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 13 Jan 2011 20:31:48 +0000 (21:31 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 13 Jan 2011 20:31:48 +0000 (21:31 +0100)
43 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/FreenetInterface.java
src/main/java/net/pterodactylus/sone/core/UpdateChecker.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/core/UpdateListener.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/core/UpdateListenerManager.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/text/FreenetLinkParser.java
src/main/java/net/pterodactylus/sone/web/AboutPage.java
src/main/java/net/pterodactylus/sone/web/CreatePostPage.java
src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java
src/main/java/net/pterodactylus/sone/web/CreateSonePage.java
src/main/java/net/pterodactylus/sone/web/DeletePostPage.java
src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java
src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java
src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java
src/main/java/net/pterodactylus/sone/web/EditProfilePage.java
src/main/java/net/pterodactylus/sone/web/FollowSonePage.java
src/main/java/net/pterodactylus/sone/web/IndexPage.java
src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java
src/main/java/net/pterodactylus/sone/web/LikePage.java
src/main/java/net/pterodactylus/sone/web/LockSonePage.java
src/main/java/net/pterodactylus/sone/web/LoginPage.java
src/main/java/net/pterodactylus/sone/web/LogoutPage.java
src/main/java/net/pterodactylus/sone/web/OptionsPage.java
src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java
src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java
src/main/java/net/pterodactylus/sone/web/UnlikePage.java
src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java
src/main/java/net/pterodactylus/sone/web/ViewPostPage.java
src/main/java/net/pterodactylus/sone/web/ViewSonePage.java
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java
src/main/java/net/pterodactylus/sone/web/page/TemplatePage.java
src/main/resources/i18n/sone.en.properties
src/main/resources/static/css/sone.css
src/main/resources/static/javascript/sone.js
src/main/resources/templates/include/head.html
src/main/resources/templates/include/tail.html
src/main/resources/templates/index.html
src/main/resources/templates/notify/newVersionNotification.html [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index e1fe047..eae705c 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.6-5</version>
+       <version>0.3.7</version>
        <dependencies>
                <dependency>
                        <groupId>net.pterodactylus</groupId>
                        <artifactId>utils</artifactId>
-                       <version>0.7.6</version>
+                       <version>0.7.7</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
index 493221b..78df9ef 100644 (file)
@@ -45,6 +45,7 @@ import net.pterodactylus.util.config.Configuration;
 import net.pterodactylus.util.config.ConfigurationException;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.version.Version;
 import freenet.keys.FreenetURI;
 
 /**
@@ -52,7 +53,7 @@ import freenet.keys.FreenetURI;
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class Core implements IdentityListener {
+public class Core implements IdentityListener, UpdateListener {
 
        /**
         * Enumeration for the possible states of a {@link Sone}.
@@ -98,6 +99,9 @@ public class Core implements IdentityListener {
        /** The Sone downloader. */
        private final SoneDownloader soneDownloader;
 
+       /** The update checker. */
+       private final UpdateChecker updateChecker;
+
        /** Whether the core has been stopped. */
        private volatile boolean stopped;
 
@@ -162,6 +166,7 @@ public class Core implements IdentityListener {
                this.freenetInterface = freenetInterface;
                this.identityManager = identityManager;
                this.soneDownloader = new SoneDownloader(this, freenetInterface);
+               this.updateChecker = new UpdateChecker(freenetInterface);
        }
 
        //
@@ -233,6 +238,15 @@ public class Core implements IdentityListener {
        }
 
        /**
+        * Returns the update checker.
+        *
+        * @return The update checker
+        */
+       public UpdateChecker getUpdateChecker() {
+               return updateChecker;
+       }
+
+       /**
         * Returns the status of the given Sone.
         *
         * @param sone
@@ -1400,6 +1414,8 @@ public class Core implements IdentityListener {
         */
        public void start() {
                loadConfiguration();
+               updateChecker.addUpdateListener(this);
+               updateChecker.start();
        }
 
        /**
@@ -1411,6 +1427,8 @@ public class Core implements IdentityListener {
                                soneInserter.stop();
                        }
                }
+               updateChecker.stop();
+               updateChecker.removeUpdateListener(this);
                soneDownloader.stop();
                saveConfiguration();
                stopped = true;
@@ -1622,4 +1640,16 @@ public class Core implements IdentityListener {
                /* TODO */
        }
 
+       //
+       // INTERFACE UpdateListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void updateFound(Version version, long releaseTime) {
+               coreListenerManager.fireUpdateFound(version, releaseTime);
+       }
+
 }
index 5d5e715..5fbb333 100644 (file)
@@ -22,6 +22,7 @@ import java.util.EventListener;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.version.Version;
 
 /**
  * Listener interface for objects that want to be notified on certain
@@ -127,4 +128,14 @@ public interface CoreListener extends EventListener {
         */
        public void soneUnlocked(Sone sone);
 
+       /**
+        * Notifies a listener that a new version has been found.
+        *
+        * @param version
+        *            The version that was found
+        * @param releaseTime
+        *            The release time of the new version
+        */
+       public void updateFound(Version version, long releaseTime);
+
 }
index 464342c..4fc9532 100644 (file)
@@ -21,6 +21,7 @@ import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.util.event.AbstractListenerManager;
+import net.pterodactylus.util.version.Version;
 
 /**
  * Manager for {@link CoreListener}s.
@@ -197,4 +198,19 @@ public class CoreListenerManager extends AbstractListenerManager<Core, CoreListe
                }
        }
 
+       /**
+        * Notifies all listeners that a new version was found.
+        *
+        * @see CoreListener#updateFound(Version, long)
+        * @param version
+        *            The new version
+        * @param releaseTime
+        *            The release time of the new version
+        */
+       void fireUpdateFound(Version version, long releaseTime) {
+               for (CoreListener coreListener : getListeners()) {
+                       coreListener.updateFound(version, releaseTime);
+               }
+       }
+
 }
index 6737d63..52c2857 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.core;
 
 import java.net.MalformedURLException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.logging.Level;
@@ -60,6 +61,9 @@ public class FreenetInterface {
        /** The USK callbacks. */
        private final Map<String, USKCallback> soneUskCallbacks = new HashMap<String, USKCallback>();
 
+       /** The not-Sone-related USK callbacks. */
+       private final Map<FreenetURI, USKCallback> uriUskCallbacks = Collections.synchronizedMap(new HashMap<FreenetURI, USKCallback>());
+
        /**
         * Creates a new Freenet interface.
         *
@@ -197,4 +201,83 @@ public class FreenetInterface {
                }
        }
 
+       /**
+        * Registers an arbitrary URI and calls the given callback if a new edition
+        * is found.
+        *
+        * @param uri
+        *            The URI to watch
+        * @param callback
+        *            The callback to call
+        */
+       public void registerUsk(FreenetURI uri, final Callback callback) {
+               USKCallback uskCallback = new USKCallback() {
+
+                       @Override
+                       public void onFoundEdition(long edition, USK key, ObjectContainer objectContainer, ClientContext clientContext, boolean metadata, short codec, byte[] data, boolean newKnownGood, boolean newSlotToo) {
+                               callback.editionFound(key.getURI(), edition, newKnownGood, newSlotToo);
+                       }
+
+                       @Override
+                       public short getPollingPriorityNormal() {
+                               return RequestStarter.PREFETCH_PRIORITY_CLASS;
+                       }
+
+                       @Override
+                       public short getPollingPriorityProgress() {
+                               return RequestStarter.INTERACTIVE_PRIORITY_CLASS;
+                       }
+
+               };
+               try {
+                       node.clientCore.uskManager.subscribe(USK.create(uri), uskCallback, true, (HighLevelSimpleClientImpl) client);
+                       uriUskCallbacks.put(uri, uskCallback);
+               } catch (MalformedURLException mue1) {
+                       logger.log(Level.WARNING, "Could not subscribe to USK: " + uri, uri);
+               }
+       }
+
+       /**
+        * Unregisters the USK watcher for the given URI.
+        *
+        * @param uri
+        *            The URI to unregister the USK watcher for
+        */
+       public void unregisterUsk(FreenetURI uri) {
+               USKCallback uskCallback = uriUskCallbacks.remove(uri);
+               if (uskCallback == null) {
+                       logger.log(Level.INFO, "Could not unregister unknown USK: " + uri);
+                       return;
+               }
+               try {
+                       node.clientCore.uskManager.unsubscribe(USK.create(uri), uskCallback);
+               } catch (MalformedURLException mue1) {
+                       logger.log(Level.INFO, "Could not unregister invalid USK: " + uri);
+               }
+       }
+
+       /**
+        * Callback for USK watcher events.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       public static interface Callback {
+
+               /**
+                * Notifies a listener that a new edition was found for a URI.
+                *
+                * @param uri
+                *            The URI that a new edition was found for
+                * @param edition
+                *            The found edition
+                * @param newKnownGood
+                *            Whether the found edition was actually fetched
+                * @param newSlot
+                *            Whether the found edition is higher than all previously
+                *            found editions
+                */
+               public void editionFound(FreenetURI uri, long edition, boolean newKnownGood, boolean newSlot);
+
+       }
+
 }
diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java b/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java
new file mode 100644 (file)
index 0000000..0839cb9
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Sone - UpdateChecker.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.util.Date;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.sone.main.SonePlugin;
+import net.pterodactylus.util.collection.Pair;
+import net.pterodactylus.util.io.Closer;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.version.Version;
+import freenet.client.FetchResult;
+import freenet.keys.FreenetURI;
+import freenet.support.api.Bucket;
+
+/**
+ * Watches the official Sone homepage for new releases.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UpdateChecker {
+
+       /** The logger. */
+       private static final Logger logger = Logging.getLogger(UpdateChecker.class);
+
+       /** The key of the Sone homepage. */
+       private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
+
+       /** The current latest known edition. */
+       private static final int LATEST_EDITION = 23;
+
+       /** The Freenet interface. */
+       private final FreenetInterface freenetInterface;
+
+       /** The update listener manager. */
+       private final UpdateListenerManager updateListenerManager = new UpdateListenerManager();
+
+       /** The current URI of the homepage. */
+       private FreenetURI currentUri;
+
+       /** The current latest known version. */
+       private Version currentLatestVersion = SonePlugin.VERSION;
+
+       /** The release date of the latest version. */
+       private long latestVersionDate;
+
+       /**
+        * Creates a new update checker.
+        *
+        * @param freenetInterface
+        *            The freenet interface to use
+        */
+       public UpdateChecker(FreenetInterface freenetInterface) {
+               this.freenetInterface = freenetInterface;
+       }
+
+       //
+       // EVENT LISTENER MANAGEMENT
+       //
+
+       /**
+        * Adds the given listener to the list of registered listeners.
+        *
+        * @param updateListener
+        *            The listener to add
+        */
+       public void addUpdateListener(UpdateListener updateListener) {
+               updateListenerManager.addListener(updateListener);
+       }
+
+       /**
+        * Removes the given listener from the list of registered listeners.
+        *
+        * @param updateListener
+        *            The listener to remove
+        */
+       public void removeUpdateListener(UpdateListener updateListener) {
+               updateListenerManager.removeListener(updateListener);
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns whether a version that is later than the currently running
+        * version has been found.
+        *
+        * @return {@code true} if a new version was found
+        */
+       public boolean hasLatestVersion() {
+               return currentLatestVersion.compareTo(SonePlugin.VERSION) > 0;
+       }
+
+       /**
+        * Returns the latest version. If no new latest version has been found, the
+        * current version is returned.
+        *
+        * @return The latest known version
+        */
+       public Version getLatestVersion() {
+               return currentLatestVersion;
+       }
+
+       /**
+        * Returns the release time of the latest version. If no new latest version
+        * has been found, the returned value is undefined.
+        *
+        * @return The release time of the latest version, if a new version was
+        *         found
+        */
+       public long getLatestVersionDate() {
+               return latestVersionDate;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Starts the update checker.
+        */
+       public void start() {
+               try {
+                       currentUri = new FreenetURI(SONE_HOMEPAGE + LATEST_EDITION);
+               } catch (MalformedURLException mue1) {
+                       /* this can not really happen unless I screw up. */
+                       logger.log(Level.SEVERE, "Sone Homepage URI invalid!", mue1);
+               }
+               freenetInterface.registerUsk(currentUri, new FreenetInterface.Callback() {
+
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void editionFound(FreenetURI uri, long edition, boolean newKnownGood, boolean newSlot) {
+                               logger.log(Level.FINEST, "Found update for %s: %d, %s, %s", new Object[] { uri, edition, newKnownGood, newSlot });
+                               if (newKnownGood || newSlot) {
+                                       Pair<FreenetURI, FetchResult> uriResult = freenetInterface.fetchUri(uri.setMetaString(new String[] { "sone.properties" }));
+                                       if (uriResult == null) {
+                                               logger.log(Level.WARNING, "Could not fetch properties of latest homepage: %s", uri);
+                                               return;
+                                       }
+                                       Bucket resultBucket = uriResult.getRight().asBucket();
+                                       try {
+                                               parseProperties(resultBucket.getInputStream());
+                                       } catch (IOException ioe1) {
+                                               logger.log(Level.WARNING, "Could not parse sone.properties of " + uri, ioe1);
+                                       } finally {
+                                               resultBucket.free();
+                                       }
+                               }
+                       }
+               });
+       }
+
+       /**
+        * Stops the update checker.
+        */
+       public void stop() {
+               freenetInterface.unregisterUsk(currentUri);
+       }
+
+       //
+       // PRIVATE ACTIONS
+       //
+
+       /**
+        * Parses the properties of the latest version and fires events, if
+        * necessary.
+        *
+        * @see UpdateListener#updateFound(Version, long)
+        * @see UpdateListenerManager#fireUpdateFound(Version, long)
+        * @param propertiesInputStream
+        *            The input stream to parse
+        * @throws IOException
+        *             if an I/O error occured
+        */
+       private void parseProperties(InputStream propertiesInputStream) throws IOException {
+               Properties properties = new Properties();
+               InputStreamReader inputStreamReader = null;
+               try {
+                       inputStreamReader = new InputStreamReader(propertiesInputStream, "UTF-8");
+                       properties.load(inputStreamReader);
+               } finally {
+                       Closer.close(inputStreamReader);
+               }
+               String versionString = properties.getProperty("CurrentVersion/Version");
+               String releaseTimeString = properties.getProperty("CurrentVersion/ReleaseTime");
+               if ((versionString == null) || (releaseTimeString == null)) {
+                       logger.log(Level.INFO, "Invalid data parsed from properties.");
+                       return;
+               }
+               Version version = Version.parse(versionString);
+               long releaseTime = 0;
+               try {
+                       releaseTime = Long.parseLong(releaseTimeString);
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               if ((version == null) || (releaseTime == 0)) {
+                       logger.log(Level.INFO, "Could not parse data from properties.");
+                       return;
+               }
+               if (version.compareTo(currentLatestVersion) > 0) {
+                       currentLatestVersion = version;
+                       latestVersionDate = releaseTime;
+                       logger.log(Level.INFO, "Found new version: %s (%tc)", new Object[] { version, new Date(releaseTime) });
+                       updateListenerManager.fireUpdateFound(version, releaseTime);
+               }
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateListener.java b/src/main/java/net/pterodactylus/sone/core/UpdateListener.java
new file mode 100644 (file)
index 0000000..1469afa
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Sone - UpdateListener.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import java.util.EventListener;
+
+import net.pterodactylus.util.version.Version;
+
+/**
+ * Listener interface for {@link UpdateChecker} events.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface UpdateListener extends EventListener {
+
+       /**
+        * Notifies a listener that a newer version than the current version was
+        * found.
+        *
+        * @param version
+        *            The version that was found
+        * @param releaseTime
+        *            The release time of the version
+        */
+       public void updateFound(Version version, long releaseTime);
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateListenerManager.java b/src/main/java/net/pterodactylus/sone/core/UpdateListenerManager.java
new file mode 100644 (file)
index 0000000..3f3b21f
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Sone - UpdateListenerManager.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import net.pterodactylus.util.event.AbstractListenerManager;
+import net.pterodactylus.util.version.Version;
+
+/**
+ * Listener manager for {@link UpdateListener} events.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UpdateListenerManager extends AbstractListenerManager<Void, UpdateListener> {
+
+       /**
+        * Creates a new update listener manager.
+        */
+       public UpdateListenerManager() {
+               super(null);
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Notifies all listeners that a new version has been found.
+        *
+        * @param version
+        *            The new version
+        * @param releaseTime
+        *            The release time of the new version
+        */
+       void fireUpdateFound(Version version, long releaseTime) {
+               for (UpdateListener updateListener : getListeners()) {
+                       updateListener.updateFound(version, releaseTime);
+               }
+       }
+
+}
index 68c9d90..a0a22f9 100644 (file)
@@ -78,7 +78,7 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
        }
 
        /** The version. */
-       public static final Version VERSION = new Version(0, 3, 6, 5);
+       public static final Version VERSION = new Version(0, 3, 7);
 
        /** The logger. */
        private static final Logger logger = Logging.getLogger(SonePlugin.class);
@@ -166,28 +166,28 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
                        }
                }
 
-               /* create freenet interface. */
-               FreenetInterface freenetInterface = new FreenetInterface(pluginRespirator.getNode());
+               boolean startupFailed = true;
+               try {
+                       /* create freenet interface. */
+                       FreenetInterface freenetInterface = new FreenetInterface(pluginRespirator.getNode());
 
-               /* create web of trust connector. */
-               PluginConnector pluginConnector = new PluginConnector(pluginRespirator);
-               WebOfTrustConnector webOfTrustConnector = new WebOfTrustConnector(pluginConnector);
-               identityManager = new IdentityManager(webOfTrustConnector);
-               identityManager.setContext("Sone");
+                       /* create web of trust connector. */
+                       PluginConnector pluginConnector = new PluginConnector(pluginRespirator);
+                       WebOfTrustConnector webOfTrustConnector = new WebOfTrustConnector(pluginConnector);
+                       identityManager = new IdentityManager(webOfTrustConnector);
+                       identityManager.setContext("Sone");
 
-               /* create core. */
-               core = new Core(oldConfiguration, freenetInterface, identityManager);
+                       /* create core. */
+                       core = new Core(oldConfiguration, freenetInterface, identityManager);
 
-               /* create the web interface. */
-               webInterface = new WebInterface(this);
-               core.addCoreListener(webInterface);
+                       /* create the web interface. */
+                       webInterface = new WebInterface(this);
+                       core.addCoreListener(webInterface);
 
-               /* create the identity manager. */
-               identityManager.addIdentityListener(core);
+                       /* create the identity manager. */
+                       identityManager.addIdentityListener(core);
 
-               /* start core! */
-               boolean startupFailed = true;
-               try {
+                       /* start core! */
                        core.start();
                        if ((newConfiguration != null) && (oldConfiguration != newConfiguration)) {
                                logger.log(Level.INFO, "Setting configuration to file-based configuration.");
index 63f68b8..1ef77d2 100644 (file)
@@ -147,36 +147,35 @@ public class FreenetLinkParser implements Parser<FreenetLinkParserContext> {
                                        String name = link;
                                        logger.log(Level.FINER, "Found link: %s", link);
                                        logger.log(Level.FINEST, "Next: %d, CHK: %d, SSK: %d, USK: %d", new Object[] { next, nextChk, nextSsk, nextUsk });
-                                       if (linkType == LinkType.KSK) {
-                                               name = link.substring(4);
-                                       } else if ((linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
-                                               if (name.indexOf('/') > -1) {
-                                                       if (!name.endsWith("/")) {
-                                                               name = name.substring(name.lastIndexOf('/') + 1);
-                                                       } else {
-                                                               if (name.indexOf('/') != name.lastIndexOf('/')) {
-                                                                       name = name.substring(name.lastIndexOf('/', name.lastIndexOf('/') - 1));
-                                                               } else {
-                                                                       /* shorten to 5 chars. */
-                                                                       name = name.substring(4, Math.min(9, name.length()));
-                                                               }
-                                                       }
-                                               }
+
+                                       if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
+                                               FreenetURI uri;
                                                if (name.indexOf('?') > -1) {
                                                        name = name.substring(0, name.indexOf('?'));
                                                }
-                                               boolean fromPostingSone = false;
-                                               if ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
-                                                       try {
-                                                               new FreenetURI(link);
-                                                               fromPostingSone = link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
-                                                               parts.add(fromPostingSone ? createTrustedFreenetLinkPart(link, name) : createFreenetLinkPart(link, name));
-                                                       } catch (MalformedURLException mue1) {
-                                                               /* it’s not a valid link. */
-                                                               parts.add(createPlainTextPart(link));
+                                               if (name.endsWith("/")) {
+                                                       name = name.substring(0, name.length() - 1);
+                                               }
+                                               try {
+                                                       uri = new FreenetURI(name);
+                                                       name = uri.lastMetaString();
+                                                       if (name == null) {
+                                                               name = uri.getDocName();
+                                                       }
+                                                       if (name == null) {
+                                                               name = link.substring(0, Math.min(9, link.length()));
                                                        }
-                                               } else {
+                                                       boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
                                                        parts.add(fromPostingSone ? createTrustedFreenetLinkPart(link, name) : createFreenetLinkPart(link, name));
+                                               } catch (MalformedURLException mue1) {
+                                                       /* not a valid link, insert as plain text. */
+                                                       parts.add(createPlainTextPart(link));
+                                               } catch (NullPointerException npe1) {
+                                                       /* FreenetURI sometimes throws these, too. */
+                                                       parts.add(createPlainTextPart(link));
+                                               } catch (ArrayIndexOutOfBoundsException aioobe1) {
+                                                       /* oh, and these, too. */
+                                                       parts.add(createPlainTextPart(link));
                                                }
                                        } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
                                                name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
index c20c72e..4a0dd22 100644 (file)
@@ -17,6 +17,7 @@
 
 package net.pterodactylus.sone.web;
 
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.version.Version;
 
@@ -53,9 +54,9 @@ public class AboutPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
-               template.set("version", version);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
+               dataProvider.set("version", version);
        }
 
 }
index a8e2128..aba4700 100644 (file)
@@ -20,6 +20,7 @@ package net.pterodactylus.sone.web;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -49,8 +50,8 @@ public class CreatePostPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
                if (request.getMethod() == Method.POST) {
                        String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
@@ -61,9 +62,9 @@ public class CreatePostPage extends SoneTemplatePage {
                                webInterface.getCore().createPost(currentSone, recipient, System.currentTimeMillis(), text);
                                throw new RedirectException(returnPage);
                        }
-                       template.set("errorTextEmpty", true);
+                       dataProvider.set("errorTextEmpty", true);
                }
-               template.set("returnPage", returnPage);
+               dataProvider.set("returnPage", returnPage);
        }
 
 }
index 2bfab6f..d4a9775 100644 (file)
@@ -20,6 +20,7 @@ package net.pterodactylus.sone.web;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -49,8 +50,8 @@ public class CreateReplyPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
                String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
                String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
@@ -61,11 +62,11 @@ public class CreateReplyPage extends SoneTemplatePage {
                                webInterface.getCore().createReply(currentSone, post, text);
                                throw new RedirectException(returnPage);
                        }
-                       template.set("errorTextEmpty", true);
+                       dataProvider.set("errorTextEmpty", true);
                }
-               template.set("postId", postId);
-               template.set("text", text);
-               template.set("returnPage", returnPage);
+               dataProvider.set("postId", postId);
+               dataProvider.set("text", text);
+               dataProvider.set("returnPage", returnPage);
        }
 
 }
index 1c16d31..04b5d37 100644 (file)
@@ -30,6 +30,7 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
 import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import freenet.clients.http.ToadletContext;
 
@@ -93,10 +94,10 @@ public class CreateSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                List<OwnIdentity> ownIdentitiesWithoutSone = getOwnIdentitiesWithoutSone(webInterface.getCore());
-               template.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
+               dataProvider.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
                if (request.getMethod() == Method.POST) {
                        String id = request.getHttpRequest().getPartAsStringFailsafe("identity", 44);
                        OwnIdentity selectedIdentity = null;
@@ -107,7 +108,7 @@ public class CreateSonePage extends SoneTemplatePage {
                                }
                        }
                        if (selectedIdentity == null) {
-                               template.set("errorNoIdentity", true);
+                               dataProvider.set("errorNoIdentity", true);
                                return;
                        }
                        /* create Sone. */
index 4a64ad0..5722ee3 100644 (file)
@@ -20,6 +20,7 @@ package net.pterodactylus.sone.web;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -49,14 +50,14 @@ public class DeletePostPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                if (request.getMethod() == Method.GET) {
                        String postId = request.getHttpRequest().getParam("post");
                        String returnPage = request.getHttpRequest().getParam("returnPage");
                        Post post = webInterface.getCore().getPost(postId);
-                       template.set("post", post);
-                       template.set("returnPage", returnPage);
+                       dataProvider.set("post", post);
+                       dataProvider.set("returnPage", returnPage);
                        return;
                } else if (request.getMethod() == Method.POST) {
                        String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
@@ -72,8 +73,8 @@ public class DeletePostPage extends SoneTemplatePage {
                        } else if (request.getHttpRequest().isPartSet("abortDelete")) {
                                throw new RedirectException(returnPage);
                        }
-                       template.set("post", post);
-                       template.set("returnPage", returnPage);
+                       dataProvider.set("post", post);
+                       dataProvider.set("returnPage", returnPage);
                }
        }
 
index 00a1a0e..8d90474 100644 (file)
@@ -20,6 +20,7 @@ package net.pterodactylus.sone.web;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -49,8 +50,8 @@ public class DeleteReplyPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", 36);
                Reply reply = webInterface.getCore().getReply(replyId);
                String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
@@ -66,8 +67,8 @@ public class DeleteReplyPage extends SoneTemplatePage {
                                throw new RedirectException(returnPage);
                        }
                }
-               template.set("reply", reply);
-               template.set("returnPage", returnPage);
+               dataProvider.set("reply", reply);
+               dataProvider.set("returnPage", returnPage);
        }
 
 }
index ae01573..abe26ea 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -50,8 +51,8 @@ public class DeleteSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                if (request.getMethod() == Method.POST) {
                        if (request.getHttpRequest().isPartSet("deleteSone")) {
                                Sone currentSone = getCurrentSone(request.getToadletContext());
index b1838da..2d61a95 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.web;
 
 import net.pterodactylus.util.notify.Notification;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -47,8 +48,8 @@ public class DismissNotificationPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String notificationId = request.getHttpRequest().getPartAsStringFailsafe("notification", 36);
                Notification notification = webInterface.getNotifications().getNotification(notificationId);
                if ((notification != null) && notification.isDismissable()) {
index 965caf9..01d4815 100644 (file)
@@ -21,6 +21,7 @@ import net.pterodactylus.sone.data.Profile;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
 import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import freenet.clients.http.ToadletContext;
 
@@ -51,8 +52,8 @@ public class EditProfilePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                ToadletContext toadletContenxt = request.getToadletContext();
                Sone currentSone = getCurrentSone(toadletContenxt);
                Profile profile = currentSone.getProfile();
@@ -78,12 +79,12 @@ public class EditProfilePage extends SoneTemplatePage {
                        }
                        throw new RedirectException("index.html");
                }
-               template.set("firstName", firstName);
-               template.set("middleName", middleName);
-               template.set("lastName", lastName);
-               template.set("birthDay", birthDay);
-               template.set("birthMonth", birthMonth);
-               template.set("birthYear", birthYear);
+               dataProvider.set("firstName", firstName);
+               dataProvider.set("middleName", middleName);
+               dataProvider.set("lastName", lastName);
+               dataProvider.set("birthDay", birthDay);
+               dataProvider.set("birthMonth", birthMonth);
+               dataProvider.set("birthYear", birthYear);
        }
 
 }
index c71c280..864480a 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -46,8 +47,8 @@ public class FollowSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                if (request.getMethod() == Method.POST) {
                        String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
index a33072f..b0a27f7 100644 (file)
@@ -24,6 +24,9 @@ import java.util.List;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.collection.Pagination;
+import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -52,8 +55,8 @@ public class IndexPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                Sone currentSone = getCurrentSone(request.getToadletContext());
                List<Post> allPosts = new ArrayList<Post>();
                allPosts.addAll(currentSone.getPosts());
@@ -71,16 +74,18 @@ public class IndexPage extends SoneTemplatePage {
                        }
                }
                Collections.sort(allPosts, Post.TIME_COMPARATOR);
-               template.set("posts", allPosts);
+               Pagination<Post> pagination = new Pagination<Post>(allPosts, 25).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
+               dataProvider.set("pagination", pagination);
+               dataProvider.set("posts", pagination.getItems());
        }
 
        /**
         * {@inheritDoc}
         */
        @Override
-       protected void postProcess(Request request, Template template) {
+       protected void postProcess(Request request, DataProvider dataProvider) {
                @SuppressWarnings("unchecked")
-               List<Post> posts = (List<Post>) template.get("posts");
+               List<Post> posts = (List<Post>) dataProvider.get("posts");
                for (Post post : posts) {
                        webInterface.getCore().markPostKnown(post);
                        for (Reply reply : webInterface.getCore().getReplies(post)) {
index d413f61..774f410 100644 (file)
@@ -22,6 +22,7 @@ import java.util.Collections;
 import java.util.List;
 
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -51,11 +52,11 @@ public class KnownSonesPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                List<Sone> knownSones = new ArrayList<Sone>(webInterface.getCore().getSones());
                Collections.sort(knownSones, Sone.NICE_NAME_COMPARATOR);
-               template.set("knownSones", knownSones);
+               dataProvider.set("knownSones", knownSones);
        }
 
 }
index dd693ef..c84ea57 100644 (file)
@@ -20,6 +20,7 @@ package net.pterodactylus.sone.web;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -49,8 +50,8 @@ public class LikePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                if (request.getMethod() == Method.POST) {
                        String type=request.getHttpRequest().getPartAsStringFailsafe("type", 16);
                        String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36);
index 05c8ad7..7888659 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -48,8 +49,8 @@ public class LockSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
                Sone sone = webInterface.getCore().getLocalSone(soneId, false);
                if (sone != null) {
index dcfd8a7..ff81562 100644 (file)
@@ -26,6 +26,7 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
 import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import freenet.clients.http.ToadletContext;
 
@@ -60,12 +61,12 @@ public class LoginPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                /* get all own identities. */
                List<Sone> localSones = new ArrayList<Sone>(webInterface.getCore().getLocalSones());
                Collections.sort(localSones, Sone.NICE_NAME_COMPARATOR);
-               template.set("sones", localSones);
+               dataProvider.set("sones", localSones);
                if (request.getMethod() == Method.POST) {
                        String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone-id", 100);
                        Sone selectedSone = webInterface.getCore().getLocalSone(soneId, false);
@@ -75,7 +76,7 @@ public class LoginPage extends SoneTemplatePage {
                        }
                }
                List<OwnIdentity> ownIdentitiesWithoutSone = CreateSonePage.getOwnIdentitiesWithoutSone(webInterface.getCore());
-               template.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
+               dataProvider.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
        }
 
        /**
index f1c0b48..0b6e6e7 100644 (file)
@@ -17,6 +17,7 @@
 
 package net.pterodactylus.sone.web;
 
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import freenet.clients.http.ToadletContext;
 
@@ -45,9 +46,9 @@ public class LogoutPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
                setCurrentSone(request.getToadletContext(), null);
-               super.processTemplate(request, template);
+               super.processTemplate(request, dataProvider);
                throw new RedirectException("index.html");
        }
 
index 77f1a07..63ed6a7 100644 (file)
@@ -20,6 +20,7 @@ package net.pterodactylus.sone.web;
 import net.pterodactylus.sone.core.Options;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
 import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -49,8 +50,8 @@ public class OptionsPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                Options options = webInterface.getCore().getOptions();
                if (request.getMethod() == Method.POST) {
                        Integer insertionDelay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("insertion-delay", 16));
@@ -64,10 +65,10 @@ public class OptionsPage extends SoneTemplatePage {
                        webInterface.getCore().saveConfiguration();
                        throw new RedirectException(getPath());
                }
-               template.set("insertion-delay", options.getIntegerOption("InsertionDelay").get());
-               template.set("sone-rescue-mode", options.getBooleanOption("SoneRescueMode").get());
-               template.set("clear-on-next-restart", options.getBooleanOption("ClearOnNextRestart").get());
-               template.set("really-clear-on-next-restart", options.getBooleanOption("ReallyClearOnNextRestart").get());
+               dataProvider.set("insertion-delay", options.getIntegerOption("InsertionDelay").get());
+               dataProvider.set("sone-rescue-mode", options.getBooleanOption("SoneRescueMode").get());
+               dataProvider.set("clear-on-next-restart", options.getBooleanOption("ClearOnNextRestart").get());
+               dataProvider.set("really-clear-on-next-restart", options.getBooleanOption("ReallyClearOnNextRestart").get());
        }
 
 }
index 23ca247..dc41865 100644 (file)
@@ -21,8 +21,10 @@ import java.util.Arrays;
 import java.util.Collection;
 
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.main.SonePlugin;
 import net.pterodactylus.sone.web.page.Page;
 import net.pterodactylus.sone.web.page.TemplatePage;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import freenet.clients.http.SessionManager.Session;
 import freenet.clients.http.ToadletContext;
@@ -184,10 +186,14 @@ public class SoneTemplatePage extends TemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
-               template.set("currentSone", getCurrentSone(request.getToadletContext(), false));
-               template.set("request", request);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
+               dataProvider.set("currentSone", getCurrentSone(request.getToadletContext(), false));
+               dataProvider.set("request", request);
+               dataProvider.set("currentVersion", SonePlugin.VERSION);
+               dataProvider.set("hasLatestVersion", webInterface.getCore().getUpdateChecker().hasLatestVersion());
+               dataProvider.set("latestVersion", webInterface.getCore().getUpdateChecker().getLatestVersion());
+               dataProvider.set("latestVersionTime", webInterface.getCore().getUpdateChecker().getLatestVersionDate());
        }
 
        /**
index 5b0c652..316e408 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -46,8 +47,8 @@ public class UnfollowSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                if (request.getMethod() == Method.POST) {
                        String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
index 1ecacc8..d08689f 100644 (file)
@@ -20,6 +20,7 @@ package net.pterodactylus.sone.web;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -49,8 +50,8 @@ public class UnlikePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                if (request.getMethod() == Method.POST) {
                        String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16);
                        String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36);
index 938e451..35d69ff 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -47,8 +48,8 @@ public class UnlockSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
                Sone sone = webInterface.getCore().getLocalSone(soneId, false);
                if (sone != null) {
index 3bc3b36..3c4eddf 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -48,19 +49,19 @@ public class ViewPostPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String postId = request.getHttpRequest().getParam("post");
                Post post = webInterface.getCore().getPost(postId);
-               template.set("post", post);
+               dataProvider.set("post", post);
        }
 
        /**
         * {@inheritDoc}
         */
        @Override
-       protected void postProcess(Request request, Template template) {
-               Post post = (Post) template.get("post");
+       protected void postProcess(Request request, DataProvider dataProvider) {
+               Post post = (Post) dataProvider.get("post");
                webInterface.getCore().markPostKnown(post);
                for (Reply reply : webInterface.getCore().getReplies(post)) {
                        webInterface.getCore().markReplyKnown(reply);
index d933bc7..9fd6fae 100644 (file)
@@ -22,6 +22,7 @@ import java.util.List;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 
 /**
@@ -51,19 +52,19 @@ public class ViewSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(Request request, Template template) throws RedirectException {
-               super.processTemplate(request, template);
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+               super.processTemplate(request, dataProvider);
                String soneId = request.getHttpRequest().getParam("sone");
                Sone sone = webInterface.getCore().getSone(soneId, false);
-               template.set("sone", sone);
+               dataProvider.set("sone", sone);
        }
 
        /**
         * {@inheritDoc}
         */
        @Override
-       protected void postProcess(Request request, Template template) {
-               Sone sone = (Sone) template.get("sone");
+       protected void postProcess(Request request, DataProvider dataProvider) {
+               Sone sone = (Sone) dataProvider.get("sone");
                List<Post> posts = sone.getPosts();
                for (Post post : posts) {
                        webInterface.getCore().markPostKnown(post);
index d3ec1ee..6085ff8 100644 (file)
@@ -88,6 +88,7 @@ import net.pterodactylus.util.template.TemplateFactory;
 import net.pterodactylus.util.template.TemplateProvider;
 import net.pterodactylus.util.template.XmlFilter;
 import net.pterodactylus.util.thread.Ticker;
+import net.pterodactylus.util.version.Version;
 import freenet.clients.http.SessionManager;
 import freenet.clients.http.SessionManager.Session;
 import freenet.clients.http.ToadletContainer;
@@ -141,6 +142,9 @@ public class WebInterface implements CoreListener {
        /** The “Sone locked” notification. */
        private final ListNotification<Sone> lockedSonesNotification;
 
+       /** The “new version” notification. */
+       private final TemplateNotification newVersionNotification;
+
        /**
         * Creates a new web interface.
         *
@@ -189,6 +193,9 @@ public class WebInterface implements CoreListener {
 
                Template lockedSonesTemplate = templateFactory.createTemplate(createReader("/templates/notify/lockedSonesNotification.html"));
                lockedSonesNotification = new ListNotification<Sone>("sones-locked-notification", "sones", lockedSonesTemplate);
+
+               Template newVersionTemplate = templateFactory.createTemplate(createReader("/templates/notify/newVersionNotification.html"));
+               newVersionNotification = new TemplateNotification("new-version-notification", newVersionTemplate);
        }
 
        //
@@ -701,6 +708,16 @@ public class WebInterface implements CoreListener {
        }
 
        /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void updateFound(Version version, long releaseTime) {
+               newVersionNotification.set("version", version);
+               newVersionNotification.set("releaseTime", releaseTime);
+               notificationManager.addNotification(newVersionNotification);
+       }
+
+       /**
         * Template provider implementation that uses
         * {@link WebInterface#createReader(String)} to load templates for
         * inclusion.
index 015add5..a358fb1 100644 (file)
@@ -91,13 +91,23 @@ public class GetStatusAjaxPage extends JsonPage {
                Set<Post> newPosts = webInterface.getNewPosts();
                JsonArray jsonPosts = new JsonArray();
                for (Post post : newPosts) {
-                       jsonPosts.add(post.getId());
+                       JsonObject jsonPost = new JsonObject();
+                       jsonPost.put("id", post.getId());
+                       jsonPost.put("sone", post.getSone().getId());
+                       jsonPost.put("recipient", (post.getRecipient() != null) ? post.getRecipient().getId() : null);
+                       jsonPost.put("time", post.getTime());
+                       jsonPosts.add(jsonPost);
                }
                /* load new replies. */
                Set<Reply> newReplies = webInterface.getNewReplies();
                JsonArray jsonReplies = new JsonArray();
                for (Reply reply : newReplies) {
-                       jsonReplies.add(reply.getId());
+                       JsonObject jsonReply = new JsonObject();
+                       jsonReply.put("id", reply.getId());
+                       jsonReply.put("sone", reply.getSone().getId());
+                       jsonReply.put("post", reply.getPost().getId());
+                       jsonReply.put("postSone", reply.getPost().getSone().getId());
+                       jsonReplies.add(jsonReply);
                }
                return createSuccessJsonObject().put("sones", jsonSones).put("notifications", jsonNotifications).put("removedNotifications", jsonRemovedNotifications).put("newPosts", jsonPosts).put("newReplies", jsonReplies);
        }
index e823eb5..e6ee539 100644 (file)
@@ -25,6 +25,7 @@ import java.util.logging.Logger;
 
 import net.pterodactylus.sone.web.page.Page.Request.Method;
 import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.template.DataProvider;
 import net.pterodactylus.util.template.Template;
 import freenet.clients.http.LinkEnabledCallback;
 import freenet.clients.http.PageMaker;
@@ -46,7 +47,7 @@ public class TemplatePage implements Page, LinkEnabledCallback {
        private final String path;
 
        /** The template to render. */
-       protected final Template template;
+       private final Template template;
 
        /** The L10n handler. */
        private final BaseL10n l10n;
@@ -116,9 +117,10 @@ public class TemplatePage implements Page, LinkEnabledCallback {
                        pageNode.addForwardLink("icon", shortcutIcon);
                }
 
+               DataProvider dataProvider = template.createDataProvider();
                try {
                        long start = System.nanoTime();
-                       processTemplate(request, template);
+                       processTemplate(request, dataProvider);
                        long finish = System.nanoTime();
                        logger.log(Level.FINEST, "Template was rendered in " + ((finish - start) / 1000) / 1000.0 + "ms.");
                } catch (RedirectException re1) {
@@ -126,10 +128,10 @@ public class TemplatePage implements Page, LinkEnabledCallback {
                }
 
                StringWriter stringWriter = new StringWriter();
-               template.render(stringWriter);
+               template.render(dataProvider, stringWriter);
                pageNode.content.addChild("%", stringWriter.toString());
 
-               postProcess(request, template);
+               postProcess(request, dataProvider);
 
                return new Response(200, "OK", "text/html", pageNode.outer.generate());
        }
@@ -159,29 +161,29 @@ public class TemplatePage implements Page, LinkEnabledCallback {
         *
         * @param request
         *            The request that is rendered
-        * @param template
-        *            The template to set variables in
+        * @param dataProvider
+        *            The data provider to set variables in
         * @throws RedirectException
         *             if the processing page wants to redirect after processing
         */
-       protected void processTemplate(Request request, Template template) throws RedirectException {
+       protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
                /* do nothing. */
        }
 
        /**
         * This method will be called after
-        * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, Template)}
+        * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, DataProvider)}
         * has processed the template and the template was rendered. This method
         * will not be called if
-        * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, Template)}
+        * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, DataProvider)}
         * throws a {@link RedirectException}!
         *
         * @param request
         *            The request being processed
-        * @param template
-        *            The template that was rendered
+        * @param dataProvider
+        *            The data provider that supplied the rendered data
         */
-       protected void postProcess(Request request, Template template) {
+       protected void postProcess(Request request, DataProvider dataProvider) {
                /* do nothing. */
        }
 
@@ -212,7 +214,7 @@ public class TemplatePage implements Page, LinkEnabledCallback {
        /**
         * Exception that can be thrown to signal that a subclassed {@link Page}
         * wants to redirect the user during the
-        * {@link TemplatePage#processTemplate(net.pterodactylus.sone.web.page.Page.Request, Template)}
+        * {@link TemplatePage#processTemplate(net.pterodactylus.sone.web.page.Page.Request, DataProvider)}
         * method call.
         *
         * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
index 533a01b..740cf45 100644 (file)
@@ -175,6 +175,8 @@ WebInterface.SelectBox.Choose=Choose…
 WebInterface.SelectBox.Yes=Yes
 WebInterface.SelectBox.No=No
 WebInterface.ClickToShow.Replies=Click here to show hidden replies.
+WebInterface.VersionInformation.CurrentVersion=Current Version:
+WebInterface.VersionInformation.LatestVersion=Latest Version:
 
 Notification.ClickHereToRead=Click here to read the full text of the notification.
 Notification.FirstStart.Text=This seems to be the first time you start Sone. To start, create a new Sone from a web of trust identity and start following other Sones.
@@ -191,3 +193,4 @@ Notification.SoneIsBeingRescued.Text=The following Sones are currently being res
 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:
+Notification.NewVersion.Text=A new version of the Sone plugin was found: Version {version}.
index 89d9854..4f0c49e 100644 (file)
@@ -20,6 +20,13 @@ textarea {
        height: 4em;
 }
 
+#sone button {
+       background-color: #ddd;
+       border-width: 1px;
+       color: #444;
+       padding: 0.5ex 1.5ex;
+}
+
 #sone form {
        margin: 0px;
 }
@@ -444,6 +451,10 @@ textarea {
        color: #888;
 }
 
+#sone #tail #version-information {
+       margin-top: 1em;
+}
+
 #sone #add-sone textarea, #sone #create-sone textarea, #sone #load-sone textarea, #sone #edit-profile textarea {
        height: 1.5em;
 }
index 9aa0b82..3c229e0 100644 (file)
@@ -453,7 +453,7 @@ function ajaxifyPost(postElement) {
                        postReply(postId, text, function(success, error, replyId) {
                                if (success) {
                                        $(inputField).val("");
-                                       loadNewReply(replyId);
+                                       loadNewReply(replyId, getCurrentSoneId(), postId);
                                        markPostAsKnown(getPostElement(inputField));
                                        $("#sone .post#" + postId + " .create-reply").addClass("hidden");
                                } else {
@@ -585,11 +585,11 @@ function getStatus() {
                        });
                        /* process new posts. */
                        $.each(data.newPosts, function(index, value) {
-                               loadNewPost(value);
+                               loadNewPost(value.id, value.sone, value.recipient, value.time);
                        });
                        /* process new replies. */
                        $.each(data.newReplies, function(index, value) {
-                               loadNewReply(value);
+                               loadNewReply(value.id, value.sone, value.post, value.postSone);
                        });
                        /* do it again in 5 seconds. */
                        setTimeout(getStatus, 5000);
@@ -604,6 +604,16 @@ function getStatus() {
 }
 
 /**
+ * Returns the ID of the currently logged in Sone.
+ *
+ * @return The ID of the current Sone, or an empty string if no Sone is logged
+ *         in
+ */
+function getCurrentSoneId() {
+       return $("#currentSoneId").text();
+}
+
+/**
  * Returns the content of the page-id attribute.
  *
  * @returns The page ID
@@ -696,10 +706,20 @@ function hasReply(replyId) {
        return $("#sone .reply#" + replyId).length > 0;
 }
 
-function loadNewPost(postId) {
+function loadNewPost(postId, soneId, recipientId, time) {
        if (hasPost(postId)) {
                return;
        }
+       if (!isIndexPage()) {
+               if (!isViewPostPage() || (getShownPostId() != postId)) {
+                       if (!isViewSonePage() || ((getShownSoneId() != soneId) && (getShownSoneId() != recipientId))) {
+                               return;
+                       }
+               }
+       }
+       if (getPostTime($("#sone .post").last()) > time) {
+               return;
+       }
        $.getJSON("getPost.ajax", { "post" : postId }, function(data, textStatus) {
                if ((data != null) && data.success) {
                        if (hasPost(data.post.id)) {
@@ -718,8 +738,6 @@ function loadNewPost(postId) {
                        newPost = $(data.post.html).addClass("hidden");
                        if (firstOlderPost != null) {
                                newPost.insertBefore(firstOlderPost);
-                       } else {
-                               $("#sone #posts").append(newPost);
                        }
                        ajaxifyPost(newPost);
                        newPost.slideDown();
@@ -728,10 +746,13 @@ function loadNewPost(postId) {
        });
 }
 
-function loadNewReply(replyId) {
+function loadNewReply(replyId, soneId, postId, postSoneId) {
        if (hasReply(replyId)) {
                return;
        }
+       if (!hasPost(postId)) {
+               return;
+       }
        $.getJSON("getReply.ajax", { "reply": replyId }, function(data, textStatus) {
                /* find post. */
                if ((data != null) && data.success) {
@@ -859,7 +880,7 @@ $(document).ready(function() {
                        text = $(this).find(":input:enabled").val();
                        $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "text": text }, function(data, textStatus) {
                                if ((data != null) && data.success) {
-                                       loadNewPost(data.postId);
+                                       loadNewPost(data.postId, getCurrentSoneId());
                                }
                        });
                        $(this).find(":input:enabled").val("").blur();
@@ -874,7 +895,7 @@ $(document).ready(function() {
                        text = $(this).find(":input:enabled").val();
                        $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "recipient": getShownSoneId(), "text": text }, function(data, textStatus) {
                                if ((data != null) && data.success) {
-                                       loadNewPost(data.postId);
+                                       loadNewPost(data.postId, getCurrentSoneId());
                                }
                        });
                        $(this).find(":input:enabled").val("").blur();
index 1d8e284..2c2cf3f 100644 (file)
@@ -1,6 +1,7 @@
 <div id="sone" class="<%ifnull ! currentSone>online<%else>offline<%/if>">
 
        <div id="formPassword"><% formPassword|html></div>
+       <div id="currentSoneId" class="hidden"><% currentSone.id|html></div>
 
        <script src="javascript/jquery-1.4.2.js" language="javascript"></script>
        <script src="javascript/sone.js" language="javascript"></script>
index 180bcf2..dd2e25e 100644 (file)
@@ -7,6 +7,13 @@
                                <img src="images/flattr-badge-large.png" alt="Flattr Sone" title="Flattr Sone" />
                        </a>
                </div>
+
+               <div id="version-information">
+                       <div class="current-version"><%= WebInterface.VersionInformation.CurrentVersion|l10n|html> <b><% currentVersion|html></b></div>
+                       <%if hasLatestVersion>
+                               <div class="latest-version"><%= WebInterface.VersionInformation.LatestVersion|l10n|html> <b><% latestVersion|html></b></div>
+                       <%/if>
+               </div>
        </div>
 
 </div>
index 39d53f1..345f376 100644 (file)
@@ -5,8 +5,6 @@
        <h1><%= Page.Index.PostList.Title|l10n|html></h1>
 
        <div id="posts">
-               <%getpage>
-               <%paginate list=posts pagesize=25>
                <%= page|store key=pageParameter>
                <%include include/pagination.html>
                <%foreach pagination.items post>
diff --git a/src/main/resources/templates/notify/newVersionNotification.html b/src/main/resources/templates/notify/newVersionNotification.html
new file mode 100644 (file)
index 0000000..23a5855
--- /dev/null
@@ -0,0 +1 @@
+<div class="text"><%= Notification.NewVersion.Text|l10n|html|replace needle="{version}" replacementKey=version></div>