Merge branch 'release-0.8.3' 0.8.3
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 8 Oct 2012 12:14:38 +0000 (14:14 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 8 Oct 2012 12:14:38 +0000 (14:14 +0200)
59 files changed:
pom.xml
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/Options.java
src/main/java/net/pterodactylus/sone/core/UpdateChecker.java
src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/data/Sone.java
src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java
src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java
src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java
src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java
src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java
src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java
src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java
src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java
src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java
src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java
src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java
src/main/java/net/pterodactylus/sone/template/ParserFilter.java
src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java
src/main/java/net/pterodactylus/sone/template/SoneAccessor.java
src/main/java/net/pterodactylus/sone/text/LinkPart.java
src/main/java/net/pterodactylus/sone/text/Part.java
src/main/java/net/pterodactylus/sone/text/PartContainer.java
src/main/java/net/pterodactylus/sone/text/PlainTextPart.java
src/main/java/net/pterodactylus/sone/text/PostPart.java
src/main/java/net/pterodactylus/sone/text/SonePart.java
src/main/java/net/pterodactylus/sone/text/SoneTextParser.java
src/main/java/net/pterodactylus/sone/web/EditProfilePage.java
src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java
src/main/java/net/pterodactylus/sone/web/OptionsPage.java
src/main/java/net/pterodactylus/sone/web/SearchPage.java
src/main/java/net/pterodactylus/sone/web/UploadImagePage.java
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java
src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java
src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java
src/main/resources/i18n/sone.de.properties
src/main/resources/i18n/sone.en.properties
src/main/resources/i18n/sone.fr.properties
src/main/resources/i18n/sone.no.properties
src/main/resources/i18n/sone.pl.properties
src/main/resources/i18n/sone.ru.properties
src/main/resources/templates/editProfile.html
src/main/resources/templates/imageBrowser.html
src/main/resources/templates/include/tail.html
src/main/resources/templates/include/viewPost.html
src/main/resources/templates/include/viewReply.html
src/main/resources/templates/index.html
src/main/resources/templates/options.html
src/main/resources/templates/viewSone.html
src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java

diff --git a/pom.xml b/pom.xml
index 9ea02ae..bcc9946 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.8.2</version>
+       <version>0.8.3</version>
        <dependencies>
                <dependency>
                        <groupId>net.pterodactylus</groupId>
                        <artifactId>utils</artifactId>
-                       <version>0.12</version>
+                       <version>0.12.2</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
index 4001f46..c391582 100644 (file)
@@ -53,8 +53,6 @@ import net.pterodactylus.sone.freenet.wot.Identity;
 import net.pterodactylus.sone.freenet.wot.IdentityListener;
 import net.pterodactylus.sone.freenet.wot.IdentityManager;
 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-import net.pterodactylus.sone.freenet.wot.Trust;
-import net.pterodactylus.sone.freenet.wot.WebOfTrustException;
 import net.pterodactylus.sone.main.SonePlugin;
 import net.pterodactylus.util.config.Configuration;
 import net.pterodactylus.util.config.ConfigurationException;
@@ -79,6 +77,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        /** The logger. */
        private static final Logger logger = Logging.getLogger(Core.class);
 
+       /** The start time. */
+       private final long startupTime = System.currentTimeMillis();
+
        /** The options. */
        private final Options options = new Options();
 
@@ -112,6 +113,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        /** The update checker. */
        private final UpdateChecker updateChecker;
 
+       /** The trust updater. */
+       private final WebOfTrustUpdater webOfTrustUpdater;
+
        /** The FCP interface. */
        private volatile FcpInterface fcpInterface;
 
@@ -184,8 +188,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         *            The freenet interface
         * @param identityManager
         *            The identity manager
+        * @param webOfTrustUpdater
+        *            The WebOfTrust updater
         */
-       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager) {
+       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater) {
                super("Sone Core");
                this.configuration = configuration;
                this.freenetInterface = freenetInterface;
@@ -193,6 +199,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                this.soneDownloader = new SoneDownloader(this, freenetInterface);
                this.imageInserter = new ImageInserter(this, freenetInterface);
                this.updateChecker = new UpdateChecker(freenetInterface);
+               this.webOfTrustUpdater = webOfTrustUpdater;
        }
 
        //
@@ -224,6 +231,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        //
 
        /**
+        * Returns the time Sone was started.
+        *
+        * @return The startup time (in milliseconds since Jan 1, 1970 UTC)
+        */
+       public long getStartupTime() {
+               return startupTime;
+       }
+
+       /**
         * Sets the configuration to use. This will automatically save the current
         * configuration to the given configuration.
         *
@@ -885,10 +901,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         * @return The created Sone
         */
        public Sone createSone(OwnIdentity ownIdentity) {
-               try {
-                       ownIdentity.addContext("Sone");
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.SEVERE, String.format("Could not add “Sone” context to own identity: %s", ownIdentity), wote1);
+               if (!webOfTrustUpdater.addContextWait(ownIdentity, "Sone")) {
+                       logger.log(Level.SEVERE, String.format("Could not add “Sone” context to own identity: %s", ownIdentity));
                        return null;
                }
                Sone sone = addLocalSone(ownIdentity);
@@ -1039,25 +1053,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        }
 
        /**
-        * Retrieves the trust relationship from the origin to the target. If the
-        * trust relationship can not be retrieved, {@code null} is returned.
-        *
-        * @see Identity#getTrust(OwnIdentity)
-        * @param origin
-        *            The origin of the trust tree
-        * @param target
-        *            The target of the trust
-        * @return The trust relationship
-        */
-       public Trust getTrust(Sone origin, Sone target) {
-               if (!isLocalSone(origin)) {
-                       logger.log(Level.WARNING, String.format("Tried to get trust from remote Sone: %s", origin));
-                       return null;
-               }
-               return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity());
-       }
-
-       /**
         * Sets the trust value of the given origin Sone for the target Sone.
         *
         * @param origin
@@ -1069,11 +1064,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         */
        public void setTrust(Sone origin, Sone target, int trustValue) {
                Validation.begin().isNotNull("Trust Origin", origin).check().isInstanceOf("Trust Origin", origin.getIdentity(), OwnIdentity.class).isNotNull("Trust Target", target).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check();
-               try {
-                       ((OwnIdentity) origin.getIdentity()).setTrust(target.getIdentity(), trustValue, preferences.getTrustComment());
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.WARNING, String.format("Could not set trust for Sone: %s", target), wote1);
-               }
+               webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), trustValue, preferences.getTrustComment());
        }
 
        /**
@@ -1086,11 +1077,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         */
        public void removeTrust(Sone origin, Sone target) {
                Validation.begin().isNotNull("Trust Origin", origin).isNotNull("Trust Target", target).check().isInstanceOf("Trust Origin Identity", origin.getIdentity(), OwnIdentity.class).check();
-               try {
-                       ((OwnIdentity) origin.getIdentity()).removeTrust(target.getIdentity());
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.WARNING, String.format("Could not remove trust for Sone: %s", target), wote1);
-               }
+               webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), null, null);
        }
 
        /**
@@ -1280,12 +1267,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        soneInserter.removeSoneInsertListener(this);
                        soneInserter.stop();
                }
-               try {
-                       ((OwnIdentity) sone.getIdentity()).removeContext("Sone");
-                       ((OwnIdentity) sone.getIdentity()).removeProperty("Sone.LatestEdition");
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.WARNING, String.format("Could not remove context and properties from Sone: %s", sone), wote1);
-               }
+               webOfTrustUpdater.removeContext((OwnIdentity) sone.getIdentity(), "Sone");
+               webOfTrustUpdater.removeProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition");
                try {
                        configuration.getLongValue("Sone/" + sone.getId() + "/Time").setValue(null);
                } catch (ConfigurationException ce1) {
@@ -1857,7 +1840,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (albums) {
                        albums.remove(album.getId());
                }
-               saveSone(album.getSone());
+               touchConfiguration();
        }
 
        /**
@@ -1897,7 +1880,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (images) {
                        images.remove(image.getId());
                }
-               saveSone(image.getSone());
+               touchConfiguration();
        }
 
        /**
@@ -1967,6 +1950,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                loadConfiguration();
                updateChecker.addUpdateListener(this);
                updateChecker.start();
+               webOfTrustUpdater.start();
        }
 
        /**
@@ -1994,11 +1978,14 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        @Override
        public void serviceStop() {
                synchronized (localSones) {
-                       for (SoneInserter soneInserter : soneInserters.values()) {
-                               soneInserter.removeSoneInsertListener(this);
-                               soneInserter.stop();
+                       for (Entry<Sone, SoneInserter> soneInserter : soneInserters.entrySet()) {
+                               soneInserter.getValue().removeSoneInsertListener(this);
+                               soneInserter.getValue().stop();
+                               saveSone(soneInserter.getKey());
                        }
                }
+               saveConfiguration();
+               webOfTrustUpdater.stop();
                updateChecker.stop();
                updateChecker.removeUpdateListener(this);
                soneDownloader.stop();
@@ -2138,13 +2125,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
 
                        configuration.save();
 
-                       ((OwnIdentity) sone.getIdentity()).setProperty("Sone.LatestEdition", String.valueOf(sone.getLatestEdition()));
+                       webOfTrustUpdater.setProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition", String.valueOf(sone.getLatestEdition()));
 
                        logger.log(Level.INFO, String.format("Sone %s saved.", sone));
                } catch (ConfigurationException ce1) {
                        logger.log(Level.WARNING, String.format("Could not save Sone: %s", sone), ce1);
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.WARNING, String.format("Could not set WoT property for Sone: %s", sone), wote1);
                }
        }
 
@@ -2174,9 +2159,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        configuration.getStringValue("Option/TrustComment").setValue(options.getStringOption("TrustComment").getReal());
                        configuration.getBooleanValue("Option/ActivateFcpInterface").setValue(options.getBooleanOption("ActivateFcpInterface").getReal());
                        configuration.getIntValue("Option/FcpFullAccessRequired").setValue(options.getIntegerOption("FcpFullAccessRequired").getReal());
-                       configuration.getBooleanValue("Option/SoneRescueMode").setValue(options.getBooleanOption("SoneRescueMode").getReal());
-                       configuration.getBooleanValue("Option/ClearOnNextRestart").setValue(options.getBooleanOption("ClearOnNextRestart").getReal());
-                       configuration.getBooleanValue("Option/ReallyClearOnNextRestart").setValue(options.getBooleanOption("ReallyClearOnNextRestart").getReal());
 
                        /* save known Sones. */
                        int soneCounter = 0;
@@ -2276,20 +2258,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        }
 
                }));
-               options.addBooleanOption("SoneRescueMode", new DefaultOption<Boolean>(false));
-               options.addBooleanOption("ClearOnNextRestart", new DefaultOption<Boolean>(false));
-               options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption<Boolean>(false));
-
-               /* read options from configuration. */
-               options.getBooleanOption("ClearOnNextRestart").set(configuration.getBooleanValue("Option/ClearOnNextRestart").getValue(null));
-               options.getBooleanOption("ReallyClearOnNextRestart").set(configuration.getBooleanValue("Option/ReallyClearOnNextRestart").getValue(null));
-               boolean clearConfiguration = options.getBooleanOption("ClearOnNextRestart").get() && options.getBooleanOption("ReallyClearOnNextRestart").get();
-               options.getBooleanOption("ClearOnNextRestart").set(null);
-               options.getBooleanOption("ReallyClearOnNextRestart").set(null);
-               if (clearConfiguration) {
-                       /* stop loading the configuration. */
-                       return;
-               }
 
                loadConfigurationValue("InsertionDelay");
                loadConfigurationValue("PostsPerPage");
@@ -2302,7 +2270,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                options.getStringOption("TrustComment").set(configuration.getStringValue("Option/TrustComment").getValue(null));
                options.getBooleanOption("ActivateFcpInterface").set(configuration.getBooleanValue("Option/ActivateFcpInterface").getValue(null));
                options.getIntegerOption("FcpFullAccessRequired").set(configuration.getIntValue("Option/FcpFullAccessRequired").getValue(null));
-               options.getBooleanOption("SoneRescueMode").set(configuration.getBooleanValue("Option/SoneRescueMode").getValue(null));
 
                /* load known Sones. */
                int soneCounter = 0;
@@ -2395,12 +2362,12 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         *            The URI to derive the Sone URI from
         * @return The derived URI
         */
-       private FreenetURI getSoneUri(String uriString) {
+       private static FreenetURI getSoneUri(String uriString) {
                try {
                        FreenetURI uri = new FreenetURI(uriString).setDocName("Sone").setMetaString(new String[0]);
                        return uri;
                } catch (MalformedURLException mue1) {
-                       logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString, mue1));
+                       logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString), mue1);
                        return null;
                }
        }
@@ -2575,7 +2542,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", image, key));
                image.setKey(key.toString());
                deleteTemporaryImage(image.getId());
-               saveSone(image.getSone());
+               touchConfiguration();
                coreListenerManager.fireImageInsertFinished(image);
        }
 
@@ -2940,58 +2907,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        return this;
                }
 
-               /**
-                * Returns whether Sone should clear its settings on the next restart.
-                * In order to be effective, {@link #isReallyClearOnNextRestart()} needs
-                * to return {@code true} as well!
-                *
-                * @return {@code true} if Sone should clear its settings on the next
-                *         restart, {@code false} otherwise
-                */
-               public boolean isClearOnNextRestart() {
-                       return options.getBooleanOption("ClearOnNextRestart").get();
-               }
-
-               /**
-                * Sets whether Sone will clear its settings on the next restart.
-                *
-                * @param clearOnNextRestart
-                *            {@code true} if Sone should clear its settings on the next
-                *            restart, {@code false} otherwise
-                * @return This preferences
-                */
-               public Preferences setClearOnNextRestart(Boolean clearOnNextRestart) {
-                       options.getBooleanOption("ClearOnNextRestart").set(clearOnNextRestart);
-                       return this;
-               }
-
-               /**
-                * Returns whether Sone should really clear its settings on next
-                * restart. This is a confirmation option that needs to be set in
-                * addition to {@link #isClearOnNextRestart()} in order to clear Sone’s
-                * settings on the next restart.
-                *
-                * @return {@code true} if Sone should really clear its settings on the
-                *         next restart, {@code false} otherwise
-                */
-               public boolean isReallyClearOnNextRestart() {
-                       return options.getBooleanOption("ReallyClearOnNextRestart").get();
-               }
-
-               /**
-                * Sets whether Sone should really clear its settings on the next
-                * restart.
-                *
-                * @param reallyClearOnNextRestart
-                *            {@code true} if Sone should really clear its settings on
-                *            the next restart, {@code false} otherwise
-                * @return This preferences
-                */
-               public Preferences setReallyClearOnNextRestart(Boolean reallyClearOnNextRestart) {
-                       options.getBooleanOption("ReallyClearOnNextRestart").set(reallyClearOnNextRestart);
-                       return this;
-               }
-
        }
 
 }
index af81d0f..0ea895a 100644 (file)
@@ -329,6 +329,8 @@ public class Options {
        /**
         * Adds an {@link Enum} {@link Option}.
         *
+        * @param <T>
+        *            The enum type
         * @param name
         *            The name of the option
         * @param enumOption
@@ -350,6 +352,8 @@ public class Options {
         * options.&lt;SomeEnum&gt; getEnumOption(&quot;SomeEnumOption&quot;).get();
         * </pre>
         *
+        * @param <T>
+        *            The enum type
         * @param name
         *            The name of the option
         * @return The enum option, or {@code null} if there is no enum option with
index 003e276..3325cd5 100644 (file)
@@ -49,7 +49,7 @@ public class UpdateChecker {
        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 = 49;
+       private static final int LATEST_EDITION = 54;
 
        /** The Freenet interface. */
        private final FreenetInterface freenetInterface;
diff --git a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java
new file mode 100644 (file)
index 0000000..bdfc1ae
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * Sone - WebOfTrustUpdater.java - Copyright © 2012 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.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.sone.freenet.plugin.PluginException;
+import net.pterodactylus.sone.freenet.wot.DefaultIdentity;
+import net.pterodactylus.sone.freenet.wot.Identity;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
+import net.pterodactylus.sone.freenet.wot.Trust;
+import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector;
+import net.pterodactylus.sone.freenet.wot.WebOfTrustException;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.service.AbstractService;
+import net.pterodactylus.util.validation.Validation;
+
+/**
+ * Updates WebOfTrust identity data in a background thread because communicating
+ * with the WebOfTrust plugin can potentially last quite long.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class WebOfTrustUpdater extends AbstractService {
+
+       /** The logger. */
+       private static final Logger logger = Logging.getLogger(WebOfTrustUpdater.class);
+
+       /** Stop job. */
+       @SuppressWarnings("synthetic-access")
+       private final WebOfTrustUpdateJob stopJob = new WebOfTrustUpdateJob();
+
+       /** The web of trust connector. */
+       private final WebOfTrustConnector webOfTrustConnector;
+
+       /** The queue for jobs. */
+       private final BlockingQueue<WebOfTrustUpdateJob> updateJobs = new LinkedBlockingQueue<WebOfTrustUpdateJob>();
+
+       /**
+        * Creates a new trust updater.
+        *
+        * @param webOfTrustConnector
+        *            The web of trust connector
+        */
+       public WebOfTrustUpdater(WebOfTrustConnector webOfTrustConnector) {
+               super("Trust Updater");
+               this.webOfTrustConnector = webOfTrustConnector;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Updates the trust relation between the truster and the trustee. This
+        * method will return immediately and perform a trust update in the
+        * background.
+        *
+        * @param truster
+        *            The identity giving the trust
+        * @param trustee
+        *            The identity receiving the trust
+        * @param score
+        *            The new level of trust (from -100 to 100, may be {@code null}
+        *            to remove the trust completely)
+        * @param comment
+        *            The comment of the trust relation
+        */
+       public void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment) {
+               SetTrustJob setTrustJob = new SetTrustJob(truster, trustee, score, comment);
+               if (updateJobs.contains(setTrustJob)) {
+                       updateJobs.remove(setTrustJob);
+               }
+               logger.log(Level.FINER, "Adding Trust Update Job: " + setTrustJob);
+               try {
+                       updateJobs.put(setTrustJob);
+               } catch (InterruptedException e) {
+                       /* the queue is unbounded so it should never block. */
+               }
+       }
+
+       /**
+        * Adds the given context to the given own identity.
+        *
+        * @param ownIdentity
+        *            The own identity to add the context to
+        * @param context
+        *            The context to add
+        */
+       public void addContext(OwnIdentity ownIdentity, String context) {
+               addContextWait(ownIdentity, context, false);
+       }
+
+       /**
+        * Adds the given context to the given own identity, waiting for completion
+        * of the operation.
+        *
+        * @param ownIdentity
+        *            The own identity to add the context to
+        * @param context
+        *            The context to add
+        * @return {@code true} if the context was added successfully, {@code false}
+        *         otherwise
+        */
+       public boolean addContextWait(OwnIdentity ownIdentity, String context) {
+               return addContextWait(ownIdentity, context, true);
+       }
+
+       /**
+        * Adds the given context to the given own identity, waiting for completion
+        * of the operation.
+        *
+        * @param ownIdentity
+        *            The own identity to add the context to
+        * @param context
+        *            The context to add
+        * @param wait
+        *            {@code true} to wait for the end of the operation,
+        *            {@code false} to return immediately
+        * @return {@code true} if the context was added successfully, {@code false}
+        *         if the context was not added successfully, or if the job should
+        *         not wait for completion
+        */
+       private boolean addContextWait(OwnIdentity ownIdentity, String context, boolean wait) {
+               AddContextJob addContextJob = new AddContextJob(ownIdentity, context);
+               if (!updateJobs.contains(addContextJob)) {
+                       logger.log(Level.FINER, "Adding Context Job: " + addContextJob);
+                       try {
+                               updateJobs.put(addContextJob);
+                       } catch (InterruptedException ie1) {
+                               /* the queue is unbounded so it should never block. */
+                       }
+                       if (wait) {
+                               return addContextJob.waitForCompletion();
+                       }
+               } else if (wait) {
+                       for (WebOfTrustUpdateJob updateJob : updateJobs) {
+                               if (updateJob.equals(addContextJob)) {
+                                       return updateJob.waitForCompletion();
+                               }
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Removes the given context from the given own identity.
+        *
+        * @param ownIdentity
+        *            The own identity to remove the context from
+        * @param context
+        *            The context to remove
+        */
+       public void removeContext(OwnIdentity ownIdentity, String context) {
+               RemoveContextJob removeContextJob = new RemoveContextJob(ownIdentity, context);
+               if (!updateJobs.contains(removeContextJob)) {
+                       logger.log(Level.FINER, "Adding Context Job: " + removeContextJob);
+                       try {
+                               updateJobs.put(removeContextJob);
+                       } catch (InterruptedException ie1) {
+                               /* the queue is unbounded so it should never block. */
+                       }
+               }
+       }
+
+       /**
+        * Sets a property on the given own identity.
+        *
+        * @param ownIdentity
+        *            The own identity to set the property on
+        * @param propertyName
+        *            The name of the property to set
+        * @param propertyValue
+        *            The value of the property to set
+        */
+       public void setProperty(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
+               SetPropertyJob setPropertyJob = new SetPropertyJob(ownIdentity, propertyName, propertyValue);
+               if (updateJobs.contains(setPropertyJob)) {
+                       updateJobs.remove(setPropertyJob);
+               }
+               logger.log(Level.FINER, "Adding Property Job: " + setPropertyJob);
+               try {
+                       updateJobs.put(setPropertyJob);
+               } catch (InterruptedException e) {
+                       /* the queue is unbounded so it should never block. */
+               }
+       }
+
+       /**
+        * Removes a property from the given own identity.
+        *
+        * @param ownIdentity
+        *            The own identity to remove the property from
+        * @param propertyName
+        *            The name of the property to remove
+        */
+       public void removeProperty(OwnIdentity ownIdentity, String propertyName) {
+               setProperty(ownIdentity, propertyName, null);
+       }
+
+       //
+       // SERVICE METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void serviceRun() {
+               while (!shouldStop()) {
+                       try {
+                               WebOfTrustUpdateJob updateJob = updateJobs.take();
+                               if (shouldStop() || (updateJob == stopJob)) {
+                                       break;
+                               }
+                               logger.log(Level.FINE, "Running Trust Update Job: " + updateJob);
+                               long startTime = System.currentTimeMillis();
+                               updateJob.run();
+                               long endTime = System.currentTimeMillis();
+                               logger.log(Level.FINE, "Trust Update Job finished, took " + (endTime - startTime) + " ms.");
+                       } catch (InterruptedException ie1) {
+                               /* happens, ignore, loop. */
+                       }
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void serviceStop() {
+               try {
+                       updateJobs.put(stopJob);
+               } catch (InterruptedException ie1) {
+                       /* the queue is unbounded so it should never block. */
+               }
+       }
+
+       /**
+        * Base class for WebOfTrust update jobs.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class WebOfTrustUpdateJob {
+
+               /** Object for synchronization. */
+               @SuppressWarnings("hiding")
+               private final Object syncObject = new Object();
+
+               /** Whether the job has finished. */
+               private boolean finished;
+
+               /** Whether the job was successful. */
+               private boolean success;
+
+               //
+               // ACTIONS
+               //
+
+               /**
+                * Performs the actual update operation.
+                * <p>
+                * The implementation of this class does nothing.
+                */
+               public void run() {
+                       /* does nothing. */
+               }
+
+               /**
+                * Waits for completion of this job or stopping of the WebOfTrust
+                * updater.
+                *
+                * @return {@code true} if this job finished successfully, {@code false}
+                *         otherwise
+                *
+                * @see WebOfTrustUpdater#stop()
+                */
+               @SuppressWarnings("synthetic-access")
+               public boolean waitForCompletion() {
+                       synchronized (syncObject) {
+                               while (!finished && !shouldStop()) {
+                                       try {
+                                               syncObject.wait();
+                                       } catch (InterruptedException ie1) {
+                                               /* we’re looping, ignore. */
+                                       }
+                               }
+                               return success;
+                       }
+               }
+
+               //
+               // PROTECTED METHODS
+               //
+
+               /**
+                * Signals that this job has finished.
+                *
+                * @param success
+                *            {@code true} if this job finished successfully,
+                *            {@code false} otherwise
+                */
+               protected void finish(boolean success) {
+                       synchronized (syncObject) {
+                               finished = true;
+                               this.success = success;
+                               syncObject.notifyAll();
+                       }
+               }
+
+       }
+
+       /**
+        * Base class for WebOfTrust trust update jobs.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class WebOfTrustTrustUpdateJob extends WebOfTrustUpdateJob {
+
+               /** The identity giving the trust. */
+               protected final OwnIdentity truster;
+
+               /** The identity receiving the trust. */
+               protected final Identity trustee;
+
+               /**
+                * Creates a new trust update job.
+                *
+                * @param truster
+                *            The identity giving the trust
+                * @param trustee
+                *            The identity receiving the trust
+                */
+               @SuppressWarnings("synthetic-access")
+               public WebOfTrustTrustUpdateJob(OwnIdentity truster, Identity trustee) {
+                       this.truster = truster;
+                       this.trustee = trustee;
+               }
+
+               //
+               // OBJECT METHODS
+               //
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public boolean equals(Object object) {
+                       if ((object == null) || !object.getClass().equals(getClass())) {
+                               return false;
+                       }
+                       WebOfTrustTrustUpdateJob updateJob = (WebOfTrustTrustUpdateJob) object;
+                       return ((truster == null) ? (updateJob.truster == null) : updateJob.truster.equals(truster)) && ((trustee == null) ? (updateJob.trustee == null) : updateJob.trustee.equals(trustee));
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public int hashCode() {
+                       return getClass().hashCode() ^ ((truster == null) ? 0 : truster.hashCode()) ^ ((trustee == null) ? 0 : trustee.hashCode());
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public String toString() {
+                       return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), (truster == null) ? null : truster.getId(), (trustee == null) ? null : trustee.getId());
+               }
+
+       }
+
+       /**
+        * Update job that sets the trust relation between two identities.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class SetTrustJob extends WebOfTrustTrustUpdateJob {
+
+               /** The score of the relation. */
+               private final Integer score;
+
+               /** The comment of the relation. */
+               private final String comment;
+
+               /**
+                * Creates a new set trust job.
+                *
+                * @param truster
+                *            The identity giving the trust
+                * @param trustee
+                *            The identity receiving the trust
+                * @param score
+                *            The score of the trust (from -100 to 100, may be
+                *            {@code null} to remote the trust relation completely)
+                * @param comment
+                *            The comment of the trust relation
+                */
+               public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) {
+                       super(truster, trustee);
+                       this.score = score;
+                       this.comment = comment;
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               @SuppressWarnings("synthetic-access")
+               public void run() {
+                       try {
+                               if (score != null) {
+                                       if (trustee instanceof DefaultIdentity) {
+                                               ((DefaultIdentity) trustee).setTrust(truster, new Trust(score, null, 0));
+                                       }
+                                       webOfTrustConnector.setTrust(truster, trustee, score, comment);
+                               } else {
+                                       if (trustee instanceof DefaultIdentity) {
+                                               ((DefaultIdentity) trustee).setTrust(truster, null);
+                                       }
+                                       webOfTrustConnector.removeTrust(truster, trustee);
+                               }
+                               finish(true);
+                       } catch (WebOfTrustException wote1) {
+                               logger.log(Level.WARNING, "Could not set Trust value for " + truster + " -> " + trustee + " to " + score + " (" + comment + ")!", wote1);
+                               finish(false);
+                       }
+               }
+
+       }
+
+       /**
+        * Base class for context updates of an {@link OwnIdentity}.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class WebOfTrustContextUpdateJob extends WebOfTrustUpdateJob {
+
+               /** The own identity whose contexts to manage. */
+               protected final OwnIdentity ownIdentity;
+
+               /** The context to update. */
+               protected final String context;
+
+               /**
+                * Creates a new context update job.
+                *
+                * @param ownIdentity
+                *            The own identity to update
+                * @param context
+                *            The context to update
+                */
+               @SuppressWarnings("synthetic-access")
+               public WebOfTrustContextUpdateJob(OwnIdentity ownIdentity, String context) {
+                       Validation.begin().isNotNull("OwnIdentity", ownIdentity).isNotNull("Context", context).check();
+                       this.ownIdentity = ownIdentity;
+                       this.context = context;
+               }
+
+               //
+               // OBJECT METHODS
+               //
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public boolean equals(Object object) {
+                       if ((object == null) || !object.getClass().equals(getClass())) {
+                               return false;
+                       }
+                       WebOfTrustContextUpdateJob updateJob = (WebOfTrustContextUpdateJob) object;
+                       return updateJob.ownIdentity.equals(ownIdentity) && updateJob.context.equals(context);
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public int hashCode() {
+                       return getClass().hashCode() ^ ownIdentity.hashCode() ^ context.hashCode();
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public String toString() {
+                       return String.format("%s[ownIdentity=%s,context=%s]", getClass().getSimpleName(), ownIdentity, context);
+               }
+
+       }
+
+       /**
+        * Job that adds a context to an {@link OwnIdentity}.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class AddContextJob extends WebOfTrustContextUpdateJob {
+
+               /**
+                * Creates a new add-context job.
+                *
+                * @param ownIdentity
+                *            The own identity whose contexts to manage
+                * @param context
+                *            The context to add
+                */
+               public AddContextJob(OwnIdentity ownIdentity, String context) {
+                       super(ownIdentity, context);
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               @SuppressWarnings("synthetic-access")
+               public void run() {
+                       try {
+                               webOfTrustConnector.addContext(ownIdentity, context);
+                               ownIdentity.addContext(context);
+                               finish(true);
+                       } catch (PluginException pe1) {
+                               logger.log(Level.WARNING, String.format("Could not add Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1);
+                               finish(false);
+                       }
+               }
+
+       }
+
+       /**
+        * Job that removes a context from an {@link OwnIdentity}.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class RemoveContextJob extends WebOfTrustContextUpdateJob {
+
+               /**
+                * Creates a new remove-context job.
+                *
+                * @param ownIdentity
+                *            The own identity whose contexts to manage
+                * @param context
+                *            The context to remove
+                */
+               public RemoveContextJob(OwnIdentity ownIdentity, String context) {
+                       super(ownIdentity, context);
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               @SuppressWarnings("synthetic-access")
+               public void run() {
+                       try {
+                               webOfTrustConnector.removeContext(ownIdentity, context);
+                               ownIdentity.removeContext(context);
+                               finish(true);
+                       } catch (PluginException pe1) {
+                               logger.log(Level.WARNING, String.format("Could not remove Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1);
+                               finish(false);
+                       }
+               }
+
+       }
+
+       /**
+        * Base class for update jobs that deal with properties.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class WebOfTrustPropertyUpdateJob extends WebOfTrustUpdateJob {
+
+               /** The own identity to update properties on. */
+               protected final OwnIdentity ownIdentity;
+
+               /** The name of the property to update. */
+               protected final String propertyName;
+
+               /**
+                * Creates a new property update job.
+                *
+                * @param ownIdentity
+                *            The own identity to update the property on
+                * @param propertyName
+                *            The name of the property to update
+                */
+               @SuppressWarnings("synthetic-access")
+               public WebOfTrustPropertyUpdateJob(OwnIdentity ownIdentity, String propertyName) {
+                       this.ownIdentity = ownIdentity;
+                       this.propertyName = propertyName;
+               }
+
+               //
+               // OBJECT METHODS
+               //
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public boolean equals(Object object) {
+                       if ((object == null) || !object.getClass().equals(getClass())) {
+                               return false;
+                       }
+                       WebOfTrustPropertyUpdateJob updateJob = (WebOfTrustPropertyUpdateJob) object;
+                       return updateJob.ownIdentity.equals(ownIdentity) && updateJob.propertyName.equals(propertyName);
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public int hashCode() {
+                       return getClass().hashCode() ^ ownIdentity.hashCode() ^ propertyName.hashCode();
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public String toString() {
+                       return String.format("%s[ownIdentity=%s,propertyName=%s]", getClass().getSimpleName(), ownIdentity, propertyName);
+               }
+
+       }
+
+       /**
+        * WebOfTrust update job that sets a property on an {@link OwnIdentity}.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class SetPropertyJob extends WebOfTrustPropertyUpdateJob {
+
+               /** The value of the property to set. */
+               private final String propertyValue;
+
+               /**
+                * Creates a new set-property job.
+                *
+                * @param ownIdentity
+                *            The own identity to set the property on
+                * @param propertyName
+                *            The name of the property to set
+                * @param propertyValue
+                *            The value of the property to set
+                */
+               public SetPropertyJob(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
+                       super(ownIdentity, propertyName);
+                       this.propertyValue = propertyValue;
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               @SuppressWarnings("synthetic-access")
+               public void run() {
+                       try {
+                               if (propertyValue == null) {
+                                       webOfTrustConnector.removeProperty(ownIdentity, propertyName);
+                                       ownIdentity.removeProperty(propertyName);
+                               } else {
+                                       webOfTrustConnector.setProperty(ownIdentity, propertyName, propertyValue);
+                                       ownIdentity.setProperty(propertyName, propertyValue);
+                               }
+                               finish(true);
+                       } catch (PluginException pe1) {
+                               logger.log(Level.WARNING, String.format("Could not set Property “%2$s” to “%3$s” on Own Identity %1$s!", ownIdentity, propertyName, propertyValue), pe1);
+                               finish(false);
+                       }
+               }
+
+       }
+
+}
index ac6e41a..99d65ec 100644 (file)
@@ -791,7 +791,9 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
         */
        public void addAlbum(Album album) {
                Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check();
-               albums.add(album);
+               if (!albums.contains(album)) {
+                       albums.add(album);
+               }
        }
 
        /**
index 490a05c..1944570 100644 (file)
@@ -110,7 +110,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand {
         *            The text to encode
         * @return The encoded text
         */
-       protected String encodeString(String text) {
+       protected static String encodeString(String text) {
                return text.replaceAll("\\\\", "\\\\").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r");
        }
 
@@ -233,7 +233,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand {
         *            such as if the Sone is followed by the local Sone
         * @return The simple field set containing the given Sone
         */
-       protected SimpleFieldSet encodeSone(Sone sone, String prefix, Sone localSone) {
+       protected static SimpleFieldSet encodeSone(Sone sone, String prefix, Sone localSone) {
                SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
 
                soneBuilder.put(prefix + "Name", sone.getName());
@@ -264,7 +264,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand {
         *            {@code null})
         * @return The simple field set containing the given Sones
         */
-       protected SimpleFieldSet encodeSones(Collection<? extends Sone> sones, String prefix) {
+       protected static SimpleFieldSet encodeSones(Collection<? extends Sone> sones, String prefix) {
                SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
 
                int soneIndex = 0;
@@ -352,7 +352,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand {
         *            {@code null})
         * @return The simple field set containing the replies
         */
-       protected SimpleFieldSet encodeReplies(Collection<? extends PostReply> replies, String prefix) {
+       protected static SimpleFieldSet encodeReplies(Collection<? extends PostReply> replies, String prefix) {
                SimpleFieldSetBuilder replyBuilder = new SimpleFieldSetBuilder();
 
                int replyIndex = 0;
@@ -379,7 +379,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand {
         *            {@code null})
         * @return The simple field set containing the likes
         */
-       protected SimpleFieldSet encodeLikes(Collection<? extends Sone> likes, String prefix) {
+       protected static SimpleFieldSet encodeLikes(Collection<? extends Sone> likes, String prefix) {
                SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder();
 
                int likeIndex = 0;
index 128d46a..b3222be 100644 (file)
@@ -197,7 +197,7 @@ public class FcpInterface {
         * @throws PluginNotFoundException
         *             if the plugin can not be found
         */
-       private void sendReply(PluginReplySender pluginReplySender, String identifier, Response response) throws PluginNotFoundException {
+       private static void sendReply(PluginReplySender pluginReplySender, String identifier, Response response) throws PluginNotFoundException {
                SimpleFieldSet replyParameters = response.getReplyParameters();
                if (identifier != null) {
                        replyParameters.putOverwrite("Identifier", identifier);
index 6e13a32..b73fb1d 100644 (file)
@@ -44,7 +44,7 @@ public abstract class AbstractCommand implements Command {
         *             if there is no value for the given key in the simple field
         *             set, or the value can not be converted to a String
         */
-       protected String getString(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+       protected static String getString(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
                try {
                        return simpleFieldSet.getString(key);
                } catch (FSParseException fspe1) {
@@ -64,7 +64,7 @@ public abstract class AbstractCommand implements Command {
         *             if there is no value for the given key in the simple field
         *             set, or the value can not be converted to an int
         */
-       protected int getInt(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+       protected static int getInt(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
                try {
                        return simpleFieldSet.getInt(key);
                } catch (FSParseException fspe1) {
@@ -84,7 +84,7 @@ public abstract class AbstractCommand implements Command {
         *            The default value
         * @return The int value
         */
-       protected int getInt(SimpleFieldSet simpleFieldSet, String key, int defaultValue) {
+       protected static int getInt(SimpleFieldSet simpleFieldSet, String key, int defaultValue) {
                return simpleFieldSet.getInt(key, defaultValue);
        }
 
@@ -100,7 +100,7 @@ public abstract class AbstractCommand implements Command {
         *             if there is no value for the given key in the simple field
         *             set, or the value can not be converted to a boolean
         */
-       protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+       protected static boolean getBoolean(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
                try {
                        return simpleFieldSet.getBoolean(key);
                } catch (FSParseException fspe1) {
@@ -120,7 +120,7 @@ public abstract class AbstractCommand implements Command {
         *            The default value
         * @return The boolean value
         */
-       protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key, boolean defaultValue) {
+       protected static boolean getBoolean(SimpleFieldSet simpleFieldSet, String key, boolean defaultValue) {
                return simpleFieldSet.getBoolean(key, defaultValue);
        }
 
index 6fe4101..2ef33c3 100644 (file)
 
 package net.pterodactylus.sone.freenet.wot;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.pterodactylus.sone.freenet.plugin.PluginException;
-import net.pterodactylus.util.cache.CacheException;
-import net.pterodactylus.util.cache.CacheItem;
-import net.pterodactylus.util.cache.DefaultCacheItem;
-import net.pterodactylus.util.cache.MemoryCache;
-import net.pterodactylus.util.cache.ValueRetriever;
-import net.pterodactylus.util.cache.WritableCache;
-import net.pterodactylus.util.collection.TimedMap;
-import net.pterodactylus.util.logging.Logging;
 
 /**
  * A Web of Trust identity.
@@ -42,12 +31,6 @@ import net.pterodactylus.util.logging.Logging;
  */
 public class DefaultIdentity implements Identity {
 
-       /** The logger. */
-       private static final Logger logger = Logging.getLogger(DefaultIdentity.class);
-
-       /** The web of trust connector. */
-       private final WebOfTrustConnector webOfTrustConnector;
-
        /** The ID of the identity. */
        private final String id;
 
@@ -64,26 +47,11 @@ public class DefaultIdentity implements Identity {
        private final Map<String, String> properties = Collections.synchronizedMap(new HashMap<String, String>());
 
        /** Cached trust. */
-       /* synchronize on itself. */
-       private final WritableCache<OwnIdentity, Trust> trustCache = new MemoryCache<OwnIdentity, Trust>(new ValueRetriever<OwnIdentity, Trust>() {
-
-               @Override
-               @SuppressWarnings("synthetic-access")
-               public CacheItem<Trust> retrieve(OwnIdentity ownIdentity) throws CacheException {
-                       try {
-                               return new DefaultCacheItem<Trust>(webOfTrustConnector.getTrust(ownIdentity, DefaultIdentity.this));
-                       } catch (PluginException pe1) {
-                               throw new CacheException("Could not retrieve trust for OwnIdentity: " + ownIdentity, pe1);
-                       }
-               }
-
-       }, new TimedMap<OwnIdentity, CacheItem<Trust>>(60 * 60 * 1000));
+       private final Map<OwnIdentity, Trust> trustCache = Collections.synchronizedMap(new HashMap<OwnIdentity, Trust>());
 
        /**
         * Creates a new identity.
         *
-        * @param webOfTrustConnector
-        *            The web of trust connector
         * @param id
         *            The ID of the identity
         * @param nickname
@@ -91,8 +59,7 @@ public class DefaultIdentity implements Identity {
         * @param requestUri
         *            The request URI of the identity
         */
-       public DefaultIdentity(WebOfTrustConnector webOfTrustConnector, String id, String nickname, String requestUri) {
-               this.webOfTrustConnector = webOfTrustConnector;
+       public DefaultIdentity(String id, String nickname, String requestUri) {
                this.id = id;
                this.nickname = nickname;
                this.requestUri = requestUri;
@@ -135,47 +102,35 @@ public class DefaultIdentity implements Identity {
        }
 
        /**
-        * Sets the contexts of this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param contexts
-        *            The contexts to set
+        * {@inheritDoc}
         */
-       void setContextsPrivate(Set<String> contexts) {
-               this.contexts.clear();
-               this.contexts.addAll(contexts);
+       @Override
+       public boolean hasContext(String context) {
+               return contexts.contains(context);
        }
 
        /**
         * {@inheritDoc}
         */
        @Override
-       public boolean hasContext(String context) {
-               return contexts.contains(context);
+       public void setContexts(Collection<String> contexts) {
+               this.contexts.clear();
+               this.contexts.addAll(contexts);
        }
 
        /**
-        * Adds the given context to this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param context
-        *            The context to add
+        * {@inheritDoc}
         */
-       void addContextPrivate(String context) {
+       @Override
+       public void addContext(String context) {
                contexts.add(context);
        }
 
        /**
-        * Removes the given context from this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param context
-        *            The context to remove
+        * {@inheritDoc}
         */
-       public void removeContextPrivate(String context) {
+       @Override
+       public void removeContext(String context) {
                contexts.remove(context);
        }
 
@@ -184,64 +139,40 @@ public class DefaultIdentity implements Identity {
         */
        @Override
        public Map<String, String> getProperties() {
-               synchronized (properties) {
-                       return Collections.unmodifiableMap(properties);
-               }
+               return Collections.unmodifiableMap(properties);
        }
 
        /**
-        * Sets all properties of this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param properties
-        *            The new properties of this identity
+        * {@inheritDoc}
         */
-       void setPropertiesPrivate(Map<String, String> properties) {
-               synchronized (this.properties) {
-                       this.properties.clear();
-                       this.properties.putAll(properties);
-               }
+       @Override
+       public void setProperties(Map<String, String> properties) {
+               this.properties.clear();
+               this.properties.putAll(properties);
        }
 
        /**
-        * Sets the property with the given name to the given value.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param name
-        *            The name of the property
-        * @param value
-        *            The value of the property
+        * {@inheritDoc}
         */
-       void setPropertyPrivate(String name, String value) {
-               synchronized (properties) {
-                       properties.put(name, value);
-               }
+       @Override
+       public String getProperty(String name) {
+               return properties.get(name);
        }
 
        /**
         * {@inheritDoc}
         */
        @Override
-       public String getProperty(String name) {
-               synchronized (properties) {
-                       return properties.get(name);
-               }
+       public void setProperty(String name, String value) {
+               properties.put(name, value);
        }
 
        /**
-        * Removes the property with the given name.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param name
-        *            The name of the property to remove
+        * {@inheritDoc}
         */
-       void removePropertyPrivate(String name) {
-               synchronized (properties) {
-                       properties.remove(name);
-               }
+       @Override
+       public void removeProperty(String name) {
+               properties.remove(name);
        }
 
        /**
@@ -249,28 +180,23 @@ public class DefaultIdentity implements Identity {
         */
        @Override
        public Trust getTrust(OwnIdentity ownIdentity) {
-               try {
-                       synchronized (trustCache) {
-                               return trustCache.get(ownIdentity);
-                       }
-               } catch (CacheException ce1) {
-                       logger.log(Level.WARNING, String.format("Could not get trust for OwnIdentity: %s", ownIdentity), ce1);
-                       return null;
-               }
+               return trustCache.get(ownIdentity);
        }
 
        /**
-        * Sets the trust received for this identity by the given own identity.
-        *
-        * @param ownIdentity
-        *            The own identity that gives the trust
-        * @param trust
-        *            The trust received for this identity
+        * {@inheritDoc}
         */
-       void setTrustPrivate(OwnIdentity ownIdentity, Trust trust) {
-               synchronized (trustCache) {
-                       trustCache.put(ownIdentity, trust);
-               }
+       @Override
+       public void setTrust(OwnIdentity ownIdentity, Trust trust) {
+               trustCache.put(ownIdentity, trust);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void removeTrust(OwnIdentity ownIdentity) {
+               trustCache.remove(ownIdentity);
        }
 
        //
index c461c8b..451dd5f 100644 (file)
 
 package net.pterodactylus.sone.freenet.wot;
 
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import net.pterodactylus.util.validation.Validation;
-
 /**
  * An own identity is an identity that the owner of the node has full control
  * over.
@@ -31,17 +25,12 @@ import net.pterodactylus.util.validation.Validation;
  */
 public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity {
 
-       /** The identity manager. */
-       private final WebOfTrustConnector webOfTrustConnector;
-
        /** The insert URI of the identity. */
        private final String insertUri;
 
        /**
         * Creates a new own identity.
         *
-        * @param webOfTrustConnector
-        *            The identity manager
         * @param id
         *            The ID of the identity
         * @param nickname
@@ -51,26 +40,22 @@ public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity {
         * @param insertUri
         *            The insert URI of the identity
         */
-       public DefaultOwnIdentity(WebOfTrustConnector webOfTrustConnector, String id, String nickname, String requestUri, String insertUri) {
-               super(webOfTrustConnector, id, nickname, requestUri);
-               this.webOfTrustConnector = webOfTrustConnector;
+       public DefaultOwnIdentity(String id, String nickname, String requestUri, String insertUri) {
+               super(id, nickname, requestUri);
                this.insertUri = insertUri;
        }
 
        /**
         * Copy constructor for an own identity.
         *
-        * @param webOfTrustConnector
-        *            The web of trust connector
         * @param ownIdentity
         *            The own identity to copy
         */
-       public DefaultOwnIdentity(WebOfTrustConnector webOfTrustConnector, OwnIdentity ownIdentity) {
-               super(webOfTrustConnector, ownIdentity.getId(), ownIdentity.getNickname(), ownIdentity.getRequestUri());
-               this.webOfTrustConnector = webOfTrustConnector;
+       public DefaultOwnIdentity(OwnIdentity ownIdentity) {
+               super(ownIdentity.getId(), ownIdentity.getNickname(), ownIdentity.getRequestUri());
                this.insertUri = ownIdentity.getInsertUri();
-               setContextsPrivate(ownIdentity.getContexts());
-               setPropertiesPrivate(ownIdentity.getProperties());
+               setContexts(ownIdentity.getContexts());
+               setProperties(ownIdentity.getProperties());
        }
 
        //
@@ -85,124 +70,4 @@ public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity {
                return insertUri;
        }
 
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void addContext(String context) throws WebOfTrustException {
-               webOfTrustConnector.addContext(this, context);
-               addContextPrivate(context);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void removeContext(String context) throws WebOfTrustException {
-               webOfTrustConnector.removeContext(this, context);
-               removeContextPrivate(context);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void setContexts(Set<String> contexts) throws WebOfTrustException {
-               for (String context : getContexts()) {
-                       if (!contexts.contains(context)) {
-                               webOfTrustConnector.removeContext(this, context);
-                       }
-               }
-               for (String context : contexts) {
-                       if (!getContexts().contains(context)) {
-                               webOfTrustConnector.addContext(this, context);
-                       }
-               }
-               setContextsPrivate(contexts);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void setProperty(String name, String value) throws WebOfTrustException {
-               webOfTrustConnector.setProperty(this, name, value);
-               setPropertyPrivate(name, value);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void removeProperty(String name) throws WebOfTrustException {
-               webOfTrustConnector.removeProperty(this, name);
-               removePropertyPrivate(name);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void setProperties(Map<String, String> properties) throws WebOfTrustException {
-               for (Entry<String, String> oldProperty : getProperties().entrySet()) {
-                       if (!properties.containsKey(oldProperty.getKey())) {
-                               webOfTrustConnector.removeProperty(this, oldProperty.getKey());
-                       } else {
-                               webOfTrustConnector.setProperty(this, oldProperty.getKey(), properties.get(oldProperty.getKey()));
-                       }
-               }
-               for (Entry<String, String> newProperty : properties.entrySet()) {
-                       if (!getProperties().containsKey(newProperty.getKey())) {
-                               webOfTrustConnector.setProperty(this, newProperty.getKey(), newProperty.getValue());
-                       }
-               }
-               setPropertiesPrivate(properties);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException {
-               Validation.begin().isNotNull("Trust Target", target).isNotNull("Trust Comment", comment).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check();
-               webOfTrustConnector.setTrust(this, target, trustValue, comment);
-               if (target instanceof DefaultIdentity) {
-                       ((DefaultIdentity) target).setTrustPrivate(this, new Trust(trustValue, trustValue, 0));
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void removeTrust(Identity target) throws WebOfTrustException {
-               Validation.begin().isNotNull("Trust Target", target).check();
-               webOfTrustConnector.removeTrust(this, target);
-               if (target instanceof DefaultIdentity) {
-                       ((DefaultIdentity) target).setTrustPrivate(this, new Trust(null, null, null));
-               }
-       }
-
-       //
-       // OBJECT METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public int hashCode() {
-               /* The hash of DefaultIdentity is fine. */
-               return super.hashCode();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public boolean equals(Object object) {
-               /* The ID of the superclass is still enough. */
-               return super.equals(object);
-       }
-
 }
index 0a9fb93..2849da9 100644 (file)
 
 package net.pterodactylus.sone.freenet.wot;
 
+import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 
 /**
  * Interface for web of trust identities, defining all functions that can be
- * performed on an identity. The identity is the main entry point for identity
- * management.
+ * performed on an identity. An identity is only a container for identity data
+ * and will not perform any updating in the WebOfTrust plugin itself.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
@@ -68,6 +69,30 @@ public interface Identity {
        public boolean hasContext(String context);
 
        /**
+        * Adds the given context to this identity.
+        *
+        * @param context
+        *            The context to add
+        */
+       public void addContext(String context);
+
+       /**
+        * Sets all contexts of this identity.
+        *
+        * @param contexts
+        *            All contexts of the identity
+        */
+       public void setContexts(Collection<String> contexts);
+
+       /**
+        * Removes the given context from this identity.
+        *
+        * @param context
+        *            The context to remove
+        */
+       public void removeContext(String context);
+
+       /**
         * Returns all properties of this identity.
         *
         * @return All properties of this identity
@@ -84,6 +109,32 @@ public interface Identity {
        public String getProperty(String name);
 
        /**
+        * Sets the property with the given name to the given value.
+        *
+        * @param name
+        *            The name of the property
+        * @param value
+        *            The value of the property
+        */
+       public void setProperty(String name, String value);
+
+       /**
+        * Sets all properties of this identity.
+        *
+        * @param properties
+        *            The new properties of this identity
+        */
+       public void setProperties(Map<String, String> properties);
+
+       /**
+        * Removes the property with the given name.
+        *
+        * @param name
+        *            The name of the property to remove
+        */
+       public void removeProperty(String name);
+
+       /**
         * Retrieves the trust that this identity receives from the given own
         * identity. If this identity is not in the own identity’s trust tree, a
         * {@link Trust} is returned that has all its elements set to {@code null}.
@@ -96,4 +147,23 @@ public interface Identity {
         */
        public Trust getTrust(OwnIdentity ownIdentity);
 
+       /**
+        * Sets the trust given by an own identity to this identity.
+        *
+        * @param ownIdentity
+        *            The own identity that gave trust to this identity
+        * @param trust
+        *            The trust given by the given own identity
+        */
+       public void setTrust(OwnIdentity ownIdentity, Trust trust);
+
+       /**
+        * Removes trust assignment from the given own identity for this identity.
+        *
+        * @param ownIdentity
+        *            The own identity that removed the trust assignment for this
+        *            identity
+        */
+       public void removeTrust(OwnIdentity ownIdentity);
+
 }
index 32c0d27..bca6ac1 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.freenet.wot;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -26,8 +27,6 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import net.pterodactylus.sone.freenet.plugin.PluginException;
-import net.pterodactylus.util.collection.mapper.Mapper;
-import net.pterodactylus.util.collection.mapper.Mappers;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.service.AbstractService;
 
@@ -60,21 +59,28 @@ public class IdentityManager extends AbstractService {
        private final WebOfTrustConnector webOfTrustConnector;
 
        /** The context to filter for. */
-       private volatile String context;
+       private final String context;
 
        /** The currently known own identities. */
        /* synchronize access on syncObject. */
-       private Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
+       private final Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
+
+       /** The last time all identities were loaded. */
+       private volatile long identitiesLastLoaded;
 
        /**
         * Creates a new identity manager.
         *
         * @param webOfTrustConnector
         *            The Web of Trust connector
+        * @param context
+        *            The context to focus on (may be {@code null} to ignore
+        *            contexts)
         */
-       public IdentityManager(WebOfTrustConnector webOfTrustConnector) {
+       public IdentityManager(WebOfTrustConnector webOfTrustConnector, String context) {
                super("Sone Identity Manager", false);
                this.webOfTrustConnector = webOfTrustConnector;
+               this.context = context;
        }
 
        //
@@ -106,13 +112,13 @@ public class IdentityManager extends AbstractService {
        //
 
        /**
-        * Sets the context to filter own identities and trusted identities for.
+        * Returns the last time all identities were loaded.
         *
-        * @param context
-        *            The context to filter for, or {@code null} to not filter
+        * @return The last time all identities were loaded (in milliseconds since
+        *         Jan 1, 1970 UTC)
         */
-       public void setContext(String context) {
-               this.context = context;
+       public long getIdentitiesLastLoaded() {
+               return identitiesLastLoaded;
        }
 
        /**
@@ -143,7 +149,7 @@ public class IdentityManager extends AbstractService {
                Set<OwnIdentity> allOwnIdentities = getAllOwnIdentities();
                for (OwnIdentity ownIdentity : allOwnIdentities) {
                        if (ownIdentity.getId().equals(id)) {
-                               return new DefaultOwnIdentity(webOfTrustConnector, ownIdentity);
+                               return new DefaultOwnIdentity(ownIdentity);
                        }
                }
                return null;
@@ -155,28 +161,7 @@ public class IdentityManager extends AbstractService {
         * @return All own identities
         */
        public Set<OwnIdentity> getAllOwnIdentities() {
-               try {
-                       Set<OwnIdentity> ownIdentities = webOfTrustConnector.loadAllOwnIdentities();
-                       Map<String, OwnIdentity> newOwnIdentities = new HashMap<String, OwnIdentity>();
-                       for (OwnIdentity ownIdentity : ownIdentities) {
-                               newOwnIdentities.put(ownIdentity.getId(), ownIdentity);
-                       }
-                       checkOwnIdentities(newOwnIdentities);
-                       return Mappers.mappedSet(ownIdentities, new Mapper<OwnIdentity, OwnIdentity>() {
-
-                               /**
-                                * {@inheritDoc}
-                                */
-                               @Override
-                               @SuppressWarnings("synthetic-access")
-                               public OwnIdentity map(OwnIdentity input) {
-                                       return new DefaultOwnIdentity(webOfTrustConnector, input);
-                               }
-                       });
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.WARNING, "Could not load all own identities!", wote1);
-                       return Collections.emptySet();
-               }
+               return new HashSet<OwnIdentity>(currentOwnIdentities.values());
        }
 
        //
@@ -214,6 +199,7 @@ public class IdentityManager extends AbstractService {
                                        }
                                }
                                identitiesLoaded = true;
+                               identitiesLastLoaded = System.currentTimeMillis();
                        } catch (WebOfTrustException wote1) {
                                logger.log(Level.WARNING, "WoT has disappeared!", wote1);
                        }
@@ -310,7 +296,7 @@ public class IdentityManager extends AbstractService {
                        for (OwnIdentity oldOwnIdentity : currentOwnIdentities.values()) {
                                OwnIdentity newOwnIdentity = newOwnIdentities.get(oldOwnIdentity.getId());
                                if ((newOwnIdentity == null) || ((context != null) && oldOwnIdentity.hasContext(context) && !newOwnIdentity.hasContext(context))) {
-                                       identityListenerManager.fireOwnIdentityRemoved(new DefaultOwnIdentity(webOfTrustConnector, oldOwnIdentity));
+                                       identityListenerManager.fireOwnIdentityRemoved(new DefaultOwnIdentity(oldOwnIdentity));
                                }
                        }
 
@@ -318,7 +304,7 @@ public class IdentityManager extends AbstractService {
                        for (OwnIdentity currentOwnIdentity : newOwnIdentities.values()) {
                                OwnIdentity oldOwnIdentity = currentOwnIdentities.get(currentOwnIdentity.getId());
                                if (((oldOwnIdentity == null) && ((context == null) || currentOwnIdentity.hasContext(context))) || ((oldOwnIdentity != null) && (context != null) && (!oldOwnIdentity.hasContext(context) && currentOwnIdentity.hasContext(context)))) {
-                                       identityListenerManager.fireOwnIdentityAdded(new DefaultOwnIdentity(webOfTrustConnector, currentOwnIdentity));
+                                       identityListenerManager.fireOwnIdentityAdded(new DefaultOwnIdentity(currentOwnIdentity));
                                }
                        }
 
index 6c6224f..4272669 100644 (file)
@@ -17,8 +17,6 @@
 
 package net.pterodactylus.sone.freenet.wot;
 
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Defines a local identity, an own identity.
@@ -34,100 +32,4 @@ public interface OwnIdentity extends Identity {
         */
        public String getInsertUri();
 
-       /**
-        * Adds the given context to this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param context
-        *            The context to add
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void addContext(String context) throws WebOfTrustException;
-
-       /**
-        * Sets all contexts of this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param contexts
-        *            All contexts of the identity
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void setContexts(Set<String> contexts) throws WebOfTrustException;
-
-       /**
-        * Removes the given context from this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param context
-        *            The context to remove
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void removeContext(String context) throws WebOfTrustException;
-
-       /**
-        * Sets the property with the given name to the given value.
-        *
-        * @param name
-        *            The name of the property
-        * @param value
-        *            The value of the property
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void setProperty(String name, String value) throws WebOfTrustException;
-
-       /**
-        * Sets all properties of this identity.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param properties
-        *            The new properties of this identity
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void setProperties(Map<String, String> properties) throws WebOfTrustException;
-
-       /**
-        * Removes the property with the given name.
-        * <p>
-        * This method is only called by the {@link IdentityManager}.
-        *
-        * @param name
-        *            The name of the property to remove
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void removeProperty(String name) throws WebOfTrustException;
-
-       /**
-        * Sets the trust for the given target identity.
-        *
-        * @param target
-        *            The target to set the trust for
-        * @param trustValue
-        *            The new trust value (from {@code -100} or {@code 100})
-        * @param comment
-        *            The comment for the trust assignment
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException;
-
-       /**
-        * Removes any trust assignment for the given target identity.
-        *
-        * @param target
-        *            The targe to remove the trust assignment for
-        * @throws WebOfTrustException
-        *             if an error occurs
-        */
-       public void removeTrust(Identity target) throws WebOfTrustException;
-
 }
index a7c6524..53a45b1 100644 (file)
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -28,6 +29,7 @@ import net.pterodactylus.sone.freenet.plugin.ConnectorListener;
 import net.pterodactylus.sone.freenet.plugin.PluginConnector;
 import net.pterodactylus.sone.freenet.plugin.PluginException;
 import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.number.Numbers;
 import freenet.support.SimpleFieldSet;
 import freenet.support.api.Bucket;
 
@@ -36,7 +38,7 @@ import freenet.support.api.Bucket;
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class WebOfTrustConnector implements ConnectorListener {
+public class WebOfTrustConnector {
 
        /** The logger. */
        private static final Logger logger = Logging.getLogger(WebOfTrustConnector.class);
@@ -44,11 +46,8 @@ public class WebOfTrustConnector implements ConnectorListener {
        /** The name of the WoT plugin. */
        private static final String WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust";
 
-       /** A random connection identifier. */
-       private static final String PLUGIN_CONNECTION_IDENTIFIER = "Sone-WoT-Connector-" + Math.abs(Math.random());
-
-       /** The current reply. */
-       private Reply reply;
+       /** Counter for connection identifiers. */
+       private final AtomicLong counter = new AtomicLong();
 
        /** The plugin connector. */
        private final PluginConnector pluginConnector;
@@ -62,7 +61,6 @@ public class WebOfTrustConnector implements ConnectorListener {
         */
        public WebOfTrustConnector(PluginConnector pluginConnector) {
                this.pluginConnector = pluginConnector;
-               pluginConnector.addConnectorListener(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, this);
        }
 
        //
@@ -73,10 +71,7 @@ public class WebOfTrustConnector implements ConnectorListener {
         * Stops the web of trust connector.
         */
        public void stop() {
-               pluginConnector.removeConnectorListener(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, this);
-               synchronized (reply) {
-                       reply.notifyAll();
-               }
+               /* does nothing. */
        }
 
        /**
@@ -99,9 +94,9 @@ public class WebOfTrustConnector implements ConnectorListener {
                        String requestUri = fields.get("RequestURI" + ownIdentityCounter);
                        String insertUri = fields.get("InsertURI" + ownIdentityCounter);
                        String nickname = fields.get("Nickname" + ownIdentityCounter);
-                       DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(this, id, nickname, requestUri, insertUri);
-                       ownIdentity.setContextsPrivate(parseContexts("Contexts" + ownIdentityCounter + ".", fields));
-                       ownIdentity.setPropertiesPrivate(parseProperties("Properties" + ownIdentityCounter + ".", fields));
+                       DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(id, nickname, requestUri, insertUri);
+                       ownIdentity.setContexts(parseContexts("Contexts" + ownIdentityCounter + ".", fields));
+                       ownIdentity.setProperties(parseProperties("Properties" + ownIdentityCounter + ".", fields));
                        ownIdentities.add(ownIdentity);
                }
                return ownIdentities;
@@ -134,7 +129,7 @@ public class WebOfTrustConnector implements ConnectorListener {
         *             if an error occured talking to the Web of Trust plugin
         */
        public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity, String context) throws PluginException {
-               Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).get());
+               Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).put("WantTrustValues", "true").get());
                SimpleFieldSet fields = reply.getFields();
                Set<Identity> identities = new HashSet<Identity>();
                int identityCounter = -1;
@@ -145,9 +140,13 @@ public class WebOfTrustConnector implements ConnectorListener {
                        }
                        String nickname = fields.get("Nickname" + identityCounter);
                        String requestUri = fields.get("RequestURI" + identityCounter);
-                       DefaultIdentity identity = new DefaultIdentity(this, id, nickname, requestUri);
-                       identity.setContextsPrivate(parseContexts("Contexts" + identityCounter + ".", fields));
-                       identity.setPropertiesPrivate(parseProperties("Properties" + identityCounter + ".", fields));
+                       DefaultIdentity identity = new DefaultIdentity(id, nickname, requestUri);
+                       identity.setContexts(parseContexts("Contexts" + identityCounter + ".", fields));
+                       identity.setProperties(parseProperties("Properties" + identityCounter + ".", fields));
+                       Integer trust = Numbers.safeParseInteger(fields.get("Trust" + identityCounter), null);
+                       int score = Numbers.safeParseInteger(fields.get("Score" + identityCounter));
+                       int rank = Numbers.safeParseInteger(fields.get("Rank" + identityCounter));
+                       identity.setTrust(ownIdentity, new Trust(trust, score, rank));
                        identities.add(identity);
                }
                return identities;
@@ -318,7 +317,7 @@ public class WebOfTrustConnector implements ConnectorListener {
         *            The fields to parse the contexts from
         * @return The parsed contexts
         */
-       private Set<String> parseContexts(String prefix, SimpleFieldSet fields) {
+       private static Set<String> parseContexts(String prefix, SimpleFieldSet fields) {
                Set<String> contexts = new HashSet<String>();
                int contextCounter = -1;
                while (true) {
@@ -340,7 +339,7 @@ public class WebOfTrustConnector implements ConnectorListener {
         *            The fields to parse the properties from
         * @return The parsed properties
         */
-       private Map<String, String> parseProperties(String prefix, SimpleFieldSet fields) {
+       private static Map<String, String> parseProperties(String prefix, SimpleFieldSet fields) {
                Map<String, String> properties = new HashMap<String, String>();
                int propertiesCounter = -1;
                while (true) {
@@ -380,15 +379,37 @@ public class WebOfTrustConnector implements ConnectorListener {
         * @throws PluginException
         *             if the request could not be sent
         */
-       private synchronized Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException {
-               reply = new Reply();
+       private Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException {
+               final String identifier = "FCP-Command-" + System.currentTimeMillis() + "-" + counter.getAndIncrement();
+               final Reply reply = new Reply();
                logger.log(Level.FINE, String.format("Sending FCP Request: %s", fields.get("Message")));
+               ConnectorListener connectorListener = new ConnectorListener() {
+
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data) {
+                               String messageName = fields.get("Message");
+                               logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", messageName));
+                               synchronized (reply) {
+                                       reply.setFields(fields);
+                                       reply.setData(data);
+                                       reply.notify();
+                               }
+                       }
+               };
+               pluginConnector.addConnectorListener(WOT_PLUGIN_NAME, identifier, connectorListener);
                synchronized (reply) {
-                       pluginConnector.sendRequest(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, fields, data);
                        try {
-                               reply.wait();
-                       } catch (InterruptedException ie1) {
-                               logger.log(Level.WARNING, String.format("Got interrupted while waiting for reply on %s.", fields.get("Message")), ie1);
+                               pluginConnector.sendRequest(WOT_PLUGIN_NAME, identifier, fields, data);
+                               while (reply.getFields() == null) {
+                                       try {
+                                               reply.wait();
+                                       } catch (InterruptedException ie1) {
+                                               logger.log(Level.WARNING, String.format("Got interrupted while waiting for reply on %s.", fields.get("Message")), ie1);
+                                       }
+                               }
+                       } finally {
+                               pluginConnector.removeConnectorListener(WOT_PLUGIN_NAME, identifier, connectorListener);
                        }
                }
                logger.log(Level.FINEST, String.format("Received FCP Response for %s: %s", fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null));
@@ -398,24 +419,6 @@ public class WebOfTrustConnector implements ConnectorListener {
                return reply;
        }
 
-       //
-       // INTERFACE ConnectorListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data) {
-               String messageName = fields.get("Message");
-               logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", messageName));
-               synchronized (reply) {
-                       reply.setFields(fields);
-                       reply.setData(data);
-                       reply.notify();
-               }
-       }
-
        /**
         * Container for the data of the reply from a plugin.
         *
index cca2c7b..0f8ba97 100644 (file)
@@ -24,6 +24,7 @@ import java.util.logging.Logger;
 
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.core.FreenetInterface;
+import net.pterodactylus.sone.core.WebOfTrustUpdater;
 import net.pterodactylus.sone.fcp.FcpInterface;
 import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend;
 import net.pterodactylus.sone.freenet.plugin.PluginConnector;
@@ -83,7 +84,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
        }
 
        /** The version. */
-       public static final Version VERSION = new Version(0, 8, 2);
+       public static final Version VERSION = new Version(0, 8, 3);
 
        /** The logger. */
        private static final Logger logger = Logging.getLogger(SonePlugin.class);
@@ -185,11 +186,14 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
                        /* create web of trust connector. */
                        PluginConnector pluginConnector = new PluginConnector(pluginRespirator);
                        webOfTrustConnector = new WebOfTrustConnector(pluginConnector);
-                       identityManager = new IdentityManager(webOfTrustConnector);
-                       identityManager.setContext("Sone");
+                       identityManager = new IdentityManager(webOfTrustConnector, "Sone");
+
+                       /* create trust updater. */
+                       WebOfTrustUpdater trustUpdater = new WebOfTrustUpdater(webOfTrustConnector);
+                       trustUpdater.init();
 
                        /* create core. */
-                       core = new Core(oldConfiguration, freenetInterface, identityManager);
+                       core = new Core(oldConfiguration, freenetInterface, identityManager, trustUpdater);
 
                        /* create the web interface. */
                        webInterface = new WebInterface(this);
index 51bb1c0..6efee27 100644 (file)
@@ -233,7 +233,14 @@ public class ListNotificationFilters {
                                        return false;
                                }
                        } else {
-                               return false;
+                               /*
+                                * a null trust means that the trust updater has not yet
+                                * received a trust value for this relation. if we return false,
+                                * the post feed will stay empty until the trust updater has
+                                * received trust values. to prevent this we simply assume that
+                                * posts are visible if there is no trust.
+                                */
+                               return true;
                        }
                        if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.equals(post.getRecipient())) {
                                return false;
index 7a5d6d3..e22e00b 100644 (file)
@@ -68,7 +68,7 @@ public class AlbumAccessor extends ReflectionAccessor {
         *            The name of the link
         * @return The created map containing the mappings
         */
-       private Map<String, String> createLink(String target, String name) {
+       private static Map<String, String> createLink(String target, String name) {
                Map<String, String> link = new HashMap<String, String>();
                link.put("target", target);
                link.put("name", name);
index 4602188..4fbe74c 100644 (file)
@@ -93,7 +93,7 @@ public class IdentityAccessor extends ReflectionAccessor {
         *            append to the nickname
         * @return The nickname with optional ID appendage
         */
-       private String getAbbreviatedNickname(Identity identity, int length) {
+       private static String getAbbreviatedNickname(Identity identity, int length) {
                return identity.getNickname() + ((length > 0) ? "@" + identity.getId().substring(0, length) : "");
        }
 
index 8302369..75eff8b 100644 (file)
@@ -60,10 +60,10 @@ public class ParserFilter implements Filter {
        private final TemplateContextFactory templateContextFactory;
 
        /** The template for {@link PlainTextPart}s. */
-       private final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>"));
+       private static final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>"));
 
        /** The template for {@link FreenetLinkPart}s. */
-       private final Template linkTemplate = TemplateParser.parse(new StringReader("<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>"));
+       private static final Template linkTemplate = TemplateParser.parse(new StringReader("<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>"));
 
        /**
         * Creates a new filter that runs its input through a {@link SoneTextParser}
@@ -241,7 +241,28 @@ public class ParserFilter implements Filter {
         *            The part to render
         */
        private void render(Writer writer, PostPart postPart) {
-               renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), getExcerpt(postPart.getPost().getText(), 20), SoneAccessor.getNiceName(postPart.getPost().getSone()), "in-sone");
+               SoneTextParser parser = new SoneTextParser(core, core);
+               SoneTextParserContext parserContext = new SoneTextParserContext(null, postPart.getPost().getSone());
+               try {
+                       Iterable<Part> parts = parser.parse(parserContext, new StringReader(postPart.getPost().getText()));
+                       StringBuilder excerpt = new StringBuilder();
+                       for (Part part : parts) {
+                               excerpt.append(part.getText());
+                               if (excerpt.length() > 20) {
+                                       int lastSpace = excerpt.lastIndexOf(" ", 20);
+                                       if (lastSpace > -1) {
+                                               excerpt.setLength(lastSpace);
+                                       } else {
+                                               excerpt.setLength(20);
+                                       }
+                                       excerpt.append("…");
+                                       break;
+                               }
+                       }
+                       renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), excerpt.toString(), SoneAccessor.getNiceName(postPart.getPost().getSone()), "in-sone");
+               } catch (IOException ioe1) {
+                       /* StringReader shouldn’t throw. */
+               }
        }
 
        /**
@@ -267,26 +288,4 @@ public class ParserFilter implements Filter {
                linkTemplate.render(templateContext, writer);
        }
 
-       //
-       // STATIC METHODS
-       //
-
-       /**
-        * Returns up to {@code length} characters from the given text, appending
-        * “…” if the text is longer.
-        *
-        * @param text
-        *            The text to get an excerpt from
-        * @param length
-        *            The maximum length of the excerpt (without the ellipsis)
-        * @return The excerpt of the text
-        */
-       private static String getExcerpt(String text, int length) {
-               String filteredText = text.replaceAll("(\r\n)+", "\r\n").replaceAll("\n+", "\n").replace("\r\n", " ").replace('\n', ' ');
-               if (filteredText.length() > length) {
-                       return filteredText.substring(0, length) + "…";
-               }
-               return filteredText;
-       }
-
 }
index 1f29b65..f004967 100644 (file)
@@ -21,6 +21,7 @@ import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.data.Profile;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
 import net.pterodactylus.sone.freenet.wot.Trust;
 import net.pterodactylus.util.template.Accessor;
 import net.pterodactylus.util.template.ReflectionAccessor;
@@ -83,7 +84,7 @@ public class ProfileAccessor extends ReflectionAccessor {
                        if (showCustomAvatars == ShowCustomAvatars.FOLLOWED) {
                                return currentSone.hasFriend(remoteSone.getId()) ? avatarId : null;
                        }
-                       Trust trust = core.getTrust(currentSone, remoteSone);
+                       Trust trust = remoteSone.getIdentity().getTrust((OwnIdentity) currentSone.getIdentity());
                        if (trust == null) {
                                return null;
                        }
index eac8a93..eca8654 100644 (file)
@@ -24,6 +24,7 @@ import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.data.Profile;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.Sone.SoneStatus;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
 import net.pterodactylus.sone.freenet.wot.Trust;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.sone.web.ajax.GetTimesAjaxPage;
@@ -106,7 +107,7 @@ public class SoneAccessor extends ReflectionAccessor {
                        if (currentSone == null) {
                                return null;
                        }
-                       Trust trust = core.getTrust(currentSone, sone);
+                       Trust trust = sone.getIdentity().getTrust((OwnIdentity) currentSone.getIdentity());
                        logger.log(Level.FINEST, String.format("Trust for %s by %s: %s", sone, currentSone, trust));
                        if (trust == null) {
                                return new Trust(null, null, null);
index 1b47080..202b9db 100644 (file)
@@ -77,21 +77,26 @@ public class LinkPart implements Part {
        }
 
        /**
-        * Returns the text of this part.
+        * Returns the title of this part.
         *
-        * @return The text of this part
+        * @return The title of this part
         */
-       public String getText() {
-               return text;
+       public String getTitle() {
+               return title;
        }
 
+       //
+       // PART METHODS
+       //
+
        /**
-        * Returns the title of this part.
+        * Returns the text of this part.
         *
-        * @return The title of this part
+        * @return The text of this part
         */
-       public String getTitle() {
-               return title;
+       @Override
+       public String getText() {
+               return text;
        }
 
 }
index 76e80ef..79c59dc 100644 (file)
@@ -26,6 +26,12 @@ package net.pterodactylus.sone.text;
  */
 public interface Part {
 
-       /* no methods. */
+       /**
+        * Returns the text contained in this part. This should return plain text
+        * without any format information.
+        *
+        * @return The plain text of this part
+        */
+       public String getText();
 
 }
index e456cd9..a8a7e85 100644 (file)
@@ -81,6 +81,22 @@ public class PartContainer implements Part, Iterable<Part> {
        }
 
        //
+       // PART METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getText() {
+               StringBuilder partText = new StringBuilder();
+               for (Part part : parts) {
+                       partText.append(part.getText());
+               }
+               return partText.toString();
+       }
+
+       //
        // ITERABLE METHODS
        //
 
index 09c1fba..2c29ee2 100644 (file)
@@ -38,7 +38,7 @@ public class PlainTextPart implements Part {
        }
 
        //
-       // ACCESSORS
+       // PART METHODS
        //
 
        /**
@@ -46,6 +46,7 @@ public class PlainTextPart implements Part {
         *
         * @return The text of this part
         */
+       @Override
        public String getText() {
                return text;
        }
index c416c57..6241b7a 100644 (file)
@@ -52,4 +52,16 @@ public class PostPart implements Part {
                return post;
        }
 
+       //
+       // PART METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getText() {
+               return post.getText();
+       }
+
 }
index 475c091..37f098b 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.text;
 
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.template.SoneAccessor;
 
 /**
  * {@link Part} implementation that stores a reference to a {@link Sone}.
@@ -52,4 +53,16 @@ public class SonePart implements Part {
                return sone;
        }
 
+       //
+       // PART METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getText() {
+               return SoneAccessor.getNiceName(sone);
+       }
+
 }
index dd0da28..baa3dc9 100644 (file)
@@ -30,6 +30,7 @@ import net.pterodactylus.sone.core.PostProvider;
 import net.pterodactylus.sone.core.SoneProvider;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.logging.Logging;
 import freenet.keys.FreenetURI;
 
@@ -109,187 +110,193 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
        public Iterable<Part> parse(SoneTextParserContext context, Reader source) throws IOException {
                PartContainer parts = new PartContainer();
                BufferedReader bufferedReader = (source instanceof BufferedReader) ? (BufferedReader) source : new BufferedReader(source);
-               String line;
-               boolean lastLineEmpty = true;
-               int emptyLines = 0;
-               while ((line = bufferedReader.readLine()) != null) {
-                       if (line.trim().length() == 0) {
-                               if (lastLineEmpty) {
+               try {
+                       String line;
+                       boolean lastLineEmpty = true;
+                       int emptyLines = 0;
+                       while ((line = bufferedReader.readLine()) != null) {
+                               if (line.trim().length() == 0) {
+                                       if (lastLineEmpty) {
+                                               continue;
+                                       }
+                                       parts.add(new PlainTextPart("\n"));
+                                       ++emptyLines;
+                                       lastLineEmpty = emptyLines == 2;
                                        continue;
                                }
-                               parts.add(new PlainTextPart("\n"));
-                               ++emptyLines;
-                               lastLineEmpty = emptyLines == 2;
-                               continue;
-                       }
-                       emptyLines = 0;
-                       /*
-                        * lineComplete tracks whether the block you are parsing is the
-                        * first block of the line. this is important because sometimes you
-                        * have to add an additional line break.
-                        */
-                       boolean lineComplete = true;
-                       while (line.length() > 0) {
-                               int nextKsk = line.indexOf("KSK@");
-                               int nextChk = line.indexOf("CHK@");
-                               int nextSsk = line.indexOf("SSK@");
-                               int nextUsk = line.indexOf("USK@");
-                               int nextHttp = line.indexOf("http://");
-                               int nextHttps = line.indexOf("https://");
-                               int nextSone = line.indexOf("sone://");
-                               int nextPost = line.indexOf("post://");
-                               if ((nextKsk == -1) && (nextChk == -1) && (nextSsk == -1) && (nextUsk == -1) && (nextHttp == -1) && (nextHttps == -1) && (nextSone == -1) && (nextPost == -1)) {
-                                       if (lineComplete && !lastLineEmpty) {
-                                               parts.add(new PlainTextPart("\n" + line));
-                                       } else {
-                                               parts.add(new PlainTextPart(line));
+                               emptyLines = 0;
+                               /*
+                                * lineComplete tracks whether the block you are parsing is the
+                                * first block of the line. this is important because sometimes
+                                * you have to add an additional line break.
+                                */
+                               boolean lineComplete = true;
+                               while (line.length() > 0) {
+                                       int nextKsk = line.indexOf("KSK@");
+                                       int nextChk = line.indexOf("CHK@");
+                                       int nextSsk = line.indexOf("SSK@");
+                                       int nextUsk = line.indexOf("USK@");
+                                       int nextHttp = line.indexOf("http://");
+                                       int nextHttps = line.indexOf("https://");
+                                       int nextSone = line.indexOf("sone://");
+                                       int nextPost = line.indexOf("post://");
+                                       if ((nextKsk == -1) && (nextChk == -1) && (nextSsk == -1) && (nextUsk == -1) && (nextHttp == -1) && (nextHttps == -1) && (nextSone == -1) && (nextPost == -1)) {
+                                               if (lineComplete && !lastLineEmpty) {
+                                                       parts.add(new PlainTextPart("\n" + line));
+                                               } else {
+                                                       parts.add(new PlainTextPart(line));
+                                               }
+                                               break;
+                                       }
+                                       int next = Integer.MAX_VALUE;
+                                       LinkType linkType = null;
+                                       if ((nextKsk > -1) && (nextKsk < next)) {
+                                               next = nextKsk;
+                                               linkType = LinkType.KSK;
+                                       }
+                                       if ((nextChk > -1) && (nextChk < next)) {
+                                               next = nextChk;
+                                               linkType = LinkType.CHK;
+                                       }
+                                       if ((nextSsk > -1) && (nextSsk < next)) {
+                                               next = nextSsk;
+                                               linkType = LinkType.SSK;
+                                       }
+                                       if ((nextUsk > -1) && (nextUsk < next)) {
+                                               next = nextUsk;
+                                               linkType = LinkType.USK;
+                                       }
+                                       if ((nextHttp > -1) && (nextHttp < next)) {
+                                               next = nextHttp;
+                                               linkType = LinkType.HTTP;
+                                       }
+                                       if ((nextHttps > -1) && (nextHttps < next)) {
+                                               next = nextHttps;
+                                               linkType = LinkType.HTTPS;
+                                       }
+                                       if ((nextSone > -1) && (nextSone < next)) {
+                                               next = nextSone;
+                                               linkType = LinkType.SONE;
+                                       }
+                                       if ((nextPost > -1) && (nextPost < next)) {
+                                               next = nextPost;
+                                               linkType = LinkType.POST;
                                        }
-                                       break;
-                               }
-                               int next = Integer.MAX_VALUE;
-                               LinkType linkType = null;
-                               if ((nextKsk > -1) && (nextKsk < next)) {
-                                       next = nextKsk;
-                                       linkType = LinkType.KSK;
-                               }
-                               if ((nextChk > -1) && (nextChk < next)) {
-                                       next = nextChk;
-                                       linkType = LinkType.CHK;
-                               }
-                               if ((nextSsk > -1) && (nextSsk < next)) {
-                                       next = nextSsk;
-                                       linkType = LinkType.SSK;
-                               }
-                               if ((nextUsk > -1) && (nextUsk < next)) {
-                                       next = nextUsk;
-                                       linkType = LinkType.USK;
-                               }
-                               if ((nextHttp > -1) && (nextHttp < next)) {
-                                       next = nextHttp;
-                                       linkType = LinkType.HTTP;
-                               }
-                               if ((nextHttps > -1) && (nextHttps < next)) {
-                                       next = nextHttps;
-                                       linkType = LinkType.HTTPS;
-                               }
-                               if ((nextSone > -1) && (nextSone < next)) {
-                                       next = nextSone;
-                                       linkType = LinkType.SONE;
-                               }
-                               if ((nextPost > -1) && (nextPost < next)) {
-                                       next = nextPost;
-                                       linkType = LinkType.POST;
-                               }
 
-                               /* cut off “freenet:” from before keys. */
-                               if (((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (next >= 8) && (line.substring(next - 8, next).equals("freenet:"))) {
-                                       next -= 8;
-                                       line = line.substring(0, next) + line.substring(next + 8);
-                               }
+                                       /* cut off “freenet:” from before keys. */
+                                       if (((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (next >= 8) && (line.substring(next - 8, next).equals("freenet:"))) {
+                                               next -= 8;
+                                               line = line.substring(0, next) + line.substring(next + 8);
+                                       }
 
-                               /* if there is text before the next item, write it out. */
-                               if (lineComplete && !lastLineEmpty) {
-                                       parts.add(new PlainTextPart("\n"));
-                               }
-                               if (next > 0) {
-                                       parts.add(new PlainTextPart(line.substring(0, next)));
-                                       line = line.substring(next);
-                                       next = 0;
-                               }
-                               lineComplete = false;
+                                       /* if there is text before the next item, write it out. */
+                                       if (lineComplete && !lastLineEmpty) {
+                                               parts.add(new PlainTextPart("\n"));
+                                       }
+                                       if (next > 0) {
+                                               parts.add(new PlainTextPart(line.substring(0, next)));
+                                               line = line.substring(next);
+                                               next = 0;
+                                       }
+                                       lineComplete = false;
 
-                               if (linkType == LinkType.SONE) {
-                                       if (line.length() >= (7 + 43)) {
-                                               String soneId = line.substring(7, 50);
-                                               Sone sone = soneProvider.getSone(soneId, false);
-                                               if (sone == null) {
-                                                       /*
-                                                        * don’t use create=true above, we don’t want the
-                                                        * empty shell.
-                                                        */
-                                                       sone = new Sone(soneId);
+                                       if (linkType == LinkType.SONE) {
+                                               if (line.length() >= (7 + 43)) {
+                                                       String soneId = line.substring(7, 50);
+                                                       Sone sone = soneProvider.getSone(soneId, false);
+                                                       if (sone == null) {
+                                                               /*
+                                                                * don’t use create=true above, we don’t want
+                                                                * the empty shell.
+                                                                */
+                                                               sone = new Sone(soneId);
+                                                       }
+                                                       parts.add(new SonePart(sone));
+                                                       line = line.substring(50);
+                                               } else {
+                                                       parts.add(new PlainTextPart(line));
+                                                       line = "";
                                                }
-                                               parts.add(new SonePart(sone));
-                                               line = line.substring(50);
-                                       } else {
-                                               parts.add(new PlainTextPart(line));
-                                               line = "";
+                                               continue;
                                        }
-                                       continue;
-                               }
-                               if (linkType == LinkType.POST) {
-                                       if (line.length() >= (7 + 36)) {
-                                               String postId = line.substring(7, 43);
-                                               Post post = postProvider.getPost(postId, false);
-                                               if ((post != null) && (post.getSone() != null)) {
-                                                       parts.add(new PostPart(post));
+                                       if (linkType == LinkType.POST) {
+                                               if (line.length() >= (7 + 36)) {
+                                                       String postId = line.substring(7, 43);
+                                                       Post post = postProvider.getPost(postId, false);
+                                                       if ((post != null) && (post.getSone() != null)) {
+                                                               parts.add(new PostPart(post));
+                                                       } else {
+                                                               parts.add(new PlainTextPart(line.substring(0, 43)));
+                                                       }
+                                                       line = line.substring(43);
                                                } else {
-                                                       parts.add(new PlainTextPart(line.substring(0, 43)));
+                                                       parts.add(new PlainTextPart(line));
+                                                       line = "";
                                                }
-                                               line = line.substring(43);
-                                       } else {
-                                               parts.add(new PlainTextPart(line));
-                                               line = "";
+                                               continue;
                                        }
-                                       continue;
-                               }
-                               Matcher matcher = whitespacePattern.matcher(line);
-                               int nextSpace = matcher.find(0) ? matcher.start() : line.length();
-                               String link = line.substring(0, nextSpace);
-                               String name = link;
-                               logger.log(Level.FINER, String.format("Found link: %s", link));
-                               logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk));
+                                       Matcher matcher = whitespacePattern.matcher(line);
+                                       int nextSpace = matcher.find(0) ? matcher.start() : line.length();
+                                       String link = line.substring(0, nextSpace);
+                                       String name = link;
+                                       logger.log(Level.FINER, String.format("Found link: %s", link));
+                                       logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk));
 
-                               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('?'));
-                                       }
-                                       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 ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
+                                               FreenetURI uri;
+                                               if (name.indexOf('?') > -1) {
+                                                       name = name.substring(0, name.indexOf('?'));
                                                }
-                                               if (name == null) {
-                                                       name = link.substring(0, Math.min(9, link.length()));
+                                               if (name.endsWith("/")) {
+                                                       name = name.substring(0, name.length() - 1);
                                                }
-                                               boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
-                                               parts.add(new FreenetLinkPart(link, name, fromPostingSone));
-                                       } catch (MalformedURLException mue1) {
-                                               /* not a valid link, insert as plain text. */
-                                               parts.add(new PlainTextPart(link));
-                                       } catch (NullPointerException npe1) {
-                                               /* FreenetURI sometimes throws these, too. */
-                                               parts.add(new PlainTextPart(link));
-                                       } catch (ArrayIndexOutOfBoundsException aioobe1) {
-                                               /* oh, and these, too. */
-                                               parts.add(new PlainTextPart(link));
-                                       }
-                               } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
-                                       name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
-                                       int firstSlash = name.indexOf('/');
-                                       int lastSlash = name.lastIndexOf('/');
-                                       if ((lastSlash - firstSlash) > 3) {
-                                               name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash);
-                                       }
-                                       if (name.endsWith("/")) {
-                                               name = name.substring(0, name.length() - 1);
-                                       }
-                                       if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
-                                               name = name.substring(4);
-                                       }
-                                       if (name.indexOf('?') > -1) {
-                                               name = name.substring(0, name.indexOf('?'));
+                                               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()));
+                                                       }
+                                                       boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
+                                                       parts.add(new FreenetLinkPart(link, name, fromPostingSone));
+                                               } catch (MalformedURLException mue1) {
+                                                       /* not a valid link, insert as plain text. */
+                                                       parts.add(new PlainTextPart(link));
+                                               } catch (NullPointerException npe1) {
+                                                       /* FreenetURI sometimes throws these, too. */
+                                                       parts.add(new PlainTextPart(link));
+                                               } catch (ArrayIndexOutOfBoundsException aioobe1) {
+                                                       /* oh, and these, too. */
+                                                       parts.add(new PlainTextPart(link));
+                                               }
+                                       } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
+                                               name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
+                                               int firstSlash = name.indexOf('/');
+                                               int lastSlash = name.lastIndexOf('/');
+                                               if ((lastSlash - firstSlash) > 3) {
+                                                       name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash);
+                                               }
+                                               if (name.endsWith("/")) {
+                                                       name = name.substring(0, name.length() - 1);
+                                               }
+                                               if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
+                                                       name = name.substring(4);
+                                               }
+                                               if (name.indexOf('?') > -1) {
+                                                       name = name.substring(0, name.indexOf('?'));
+                                               }
+                                               parts.add(new LinkPart(link, name));
                                        }
-                                       parts.add(new LinkPart(link, name));
+                                       line = line.substring(nextSpace);
                                }
-                               line = line.substring(nextSpace);
+                               lastLineEmpty = false;
+                       }
+               } finally {
+                       if (bufferedReader != source) {
+                               Closer.close(bufferedReader);
                        }
-                       lastLineEmpty = false;
                }
                for (int partIndex = parts.size() - 1; partIndex >= 0; --partIndex) {
                        Part part = parts.getPart(partIndex);
index 30c90bc..a1b759b 100644 (file)
@@ -158,7 +158,7 @@ public class EditProfilePage extends SoneTemplatePage {
         * @return The parsed ID, or {@code null} if there was no part matching the
         *         given string
         */
-       private String getFieldId(FreenetRequest request, String partNameStart) {
+       private static String getFieldId(FreenetRequest request, String partNameStart) {
                for (String partName : request.getHttpRequest().getParts()) {
                        if (partName.startsWith(partNameStart)) {
                                return partName.substring(partNameStart.length());
index a6d3daf..da48ec5 100644 (file)
@@ -65,6 +65,7 @@ public class ImageBrowserPage extends SoneTemplatePage {
                        Album album = webInterface.getCore().getAlbum(albumId, false);
                        templateContext.set("albumRequested", true);
                        templateContext.set("album", album);
+                       templateContext.set("page", request.getHttpRequest().getParam("page"));
                        return;
                }
                String imageId = request.getHttpRequest().getParam("image", null);
index ab962d8..c770da3 100644 (file)
@@ -132,10 +132,6 @@ public class OptionsPage extends SoneTemplatePage {
                        Integer fcpFullAccessRequiredInteger = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("fcp-full-access-required", 1), preferences.getFcpFullAccessRequired().ordinal());
                        FullAccessRequired fcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequiredInteger];
                        preferences.setFcpFullAccessRequired(fcpFullAccessRequired);
-                       boolean clearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("clear-on-next-restart", 5));
-                       preferences.setClearOnNextRestart(clearOnNextRestart);
-                       boolean reallyClearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("really-clear-on-next-restart", 5));
-                       preferences.setReallyClearOnNextRestart(reallyClearOnNextRestart);
                        webInterface.getCore().touchConfiguration();
                        if (fieldErrors.isEmpty()) {
                                throw new RedirectException(getPath());
@@ -161,8 +157,6 @@ public class OptionsPage extends SoneTemplatePage {
                templateContext.set("trust-comment", preferences.getTrustComment());
                templateContext.set("fcp-interface-active", preferences.isFcpInterfaceActive());
                templateContext.set("fcp-full-access-required", preferences.getFcpFullAccessRequired().ordinal());
-               templateContext.set("clear-on-next-restart", preferences.isClearOnNextRestart());
-               templateContext.set("really-clear-on-next-restart", preferences.isReallyClearOnNextRestart());
        }
 
 }
index a4da9f4..0f12abf 100644 (file)
@@ -111,6 +111,26 @@ public class SearchPage extends SoneTemplatePage {
                        throw new RedirectException("index.html");
                }
 
+               /* check for a couple of shortcuts. */
+               if (phrases.size() == 1) {
+                       String phrase = phrases.get(0).getPhrase();
+
+                       /* is it a Sone ID? */
+                       redirectIfNotNull(getSoneId(phrase), "viewSone.html?sone=");
+
+                       /* is it a post ID? */
+                       redirectIfNotNull(getPostId(phrase), "viewPost.html?post=");
+
+                       /* is it a reply ID? show the post. */
+                       redirectIfNotNull(getReplyPostId(phrase), "viewPost.html?post=");
+
+                       /* is it an album ID? */
+                       redirectIfNotNull(getAlbumId(phrase), "imageBrowser.html?album=");
+
+                       /* is it an image ID? */
+                       redirectIfNotNull(getImageId(phrase), "imageBrowser.html?image=");
+               }
+
                Set<Sone> sones = webInterface.getCore().getSones();
                Set<Hit<Sone>> soneHits = getHits(sones, phrases, SoneStringGenerator.COMPLETE_GENERATOR);
 
@@ -168,7 +188,7 @@ public class SearchPage extends SoneTemplatePage {
         *            The string generator for the objects
         * @return The hits for the given phrases
         */
-       private <T> Set<Hit<T>> getHits(Collection<T> objects, List<Phrase> phrases, StringGenerator<T> stringGenerator) {
+       private static <T> Set<Hit<T>> getHits(Collection<T> objects, List<Phrase> phrases, StringGenerator<T> stringGenerator) {
                Set<Hit<T>> hits = new HashSet<Hit<T>>();
                for (T object : objects) {
                        String objectString = stringGenerator.generateString(object);
@@ -189,7 +209,7 @@ public class SearchPage extends SoneTemplatePage {
         *            The query to parse
         * @return The parsed phrases
         */
-       private List<Phrase> parseSearchPhrases(String query) {
+       private static List<Phrase> parseSearchPhrases(String query) {
                List<String> parsedPhrases = null;
                try {
                        parsedPhrases = StringEscaper.parseLine(query);
@@ -229,7 +249,7 @@ public class SearchPage extends SoneTemplatePage {
         *            The expression to search
         * @return The score of the expression
         */
-       private double calculateScore(List<Phrase> phrases, String expression) {
+       private static double calculateScore(List<Phrase> phrases, String expression) {
                logger.log(Level.FINEST, String.format("Calculating Score for “%s”…", expression));
                double optionalHits = 0;
                double requiredHits = 0;
@@ -271,6 +291,91 @@ public class SearchPage extends SoneTemplatePage {
        }
 
        /**
+        * Throws a
+        * {@link net.pterodactylus.sone.web.page.FreenetTemplatePage.RedirectException}
+        * if the given object is not {@code null}, appending the object to the
+        * given target URL.
+        *
+        * @param object
+        *            The object on which to redirect
+        * @param target
+        *            The target of the redirect
+        * @throws RedirectException
+        *             if {@code object} is not {@code null}
+        */
+       private static void redirectIfNotNull(String object, String target) throws RedirectException {
+               if (object != null) {
+                       throw new RedirectException(target + object);
+               }
+       }
+
+       /**
+        * If the given phrase contains a Sone ID (optionally prefixed by
+        * “sone://”), returns said Sone ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is a Sone ID
+        * @return The Sone ID, or {@code null}
+        */
+       private String getSoneId(String phrase) {
+               String soneId = phrase.startsWith("sone://") ? phrase.substring(7) : phrase;
+               return (webInterface.getCore().getSone(soneId, false) != null) ? soneId : null;
+       }
+
+       /**
+        * If the given phrase contains a post ID (optionally prefixed by
+        * “post://”), returns said post ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is a post ID
+        * @return The post ID, or {@code null}
+        */
+       private String getPostId(String phrase) {
+               String postId = phrase.startsWith("post://") ? phrase.substring(7) : phrase;
+               return (webInterface.getCore().getPost(postId, false) != null) ? postId : null;
+       }
+
+       /**
+        * If the given phrase contains a reply ID (optionally prefixed by
+        * “reply://”), returns the ID of the post the reply belongs to, otherwise
+        * return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is a reply ID
+        * @return The reply’s post ID, or {@code null}
+        */
+       private String getReplyPostId(String phrase) {
+               String replyId = phrase.startsWith("reply://") ? phrase.substring(8) : phrase;
+               return (webInterface.getCore().getReply(replyId, false) != null) ? webInterface.getCore().getReply(replyId, false).getPost().getId() : null;
+       }
+
+       /**
+        * If the given phrase contains an album ID (optionally prefixed by
+        * “album://”), returns said album ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is an album ID
+        * @return The album ID, or {@code null}
+        */
+       private String getAlbumId(String phrase) {
+               String albumId = phrase.startsWith("album://") ? phrase.substring(8) : phrase;
+               return (webInterface.getCore().getAlbum(albumId, false) != null) ? albumId : null;
+       }
+
+       /**
+        * If the given phrase contains an image ID (optionally prefixed by
+        * “image://”), returns said image ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is an image ID
+        * @return The image ID, or {@code null}
+        */
+       private String getImageId(String phrase) {
+               String imageId = phrase.startsWith("image://") ? phrase.substring(8) : phrase;
+               return (webInterface.getCore().getImage(imageId, false) != null) ? imageId : null;
+       }
+
+       /**
         * Converts a given object into a {@link String}.
         *
         * @param <T>
index 9045cdf..559a8ff 100644 (file)
@@ -145,7 +145,7 @@ public class UploadImagePage extends SoneTemplatePage {
         * @return The MIME type of the image, or “application/octet-stream” if the
         *         image type could not be detected
         */
-       private String getMimeType(byte[] imageData) {
+       private static String getMimeType(byte[] imageData) {
                ByteArrayInputStream imageDataInputStream = new ByteArrayInputStream(imageData);
                try {
                        ImageInputStream imageInputStream = ImageIO.createImageInputStream(imageDataInputStream);
index c2bb1c8..00f8cf9 100644 (file)
@@ -103,18 +103,13 @@ import net.pterodactylus.sone.web.ajax.UntrustAjaxPage;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.sone.web.page.PageToadlet;
 import net.pterodactylus.sone.web.page.PageToadletFactory;
-import net.pterodactylus.util.cache.Cache;
-import net.pterodactylus.util.cache.CacheException;
-import net.pterodactylus.util.cache.CacheItem;
-import net.pterodactylus.util.cache.DefaultCacheItem;
-import net.pterodactylus.util.cache.MemoryCache;
-import net.pterodactylus.util.cache.ValueRetriever;
 import net.pterodactylus.util.collection.SetBuilder;
 import net.pterodactylus.util.collection.filter.Filters;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.notify.Notification;
 import net.pterodactylus.util.notify.NotificationManager;
 import net.pterodactylus.util.notify.TemplateNotification;
+import net.pterodactylus.util.template.ClassPathTemplateProvider;
 import net.pterodactylus.util.template.CollectionSortFilter;
 import net.pterodactylus.util.template.ContainsFilter;
 import net.pterodactylus.util.template.DateFilter;
@@ -123,15 +118,13 @@ import net.pterodactylus.util.template.HtmlFilter;
 import net.pterodactylus.util.template.MatchFilter;
 import net.pterodactylus.util.template.ModFilter;
 import net.pterodactylus.util.template.PaginationFilter;
-import net.pterodactylus.util.template.Provider;
 import net.pterodactylus.util.template.ReflectionAccessor;
 import net.pterodactylus.util.template.ReplaceFilter;
 import net.pterodactylus.util.template.StoreFilter;
 import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
 import net.pterodactylus.util.template.TemplateContextFactory;
-import net.pterodactylus.util.template.TemplateException;
 import net.pterodactylus.util.template.TemplateParser;
+import net.pterodactylus.util.template.TemplateProvider;
 import net.pterodactylus.util.template.XmlFilter;
 import net.pterodactylus.util.thread.Ticker;
 import net.pterodactylus.util.version.Version;
@@ -222,7 +215,6 @@ public class WebInterface implements CoreListener {
         * @param sonePlugin
         *            The Sone plugin
         */
-       @SuppressWarnings("synthetic-access")
        public WebInterface(SonePlugin sonePlugin) {
                this.sonePlugin = sonePlugin;
                formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
@@ -261,8 +253,8 @@ public class WebInterface implements CoreListener {
                templateContextFactory.addFilter("unique", new UniqueElementFilter());
                templateContextFactory.addFilter("mod", new ModFilter());
                templateContextFactory.addFilter("paginate", new PaginationFilter());
-               templateContextFactory.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER);
-               templateContextFactory.addProvider(new ClassPathTemplateProvider());
+               templateContextFactory.addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER);
+               templateContextFactory.addProvider(new ClassPathTemplateProvider(WebInterface.class, "/templates/"));
                templateContextFactory.addTemplateObject("webInterface", this);
                templateContextFactory.addTemplateObject("formPassword", formPassword);
 
@@ -1013,66 +1005,4 @@ public class WebInterface implements CoreListener {
                notificationManager.addNotification(imageInsertFailedNotification);
        }
 
-       /**
-        * Template provider implementation that uses
-        * {@link WebInterface#createReader(String)} to load templates for
-        * inclusion.
-        *
-        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
-        */
-       private class ClassPathTemplateProvider implements Provider {
-
-               /** Cache for templates. */
-               private final Cache<String, Template> templateCache = new MemoryCache<String, Template>(new ValueRetriever<String, Template>() {
-
-                       @Override
-                       @SuppressWarnings("synthetic-access")
-                       public CacheItem<Template> retrieve(String key) throws CacheException {
-                               Template template = findTemplate(key);
-                               if (template != null) {
-                                       return new DefaultCacheItem<Template>(template);
-                               }
-                               return null;
-                       }
-               });
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               @SuppressWarnings("synthetic-access")
-               public Template getTemplate(TemplateContext templateContext, String templateName) {
-                       try {
-                               return templateCache.get(templateName);
-                       } catch (CacheException ce1) {
-                               logger.log(Level.WARNING, String.format("Could not get template for %s!", templateName), ce1);
-                               return null;
-                       }
-               }
-
-               /**
-                * Locates a template in the class path.
-                *
-                * @param templateName
-                *            The name of the template to load
-                * @return The loaded template, or {@code null} if no template could be
-                *         found
-                */
-               @SuppressWarnings("synthetic-access")
-               private Template findTemplate(String templateName) {
-                       Reader templateReader = createReader("/templates/" + templateName);
-                       if (templateReader == null) {
-                               return null;
-                       }
-                       Template template = null;
-                       try {
-                               template = TemplateParser.parse(templateReader);
-                       } catch (TemplateException te1) {
-                               logger.log(Level.WARNING, String.format("Could not parse template “%s” for inclusion!", templateName), te1);
-                       }
-                       return template;
-               }
-
-       }
-
 }
index f25b19c..6e72a5a 100644 (file)
@@ -19,7 +19,6 @@ package net.pterodactylus.sone.web.ajax;
 
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.freenet.wot.Trust;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.json.JsonObject;
@@ -57,11 +56,7 @@ public class DistrustAjaxPage extends JsonPage {
                        return createErrorJsonObject("invalid-sone-id");
                }
                webInterface.getCore().distrustSone(currentSone, sone);
-               Trust trust = webInterface.getCore().getTrust(currentSone, sone);
-               if (trust == null) {
-                       return createErrorJsonObject("wot-plugin");
-               }
-               return createSuccessJsonObject().put("trustValue", trust.getExplicit());
+               return createSuccessJsonObject().put("trustValue", webInterface.getCore().getPreferences().getNegativeTrust());
        }
 
 }
index 83fa15b..293b4b0 100644 (file)
@@ -94,7 +94,7 @@ public class GetLikesAjaxPage extends JsonPage {
         *            The Sones to convert to an array
         * @return The Sones, sorted by name
         */
-       private JsonArray getSones(Set<Sone> sones) {
+       private static JsonArray getSones(Set<Sone> sones) {
                JsonArray soneArray = new JsonArray();
                List<Sone> sortedSones = new ArrayList<Sone>(sones);
                Collections.sort(sortedSones, Sone.NICE_NAME_COMPARATOR);
index 4e9a879..98a204b 100644 (file)
@@ -142,7 +142,7 @@ public class GetNotificationsAjaxPage extends JsonPage {
         *            The current Sone (may be {@code null})
         * @return The current options
         */
-       private JsonObject createJsonOptions(Sone currentSone) {
+       private static JsonObject createJsonOptions(Sone currentSone) {
                JsonObject options = new JsonObject();
                if (currentSone != null) {
                        options.put("ShowNotification/NewSones", currentSone.getOptions().getBooleanOption("ShowNotification/NewSones").get());
index 223a79f..4c61f89 100644 (file)
@@ -66,19 +66,14 @@ public class GetStatusAjaxPage extends JsonPage {
        @Override
        protected JsonObject createJsonObject(FreenetRequest request) {
                final Sone currentSone = getCurrentSone(request.getToadletContext(), false);
-               /* load Sones. */
-               boolean loadAllSones = Boolean.parseBoolean(request.getHttpRequest().getParam("loadAllSones", "false"));
+               /* load Sones. always return the status of the current Sone. */
                Set<Sone> sones = new HashSet<Sone>(Collections.singleton(getCurrentSone(request.getToadletContext(), false)));
-               if (loadAllSones) {
-                       sones.addAll(webInterface.getCore().getSones());
-               } else {
-                       String loadSoneIds = request.getHttpRequest().getParam("soneIds");
-                       if (loadSoneIds.length() > 0) {
-                               String[] soneIds = loadSoneIds.split(",");
-                               for (String soneId : soneIds) {
-                                       /* just add it, we skip null further down. */
-                                       sones.add(webInterface.getCore().getSone(soneId, false));
-                               }
+               String loadSoneIds = request.getHttpRequest().getParam("soneIds");
+               if (loadSoneIds.length() > 0) {
+                       String[] soneIds = loadSoneIds.split(",");
+                       for (String soneId : soneIds) {
+                               /* just add it, we skip null further down. */
+                               sones.add(webInterface.getCore().getSone(soneId, false));
                        }
                }
                JsonArray jsonSones = new JsonArray();
@@ -198,7 +193,7 @@ public class GetStatusAjaxPage extends JsonPage {
         *            The current Sone (may be {@code null})
         * @return The current options
         */
-       private JsonObject createJsonOptions(Sone currentSone) {
+       private static JsonObject createJsonOptions(Sone currentSone) {
                JsonObject options = new JsonObject();
                if (currentSone != null) {
                        options.put("ShowNotification/NewSones", currentSone.getOptions().getBooleanOption("ShowNotification/NewSones").get());
index 84badf2..f6bc05d 100644 (file)
@@ -66,7 +66,9 @@ public class GetTimesAjaxPage extends JsonPage {
                                Time time = getTime(post.getTime());
                                postTime.put("timeText", time.getText());
                                postTime.put("refreshTime", time.getRefresh() / Time.SECOND);
-                               postTime.put("tooltip", dateFormat.format(new Date(post.getTime())));
+                               synchronized (dateFormat) {
+                                       postTime.put("tooltip", dateFormat.format(new Date(post.getTime())));
+                               }
                                postTimes.put(id, postTime);
                        }
                }
@@ -83,7 +85,9 @@ public class GetTimesAjaxPage extends JsonPage {
                                Time time = getTime(reply.getTime());
                                replyTime.put("timeText", time.getText());
                                replyTime.put("refreshTime", time.getRefresh() / Time.SECOND);
-                               replyTime.put("tooltip", dateFormat.format(new Date(reply.getTime())));
+                               synchronized (dateFormat) {
+                                       replyTime.put("tooltip", dateFormat.format(new Date(reply.getTime())));
+                               }
                                replyTimes.put(id, replyTime);
                        }
                }
index c6b7738..22eeec8 100644 (file)
@@ -139,6 +139,7 @@ public abstract class JsonPage implements FreenetPage {
         * @return {@code true} if the form password (given as “formPassword”) is
         *         required, {@code false} otherwise
         */
+       @SuppressWarnings("static-method")
        protected boolean needsFormPassword() {
                return true;
        }
@@ -149,6 +150,7 @@ public abstract class JsonPage implements FreenetPage {
         * @return {@code true} if the user needs to be logged in to use this page,
         *         {@code false} otherwise
         */
+       @SuppressWarnings("static-method")
        protected boolean requiresLogin() {
                return true;
        }
@@ -162,7 +164,7 @@ public abstract class JsonPage implements FreenetPage {
         *
         * @return A reply signaling success
         */
-       protected JsonObject createSuccessJsonObject() {
+       protected static JsonObject createSuccessJsonObject() {
                return new JsonObject().put("success", true);
        }
 
@@ -173,7 +175,7 @@ public abstract class JsonPage implements FreenetPage {
         *            The error that has occured
         * @return The JSON object, signalling failure and the error code
         */
-       protected JsonObject createErrorJsonObject(String error) {
+       protected static JsonObject createErrorJsonObject(String error) {
                return new JsonObject().put("success", false).put("error", error);
        }
 
index 9b86c5e..5b27d1b 100644 (file)
@@ -19,7 +19,6 @@ package net.pterodactylus.sone.web.ajax;
 
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.freenet.wot.Trust;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.json.JsonObject;
@@ -57,11 +56,7 @@ public class TrustAjaxPage extends JsonPage {
                        return createErrorJsonObject("invalid-sone-id");
                }
                webInterface.getCore().trustSone(currentSone, sone);
-               Trust trust = webInterface.getCore().getTrust(currentSone, sone);
-               if (trust == null) {
-                       return createErrorJsonObject("wot-plugin");
-               }
-               return createSuccessJsonObject().put("trustValue", trust.getExplicit());
+               return createSuccessJsonObject().put("trustValue", webInterface.getCore().getPreferences().getPositiveTrust());
        }
 
 }
index 932be6c..916b6e9 100644 (file)
@@ -19,7 +19,6 @@ package net.pterodactylus.sone.web.ajax;
 
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.freenet.wot.Trust;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.json.JsonObject;
@@ -57,11 +56,7 @@ public class UntrustAjaxPage extends JsonPage {
                        return createErrorJsonObject("invalid-sone-id");
                }
                webInterface.getCore().untrustSone(currentSone, sone);
-               Trust trust = webInterface.getCore().getTrust(currentSone, sone);
-               if (trust == null) {
-                       return createErrorJsonObject("wot-plugin");
-               }
-               return createSuccessJsonObject().put("trustValue", trust.getExplicit());
+               return createSuccessJsonObject().put("trustValue", (String) null);
        }
 
 }
index 30071f5..97ee53b 100644 (file)
@@ -100,6 +100,7 @@ public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
         *            The request to serve
         * @return The title of the page
         */
+       @SuppressWarnings("static-method")
        protected String getPageTitle(FreenetRequest request) {
                return null;
        }
@@ -175,6 +176,7 @@ public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
         *
         * @return Additional style sheets to load
         */
+       @SuppressWarnings("static-method")
        protected Collection<String> getStyleSheets() {
                return Collections.emptySet();
        }
@@ -184,6 +186,7 @@ public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
         *
         * @return The URL of the shortcut icon, or {@code null} for no icon
         */
+       @SuppressWarnings("static-method")
        protected String getShortcutIcon() {
                return null;
        }
@@ -227,6 +230,7 @@ public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
         *            The request that is processed
         * @return The URL to redirect to, or {@code null} to not redirect
         */
+       @SuppressWarnings("static-method")
        protected String getRedirectTarget(FreenetRequest request) {
                return null;
        }
@@ -238,6 +242,7 @@ public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
         *            The request for which to return the link nodes
         * @return All link nodes that should be added to the HTML head
         */
+       @SuppressWarnings("static-method")
        protected List<Map<String, String>> getAdditionalLinkNodes(FreenetRequest request) {
                return Collections.emptyList();
        }
@@ -249,6 +254,7 @@ public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
         * @return {@code true} if this page should only be allowed for hosts with
         *         full access, {@code false} to allow this page for any host
         */
+       @SuppressWarnings("static-method")
        protected boolean isFullAccessOnly() {
                return false;
        }
index 709b307..e253ad5 100644 (file)
@@ -51,10 +51,11 @@ Page.Options.Option.ShowAvatars.Never.Description=Niemals benutzerdefinierte Ava
 Page.Options.Option.ShowAvatars.Followed.Description=Nur benutzerdefinierte Avatare von Sones, denen Sie folgen, anzeigen.
 Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=Nur benutzerdefinierte Avatare von Sones, denen Sie manuell einen Vertrauenswert von mehr als 0 zugewiesen haben, anzeigen.
 Page.Options.Option.ShowAvatars.Trusted.Description=Nur benutzerdefinierte Avatare von Sones, die einen berechneten Vertrauenswert von mehr als 0 haben, anzeigen.
-Page.Options.Option.ShowAvatars.Always.Description=Immer benutzerdefinierte Avatare anzeigen. Warnung: Benutzerdefinierte Avatare können beliebiges Bildmaterial enthalten! 
+Page.Options.Option.ShowAvatars.Always.Description=Immer benutzerdefinierte Avatare anzeigen. Warnung: Benutzerdefinierte Avatare können beliebiges Bildmaterial enthalten!
 Page.Options.Section.RuntimeOptions.Title=Laufzeitverhalten
 Page.Options.Option.InsertionDelay.Description=Anzahl der Sekunden, die vor dem Hochladen einer Sone nach einer Änderung gewartet wird.
 Page.Options.Option.PostsPerPage.Description=Anzahl der Nachrichten pro Seite.
+Page.Options.Option.ImagesPerPage.Description=Anzahl der Bilder pro Seite.
 Page.Options.Option.CharactersPerPost.Description=Die Anzahl der Zeichen, die eine Nachricht enthalten muss, damit sie gekürzt angezeigt wird (-1 für „nie kürzen“). Die Anzahl der tatsächlich angezeigten Zeichen wird in der nächsten Option konfiguriert.
 Page.Options.Option.PostCutOffLength.Description=Die Anzahl der Zeichen, die von einer gekürzten Nachricht sichtbar sind (siehe Option hierüber).
 Page.Options.Option.RequireFullAccess.Description=Zugriff auf Sone für alle Rechner, die keinen vollen Zugriff haben, unterbinden.
@@ -95,6 +96,7 @@ Page.Index.Label.Sender=Absender:
 Page.Index.Button.Post=Abschicken!
 Page.Index.PostList.Title=Nachrichtenliste
 Page.Index.PostList.Text.NoPostYet=Bisher hat noch niemand etwas geschrieben. Sie sollten sofort damit anfangen!
+Page.Index.PostList.Text.FollowSomeSones=Oder vielleicht folgen Sie gar keinen Sones? Werfen Sie doch mal einen Blick auf {link}bekannte Sones{/link} und folgen Sie Sones, die interessant aussehen!
 
 Page.New.Title=Neue Nachrichten und Antworten - Sone
 Page.New.Page.Title=Neue Nachrichten und Antworten
@@ -408,8 +410,9 @@ WebInterface.DefaultText.UploadImage.Description=Bildbeschreibung
 WebInterface.DefaultText.EditImage.Title=Bildtitel
 WebInterface.DefaultText.EditImage.Description=Bildbeschreibung
 WebInterface.DefaultText.Option.PostsPerPage=Anzahl der Nachrichten pro Seite
+WebInterface.DefaultText.Option.ImagesPerPage=Anzahl der Bilder pro Seite
 WebInterface.DefaultText.Option.CharactersPerPost=Anzahl der Zeichen, die eine Nachricht haben muss, damit er gekürzt wird
-WebInterface.DefaultText.Option.PostCutOffLength=Anzahl der Zeichen, die von einer gekürzten Nachricht angezeigt werden 
+WebInterface.DefaultText.Option.PostCutOffLength=Anzahl der Zeichen, die von einer gekürzten Nachricht angezeigt werden
 WebInterface.DefaultText.Option.PositiveTrust=Der positive Vertrauenswert
 WebInterface.DefaultText.Option.NegativeTrust=Der negative Vertrauenswert
 WebInterface.DefaultText.Option.TrustComment=Der Kommentar für die Vertrauenszuweisung
@@ -422,6 +425,7 @@ WebInterface.SelectBox.No=Nein
 WebInterface.ClickToShow.Replies=Hier klicken, um ausgeblendete Antworten zu zeigen.
 WebInterface.VersionInformation.CurrentVersion=Aktuelle Version:
 WebInterface.VersionInformation.LatestVersion=Neueste Version:
+WebInterface.VersionInformation.Homepage=Homepage
 
 Notification.ClickHereToRead=Hier klicken, um den vollen Text der Benachrichtigung zu lesen.
 Notification.FirstStart.Text=Es scheint, als wäre dies das erste Mal, dass Sie Sone starten. Legen Sie eine neue Sone an und folgen Sie anderen Sones!
index 8a85604..5c4367a 100644 (file)
@@ -96,6 +96,7 @@ Page.Index.Label.Sender=Sender:
 Page.Index.Button.Post=Post!
 Page.Index.PostList.Title=Post Feed
 Page.Index.PostList.Text.NoPostYet=Nobody has written any posts yet. You should probably start it right now!
+Page.Index.PostList.Text.FollowSomeSones=Or maybe you are not following any Sones? Take a look at the list of {link}known Sones{/link} and follow whoever looks interesting!
 
 Page.New.Title=New Posts and Replies - Sone
 Page.New.Page.Title=New Posts and Replies
@@ -424,6 +425,7 @@ WebInterface.SelectBox.No=No
 WebInterface.ClickToShow.Replies=Click here to show hidden replies.
 WebInterface.VersionInformation.CurrentVersion=Current Version:
 WebInterface.VersionInformation.LatestVersion=Latest Version:
+WebInterface.VersionInformation.Homepage=Homepage
 
 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.
index 294a483..ac36e74 100644 (file)
@@ -16,21 +16,21 @@ Navigation.Menu.Sone.Item.EditProfile.Name=Editer le profil
 Navigation.Menu.Sone.Item.EditProfile.Tooltip=Editer le profil de votre Sone
 Navigation.Menu.Sone.Item.ImageBrowser.Name=Images
 Navigation.Menu.Sone.Item.ImageBrowser.Tooltip=Gérer vos images
-Navigation.Menu.Sone.Item.DeleteSone.Name=Efface Sone
-Navigation.Menu.Sone.Item.DeleteSone.Tooltip=Efface les Sones actuels
+Navigation.Menu.Sone.Item.DeleteSone.Name=Effacer ce Sone
+Navigation.Menu.Sone.Item.DeleteSone.Tooltip=Effacer les Sones actuels
 Navigation.Menu.Sone.Item.Logout.Name=Déconnexion
 Navigation.Menu.Sone.Item.Logout.Tooltip=Vous déconnecte de votre Sone actuel
 Navigation.Menu.Sone.Item.Options.Name=Options
 Navigation.Menu.Sone.Item.Options.Tooltip=Options concernant le plugin Sone
-Navigation.Menu.Sone.Item.Rescue.Name=Réparation
-Navigation.Menu.Sone.Item.Rescue.Tooltip=Réparer Sone
+Navigation.Menu.Sone.Item.Rescue.Name=Récupération
+Navigation.Menu.Sone.Item.Rescue.Tooltip=Récupération de votre Sone
 Navigation.Menu.Sone.Item.About.Name=A propos
 Navigation.Menu.Sone.Item.About.Tooltip=Informations à propos de Sone
 
 Page.About.Title=A propos de - Sone
 Page.About.Page.Title=A propos
-Page.About.Flattr.Description=Si vous aimez Sone et aue vous voulez me récompenser, vous pouvez utiliser le bouton Flattr au bas de la page de recherche. Flattr est un système de micro-payement non anonyme comparable au principe du pouboire via internet où le montant que chaque utilisateur dépense est limité (le minimum est de 2€ par mois). Plus d'informations sur {link}flattr.com{/link}.
-Page.About.Homepage.Title=Paage d'accueil
+Page.About.Flattr.Description=Si vous aimez Sone et que vous voulez me récompenser, vous pouvez utiliser le bouton Flattr au bas de la page de recherche. Flattr est un système de micro-payement non anonyme comparable au principe du pourboire via internet où le montant que chaque utilisateur dépense est limité (le minimum est de 2€ par mois). Plus d'informations sur {link}flattr.com{/link}.
+Page.About.Homepage.Title=Page d'accueil
 Page.About.Homepage.Description=Vous pouvez trouver plus d'informations ainsi que le code source de Sone sur la {link}page d'accueil{/link}.
 Page.About.License.Title=Licence
 
@@ -39,9 +39,9 @@ Page.Options.Page.Title=Options
 Page.Options.Page.Description=Ces options influencent le comportement de l'exécution du plugin Sone
 Page.Options.Section.SoneSpecificOptions.Title=Options spécifiques à Sone
 Page.Options.Section.SoneSpecificOptions.NotLoggedIn=Ces options sont uniquement disponibles si vous êtes {link}connecté{/link}.
-Page.Options.Section.SoneSpecificOptions.LoggedIn=Ces options sont uniquement disponibles pour le Sone entant que tel vous êtes enregistré.
+Page.Options.Section.SoneSpecificOptions.LoggedIn=Ces options sont uniquement disponibles pour le Sone actif.
 Page.Options.Option.AutoFollow.Description=si un nouveau Sone est découvert, automatiquement le suivre. Veuillez noter que cela suivra uniquement les Sones qui seront découverts après que cette option soit activée!
-Page.Options.Option.EnableSoneInsertNotifications.Description=Si activé, cela affichera les notifications chaque fois que votre Sone insère un message ou qu'il a fini d'insérer.
+Page.Options.Option.EnableSoneInsertNotifications.Description=Si cette option est activée, Sone affichera les notifications chaque fois que vous insèrerez un message ou qu'il sera correctement inséré.
 Page.Options.Option.ShowNotificationNewSones.Description=Affiche les notifications des nouveaux Sones.
 Page.Options.Option.ShowNotificationNewPosts.Description=Affiche une notification lors d'un nouveau message.
 Page.Options.Option.ShowNotificationNewReplies.Description=Affiche une notification lors d'une nouvelle réponse.
@@ -54,23 +54,24 @@ Page.Options.Option.ShowAvatars.Trusted.Description=Ne montrer que les avatars d
 Page.Options.Option.ShowAvatars.Always.Description=Montre tout le temps les avatars personnalisés.Attention: certains avatars peuvent être offensants !
 Page.Options.Section.RuntimeOptions.Title=Comportement runtime
 Page.Options.Option.InsertionDelay.Description=Le nombre de secondes que l'inserteur de Sone attends après une modification d'un Sone avant qu'elle soit insérée.
-Page.Options.Option.PostsPerPage.Description=Le nombre de message à afficher par page avant que les boutons de contrôle de page sont affichés.
+Page.Options.Option.PostsPerPage.Description=Le nombre de message à afficher par page avant que les boutons de pagination soit affichés.
+Page.Options.Option.ImagesPerPage.Description=Le nombre de message à afficher par page avant que les boutons de pagination soit affichés.
 Page.Options.Option.CharactersPerPost.Description=Le nombre de caractères à afficher par message avant que le lien proposant de voir l'intégralité ne soit proposé (-1 pour désactiver). La taille du composant est determinée par l'option ci-desssous.
-Page.Options.Option.PostCutOffLength.Description=Le nombre de charactère à afficher avant que le message ne soit considéré comme trops long. (voir option du dessus)
-Page.Options.Option.RequireFullAccess.Description=Que ce soit pour refuser l'accès à Sone à tout hôte à qui un accès complet n'a pas été accordé.
+Page.Options.Option.PostCutOffLength.Description=Le nombre de charactère à afficher avant que le message ne soit considéré comme trop long. (voir option du dessus)
+Page.Options.Option.RequireFullAccess.Description=Pour refuser l'accès à Sone à tout hôte à qui un accès complet n'a pas été accordé.
 Page.Options.Section.TrustOptions.Title=Réglages de confiance
 Page.Options.Option.PositiveTrust.Description=La quantité de note de confiance positive que vous voulez assigner à d'autres Sones en cochant la case en dessous d'un message ou d'une réponse.
 Page.Options.Option.NegativeTrust.Description=La quantité de note de confiance que vous voulez assigner à d'autres Sones en cliquand le X rouge en dessous d'un message ou d'une réponse. Cette valeur devrait être négative.
 Page.Options.Option.TrustComment.Description=Le commentaire qui sera mis dans le web of trust pour chaque note de confiance que vous assignez de Sone.
 Page.Options.Section.FcpOptions.Title=Réglages de l'Interface FCP
-Page.Options.Option.FcpInterfaceActive.Description=Activer l'interface FCP aafin de permettre à d'autres plugins et clients à distance d'accéder votre plugin Sone.
+Page.Options.Option.FcpInterfaceActive.Description=Activer l'interface FCP afin de permettre à d'autres plugins et clients à distance d'accéder votre plugin Sone.
 Page.Options.Option.FcpFullAccessRequired.Description=Requière une connexion FCP d'hôtes autorisés (Veuillez voir votre {link}configuration du noeud, section “FCP”{/link})
 Page.Options.Option.FcpFullAccessRequired.Value.No=Non
 Page.Options.Option.FcpFullAccessRequired.Value.Writing=Pour accès à l'écriture
 Page.Options.Option.FcpFullAccessRequired.Value.Always=toujours
 Page.Options.Section.Cleaning.Title=Nettoyer
-Page.Options.Option.ClearOnNextRestart.Description=Réinitialiser la configuration du plugin Sone au prochain redémarrage. Attention! {strong}Cela détruira tous vos Sones{/strong}. Soyez sûr d'avoir sauvegardé tout ce dont vous avez besoin! Vous devez également choisir "vrai" à l'option suivante pour procéder à la réinitialisation.
-Page.Options.Option.ReallyClearOnNextRestart.Description=Choisir "oui" pour cette option si vous voulez vraiment{strong}vraiment{/strong} effacer la configuration au prochain redémarrage.
+Page.Options.Option.ClearOnNextRestart.Description=Réinitialiser la configuration du plugin Sone au prochain redémarrage. Attention! {strong}Cela détruira tous vos Sones{/strong}. Soyez sûr d'avoir sauvegardé tout ce dont vous avez besoin! Vous devez également choisir "Oui" à l'option suivante pour procéder à la réinitialisation.
+Page.Options.Option.ReallyClearOnNextRestart.Description=Choisir "Oui" pour cette option si vous voulez vraiment{strong}vraiment{/strong} effacer la configuration au prochain redémarrage.
 Page.Options.Warnings.ValueNotChanged=Cette option n'a pas été modifiée car la valeur que vous avez spécifiée n'est pas valide.
 Page.Options.Button.Save=Enregistrer
 
@@ -85,7 +86,7 @@ Page.CreateSone.Title=Créer un Sone - Sone
 
 Page.DeleteSone.Title=Effacer Sone - Sone
 Page.DeleteSone.Page.Title=Effacer Sone “{sone}”?
-Page.DeleteSone.Page.Description=Ceci n'effacera pas le Sone de Freenet(car ceci est impossible), celaa déconnectera seulement votre identité Web of Trust de Sone.
+Page.DeleteSone.Page.Description=Ceci n'effacera pas le Sone de Freenet(car ceci est impossible), cela déconnectera seulement votre identité Web of Trust de Sone.
 Page.DeleteSone.Button.Yes=Oui, effacer.
 Page.DeleteSone.Button.No=Non, ne pas effacer.
 
@@ -95,6 +96,7 @@ Page.Index.Label.Sender=Expéditeur:
 Page.Index.Button.Post=Envoyer!
 Page.Index.PostList.Title=Envoyer du contenu
 Page.Index.PostList.Text.NoPostYet=Personne n'a encore écrit de message. vous devriez probablement commencer maintenant!
+Page.Index.PostList.Text.FollowSomeSones=Peut-être ne suivez vous pas encore de Sones? Jetez un oeil à la liste des {link}Sones connus{/link} et suivez ceux qui pourrait vous intéresser!
 
 Page.New.Title=Nouveaux messages et réponses - Sone
 Page.New.Page.Title=Nouveaux messages et réponses
@@ -122,7 +124,7 @@ Page.KnownSones.Button.UnfollowAllSones=Ne plus suivre tous les Sones de cette p
 Page.EditProfile.Title=Editer le profil - Sone
 Page.EditProfile.Page.Title=Éditer le profil
 Page.EditProfile.Page.Description=Sur cette page, vous pouvez entrer les données de votre profil.
-Page.EditProfile.Page.Hint.Optionality=Veuillez noter que chaque champ de ce profil est optionnel! Vous n'êtes pas obligés de remplir quoi que ce soit ici! Tout ce que vous remplissez ici serq probqblement stocké dans Freenet pour très longtemps!
+Page.EditProfile.Page.Hint.Optionality=Veuillez noter que chaque champ de ce profil est optionnel! Vous n'êtes pas obligés de remplir quoi que ce soit ici! Tout ce que vous remplissez ici sera probablement stocké dans Freenet pour très longtemps!
 Page.EditProfile.Label.FirstName=Prénom:
 Page.EditProfile.Label.MiddleName=Prénom(s):
 Page.EditProfile.Label.LastName=Nom de famille:
@@ -134,7 +136,7 @@ Page.EditProfile.Avatar.Title=Avatar
 Page.EditProfile.Avatar.Description=Vous pouvez selectionner une de vos images comme avatar. Elle ne doit pas dépasser la taille de 64x64 pixels car c'est la taille la plus grande vue par les autre. (La taille 80x80 est réservée pour l'entête).
 Page.EditProfile.Avatar.Delete=Pas d'avatar
 Page.EditProfile.Fields.Title=Champs personnalisés
-Page.EditProfile.Fields.Description=Vous pouvez ajouter ici des champs personalisés à votre profil. Ces champs peuvent contenir tout ce que vous voulez et être aussi laconique ou aussi long que vous le souhaitez. Veuillez juste vous souvenir que quand on parle d'anonymat, parfois le moins, le mieux.
+Page.EditProfile.Fields.Description=Vous pouvez ajouter ici des champs personalisés à votre profil. Ces champs peuvent contenir tout ce que vous voulez et être aussi laconique ou aussi long que vous le souhaitez. Gardez à l'esprit qu'en terme d'anonymat, moins vous en dite, mieux c'est!
 Page.EditProfile.Fields.Button.Edit=Éditer
 Page.EditProfile.Fields.Button.MoveUp=Déplacer vers le haut
 Page.EditProfile.Fields.Button.MoveDown=Déplacer vers le bas
@@ -180,11 +182,11 @@ Page.ViewSone.UnknownSone.LinkToWebOfTrust=Ce Sone n'est pas connu, son profile
 Page.ViewSone.WriteAMessage=Vous pouvez écrire un message à ce Sone ici. Veuillez noter que tout le monde sera capable de lire ce message!
 Page.ViewSone.PostList.Title=Messages par {sone}
 Page.ViewSone.PostList.Text.NoPostYet=Ce Sone n'as encore rien envoyé.
-Page.ViewSone.Profile.Title=Profil
+Page.ViewSone.Profile.Title=Profile
 Page.ViewSone.Profile.Label.Name=Nom
 Page.ViewSone.Profile.Label.Albums=Albums
-Page.ViewSone.Profile.Albums.Text.All=All Albums
-Page.ViewSone.Profile.Name.WoTLink=Profil Web of trust
+Page.ViewSone.Profile.Albums.Text.All=Tous les albums
+Page.ViewSone.Profile.Name.WoTLink=Profile Web of trust
 Page.ViewSone.Replies.Title=Messages {sone} a répondu à
 
 Page.ViewPost.Title=Voir message - Sone
@@ -215,51 +217,51 @@ Page.FollowSone.Title=Suivre Sone - Sone
 
 Page.UnfollowSone.Title=Ne plus suivre Sone - Sone
 
-Page.ImageBrowser.Title=Image Browser - Sone
+Page.ImageBrowser.Title=Explorateur d'images - Sone
 Page.ImageBrowser.Album.Title=Album “{album}”
-Page.ImageBrowser.Album.Error.NotFound.Text=l'Album recherché n'est pas disponible. Il est possible qu'il ne soit pas encore mis à jour, ou qu'il est été dupprimé.
+Page.ImageBrowser.Album.Error.NotFound.Text=l'Album recherché n'est pas disponible. Il est possible qu'il ne soit pas encore mis à jour, ou qu'il est été supprimé.
 Page.ImageBrowser.Sone.Title=Albums de {sone}
 Page.ImageBrowser.Sone.Error.NotFound.Text=Le Sone recherché ne peut pas être trouvé. Il est possible qu'il ne soit pas encore téléchargé.
 Page.ImageBrowser.Header.Albums=Albums
 Page.ImageBrowser.Header.Images=Images
 Page.ImageBrowser.Link.All=Tous les Sones
-Page.ImageBrowser.CreateAlbum.Button.CreateAlbum=Creer un Album
-Page.ImageBrowser.Album.Edit.Title=Editer l'Album
-Page.ImageBrowser.Album.Delete.Title=Supprimer l'Album
-Page.ImageBrowser.Album.Label.AlbumImage=Album d'Images:
-Page.ImageBrowser.Album.Label.Title=Title:
+Page.ImageBrowser.CreateAlbum.Button.CreateAlbum=Creer un album
+Page.ImageBrowser.Album.Edit.Title=Editer l'album
+Page.ImageBrowser.Album.Delete.Title=Supprimer l'album
+Page.ImageBrowser.Album.Label.AlbumImage=Album d'images:
+Page.ImageBrowser.Album.Label.Title=Titre:
 Page.ImageBrowser.Album.Label.Description=Description:
-Page.ImageBrowser.Album.AlbumImage.Choose=Vhoisissez un Album d'images…
-Page.ImageBrowser.Album.Button.Save=Sauver l'Album
-Page.ImageBrowser.Album.Button.Delete=Supprimer l'Album
+Page.ImageBrowser.Album.AlbumImage.Choose=Choisissez un album d'images…
+Page.ImageBrowser.Album.Button.Save=Sauver l'album
+Page.ImageBrowser.Album.Button.Delete=Supprimer l'album
 Page.ImageBrowser.Image.Edit.Title=Editer l'image
-Page.ImageBrowser.Image.Title.Label=Title:
+Page.ImageBrowser.Image.Title.Label=Titre:
 Page.ImageBrowser.Image.Description.Label=Description:
 Page.ImageBrowser.Image.Button.MoveLeft=◀
-Page.ImageBrowser.Image.Button.Save=Save Image
+Page.ImageBrowser.Image.Button.Save=Sauvegarder l'image
 Page.ImageBrowser.Image.Button.MoveRight=►
-Page.ImageBrowser.Image.Delete.Title=Supprimer l'Image
-Page.ImageBrowser.Image.Button.Delete=Supprimer l'Image
+Page.ImageBrowser.Image.Delete.Title=Supprimer l'image
+Page.ImageBrowser.Image.Button.Delete=Supprimer l'image
 
-Page.CreateAlbum.Title=Créer un Album - Sone
-Page.CreateAlbum.Page.Title=Créer un Album
+Page.CreateAlbum.Title=Créer un album - Sone
+Page.CreateAlbum.Page.Title=Créer un album
 Page.CreateAlbum.Error.NameMissing=Vous avez oublié de donner un nom à votre Album.
 
 Page.UploadImage.Title=Insérer une Image - Sone
-Page.UploadImage.Error.InvalidImage=L'Image qu vous éssayez d'insérer n'est pas reconnue. Merci de n'insérer que des JPEG (*.jpg or *.jpeg), ou des PNG (*.png).
+Page.UploadImage.Error.InvalidImage=L'image qu vous essayez d'insérer n'est pas reconnue. Merci de n'insérer que des JPEG (*.jpg or *.jpeg), ou des PNG (*.png).
 
-Page.EditImage.Title=Editer l'Image - Sone
+Page.EditImage.Title=Editer l'image - Sone
 
-Page.DeleteImage.Title=Supprimer l'Image - Sone
-Page.DeleteImage.Page.Title=Supprimer l'Image
-Page.DeleteImage.Text.ImageWillBeGone=Ceci supprimera l'image “{image}” de l'Album “{album}”. Si elle a déjà été insérée sur Freenet elle ne pourra pas être supprimée. Voulez-vous supprimer cette Image ?
+Page.DeleteImage.Title=Supprimer l'image - Sone
+Page.DeleteImage.Page.Title=Supprimer l'image
+Page.DeleteImage.Text.ImageWillBeGone=Ceci supprimera l'image “{image}” de l'Album “{album}”. Si elle a déjà été insérée sur Freenet elle ne pourra pas être supprimée. Voulez-vous supprimer cette image ?
 Page.DeleteImage.Button.Yes=Oui, supprimer cette image.
 Page.DeleteImage.Button.No=Non, ne pas supprimer cette image.
 
-Page.EditAlbum.Title=Editer l'Album - Sone
+Page.EditAlbum.Title=Editer l'album - Sone
 
-Page.DeleteAlbum.Title=Supprimer l'Album - Sone
-Page.DeleteAlbum.Page.Title=Supprimer l'Album
+Page.DeleteAlbum.Title=Supprimer l'album - Sone
+Page.DeleteAlbum.Page.Title=Supprimer l'album
 Page.DeleteAlbum.Text.AlbumWillBeGone=Vous allez supprimer l'Album “{title}”. Etes vous certain de vouloir faire cela ?
 Page.DeleteAlbum.Button.Yes=Oui, supprimer cet album.
 Page.DeleteAlbum.Button.No=Non, ne pas supprimer cet album.
@@ -285,14 +287,14 @@ Page.Search.Text.SoneHits=Les Sones suivants correspondent aux termes de votre r
 Page.Search.Text.PostHits=Les messages suivants correspondent aux termes de votre recherche.
 Page.Search.Text.NoHits=Aucun Sone ou message ne correspond aux termes de votre recherche.
 
-Page.Rescue.Title=Secours Sone - Sone
-Page.Rescue.Page.Title=Secours Sone “{0}”
-Page.Rescue.Text.Description=Le Mode Secours vous permet de restaurer une version précédente de Sone. Cela peut être nécessaire si votre configuration est perdue
-Page.Rescue.Text.Procedure=Le Mode secours fonctionne en récupérant vos dernières insertions. Si une insertion est récupérée elle sera chargé dans votre Sone, vous permettant de reprendre le controle de vos messages, profile, et autres configurations (vous pouvez faire cela dans une seconde fenetre ou tabulation Sone). Si vos messages ne peuvent être récupérés, demandez au Mode Secours de récupérer les anciennes versions ci-dessous.
-Page.Rescue.Text.Fetching=Le Secouriste de Sone est en train de récupérer la version {0} de votre Sone.
-Page.Rescue.Text.Fetched=Le Secouriste de Sone a récupéré la version {0} de votre Sone. Merci de vérifire vos messages, réponses et profile. Si les informations vous conviennent, débloquez la version.
-Page.Rescue.Text.FetchedLast=Le secouriste de Sone a récupéré la dernière version disponible. Si vous ne souhaitiez pas récupérer une ancienne version de Sone. Consiédérez que vous n'avez pas de chance.
-Page.Rescue.Text.NotFetched=Le Secouriste de Sone ne peut pas récupérer la version {0} de votre Sone. Merci de réessayer, ou essayez avec une version plus ancienne.
+Page.Rescue.Title=Récupération de Sone - Sone
+Page.Rescue.Page.Title=Récupération du Sone “{0}”
+Page.Rescue.Text.Description=Le Mode Récupération vous permet de restaurer une version précédente de Sone. Cela peut être nécessaire si votre configuration est perdue
+Page.Rescue.Text.Procedure=Le Mode Récupération fonctionne en récupérant vos dernières insertions. Si une insertion est récupérée elle sera chargé dans votre Sone, vous permettant de reprendre le controle de vos messages, profile, et autres configurations (vous pouvez faire cela dans une seconde fenêtre ou tabulation Sone). Si vos messages ne peuvent être récupérés, demandez au Mode Récupération de restaurer les anciennes versions ci-dessous.
+Page.Rescue.Text.Fetching=Le récupérateur de Sone est en train de restaurer la version {0} de votre Sone.
+Page.Rescue.Text.Fetched=Le récupérateur de Sone a restauré la version {0} de votre Sone. Merci de vérifier vos messages, réponses et profile. Si les informations vous conviennent, débloquez la version.
+Page.Rescue.Text.FetchedLast=Le récupérateur de Sone a restauré la dernière version disponible. Si vous ne souhaitiez pas récupérer une ancienne version de Sone. Considérez que vous n'avez pas de chance.
+Page.Rescue.Text.NotFetched=Le récupérateur de Sone ne peut pas restaurer la version {0} de votre Sone. Merci de réessayer, ou essayez avec une version plus ancienne.
 Page.Rescue.Label.NextEdition=Prochaine version:
 Page.Rescue.Button.Fetch=Récupérer la version.
 
@@ -303,7 +305,7 @@ Page.NoPermission.Text.NoPermission=Vous avez tenté une action pour laquelle vo
 Page.DismissNotification.Title=Effacer la notification - Sone
 
 Page.WotPluginMissing.Text.LoadPlugin=Veuillez charger le plugin Web of Trust dans le {link}plugin manager{/link}.
-Page.WotPluginMissing.Text.WotRequired=Parce que le Web of Trust est une partie intégrante de Sone, le plugin Web of trust doit être chargé afin de pouvoir faire fontionner Sone.
+Page.WotPluginMissing.Text.WotRequired=Parce que Web of Trust est une partie intégrante de Sone, le plugin Web of trust doit être chargé afin de pouvoir faire fontionner Sone.
 
 Page.Logout.Title=Déconnexion - Sone
 
@@ -337,11 +339,11 @@ View.Sone.Status.Idle=Ce Sone est inactif, c'est à dire qu'il n'est pas en trai
 View.Sone.Status.Downloading=Ce Sone est en train d'être téléchargé.
 View.Sone.Status.Inserting=Ce Sone est en train d'être inséré.
 
-View.SoneMenu.Link.AllAlbums=tous les Albums
+View.SoneMenu.Link.AllAlbums=Tous les Albums
 
 View.Post.UnknownAuthor=(inconnu)
-View.Post.WebOfTrustLink=profile web of trust
-View.Post.Permalink=lier le message
+View.Post.WebOfTrustLink=Profile web of trust
+View.Post.Permalink=Lier le message
 View.Post.PermalinkAuthor=Lier l'auteur
 View.Post.Bookmarks.PostIsBookmarked=Ce message a été ajouté aux marque-pages, cliquer pour retirer des marque-pages
 View.Post.Bookmarks.PostIsNotBookmarked=Ce message n'est pas dans les marque-pages, cliquer pour marquer cette page
@@ -352,8 +354,8 @@ View.Post.LikeLink=Aime
 View.Post.UnlikeLink=N'aime pas
 View.Post.ShowSource=Activer/Désactiver le parser
 View.Post.NotDownloaded=Ce message n'a pas encore été téléchargé, ou a été effacé.
-View.Post.ShowMore=voir plus
-View.Post.ShowLess=vois moins
+View.Post.ShowMore=Voir plus
+View.Post.ShowLess=Vois moins
 
 View.UpdateStatus.Text.ChooseSenderIdentity=Choisir l'identité de l'expéditeur
 
@@ -396,20 +398,21 @@ WebInterface.DefaultText.LastName=Nom de famille
 WebInterface.DefaultText.BirthDay=Jour
 WebInterface.DefaultText.BirthMonth=Mois
 WebInterface.DefaultText.BirthYear=Année
-WebInterface.DefaultText.FieldName=nom du champ
-WebInterface.DefaultText.Option.InsertionDelay=Temps d'attente après qu'un Sone aie été modifié avant l'insertion (en secondes)
+WebInterface.DefaultText.FieldName=Nom du champ
+WebInterface.DefaultText.Option.InsertionDelay=Temps d'attente après qu'un Sone ai été modifié avant l'insertion (en secondes)
 WebInterface.DefaultText.Search=Que recherchez vous ?
-WebInterface.DefaultText.CreateAlbum.Name=Album title
-WebInterface.DefaultText.CreateAlbum.Description=Album description
-WebInterface.DefaultText.EditAlbum.Title=Album title
-WebInterface.DefaultText.EditAlbum.Description=Album description
-WebInterface.DefaultText.UploadImage.Title=Image title
-WebInterface.DefaultText.UploadImage.Description=Image description
-WebInterface.DefaultText.EditImage.Title=Image title
-WebInterface.DefaultText.EditImage.Description=Image description
+WebInterface.DefaultText.CreateAlbum.Name=Titre de l'album
+WebInterface.DefaultText.CreateAlbum.Description=Description de l'album
+WebInterface.DefaultText.EditAlbum.Title=Title de l'album
+WebInterface.DefaultText.EditAlbum.Description=Description de l'album
+WebInterface.DefaultText.UploadImage.Title=Titre de l'image
+WebInterface.DefaultText.UploadImage.Description=Description de l'image
+WebInterface.DefaultText.EditImage.Title=Titre de l'image
+WebInterface.DefaultText.EditImage.Description=Description de l'image
 WebInterface.DefaultText.Option.PostsPerPage=Nombre de messages à afficher par page
-WebInterface.DefaultText.Option.CharactersPerPost=Number of characters a post must have to be shortened
-WebInterface.DefaultText.Option.PostCutOffLength=Number of characters for the snippet of the shortened post
+WebInterface.DefaultText.Option.ImagesPerPage=Nombre d'images à afficher par pages
+WebInterface.DefaultText.Option.CharactersPerPost=Nombre de charactère qu'une publication doit avoir pour être racourcie
+WebInterface.DefaultText.Option.PostCutOffLength=Nombre de charactère du descriptif de la publication racourcie
 WebInterface.DefaultText.Option.PositiveTrust=La note de confiance positive à assigner
 WebInterface.DefaultText.Option.NegativeTrust=Une note de confiance négative à assigner
 WebInterface.DefaultText.Option.TrustComment=Le commentaire à mettre dans le "Web of Trust"
@@ -422,11 +425,12 @@ WebInterface.SelectBox.No=Non
 WebInterface.ClickToShow.Replies=Cliquer ici pour afficher les réponses cachées.
 WebInterface.VersionInformation.CurrentVersion=Version actuelle:
 WebInterface.VersionInformation.LatestVersion=Dernière version:
+WebInterface.VersionInformation.Homepage=Accueil
 
 Notification.ClickHereToRead=Cliquer ici pour lire le texte entier de la notification.
 Notification.FirstStart.Text=Il semble que ce soit la première fois que vous démarrez Sone. Pour démarrer, créez un nouveau Sone depuis une identité Web of Trust et commencez à suivre d'autres Sones.
 Notification.Startup.Text=Sone est cours de démarrage. Cela peut prendre un certain temps afin de récupérer toutes les identités et Sones du Web of Trust. Si il vous manque certains éléments, veuillez être patient, ils réapparaîtrons probablement assez tôt.
-Notification.ConfigNotRead.Text=Le fichier de configuration “sone.properties” n'a pas pu être lu probablement car il n'a pas été sauvegardé correctement. Ceci peut arriver avec les versions antérieures à Sone 0.3.3 et il n'y a rien que vous puissiez faire à ce propos.
+Notification.ConfigNotRead.Text=Le fichier de configuration “sone.properties” n'a pas pu être lu probablement parce qu'il n'a pas été sauvegardé correctement. Ceci peut arriver avec les versions antérieures à Sone 0.3.3 et il n'y a rien que vous puissiez faire à ce propos.
 Notification.Button.Dismiss=Rejeter
 Notification.NewSone.ShortText=De nouveaux Sones ont été découverts:
 Notification.NewSone.Text=De nouveaux Sones ont été découverts:
@@ -439,7 +443,7 @@ Notification.SoneIsBeingRescued.Text=Les Sones suivants sont actuellement en cou
 Notification.SoneRescued.Text=Les Sones suivants ont été sauvés:
 Notification.SoneRescued.Text.RememberToUnlock=Veuillez vous souvenir de contrôler les messages et réponses que vous avez donnés et n'oubliez ps de déverrouiller vos Sones!
 Notification.LockedSones.Text=Les Sones suivants ont été verrouillés pour plus de 5 minutes. Veuillez vérifier si vous voulez vraiment garder ces Sones bloqués:
-Notification.NewVersion.Text=La version {version} du pluggin Sone a été trouvée. Téléchargez la depuis USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}​!
+Notification.NewVersion.Text=La version {version} du plugin Sone a été trouvée. Téléchargez la depuis USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}​!
 Notification.InsertingImages.Text=Les images suivantes sont en cours d'insertion:
 Notification.InsertedImages.Text=Les images suivantes ont été insérées:
 Notification.ImageInsertFailed.Text=Les images suivantes ne peuvent être insérées:
index e076250..3a7175d 100644 (file)
@@ -55,6 +55,7 @@ Page.Options.Option.ShowAvatars.Always.Description=Alltid vis egendefinerte avat
 Page.Options.Section.RuntimeOptions.Title=Oppførsel ved kjøretid
 Page.Options.Option.InsertionDelay.Description=Antall sekunder Sone-innsetteren skal vente etter en endring av en Sone før den blir innsatt.
 Page.Options.Option.PostsPerPage.Description=Antallet innlegg å vise pr side før side-kontroller blir vist.
+Page.Options.Option.ImagesPerPage.Description=Antall bilder å vise på en side før side-kontroller blir vist.
 Page.Options.Option.CharactersPerPost.Description=Antall tegn å vise fra et innlegg før resten blir skjult og en link blir vist for å utvide til hele innlegget (-1 for å deaktivere). Lengden på den viste teksten kan endres under.
 Page.Options.Option.PostCutOffLength.Description=Antallet tegn som blir vist hvis et innlegg er for langt (Se innstilling over).
 Page.Options.Option.RequireFullAccess.Description=For å avslå tilgang til Sone fra enhver host som ikke har blitt gitt full tilgang.
@@ -95,6 +96,7 @@ Page.Index.Label.Sender=Avsender:
 Page.Index.Button.Post=Publiser!
 Page.Index.PostList.Title=Innleggsstrøm
 Page.Index.PostList.Text.NoPostYet=Ingen har skrevet noen innlegg enda. Du burde starte med en gang!
+Page.Index.PostList.Text.FollowSomeSones=Eller kanskje du ikke følger noen Soner? Naviger til {link}Kjente Soner{/link} og følg de som ser interessante ut!
 
 Page.New.Title=Nye innlegg og svar - Sone
 Page.New.Page.Title=Nye innlegg og svar
@@ -408,6 +410,7 @@ WebInterface.DefaultText.UploadImage.Title=Bildetittel
 WebInterface.DefaultText.EditImage.Description=Bildebeskrivelse
 WebInterface.DefaultText.EditImage.Title=Bildetittel
 WebInterface.DefaultText.Option.PostsPerPage=Antall innlegg å vise på en side
+WebInterface.DefaultText.Option.ImagesPerPage=Antall bilder å vise per side
 WebInterface.DefaultText.Option.CharactersPerPost=Antall tegn et innlegg må ha for å bli skjult.
 WebInterface.DefaultText.Option.PostCutOffLength=Antall tegn som vises når et innlegg blir skjult
 WebInterface.DefaultText.Option.PositiveTrust=Positiv tillit å gi
@@ -422,6 +425,7 @@ WebInterface.SelectBox.No=Nei
 WebInterface.ClickToShow.Replies=Klikk her for å vise skjulte svar.
 WebInterface.VersionInformation.CurrentVersion=Gjeldende utgave:
 WebInterface.VersionInformation.LatestVersion=Siste utgave:
+WebInterface.VersionInformation.Homepage=Hjemmeside
 
 Notification.ClickHereToRead=Klikk her for å lese hele varslingen.
 Notification.FirstStart.Text=Dette ser ut til å være første gang du starter Sone. For å starte, lag en ny Sone fra et 'Web Of Trust'-pseudonym og start å følge andre Soner.
index c143cde..a19ea96 100644 (file)
@@ -39,7 +39,7 @@ Page.Options.Page.Title=Opcje
 Page.Options.Page.Description=Te opcje wpływaja na pracę wtyczki Sone.
 Page.Options.Section.SoneSpecificOptions.Title=Sone-Konkretne Opcje
 Page.Options.Section.SoneSpecificOptions.NotLoggedIn=Opcje dostępne po zalogowaniu {link}zalogowany{/link}.
-Page.Options.Section.SoneSpecificOptions.LoggedIn=Opcje dostępne po zalogowaniu dla aktualnie używanego profilu Sone. 
+Page.Options.Section.SoneSpecificOptions.LoggedIn=Opcje dostępne po zalogowaniu dla aktualnie używanego profilu Sone.
 Page.Options.Option.AutoFollow.Description=Śledź automatycznie nowych uzytkowników Sone. Śledzeni będą tylko użytkownicy Sone odnalezieni po uruchomieniu tej opcji!
 Page.Options.Option.EnableSoneInsertNotifications.Description=Włączenie tej opcji uruchamia powiadomienia o tym, że twój Sone jest wysyłany lub zakończył wysyłanie na serwer.
 Page.Options.Option.ShowNotificationNewSones.Description=Pokaż powiadomienie o nowych użytkownikach Sone.
@@ -50,20 +50,21 @@ Page.Options.Option.ShowAvatars.Description=Tu możesz wyłączyć niestandardow
 Page.Options.Option.ShowAvatars.Never.Description=Nigdy nie pokazuj niestandardowych avatarów.
 Page.Options.Option.ShowAvatars.Followed.Description=Pokazuj avatary tylko śledzonych użytkowników Sone.
 Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=Pokazuj avatary tylko tych użytkowników Sone, którym ręcznie przyznałeś ilość punktów zaufania przekraczającą 0.
-Page.Options.Option.ShowAvatars.Trusted.Description=Pokazuj avatary tylko tych użytkowników Sone, którzy mają ilość punktów zaufania większą niż 0. 
-Page.Options.Option.ShowAvatars.Always.Description=Zawsze pokazuj niestandardowe avatary. Uwaga: niektóre avatary mogą zawierać obraźliwe treści. 
+Page.Options.Option.ShowAvatars.Trusted.Description=Pokazuj avatary tylko tych użytkowników Sone, którzy mają ilość punktów zaufania większą niż 0.
+Page.Options.Option.ShowAvatars.Always.Description=Zawsze pokazuj niestandardowe avatary. Uwaga: niektóre avatary mogą zawierać obraźliwe treści.
 Page.Options.Section.RuntimeOptions.Title=Tryb pracy
-Page.Options.Option.InsertionDelay.Description=Czas oczekiwania użytkownika Sone na modifikację profilu Sone przed jego załadowaniem. 
+Page.Options.Option.InsertionDelay.Description=Czas oczekiwania użytkownika Sone na modifikację profilu Sone przed jego załadowaniem.
 Page.Options.Option.PostsPerPage.Description=Ilość postów wyświetlanych na stronie przed pojawieniem się znaków paginacji.
-Page.Options.Option.CharactersPerPost.Description=Ilość znaków pokazywanych w poście zanim zostanie on obcięty i pojawi się link do jego rozszerzenia (-1 powoduje wyłączenie). Długość fragmentu zależy od poniższej opcji. 
+Page.Options.Option.ImagesPerPage.Description=Ilość obrazków wyświetlanych na stronie przed pojawieniem się znaków paginacji.
+Page.Options.Option.CharactersPerPost.Description=Ilość znaków pokazywanych w poście zanim zostanie on obcięty i pojawi się link do jego rozszerzenia (-1 powoduje wyłączenie). Długość fragmentu zależy od poniższej opcji.
 Page.Options.Option.PostCutOffLength.Description=Ilość znaków wyświetlanych w przypadku za długiego postu (zobacz opcję powyżej).
-Page.Options.Option.RequireFullAccess.Description=Opcja odmowy dostępu do Sone hostom bez przyznanego pełnego dostępu. 
+Page.Options.Option.RequireFullAccess.Description=Opcja odmowy dostępu do Sone hostom bez przyznanego pełnego dostępu.
 Page.Options.Section.TrustOptions.Title=Ustawienia Zaufania
-Page.Options.Option.PositiveTrust.Description=Punkty pozytywnego zaufania, które chcesz przyznać innym użytkownikom Sone klikając na ikonę pod postem lub odpowiedzią. 
-Page.Options.Option.NegativeTrust.Description=Punkty zaufania, które chcesz przyznać innym użytkownikom Sone klikając na czerwony krzyżyk pod postem lub odpowiedzią. Wartosć powinna być negatywna. 
-Page.Options.Option.TrustComment.Description=Komentarz, który wyświetli się w sieci zaufania w momencie przyznawania punktów zaufania w obrębie Sone. 
-Page.Options.Section.FcpOptions.Title=Ustawienia Interfejsu FCP 
-Page.Options.Option.FcpInterfaceActive.Description=Uruchom interfejs FCP, aby umożliwić innym wtyczkom i klientom zdalnym dostęp do twojej wtyczki Sone. 
+Page.Options.Option.PositiveTrust.Description=Punkty pozytywnego zaufania, które chcesz przyznać innym użytkownikom Sone klikając na ikonę pod postem lub odpowiedzią.
+Page.Options.Option.NegativeTrust.Description=Punkty zaufania, które chcesz przyznać innym użytkownikom Sone klikając na czerwony krzyżyk pod postem lub odpowiedzią. Wartosć powinna być negatywna.
+Page.Options.Option.TrustComment.Description=Komentarz, który wyświetli się w sieci zaufania w momencie przyznawania punktów zaufania w obrębie Sone.
+Page.Options.Section.FcpOptions.Title=Ustawienia Interfejsu FCP
+Page.Options.Option.FcpInterfaceActive.Description=Uruchom interfejs FCP, aby umożliwić innym wtyczkom i klientom zdalnym dostęp do twojej wtyczki Sone.
 Page.Options.Option.FcpFullAccessRequired.Description=Wymagane połączenie FCP dla hostów z dostępem (patrz twoja {link}konfiguracja Freenet, sekcja “FCP”{/link})
 Page.Options.Option.FcpFullAccessRequired.Value.No=Nie
 Page.Options.Option.FcpFullAccessRequired.Value.Writing=Dostęp z zapisem
@@ -71,7 +72,7 @@ Page.Options.Option.FcpFullAccessRequired.Value.Always=Zawsze
 Page.Options.Section.Cleaning.Title=Wyczyść
 Page.Options.Option.ClearOnNextRestart.Description=Przy kolejnym uruchomieniu zresetuj ustawienia wtyczki Sone. Uwaga!{strong}Wszystkie twoje profile Sone zostaną zniszczone{/strong}upewnij się, że wykonano kopie zapasowe wszystkich niezbędnych danych! Wymagane wybranie odpowiedzi "tak" w następnej opcji.
 Page.Options.Option.ReallyClearOnNextRestart.Description=Wymagane wybranie opcji"tak"jeśli {strong}naprawdę{/strong} jesteś zdecydowany zresetować ustawienia wtyczki konfiguracyjnej przy kolejnym uruchomieniu.
-Page.Options.Warnings.ValueNotChanged=Opcja nie została zmieniona, ponieważ wprowadzono błędną wartość. 
+Page.Options.Warnings.ValueNotChanged=Opcja nie została zmieniona, ponieważ wprowadzono błędną wartość.
 Page.Options.Button.Save=Zapisz
 
 Page.Login.Title=Login - Sone
@@ -94,7 +95,8 @@ Page.Index.Label.Text=Napisz tekst:
 Page.Index.Label.Sender=Autor:
 Page.Index.Button.Post=Napisz!
 Page.Index.PostList.Title=Wątek
-Page.Index.PostList.Text.NoPostYet=Nie ma jeszcze żądnych postów. Można rozpocząć pisanie wątku! 
+Page.Index.PostList.Text.NoPostYet=Nie ma jeszcze żądnych postów. Można rozpocząć pisanie wątku!
+Page.Index.PostList.Text.FollowSomeSones=Być może nie śledzisz żadnych Sone? Spójrz na tę listę {link}znane Sone{/link} i zacznij kogoś śledzić.
 
 Page.New.Title=Nowe Posty i Odpowiedzi - Sone
 Page.New.Page.Title=Nowe Posty i Odpowiedzi
@@ -117,10 +119,10 @@ Page.KnownSones.Filter.New=Pokazuj tylko nowe Sone
 Page.KnownSones.Filter.NotNew=Ukryj nowe Sone
 Page.KnownSones.Button.Apply=Zastosuj
 Page.KnownSones.Button.FollowAllSones=Śledź wszystkie Sone na tej stronie
-Page.KnownSones.Button.UnfollowAllSones=Przestań śledzić wszystkie Sone na tej stronie 
+Page.KnownSones.Button.UnfollowAllSones=Przestań śledzić wszystkie Sone na tej stronie
 
 Page.EditProfile.Title=Edytuj Profil - Sone
-Page.EditProfile.Page.Title=Edytuj Profil 
+Page.EditProfile.Page.Title=Edytuj Profil
 Page.EditProfile.Page.Description=Na tej stronie uzyskasz dostęp do swoich danych profilowych.
 Page.EditProfile.Page.Hint.Optionality=Pamiętaj, każde pole w profilu jest opcjonalne! Nie musisz nic wpisywać! Wszystko co wpiszesz będzie bardzo długo przechowywane na Freenet!
 Page.EditProfile.Label.FirstName=Imię:
@@ -134,7 +136,7 @@ Page.EditProfile.Avatar.Title=Avatar
 Page.EditProfile.Avatar.Description=Możesz ustawić jako avatar jedno z załadowanych zdjęć. Nie może być większe niż 64×64 pikseli, poniważ jest to największy rozmiar zdjęć wyświetlanych u innych osób (80×80 pikseli jest stosowany jako nagłówek strony).
 Page.EditProfile.Avatar.Delete=Brak avataru
 Page.EditProfile.Fields.Title=Pola niestandardowe
-Page.EditProfile.Fields.Description=Tutaj możesz wprowadzać pola nistandardowe do profilu. Pola mogą zawierać dowolne treści oraz dowolą ilość tekstu. Należy pamiętać, że ze względu na anonimowość często mniej znaczy więcej. 
+Page.EditProfile.Fields.Description=Tutaj możesz wprowadzać pola nistandardowe do profilu. Pola mogą zawierać dowolne treści oraz dowolą ilość tekstu. Należy pamiętać, że ze względu na anonimowość często mniej znaczy więcej.
 Page.EditProfile.Fields.Button.Edit=Edytuj
 Page.EditProfile.Fields.Button.MoveUp=Idź do góry
 Page.EditProfile.Fields.Button.MoveDown=Idź na dół
@@ -148,7 +150,7 @@ Page.EditProfile.Error.DuplicateFieldName=Nazwa pola “{fieldName}” już istn
 
 Page.EditProfileField.Title=Edytuj profil - Sone
 Page.EditProfileField.Page.Title=Edytuj profil
-Page.EditProfileField.Text=Wprowadź nową nazwę dla tego profilu 
+Page.EditProfileField.Text=Wprowadź nową nazwę dla tego profilu
 Page.EditProfileField.Error.DuplicateFieldName=Wprowadzona nazwa pola już istnieje.
 Page.EditProfileField.Button.Save=Zmień
 Page.EditProfileField.Button.Reset=Wróć do poprzedniej nazwy
@@ -156,8 +158,8 @@ Page.EditProfileField.Button.Cancel=Nie zmieniaj nazwy
 
 Page.DeleteProfileField.Title=Usuń profil - Sone
 Page.DeleteProfileField.Page.Title=Usuń profil
-Page.DeleteProfileField.Text=Czy naprawdę chcesz usunąć ten profil? 
-Page.DeleteProfileField.Button.Yes=Tak, usuń. 
+Page.DeleteProfileField.Text=Czy naprawdę chcesz usunąć ten profil?
+Page.DeleteProfileField.Button.Yes=Tak, usuń.
 Page.DeleteProfileField.Button.No=Nie, nie usuwaj.
 
 Page.CreatePost.Title=Napisz post - Sone
@@ -174,9 +176,9 @@ Page.CreateReply.Button.Post=Wyślij odpowiedź!
 
 Page.ViewSone.Title=Zobacz Sone - Sone
 Page.ViewSone.Page.TitleWithoutSone=Zobacz nieznany Sone
-Page.ViewSone.NoSone.Description= Nie ma obecnie Sone z tym ID {sone}. Jesli szukasz konkretnego użytkownika to upewnij się, że jest widoczny w twojej sieci zaufania. 
-Page.ViewSone.UnknownSone.Description=Ten Sone nie został jeszcze odzyskany. Proszę sprawdzić za chwilę. 
-Page.ViewSone.UnknownSone.LinkToWebOfTrust=Ten Sone nie jest jeszcze znany, ale jego profil w Sieci Zaufania może być już dostępny. 
+Page.ViewSone.NoSone.Description= Nie ma obecnie Sone z tym ID {sone}. Jesli szukasz konkretnego użytkownika to upewnij się, że jest widoczny w twojej sieci zaufania.
+Page.ViewSone.UnknownSone.Description=Ten Sone nie został jeszcze odzyskany. Proszę sprawdzić za chwilę.
+Page.ViewSone.UnknownSone.LinkToWebOfTrust=Ten Sone nie jest jeszcze znany, ale jego profil w Sieci Zaufania może być już dostępny.
 Page.ViewSone.WriteAMessage=Tu możesz napisać wiadomość do tego Sone. Każdy będzie mógł zobaczyć tą wiadomość!
 Page.ViewSone.PostList.Title=Posty {sone}
 Page.ViewSone.PostList.Text.NoPostYet=Ten Sone nie ma jeszcze żadnych postów.
@@ -184,7 +186,7 @@ Page.ViewSone.Profile.Title=Profil
 Page.ViewSone.Profile.Label.Name=Nazwa
 Page.ViewSone.Profile.Label.Albums=Album
 Page.ViewSone.Profile.Albums.Text.All=Wszystkie albumy
-Page.ViewSone.Profile.Name.WoTLink=Profil 
+Page.ViewSone.Profile.Name.WoTLink=Profil
 Page.ViewSone.Replies.Title=Posty, na które odpowiedział {sone}
 
 Page.ViewPost.Title=Zobacz Post - Sone
@@ -197,13 +199,13 @@ Page.Unlike.Title=Nie lubię tego Postu - Sone
 
 Page.DeletePost.Title=Usuń Sone - Sone
 Page.DeletePost.Page.Title=Usuń Sone
-Page.DeletePost.Text.PostWillBeGone=Usunięty post nie będzie widoczny na twoim Sone. Nie zostanie jednak usunięty z Freenetu, ponieważ nie ma takiej możliwości. Starsze wersje twojego Sone będą zawsze zawierały usunięte posty. 
+Page.DeletePost.Text.PostWillBeGone=Usunięty post nie będzie widoczny na twoim Sone. Nie zostanie jednak usunięty z Freenetu, ponieważ nie ma takiej możliwości. Starsze wersje twojego Sone będą zawsze zawierały usunięte posty.
 Page.DeletePost.Button.Yes=Tak, usuń.
 Page.DeletePost.Button.No=Nie, nie usuwaj.
 
 Page.DeleteReply.Title=Usuń odpowiedź - Sone
 Page.DeleteReply.Page.Title=Usuń odpowiedź
-Page.DeleteReply.Text.PostWillBeGone= Usunięta odpowiedź nie będzie widoczna na twoim Sone. Nie zostanie jednak usunięta z Freenet, ponieważ nie ma takiej możlowość. Starsze wersje twojego Sone zawsze będa zawierały usunięte odpowiedzi. 
+Page.DeleteReply.Text.PostWillBeGone= Usunięta odpowiedź nie będzie widoczna na twoim Sone. Nie zostanie jednak usunięta z Freenet, ponieważ nie ma takiej możlowość. Starsze wersje twojego Sone zawsze będa zawierały usunięte odpowiedzi.
 Page.DeleteReply.Button.Yes=Tak, usuń.
 Page.DeleteReply.Button.No=Nie, nie usuwaj.
 
@@ -217,9 +219,9 @@ Page.UnfollowSone.Title=Przestań śledzić Sone - Sone
 
 Page.ImageBrowser.Title=Wyszukiwarka Obrazów - Sone
 Page.ImageBrowser.Album.Title=Album “{album}”
-Page.ImageBrowser.Album.Error.NotFound.Text=Nie można znaleźć żądanego albumu. Prawdopodobnie nie został jeszcze ściagnięty, albo  został usunięty. 
+Page.ImageBrowser.Album.Error.NotFound.Text=Nie można znaleźć żądanego albumu. Prawdopodobnie nie został jeszcze ściagnięty, albo  został usunięty.
 Page.ImageBrowser.Sone.Title=Albumy {sone}
-Page.ImageBrowser.Sone.Error.NotFound.Text=Nie można znaleźć żądanego Sone. Prawdopodobnie nie został jeszcze ściągnięty. 
+Page.ImageBrowser.Sone.Error.NotFound.Text=Nie można znaleźć żądanego Sone. Prawdopodobnie nie został jeszcze ściągnięty.
 Page.ImageBrowser.Header.Albums=Albumy
 Page.ImageBrowser.Header.Images=Obrazy
 Page.ImageBrowser.Link.All=Wszystkie Sone
@@ -238,7 +240,7 @@ Page.ImageBrowser.Image.Description.Label=Opis:
 Page.ImageBrowser.Image.Button.MoveLeft=◀
 Page.ImageBrowser.Image.Button.Save=Zapisz Obraz
 Page.ImageBrowser.Image.Button.MoveRight=►
-Page.ImageBrowser.Image.Delete.Title=Usuń Obraz 
+Page.ImageBrowser.Image.Delete.Title=Usuń Obraz
 Page.ImageBrowser.Image.Button.Delete=Usuń Obraz
 
 Page.CreateAlbum.Title=Utwórz Album - Sone
@@ -254,7 +256,7 @@ Page.DeleteImage.Title=Usuń Obraz - Sone
 Page.DeleteImage.Page.Title=Usuń Obraz
 Page.DeleteImage.Text.ImageWillBeGone=Obraz “{image}” zostanie usunięty z twojego albumu “{album}”. Jeśli został już umieszczony na Freenet to nie bedzie można go usunąć. Czy chcesz usunąć obraz?
 Page.DeleteImage.Button.Yes=Tak, usuń obraz.
-Page.DeleteImage.Button.No=Nie, nie usuwaj obrazu. 
+Page.DeleteImage.Button.No=Nie, nie usuwaj obrazu.
 
 Page.EditAlbum.Title=Edytuj Album
 
@@ -276,21 +278,21 @@ Page.Bookmark.Title=Zakładka - Sone
 Page.Unbookmark.Title=Usuń zakładkę - Sone
 Page.Bookmarks.Title=Zakładka - Sone
 Page.Bookmarks.Page.Title=Zakładki
-Page.Bookmarks.Text.NoBookmarks= Nie masz obecnie żadnych zakładek. Możesz dodać posty do zakładek klikając  gwiazdkę poniżej postu. 
+Page.Bookmarks.Text.NoBookmarks= Nie masz obecnie żadnych zakładek. Możesz dodać posty do zakładek klikając  gwiazdkę poniżej postu.
 Page.Bookmarks.Text.PostsNotLoaded=Niektóre z zaznaczonych przez ciebie postów nie zostały wyświetlone, ponieważ wystąpił problem z ich załadowaniem. Dzieje się tak, jeśli Sone było niedawno restartowane, lub gdy posty zostały usunięte. Jeśli jesteś pewien, że posty zostały usunięte, to możesz je {link}odznaczyć{/link}.
 
 Page.Search.Title=Szukaj - Sone
-Page.Search.Page.Title=Szukaj 
-Page.Search.Text.SoneHits=Następujące Sone pasuja do twoich wyników wyszukiwania. 
-Page.Search.Text.PostHits=Następujące posty pasują do twoich wyników wyszukiwania. 
-Page.Search.Text.NoHits=Nie znaleziono żadnych Sone ani postów pasujących do twoich wyników wyszukiwania. 
+Page.Search.Page.Title=Szukaj
+Page.Search.Text.SoneHits=Następujące Sone pasuja do twoich wyników wyszukiwania.
+Page.Search.Text.PostHits=Następujące posty pasują do twoich wyników wyszukiwania.
+Page.Search.Text.NoHits=Nie znaleziono żadnych Sone ani postów pasujących do twoich wyników wyszukiwania.
 
 Page.Rescue.Title=Ratuj Sone
 Page.Rescue.Page.Title=Ratuj Sone “{0}”
-Page.Rescue.Text.Description=Tryb Ratunkowy pozwala przywrócić poprzednią wersję twojego Sone. Może to okazać się niezbędne, jeśli twoje ustawienia zostaną utracone. 
+Page.Rescue.Text.Description=Tryb Ratunkowy pozwala przywrócić poprzednią wersję twojego Sone. Może to okazać się niezbędne, jeśli twoje ustawienia zostaną utracone.
 Page.Rescue.Text.Procedure=Tryb Rarunkowy polega na pobraniu ostatniej wprowadzonej edycji twojego Sone. Jeśli edycja zostanie poprawnie pobrana wówczas zostanie załadowana na twój Sone, co umożliwi zarządzanie twoimi postami, profilem oraz innymi ustawieniami (można to zrobić w nowej zakładce lub oknie przegladarki). Jeśli pobrana edycja różni się od tej, którą chcesz przywrócić, wówczas ustaw Tryb Ratunkowy tak, aby poprał jeszcze wcześniejszą edycję.
-Page.Rescue.Text.Fetching=Tryb Ratunkowy Sone pobiera właśnie edycję {0} twojego Sone. 
-Page.Rescue.Text.Fetched=Tryb Ratunkowy Sone pobrał edycję {0} twojego Sone. Sprawdź swoje posty, odpowiedzi oraz profil. Jeśli podoba ci się zawartość aktualnego Sone, to mozesz go odblokować. 
+Page.Rescue.Text.Fetching=Tryb Ratunkowy Sone pobiera właśnie edycję {0} twojego Sone.
+Page.Rescue.Text.Fetched=Tryb Ratunkowy Sone pobrał edycję {0} twojego Sone. Sprawdź swoje posty, odpowiedzi oraz profil. Jeśli podoba ci się zawartość aktualnego Sone, to mozesz go odblokować.
 Page.Rescue.Text.FetchedLast= Tryb Ratunkowy Sone pobrał ostatnią dostępną edycję. Jeśli nie udało się przywrócić twojego Sone, to nie masz teraz szczęścia.
 Page.Rescue.Text.NotFetched=Tryb Ratunkowy Sone nie mógł sćiągnąć edycji  {0} twojego Sone.  Spróbuj pobrać ponownie edycję {0}, albo pobierz kolejną starszą edycję.
 Page.Rescue.Label.NextEdition=Następna edycja:
@@ -298,18 +300,18 @@ Page.Rescue.Button.Fetch=Pobierz edycję
 
 Page.NoPermission.Title=Nieupoważniony dostęp- Sone
 Page.NoPermission.Page.Title=Nieupoważniony dostęp
-Page.NoPermission.Text.NoPermission=Próbowałeś zrobić coś do czego nie masz wystarczającej autoryzacji. Na przyszłość nie podejmuj tego typu działań, albo będziemy zmuszeni podjać odpowiednie kroki.  
+Page.NoPermission.Text.NoPermission=Próbowałeś zrobić coś do czego nie masz wystarczającej autoryzacji. Na przyszłość nie podejmuj tego typu działań, albo będziemy zmuszeni podjać odpowiednie kroki.
 
 Page.DismissNotification.Title=Odrzuć powiadomienie - Sone
 
-Page.WotPluginMissing.Text.WotRequired=Sieć Zaufania jest integralną częścią Sone. Należy załądować wtyczkę Sieci Zaufania, aby móc korzystać z Sone. 
+Page.WotPluginMissing.Text.WotRequired=Sieć Zaufania jest integralną częścią Sone. Należy załądować wtyczkę Sieci Zaufania, aby móc korzystać z Sone.
 Page.WotPluginMissing.Text.LoadPlugin=Załaduj wtyczkę Sieci Zaufania w {link}menadżerze wtyczek{/link}.
 
 Page.Logout.Title=Wyloguj - Sone
 
 Page.Invalid.Title=Niepoprawne działanie
 Page.Invalid.Page.Title=Niepoprawne działanie
-Page.Invalid.Text=Podjęto niepoprawne działanie, lub też działanie było poprawne, ale parametry nie. Wróć do{link}strony głównej{/link} i spróbuj ponownie. Jeśli problem będzie się utrzymywać to najprawdopodobniej znalazłeś błąd. 
+Page.Invalid.Text=Podjęto niepoprawne działanie, lub też działanie było poprawne, ale parametry nie. Wróć do{link}strony głównej{/link} i spróbuj ponownie. Jeśli problem będzie się utrzymywać to najprawdopodobniej znalazłeś błąd.
 
 View.Search.Button.Search=Szukaj
 
@@ -328,11 +330,11 @@ View.Sone.Stats.Images={0,number} {0,choice,0#images|1#image|1<images}
 View.Sone.Button.UnlockSone=odblokuj
 View.Sone.Button.UnlockSone.Tooltip=Pozwala na załadowanie Sone
 View.Sone.Button.LockSone=zablokuj
-View.Sone.Button.LockSone.Tooltip=Uniemożliwia załadowanie Sone 
+View.Sone.Button.LockSone.Tooltip=Uniemożliwia załadowanie Sone
 View.Sone.Button.UnfollowSone=przestań śledzić
 View.Sone.Button.FollowSone=śledź
 View.Sone.Status.Modified=Ten Sone został zmodyfikowany i czeka na załadowanie
-View.Sone.Status.Unknown=Ten Sone nie został jeszcze odzyskany 
+View.Sone.Status.Unknown=Ten Sone nie został jeszcze odzyskany
 View.Sone.Status.Idle=Ten Sone nie jest używany, t.j. nie jest ani ładowany ani ściągany.
 View.Sone.Status.Downloading=Ten Sone jest właśnie ściągany.
 View.Sone.Status.Inserting=Ten Sone jest właśnie ładowany.
@@ -343,19 +345,19 @@ View.Post.UnknownAuthor=(nieznany)
 View.Post.WebOfTrustLink=Profil sieci zaufania
 View.Post.Permalink=linkuj post
 View.Post.PermalinkAuthor=linkuj autora
-View.Post.Bookmarks.PostIsBookmarked=Post został oznaczony, kliknij, żeby odznaczyć 
-View.Post.Bookmarks.PostIsNotBookmarked=Post nie jest oznaczony, kliknij, żeby dodać do zakładek 
+View.Post.Bookmarks.PostIsBookmarked=Post został oznaczony, kliknij, żeby odznaczyć
+View.Post.Bookmarks.PostIsNotBookmarked=Post nie jest oznaczony, kliknij, żeby dodać do zakładek
 View.Post.DeleteLink=Usuń
 View.Post.SendReply=Wyślij Odpowiedź!
 View.Post.Reply.DeleteLink=Usuń
 View.Post.LikeLink=Lubię
 View.Post.UnlikeLink=Nie lubię
 View.Post.ShowSource=Parsowanie
-View.Post.NotDownloaded=Ten post albo nie został jeszcze ściągnięty albo  został usunięty. 
+View.Post.NotDownloaded=Ten post albo nie został jeszcze ściągnięty albo  został usunięty.
 View.Post.ShowMore=pokaż więcej
 View.Post.ShowLess=pokaż mniej
 
-View.UpdateStatus.Text.ChooseSenderIdentity=Wybierz tożsamość nadawcy 
+View.UpdateStatus.Text.ChooseSenderIdentity=Wybierz tożsamość nadawcy
 
 View.Trust.Tooltip.Trust=Zaufaj tej osobie
 View.Trust.Tooltip.Distrust=Przypisz tej osobie nagatywny poziom zaufania
@@ -368,7 +370,7 @@ View.CreateAlbum.Label.Description=Opis:
 View.UploadImage.Title=Załaduj Obraz
 View.UploadImage.Label.Title=Tytuł:
 View.UploadImage.Label.Description=Opis:
-View.UploadImage.Button.UploadImage=Załaduj Obraz 
+View.UploadImage.Button.UploadImage=Załaduj Obraz
 
 View.Time.InTheFuture=potem
 View.Time.AFewSecondsAgo=kilka sekund temu
@@ -391,13 +393,13 @@ WebInterface.DefaultText.StatusUpdate=O czym teraz myślisz?
 WebInterface.DefaultText.Message=Napisz Wiadomość…
 WebInterface.DefaultText.Reply=Napisz Odpowiedź…
 WebInterface.DefaultText.FirstName=Imię
-WebInterface.DefaultText.MiddleName=Drugie imię 
+WebInterface.DefaultText.MiddleName=Drugie imię
 WebInterface.DefaultText.LastName=Nazwisko
 WebInterface.DefaultText.BirthDay=Dzień
 WebInterface.DefaultText.BirthMonth=Miesiąc
 WebInterface.DefaultText.BirthYear=Rok
 WebInterface.DefaultText.FieldName=Nazwa Pola
-WebInterface.DefaultText.Option.InsertionDelay=Czas potrzebny na załadowanie Sone po modyfikacji (w sekundach) 
+WebInterface.DefaultText.Option.InsertionDelay=Czas potrzebny na załadowanie Sone po modyfikacji (w sekundach)
 WebInterface.DefaultText.Search=Czego szukasz?
 WebInterface.DefaultText.CreateAlbum.Name=Tytuł Albumu
 WebInterface.DefaultText.CreateAlbum.Description=Opis Albumu
@@ -408,9 +410,10 @@ WebInterface.DefaultText.UploadImage.Description=Opis obrazka
 WebInterface.DefaultText.EditImage.Title=Tytuł Obrazka
 WebInterface.DefaultText.EditImage.Description=Opis Obrazka
 WebInterface.DefaultText.Option.PostsPerPage=Ilość postów wyświetlanych na jednej stronie
-WebInterface.DefaultText.Option.CharactersPerPost=Ilość znaków, które ma zawierać post, aby zostać skrócony 
+WebInterface.DefaultText.Option.ImagesPerPage=Ilość obrazków na stronie
+WebInterface.DefaultText.Option.CharactersPerPost=Ilość znaków, które ma zawierać post, aby zostać skrócony
 WebInterface.DefaultText.Option.PostCutOffLength=Ilość znaków w skróconym poście
-WebInterface.DefaultText.Option.PositiveTrust=Pozytywny poziom zaufania 
+WebInterface.DefaultText.Option.PositiveTrust=Pozytywny poziom zaufania
 WebInterface.DefaultText.Option.NegativeTrust=Negatywny poziom zaufania
 WebInterface.DefaultText.Option.TrustComment=Komentarz, który zostanie ustawiony w Sieci Zaufania
 WebInterface.Button.Comment=Komentuj
@@ -419,14 +422,15 @@ WebInterface.Confirmation.DeleteReplyButton=Tak, usuń!
 WebInterface.SelectBox.Choose=Wybierz…
 WebInterface.SelectBox.Yes=Tak
 WebInterface.SelectBox.No=Nie
-WebInterface.ClickToShow.Replies=Kliknij, żeby pokazać ukryte odpowiedzi. 
+WebInterface.ClickToShow.Replies=Kliknij, żeby pokazać ukryte odpowiedzi.
 WebInterface.VersionInformation.CurrentVersion=Aktualna wersja:
 WebInterface.VersionInformation.LatestVersion=Najnowsza wersja:
+WebInterface.VersionInformation.Homepage=Strona główna
 
 Notification.ClickHereToRead=Kliknij tutaj, żeby przeczytać cały tekst powiadomienia.
-Notification.FirstStart.Text=Po raz pierwszy korzystasz z Sone. Zacznij od wybrania nowego Sone z tożsamości z Sieci Zaufania i zacznij śledzić inne Sone.  
-Notification.Startup.Text=Sone właśnie się włącza. Należy chwilę poczekać zanim wszystkie tożsamości i Sone z sieci zaufania zostaną odzyskane. Jeśli brakuje jakichś elementów, należy być cierpliwym, najprawdopodobniej wkrótce się pojawią. 
-Notification.ConfigNotRead.Text=Plik konfiguracyjny “sone.properties” nie mógł zostać odczytany, prawdopodobnie dlatego, że nie został poprawnie zapisany. Dotyczy to wersji wcześniejszych niż Sone 0.3.3 i nie można nic z tym zrobić. 
+Notification.FirstStart.Text=Po raz pierwszy korzystasz z Sone. Zacznij od wybrania nowego Sone z tożsamości z Sieci Zaufania i zacznij śledzić inne Sone.
+Notification.Startup.Text=Sone właśnie się włącza. Należy chwilę poczekać zanim wszystkie tożsamości i Sone z sieci zaufania zostaną odzyskane. Jeśli brakuje jakichś elementów, należy być cierpliwym, najprawdopodobniej wkrótce się pojawią.
+Notification.ConfigNotRead.Text=Plik konfiguracyjny “sone.properties” nie mógł zostać odczytany, prawdopodobnie dlatego, że nie został poprawnie zapisany. Dotyczy to wersji wcześniejszych niż Sone 0.3.3 i nie można nic z tym zrobić.
 Notification.Button.Dismiss=Odrzuć
 Notification.NewSone.ShortText=Znaleziono nowe Sone:
 Notification.NewSone.Text=Znaleziono nowe Sone:
@@ -435,10 +439,10 @@ Notification.NewPost.Text=Znaleziono nowe posty napisane przez poniższych użyt
 Notification.NewPost.Button.MarkRead=Oznacz jako przeczytane
 Notification.NewReply.ShortText=Znaleziono nowe odpowiedzi.
 Notification.NewReply.Text=Znaleziono nowe odpowiedzi napisane przez poniższych użytkowników Sone:
-Notification.SoneIsBeingRescued.Text=Następujące Sone są aktualnie odzyskiwane: 
+Notification.SoneIsBeingRescued.Text=Następujące Sone są aktualnie odzyskiwane:
 Notification.SoneRescued.Text=Odzyskano następujące Sone:
 Notification.SoneRescued.Text.RememberToUnlock=Należy pamiętać o zarządzaniu napisanymi postami i odpowiedziami oraz  nie zapomnieć o odblokowaniu swoich Sone!
-Notification.LockedSones.Text=Następujące Sone są zablokowane od ponad 5 minut. Sprawdź czy chcesz, żeby pozostały zablokowane. 
+Notification.LockedSones.Text=Następujące Sone są zablokowane od ponad 5 minut. Sprawdź czy chcesz, żeby pozostały zablokowane.
 Notification.NewVersion.Text=Znaleziono wersję {version}wtyczki Sone. Ściągnij ją z  USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}!
 Notification.InsertingImages.Text=Ładowane są następujące obrazy:
 Notification.InsertedImages.Text=Załadowano nastepujące obrazy:
index bc43303..8263074 100644 (file)
@@ -55,6 +55,7 @@ Page.Options.Option.ShowAvatars.Always.Description=Всегда показыва
 Page.Options.Section.RuntimeOptions.Title=Поведение во время работы.
 Page.Options.Option.InsertionDelay.Description=Количество секунд, в течение которых выгрузчик Sone ожидает после изменения Sone до того, как он будет выгружен.
 Page.Options.Option.PostsPerPage.Description=Количество сообщений, которое должно быть показно на странице до того, как будут показаны кнопки переключения страниц.
+Page.Options.Option.ImagesPerPage.Description=Количество изображений, которое должно быть показно на странице до того, как будут показаны кнопки переключения страниц.
 Page.Options.Option.CharactersPerPost.Description=Количество символов сообщения, которые должны быть показаны до того, как оно будет обрезано и будет показана ссылка для его раскрытия (-1 для отключения). Фактическая длина обрезанного сообщения задается нижеследующей настройкой.
 Page.Options.Option.PostCutOffLength.Description=Количество символов, которые показываются, если сообщение посчитано слишком длинным (см. настройку выше).
 Page.Options.Option.RequireFullAccess.Description=Запрещать доступ к Sone любому хосту, которому не был дан полный доступ.
@@ -95,6 +96,7 @@ Page.Index.Label.Sender=Отправитель:
 Page.Index.Button.Post=Отправить!
 Page.Index.PostList.Title=Лента сообщений
 Page.Index.PostList.Text.NoPostYet=Никто еще не написал ни одного сообщения. Вам, наверное, следует начать это прямо сейчас!
+Page.Index.PostList.Text.FollowSomeSones=Или возможно вы не подписаны ни на один Sone? Посмотрите на список {link}Известныx Sone{/link} и попдишитесь на всех, кто покажется вам интересным!
 
 Page.New.Title=Новые сообщения и ответы - Sone
 Page.New.Page.Title=Новые сообщения и ответы
@@ -408,12 +410,13 @@ WebInterface.DefaultText.UploadImage.Description=Описание изображ
 WebInterface.DefaultText.EditImage.Title=Название изображения
 WebInterface.DefaultText.EditImage.Description=Описание изображения
 WebInterface.DefaultText.Option.PostsPerPage=Количество сообщений, показываемых на странице
+WebInterface.DefaultText.Option.ImagesPerPage=Количество изображений, показываемых на странице
 WebInterface.DefaultText.Option.CharactersPerPost=Количество символов, которое должно быть у сообщения, чтобы оно было сокращено
 WebInterface.DefaultText.Option.PostCutOffLength=Количество символов в сокращенном варианте сообщения
 WebInterface.DefaultText.Option.PositiveTrust=Положительное доверие для назначения
 WebInterface.DefaultText.Option.NegativeTrust=Отрицательное доверие для назначения
 WebInterface.DefaultText.Option.TrustComment=Комментарий для установки в web of trust
-WebInterface.Button.Comment=Comment
+WebInterface.Button.Comment=Комментировать
 WebInterface.Confirmation.DeletePostButton=Да, удалить!
 WebInterface.Confirmation.DeleteReplyButton=Да, удалить!
 WebInterface.SelectBox.Choose=Выбрать…
@@ -422,6 +425,7 @@ WebInterface.SelectBox.No=Нет
 WebInterface.ClickToShow.Replies=Нажмите, чтобы показать скрытые ответы.
 WebInterface.VersionInformation.CurrentVersion=Текущая версия:
 WebInterface.VersionInformation.LatestVersion=Последняя версия:
+WebInterface.VersionInformation.Homepage=Домашняя страница
 
 Notification.ClickHereToRead=Нажмите здесь, чтобы прочитать полный текст уведомления.
 Notification.FirstStart.Text=Видимо, вы запустили Sone в первый раз. Чтобы начать, создайте новый Sone из личность web of trust и начните подписываться на другие Sone.
index 1f03bce..f0b8b56 100644 (file)
 
                <ul id="avatar-selection">
                        <li id="no-avatar">
-                               <input type="radio" name="avatar-id" value="none"<%ifnull avatar-image> checked="checked"<%/if>/>
+                               <input type="radio" name="avatar-id" value="none"<%ifnull avatar-id> checked="checked"<%/if>/>
                                <%= Page.EditProfile.Avatar.Delete|l10n|html>
                        </li>
                        <%foreach currentSone.allImages image>
index a88c48f..7752ffb 100644 (file)
                                </div>
                        <%/if>
 
-                       <%foreach album.images image|paginate pageSize=core.preferences.imagesPerPage page=request.page>
+                       <%foreach album.images image|paginate pageSize=core.preferences.imagesPerPage page=page>
                                <%first>
                                        <h2><%= Page.ImageBrowser.Header.Images|l10n|html></h2>
                                        <%include include/pagination.html pageParameter=="page">
index 7ab6b22..6ddfd24 100644 (file)
@@ -13,6 +13,7 @@
                        <%if hasLatestVersion>
                                <div class="latest-version"><%= WebInterface.VersionInformation.LatestVersion|l10n|html> <b><% latestVersion|html></b></div>
                        <%/if>
+                       <div class="homepage"><a href="/USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/<%latestEdition|html>/"><%=WebInterface.VersionInformation.Homepage|l10n|html></a></div>
                </div>
        </div>
 
index 6a599e1..7120d06 100644 (file)
@@ -9,7 +9,7 @@
                        <%ifnull !post.sone.profile.avatar>
                                <%post.sone.profile.avatar|image-link max-width==48 max-height==48 mode==enlarge title=="Avatar Image">
                        <%else>
-                               <img src="/WebOfTrust/GetIdenticon?identity=<% post.sone.id|html>&amp;width=48&height=48" width="48" height="48" alt="Avatar Image" />
+                               <img src="/WebOfTrust/GetIdenticon?identity=<% post.sone.id|html>&amp;width=48&amp;height=48" width="48" height="48" alt="Avatar Image" />
                        <%/if>
                <%else>
                        <img src="images/sone-avatar.png" width="48" height="48" alt="Avatar Image" />
                                        <button type="submit" value="1"><%= View.Post.UnlikeLink|l10n|html></button>
                                </form>
                                <%if !post.sone.current>
-                                       <span class='separator'>·</span>
-                                       <form class="trust post-trust<%if post.sone.trust.assigned> hidden<%/if>" action="trust.html" method="post">
-                                               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-                                               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-                                               <input type="hidden" name="sone" value="<% post.sone.id|html>" />
-                                               <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
-                                       </form>
-                                       <form class="distrust post-distrust<%if post.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
-                                               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-                                               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-                                               <input type="hidden" name="sone" value="<% post.sone.id|html>" />
-                                               <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
-                                       </form>
-                                       <form class="untrust post-untrust<%if !post.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
-                                               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-                                               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-                                               <input type="hidden" name="sone" value="<% post.sone.id|html>" />
-                                               <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">↶</button>
-                                       </form>
+                                       <%ifnull !post.sone.trust>
+                                               <span class='separator'>·</span>
+                                               <form class="trust post-trust<%if post.sone.trust.assigned> hidden<%/if>" action="trust.html" method="post">
+                                                       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+                                                       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+                                                       <input type="hidden" name="sone" value="<% post.sone.id|html>" />
+                                                       <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
+                                               </form>
+                                               <form class="distrust post-distrust<%if post.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
+                                                       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+                                                       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+                                                       <input type="hidden" name="sone" value="<% post.sone.id|html>" />
+                                                       <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
+                                               </form>
+                                               <form class="untrust post-untrust<%if !post.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
+                                                       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+                                                       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+                                                       <input type="hidden" name="sone" value="<% post.sone.id|html>" />
+                                                       <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">↶</button>
+                                               </form>
+                                       <%/if>
                                <%/if>
                        <%/if>
                        <%if post.sone.local>
index 2962dd9..449a235 100644 (file)
                                        <button type="submit" value="1"><%= View.Post.UnlikeLink|l10n|html></button>
                                </form>
                                <%if !reply.sone.current>
-                                       <span class='separator'>·</span>
-                                       <form class="trust reply-trust<%if reply.sone.trust.assigned> hidden<%/if>" action="trust.html" method="post">
-                                               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-                                               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-                                               <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
-                                               <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
-                                       </form>
-                                       <form class="distrust reply-distrust<%if reply.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
-                                               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-                                               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-                                               <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
-                                               <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
-                                       </form>
-                                       <form class="untrust reply-untrust<%if !reply.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
-                                               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-                                               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-                                               <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
-                                               <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">↶</button>
-                                       </form>
+                                       <%ifnull !reply.sone.trust>
+                                               <span class='separator'>·</span>
+                                               <form class="trust reply-trust<%if reply.sone.trust.assigned> hidden<%/if>" action="trust.html" method="post">
+                                                       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+                                                       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+                                                       <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
+                                                       <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
+                                               </form>
+                                               <form class="distrust reply-distrust<%if reply.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
+                                                       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+                                                       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+                                                       <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
+                                                       <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
+                                               </form>
+                                               <form class="untrust reply-untrust<%if !reply.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
+                                                       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+                                                       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+                                                       <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
+                                                       <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">↶</button>
+                                               </form>
+                                       <%/if>
                                <%/if>
                        <%/if>
                        <%if reply.sone.local>
index 482d4d1..a5a57f6 100644 (file)
@@ -11,7 +11,8 @@
                <%foreach pagination.items post>
                        <%include include/viewPost.html>
                <%foreachelse>
-                       <div><%= Page.Index.PostList.Text.NoPostYet|l10n|html></div>
+                       <p><%= Page.Index.PostList.Text.NoPostYet|l10n|html></p>
+                       <p><%= Page.Index.PostList.Text.FollowSomeSones|l10n|html|replace needle=='{link}' replacement=='<a href="knownSones.html">'|replace needle=='{/link}' replacement=='</a>'></p>
                <%/foreach>
                <%include include/pagination.html pageParameter==page>
        </div>
index 119c8a1..4ec88cb 100644 (file)
                        </select>
                </p>
 
-               <h2><%= Page.Options.Section.Cleaning.Title|l10n|html></h2>
-
-               <p><%= Page.Options.Option.ClearOnNextRestart.Description|l10n|html|replace needle=="{strong}" replacement=="<strong>"|replace needle=="{/strong}" replacement=="</strong>"></p>
-               <p><select name="clear-on-next-restart"><option disabled="disabled"><%= WebInterface.SelectBox.Choose|l10n|html></option><option value="true"<%if clear-on-next-restart> selected="selected"<%/if>><%= WebInterface.SelectBox.Yes|l10n|html></option><option value="false"<%if ! clear-on-next-restart> selected="selected"<%/if>><%= WebInterface.SelectBox.No|l10n|html></option></select>
-
-               <p><%= Page.Options.Option.ReallyClearOnNextRestart.Description|l10n|html|replace needle=="{strong}" replacement=="<strong>"|replace needle=="{/strong}" replacement=="</strong>"></p>
-               <p><select name="really-clear-on-next-restart"><option disabled="disabled"><%= WebInterface.SelectBox.Choose|l10n|html></option><option value="true"<%if really-clear-on-next-restart> selected="selected"<%/if>><%= WebInterface.SelectBox.Yes|l10n|html></option><option value="false"<%if ! really-clear-on-next-restart> selected="selected"<%/if>><%= WebInterface.SelectBox.No|l10n|html></option></select>
-
                <p><button type="submit"><%= Page.Options.Button.Save|l10n|html></button></p>
 
        </form>
index 1cca4d0..4b88474 100644 (file)
@@ -52,7 +52,7 @@
                        <%foreach sone.profile.fields field>
                                <div class="profile-field">
                                        <div class="name"><% field.name|html></div>
-                                       <div class="value"><% field.value|parse></div>
+                                       <div class="value"><% field.value|parse sone=sone></div>
                                </div>
                        <%/foreach>
 
index 7de50fb..7baceaa 100644 (file)
@@ -41,6 +41,7 @@ public class SoneTextParserTest extends TestCase {
         * @throws IOException
         *             if an I/O error occurs
         */
+       @SuppressWarnings("static-method")
        public void testPlainText() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(null, null);
                Iterable<Part> parts;
@@ -67,6 +68,7 @@ public class SoneTextParserTest extends TestCase {
         * @throws IOException
         *             if an I/O error occurs
         */
+       @SuppressWarnings("static-method")
        public void testKSKLinks() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(null, null);
                Iterable<Part> parts;
@@ -93,7 +95,7 @@ public class SoneTextParserTest extends TestCase {
         * @throws IOException
         *             if an I/O error occurs
         */
-       @SuppressWarnings("synthetic-access")
+       @SuppressWarnings({ "synthetic-access", "static-method" })
        public void testEmptyLinesAndSoneLinks() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
                Iterable<Part> parts;
@@ -119,7 +121,7 @@ public class SoneTextParserTest extends TestCase {
         *            valid
         * @return The converted text
         */
-       private String convertText(Iterable<Part> parts, Class<?>... validClasses) {
+       private static String convertText(Iterable<Part> parts, Class<?>... validClasses) {
                StringBuilder text = new StringBuilder();
                for (Part part : parts) {
                        assertNotNull("Part", part);