Merge branch 'next' into new-database-38
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 13 Sep 2012 12:34:58 +0000 (14:34 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 13 Sep 2012 14:19:41 +0000 (16:19 +0200)
Conflicts:
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/data/Sone.java
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/text/SoneTextParser.java

89 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/WebOfTrustUpdater.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/data/Post.java
src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/database/memory/MemorySone.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/L10nFilter.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/CssClassNameFilter.java
src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java
src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java
src/main/java/net/pterodactylus/sone/template/JavascriptFilter.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/ReplyGroupFilter.java
src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java
src/main/java/net/pterodactylus/sone/template/SoneAccessor.java
src/main/java/net/pterodactylus/sone/template/SubstringFilter.java
src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java
src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.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/EditImageAjaxPage.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/static/css/sone.css
src/main/resources/static/javascript/sone.js
src/main/resources/templates/about.html
src/main/resources/templates/bookmarks.html
src/main/resources/templates/deleteAlbum.html
src/main/resources/templates/deleteImage.html
src/main/resources/templates/deleteSone.html
src/main/resources/templates/editProfile.html
src/main/resources/templates/imageBrowser.html
src/main/resources/templates/include/browseAlbums.html
src/main/resources/templates/include/createSone.html
src/main/resources/templates/include/head.html
src/main/resources/templates/include/pagination.html
src/main/resources/templates/include/soneMenu.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/include/viewSone.html
src/main/resources/templates/index.html
src/main/resources/templates/invalid.html
src/main/resources/templates/knownSones.html
src/main/resources/templates/new.html
src/main/resources/templates/notify/newVersionNotification.html
src/main/resources/templates/notify/soneInsertNotification.html
src/main/resources/templates/notify/wotMissingNotification.html
src/main/resources/templates/options.html
src/main/resources/templates/viewPost.html
src/main/resources/templates/viewSone.html

diff --git a/pom.xml b/pom.xml
index 3825dfc..9ea02ae 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.1</version>
+       <version>0.8.2</version>
        <dependencies>
                <dependency>
                        <groupId>net.pterodactylus</groupId>
                        <artifactId>utils</artifactId>
-                       <version>0.12-SNAPSHOT</version>
+                       <version>0.12</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
index 922f0d1..0b1379a 100644 (file)
@@ -47,6 +47,7 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
 import net.pterodactylus.sone.data.Sone.SoneStatus;
 import net.pterodactylus.sone.data.TemporaryImage;
+import net.pterodactylus.sone.data.impl.PostImpl;
 import net.pterodactylus.sone.database.Database;
 import net.pterodactylus.sone.fcp.FcpInterface;
 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
@@ -54,8 +55,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;
@@ -116,6 +115,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;
 
@@ -182,8 +184,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, Database database, FreenetInterface freenetInterface, IdentityManager identityManager) {
+       public Core(Configuration configuration, Database database, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater) {
                super("Sone Core");
                this.configuration = configuration;
                this.database = database;
@@ -192,6 +196,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;
        }
 
        //
@@ -471,7 +476,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (posts) {
                        Post post = posts.get(postId);
                        if ((post == null) && create) {
-                               post = new Post(postId);
+                               post = new PostImpl(postId);
                                posts.put(postId, post);
                        }
                        return post;
@@ -752,30 +757,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        }
 
        /**
-<<<<<<< HEAD
-=======
-        * Adds a local Sone from the given ID which has to be the ID of an own
-        * identity.
-        *
-        * @param id
-        *            The ID of an own identity to add a Sone for
-        * @return The added (or already existing) Sone
-        */
-       public Sone addLocalSone(String id) {
-               if (database.getLocalSone(id, false) != null) {
-                       logger.log(Level.FINE, "Tried to add known local Sone: %s", id);
-                       return database.getLocalSone(id, false);
-               }
-               OwnIdentity ownIdentity = identityManager.getOwnIdentity(id);
-               if (ownIdentity == null) {
-                       logger.log(Level.INFO, "Invalid Sone ID: %s", id);
-                       return null;
-               }
-               return addLocalSone(ownIdentity);
-       }
-
-       /**
->>>>>>> 4f36598... Store locality of a Sone in the Sone itself, remove related methods from Database.
         * Adds a local Sone from the given own identity.
         *
         * @param ownIdentity
@@ -816,10 +797,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);
@@ -969,26 +948,8 @@ 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 (!origin.isLocal()) {
-                       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.
+        * Sets the trust value of the given origin Sone for
+        * the target Sone.
         *
         * @param origin
         *            The origin Sone
@@ -999,11 +960,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());
        }
 
        /**
@@ -1016,11 +973,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);
        }
 
        /**
@@ -1208,12 +1161,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                SoneInserter soneInserter = soneInserters.remove(sone);
                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) {
@@ -1484,7 +1433,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone));
                        return null;
                }
-               final Post post = new Post(sone, time, text);
+               final Post post = new PostImpl(sone, time, text);
                if (recipient != null) {
                        post.setRecipient(recipient);
                }
@@ -1741,7 +1690,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (albums) {
                        albums.remove(album.getId());
                }
-               saveSone(album.getSone());
+               touchConfiguration();
        }
 
        /**
@@ -1781,7 +1730,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (images) {
                        images.remove(image.getId());
                }
-               saveSone(image.getSone());
+               touchConfiguration();
        }
 
        /**
@@ -1851,6 +1800,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                loadConfiguration();
                updateChecker.addUpdateListener(this);
                updateChecker.start();
+               webOfTrustUpdater.start();
        }
 
        /**
@@ -1877,10 +1827,13 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         */
        @Override
        public void serviceStop() {
-               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();
@@ -2020,13 +1973,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);
                }
        }
 
@@ -2047,6 +1998,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        configuration.getIntValue("Option/ConfigurationVersion").setValue(0);
                        configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal());
                        configuration.getIntValue("Option/PostsPerPage").setValue(options.getIntegerOption("PostsPerPage").getReal());
+                       configuration.getIntValue("Option/ImagesPerPage").setValue(options.getIntegerOption("ImagesPerPage").getReal());
                        configuration.getIntValue("Option/CharactersPerPost").setValue(options.getIntegerOption("CharactersPerPost").getReal());
                        configuration.getIntValue("Option/PostCutOffLength").setValue(options.getIntegerOption("PostCutOffLength").getReal());
                        configuration.getBooleanValue("Option/RequireFullAccess").setValue(options.getBooleanOption("RequireFullAccess").getReal());
@@ -2055,9 +2007,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;
@@ -2133,6 +2082,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
 
                }));
                options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10, new IntegerRangeValidator(1, Integer.MAX_VALUE)));
+               options.addIntegerOption("ImagesPerPage", new DefaultOption<Integer>(9, new IntegerRangeValidator(1, Integer.MAX_VALUE)));
                options.addIntegerOption("CharactersPerPost", new DefaultOption<Integer>(400, new OrValidator<Integer>(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator<Integer>(-1))));
                options.addIntegerOption("PostCutOffLength", new DefaultOption<Integer>(200, new OrValidator<Integer>(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator<Integer>(-1))));
                options.addBooleanOption("RequireFullAccess", new DefaultOption<Boolean>(false));
@@ -2156,23 +2106,10 @@ 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");
+               loadConfigurationValue("ImagesPerPage");
                loadConfigurationValue("CharactersPerPost");
                loadConfigurationValue("PostCutOffLength");
                options.getBooleanOption("RequireFullAccess").set(configuration.getBooleanValue("Option/RequireFullAccess").getValue(null));
@@ -2181,7 +2118,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;
@@ -2274,12 +2210,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;
                }
        }
@@ -2452,7 +2388,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);
        }
 
@@ -2554,6 +2490,39 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                }
 
                /**
+                * Returns the number of images to show per page.
+                *
+                * @return The number of images to show per page
+                */
+               public int getImagesPerPage() {
+                       return options.getIntegerOption("ImagesPerPage").get();
+               }
+
+               /**
+                * Validates the number of images per page.
+                *
+                * @param imagesPerPage
+                *            The number of images per page
+                * @return {@code true} if the number of images per page was valid,
+                *         {@code false} otherwise
+                */
+               public boolean validateImagesPerPage(Integer imagesPerPage) {
+                       return options.getIntegerOption("ImagesPerPage").validate(imagesPerPage);
+               }
+
+               /**
+                * Sets the number of images per page.
+                *
+                * @param imagesPerPage
+                *            The number of images per page
+                * @return This preferences object
+                */
+               public Preferences setImagesPerPage(Integer imagesPerPage) {
+                       options.getIntegerOption("ImagesPerPage").set(imagesPerPage);
+                       return this;
+               }
+
+               /**
                 * Returns the number of characters per post, or <code>-1</code> if the
                 * posts should not be cut off.
                 *
@@ -2784,58 +2753,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
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 334a944..f171475 100644 (file)
@@ -18,7 +18,6 @@
 package net.pterodactylus.sone.data;
 
 import java.util.Comparator;
-import java.util.UUID;
 
 import net.pterodactylus.util.collection.filter.Filter;
 
@@ -28,7 +27,7 @@ import net.pterodactylus.util.collection.filter.Filter;
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class Post {
+public interface Post {
 
        /** Comparator for posts, sorts descending by time. */
        public static final Comparator<Post> TIME_COMPARATOR = new Comparator<Post>() {
@@ -50,79 +49,6 @@ public class Post {
 
        };
 
-       /** The GUID of the post. */
-       private final UUID id;
-
-       /** The Sone this post belongs to. */
-       private volatile Sone sone;
-
-       /** The Sone of the recipient. */
-       private volatile Sone recipient;
-
-       /** The time of the post (in milliseconds since Jan 1, 1970 UTC). */
-       private volatile long time;
-
-       /** The text of the post. */
-       private volatile String text;
-
-       /** Whether the post is known. */
-       private volatile boolean known;
-
-       /**
-        * Creates a new post.
-        *
-        * @param id
-        *            The ID of the post
-        */
-       public Post(String id) {
-               this(id, null, 0, null);
-       }
-
-       /**
-        * Creates a new post.
-        *
-        * @param sone
-        *            The Sone this post belongs to
-        * @param text
-        *            The text of the post
-        */
-       public Post(Sone sone, String text) {
-               this(sone, System.currentTimeMillis(), text);
-       }
-
-       /**
-        * Creates a new post.
-        *
-        * @param sone
-        *            The Sone this post belongs to
-        * @param time
-        *            The time of the post (in milliseconds since Jan 1, 1970 UTC)
-        * @param text
-        *            The text of the post
-        */
-       public Post(Sone sone, long time, String text) {
-               this(UUID.randomUUID().toString(), sone, time, text);
-       }
-
-       /**
-        * Creates a new post.
-        *
-        * @param id
-        *            The ID of the post
-        * @param sone
-        *            The Sone this post belongs to
-        * @param time
-        *            The time of the post (in milliseconds since Jan 1, 1970 UTC)
-        * @param text
-        *            The text of the post
-        */
-       public Post(String id, Sone sone, long time, String text) {
-               this.id = UUID.fromString(id);
-               this.sone = sone;
-               this.time = time;
-               this.text = text;
-       }
-
        //
        // ACCESSORS
        //
@@ -132,18 +58,14 @@ public class Post {
         *
         * @return The ID of the post
         */
-       public String getId() {
-               return id.toString();
-       }
+       public String getId();
 
        /**
         * Returns the Sone this post belongs to.
         *
         * @return The Sone of this post
         */
-       public Sone getSone() {
-               return sone;
-       }
+       public Sone getSone();
 
        /**
         * Sets the Sone of this post.
@@ -152,19 +74,14 @@ public class Post {
         *            The Sone of this post
         * @return This post (for method chaining)
         */
-       public Post setSone(Sone sone) {
-               this.sone = sone;
-               return this;
-       }
+       public Post setSone(Sone sone);
 
        /**
         * Returns the recipient of this post, if any.
         *
         * @return The recipient of this post, or {@code null}
         */
-       public Sone getRecipient() {
-               return recipient;
-       }
+       public Sone getRecipient();
 
        /**
         * Sets the recipient of this post.
@@ -173,21 +90,14 @@ public class Post {
         *            The recipient of this post, or {@code null}
         * @return This post (for method chaining)
         */
-       public Post setRecipient(Sone recipient) {
-               if (!sone.equals(recipient)) {
-                       this.recipient = recipient;
-               }
-               return this;
-       }
+       public Post setRecipient(Sone recipient);
 
        /**
         * Returns the time of the post.
         *
         * @return The time of the post (in milliseconds since Jan 1, 1970 UTC)
         */
-       public long getTime() {
-               return time;
-       }
+       public long getTime();
 
        /**
         * Sets the time of this post.
@@ -196,19 +106,14 @@ public class Post {
         *            The time of this post (in milliseconds since Jan 1, 1970 UTC)
         * @return This post (for method chaining)
         */
-       public Post setTime(long time) {
-               this.time = time;
-               return this;
-       }
+       public Post setTime(long time);
 
        /**
         * Returns the text of the post.
         *
         * @return The text of the post
         */
-       public String getText() {
-               return text;
-       }
+       public String getText();
 
        /**
         * Sets the text of this post.
@@ -217,19 +122,14 @@ public class Post {
         *            The text of this post
         * @return This post (for method chaining)
         */
-       public Post setText(String text) {
-               this.text = text;
-               return this;
-       }
+       public Post setText(String text);
 
        /**
         * Returns whether this post is known.
         *
         * @return {@code true} if this post is known, {@code false} otherwise
         */
-       public boolean isKnown() {
-               return known;
-       }
+       public boolean isKnown();
 
        /**
         * Sets whether this post is known.
@@ -238,41 +138,6 @@ public class Post {
         *            {@code true} if this post is known, {@code false} otherwise
         * @return This post
         */
-       public Post setKnown(boolean known) {
-               this.known = known;
-               return this;
-       }
-
-       //
-       // OBJECT METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public int hashCode() {
-               return id.hashCode();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public boolean equals(Object object) {
-               if (!(object instanceof Post)) {
-                       return false;
-               }
-               Post post = (Post) object;
-               return post.id.equals(id);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public String toString() {
-               return getClass().getName() + "[id=" + id + ",sone=" + sone + ",time=" + time + ",text=" + text + "]";
-       }
+       public Post setKnown(boolean known);
 
 }
diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java
new file mode 100644 (file)
index 0000000..2269738
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Sone - PostImpl.java - Copyright © 2010–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.data.impl;
+
+import java.util.UUID;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+
+/**
+ * A post is a short message that a user writes in his Sone to let other users
+ * know what is going on.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PostImpl implements Post {
+
+       /** The GUID of the post. */
+       private final UUID id;
+
+       /** The Sone this post belongs to. */
+       private volatile Sone sone;
+
+       /** The Sone of the recipient. */
+       private volatile Sone recipient;
+
+       /** The time of the post (in milliseconds since Jan 1, 1970 UTC). */
+       private volatile long time;
+
+       /** The text of the post. */
+       private volatile String text;
+
+       /** Whether the post is known. */
+       private volatile boolean known;
+
+       /**
+        * Creates a new post.
+        *
+        * @param id
+        *            The ID of the post
+        */
+       public PostImpl(String id) {
+               this(id, null, 0, null);
+       }
+
+       /**
+        * Creates a new post.
+        *
+        * @param sone
+        *            The Sone this post belongs to
+        * @param text
+        *            The text of the post
+        */
+       public PostImpl(Sone sone, String text) {
+               this(sone, System.currentTimeMillis(), text);
+       }
+
+       /**
+        * Creates a new post.
+        *
+        * @param sone
+        *            The Sone this post belongs to
+        * @param time
+        *            The time of the post (in milliseconds since Jan 1, 1970 UTC)
+        * @param text
+        *            The text of the post
+        */
+       public PostImpl(Sone sone, long time, String text) {
+               this(UUID.randomUUID().toString(), sone, time, text);
+       }
+
+       /**
+        * Creates a new post.
+        *
+        * @param id
+        *            The ID of the post
+        * @param sone
+        *            The Sone this post belongs to
+        * @param time
+        *            The time of the post (in milliseconds since Jan 1, 1970 UTC)
+        * @param text
+        *            The text of the post
+        */
+       public PostImpl(String id, Sone sone, long time, String text) {
+               this.id = UUID.fromString(id);
+               this.sone = sone;
+               this.time = time;
+               this.text = text;
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getId() {
+               return id.toString();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Sone getSone() {
+               return sone;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public PostImpl setSone(Sone sone) {
+               this.sone = sone;
+               return this;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Sone getRecipient() {
+               return recipient;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public PostImpl setRecipient(Sone recipient) {
+               if (!sone.equals(recipient)) {
+                       this.recipient = recipient;
+               }
+               return this;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public long getTime() {
+               return time;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public PostImpl setTime(long time) {
+               this.time = time;
+               return this;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getText() {
+               return text;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public PostImpl setText(String text) {
+               this.text = text;
+               return this;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public boolean isKnown() {
+               return known;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public PostImpl setKnown(boolean known) {
+               this.known = known;
+               return this;
+       }
+
+       //
+       // OBJECT METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public int hashCode() {
+               return id.hashCode();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public boolean equals(Object object) {
+               if (!(object instanceof PostImpl)) {
+                       return false;
+               }
+               PostImpl post = (PostImpl) object;
+               return post.id.equals(id);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String toString() {
+               return getClass().getName() + "[id=" + id + ",sone=" + sone + ",time=" + time + ",text=" + text + "]";
+       }
+
+}
index 1c5912f..da487b3 100644 (file)
@@ -576,7 +576,9 @@ public class MemorySone implements Sone {
        @Override
        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 1cde976..7fffd5c 100644 (file)
@@ -52,7 +52,7 @@ public class L10nFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public String format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public String format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                if (parameters.isEmpty()) {
                        return webInterface.getL10n().getString(String.valueOf(data));
                }
@@ -60,11 +60,6 @@ public class L10nFilter implements Filter {
                int parameterIndex = 0;
                while (parameters.containsKey(String.valueOf(parameterIndex))) {
                        Object value = parameters.get(String.valueOf(parameterIndex));
-                       if (((String) value).startsWith("=")) {
-                               value = ((String) value).substring(1);
-                       } else {
-                               value = templateContext.get((String) value);
-                       }
                        parameterValues.add(value);
                        ++parameterIndex;
                }
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..3c3c9f9 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;
 
@@ -143,7 +142,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 +154,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());
        }
 
        //
@@ -310,7 +288,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 +296,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 da50101..42e2fe1 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.database.Database;
 import net.pterodactylus.sone.database.memory.MemoryDatabase;
 import net.pterodactylus.sone.fcp.FcpInterface;
@@ -85,7 +86,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
        }
 
        /** The version. */
-       public static final Version VERSION = new Version(0, 8, 1);
+       public static final Version VERSION = new Version(0, 8, 2);
 
        /** The logger. */
        private static final Logger logger = Logging.getLogger(SonePlugin.class);
@@ -193,8 +194,12 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
                        /* create Sone database. */
                        Database soneDatabase = new MemoryDatabase();
 
+                       /* create trust updater. */
+                       WebOfTrustUpdater trustUpdater = new WebOfTrustUpdater(webOfTrustConnector);
+                       trustUpdater.init();
+
                        /* create core. */
-                       core = new Core(oldConfiguration, soneDatabase, freenetInterface, identityManager);
+                       core = new Core(oldConfiguration, soneDatabase, 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 7db7be0..0282156 100644 (file)
@@ -35,7 +35,7 @@ public class CssClassNameFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                return String.valueOf(data).replaceAll("[^a-zA-Z0-9-]", "_");
        }
 
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 50ce8fd..06a11d7 100644 (file)
@@ -65,7 +65,7 @@ public class ImageLinkFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                Image image = null;
                if (data instanceof String) {
                        image = core.getImage((String) data, false);
@@ -75,14 +75,11 @@ public class ImageLinkFilter implements Filter {
                if (image == null) {
                        return null;
                }
-               String imageClass = parameters.get("class");
+               String imageClass = String.valueOf(parameters.get("class"));
                int maxWidth = Numbers.safeParseInteger(parameters.get("max-width"), Integer.MAX_VALUE);
                int maxHeight = Numbers.safeParseInteger(parameters.get("max-height"), Integer.MAX_VALUE);
                String mode = String.valueOf(parameters.get("mode"));
-               String title = parameters.get("title");
-               if ((title != null) && title.startsWith("=")) {
-                       title = String.valueOf(templateContext.get(title.substring(1)));
-               }
+               String title = String.valueOf(parameters.get("title"));
 
                TemplateContext linkTemplateContext = templateContextFactory.createTemplateContext();
                linkTemplateContext.set("class", imageClass);
index 966b81f..2dc963f 100644 (file)
@@ -36,7 +36,7 @@ public class JavascriptFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                StringBuilder javascriptString = new StringBuilder();
                javascriptString.append('"');
                for (char c : String.valueOf(data).toCharArray()) {
index 1b46fa0..1936656 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}
@@ -86,20 +86,16 @@ public class ParserFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                String text = String.valueOf(data);
-               int length = Numbers.safeParseInteger(parameters.get("length"), Numbers.safeParseInteger(templateContext.get(parameters.get("length")), -1));
-               int cutOffLength = Numbers.safeParseInteger(parameters.get("cut-off-length"), Numbers.safeParseInteger(templateContext.get(parameters.get("cut-off-length")), length));
-               String soneKey = parameters.get("sone");
-               if (soneKey == null) {
-                       soneKey = "sone";
-               }
-               Sone sone = (Sone) templateContext.get(soneKey);
-               if (sone == null) {
-                       sone = core.getSone(soneKey, false);
+               int length = Numbers.safeParseInteger(parameters.get("length"), Numbers.safeParseInteger(templateContext.get(String.valueOf(parameters.get("length"))), -1));
+               int cutOffLength = Numbers.safeParseInteger(parameters.get("cut-off-length"), Numbers.safeParseInteger(templateContext.get(String.valueOf(parameters.get("cut-off-length"))), length));
+               Object sone = parameters.get("sone");
+               if (sone instanceof String) {
+                       sone = core.getSone((String) sone, false);
                }
                FreenetRequest request = (FreenetRequest) templateContext.get("request");
-               SoneTextParserContext context = new SoneTextParserContext(request, sone);
+               SoneTextParserContext context = new SoneTextParserContext(request, (Sone) sone);
                StringWriter parsedTextWriter = new StringWriter();
                try {
                        Iterable<Part> parts = soneTextParser.parse(context, new StringReader(text));
@@ -245,7 +241,22 @@ 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) {
+                                       excerpt.setLength(20);
+                                       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. */
+               }
        }
 
        /**
@@ -271,26 +282,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 ceb3e22..19177c3 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 37d4175..ff4b64e 100644 (file)
@@ -42,7 +42,7 @@ public class ReplyGroupFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                @SuppressWarnings("unchecked")
                List<PostReply> allReplies = (List<PostReply>) data;
                Map<Post, Set<Sone>> postSones = new HashMap<Post, Set<Sone>>();
index 45d6ba7..3a89021 100644 (file)
@@ -33,8 +33,7 @@ import net.pterodactylus.util.template.TemplateContext;
 /**
  * This filter expects a {@link FreenetRequest} as input and outputs a
  * {@link URI} that is modified by the parameters. The name of the parameter is
- * handed in as “name”, the value may either be stored in “value”, or in a
- * template variable whose key is stored in “key”.
+ * handed in as “name”, the new value is stored in “value”.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
@@ -44,24 +43,10 @@ public class RequestChangeFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                FreenetRequest request = (FreenetRequest) data;
-               String name = parameters.get("name");
-               String nameKey = parameters.get("nameKey");
-               if (nameKey != null) {
-                       name = String.valueOf(templateContext.get(nameKey));
-               }
-               String key = parameters.get("key");
-               String value = null;
-               if (key != null) {
-                       value = String.valueOf(templateContext.get(key));
-               }
-               if (value == null) {
-                       value = parameters.get("value");
-               }
-               if (value == null) {
-                       return request.getUri();
-               }
+               String name = String.valueOf(parameters.get("name"));
+               String value = String.valueOf(parameters.get("value"));
 
                Map<String, String> values = new HashMap<String, String>();
                Collection<String> parameterNames = request.getHttpRequest().getParameterNames();
index ffad018..9d4bdc8 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;
@@ -104,7 +105,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 b60f4d3..005eb1f 100644 (file)
@@ -37,9 +37,9 @@ public class SubstringFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
-               String startString = parameters.get("start");
-               String lengthString = parameters.get("length");
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
+               String startString = String.valueOf(parameters.get("start"));
+               String lengthString = String.valueOf(parameters.get("length"));
                int start = 0;
                try {
                        start = Integer.parseInt(startString);
index 45c1cb8..e3ddd59 100644 (file)
@@ -36,7 +36,7 @@ public class UniqueElementFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                if (!(data instanceof Collection<?>)) {
                        return data;
                }
index fa68c6f..c87a97b 100644 (file)
@@ -54,7 +54,7 @@ public class UnknownDateFilter implements Filter {
         * {@inheritDoc}
         */
        @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
                if (data instanceof Long) {
                        if ((Long) data == 0) {
                                return l10nHandler.getString(unknownKey);
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 558edb0..19db65b 100644 (file)
@@ -31,6 +31,7 @@ import net.pterodactylus.sone.core.SoneProvider;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.database.memory.MemorySone;
+import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.logging.Logging;
 import freenet.keys.FreenetURI;
 
@@ -110,187 +111,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 MemorySone(soneId, 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 MemorySone(soneId, false);
+                                                       }
+                                                       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 93d35b9..da48ec5 100644 (file)
@@ -26,6 +26,8 @@ import net.pterodactylus.sone.data.Album;
 import net.pterodactylus.sone.data.Image;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.collection.Pagination;
+import net.pterodactylus.util.number.Numbers;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.template.TemplateContext;
 
@@ -63,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);
@@ -87,7 +90,9 @@ public class ImageBrowserPage extends SoneTemplatePage {
                                albums.addAll(sone.getAllAlbums());
                        }
                        Collections.sort(albums, Album.TITLE_COMPARATOR);
-                       templateContext.set("albums", albums);
+                       Pagination<Album> albumPagination = new Pagination<Album>(albums, 12).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
+                       templateContext.set("albumPagination", albumPagination);
+                       templateContext.set("albums", albumPagination.getItems());
                        return;
                }
                Sone sone = getCurrentSone(request.getToadletContext(), false);
index fbc8cf9..c770da3 100644 (file)
@@ -90,6 +90,12 @@ public class OptionsPage extends SoneTemplatePage {
                        } else {
                                preferences.setPostsPerPage(postsPerPage);
                        }
+                       Integer imagesPerPage = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("images-per-page", 4), null);
+                       if (!preferences.validateImagesPerPage(imagesPerPage)) {
+                               fieldErrors.add("images-per-page");
+                       } else {
+                               preferences.setImagesPerPage(imagesPerPage);
+                       }
                        Integer charactersPerPost = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("characters-per-post", 10), null);
                        if (!preferences.validateCharactersPerPost(charactersPerPost)) {
                                fieldErrors.add("characters-per-post");
@@ -126,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());
@@ -146,6 +148,7 @@ public class OptionsPage extends SoneTemplatePage {
                }
                templateContext.set("insertion-delay", preferences.getInsertionDelay());
                templateContext.set("posts-per-page", preferences.getPostsPerPage());
+               templateContext.set("images-per-page", preferences.getImagesPerPage());
                templateContext.set("characters-per-post", preferences.getCharactersPerPost());
                templateContext.set("post-cut-off-length", preferences.getPostCutOffLength());
                templateContext.set("require-full-access", preferences.isRequireFullAccess());
@@ -154,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 22e5b57..691453d 100644 (file)
@@ -168,7 +168,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 +189,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 +229,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;
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 7654776..b5f6dfb 100644 (file)
@@ -122,6 +122,7 @@ import net.pterodactylus.util.template.FormatFilter;
 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;
@@ -259,6 +260,7 @@ public class WebInterface implements CoreListener {
                templateContextFactory.addFilter("in", new ContainsFilter());
                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.addTemplateObject("webInterface", this);
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 b549718..4a1689b 100644 (file)
@@ -80,7 +80,7 @@ public class EditImageAjaxPage extends JsonPage {
                String description = request.getHttpRequest().getParam("description").trim();
                image.setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description));
                webInterface.getCore().touchConfiguration();
-               return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", (String) parserFilter.format(new TemplateContext(), image.getDescription(), new MapBuilder<String, String>().put("sone", image.getSone().getId()).get()));
+               return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", (String) parserFilter.format(new TemplateContext(), image.getDescription(), new MapBuilder<String, Object>().put("sone", image.getSone()).get()));
        }
 
 }
index 7c4bda4..34018af 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 3fe965e..5c4367a 100644 (file)
@@ -55,6 +55,7 @@ Page.Options.Option.ShowAvatars.Always.Description=Always show custom avatars. B
 Page.Options.Section.RuntimeOptions.Title=Runtime Behaviour
 Page.Options.Option.InsertionDelay.Description=The number of seconds the Sone inserter waits after a modification of a Sone before it is being inserted.
 Page.Options.Option.PostsPerPage.Description=The number of posts to display on a page before pagination controls are being shown.
+Page.Options.Option.ImagesPerPage.Description=The number of images to display on a page before pagination controls are being shown.
 Page.Options.Option.CharactersPerPost.Description=The number of characters to display from a post before cutting it off and showing a link to expand it (-1 to disable). The actual length of the snippet is determined by the option below.
 Page.Options.Option.PostCutOffLength.Description=The number of characters that are displayed if a post is deemed to long (see option above).
 Page.Options.Option.RequireFullAccess.Description=Whether to deny access to Sone to any host that has not been granted full access.
@@ -95,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
@@ -408,6 +410,7 @@ WebInterface.DefaultText.UploadImage.Description=Image description
 WebInterface.DefaultText.EditImage.Title=Image title
 WebInterface.DefaultText.EditImage.Description=Image description
 WebInterface.DefaultText.Option.PostsPerPage=Number of posts to show on a page
+WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a 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.PositiveTrust=The positive trust to assign
@@ -422,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..7da1634 100644 (file)
@@ -55,6 +55,7 @@ Page.Options.Option.ShowAvatars.Always.Description=Montre tout le temps les avat
 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.ImagesPerPage.Description=The number of images to display on a page before pagination controls are being shown.
 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é.
@@ -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=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=Nouveaux messages et réponses - Sone
 Page.New.Page.Title=Nouveaux messages et réponses
@@ -408,6 +410,7 @@ WebInterface.DefaultText.UploadImage.Description=Image description
 WebInterface.DefaultText.EditImage.Title=Image title
 WebInterface.DefaultText.EditImage.Description=Image description
 WebInterface.DefaultText.Option.PostsPerPage=Nombre de messages à afficher par page
+WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a 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.PositiveTrust=La note de confiance positive à assigner
@@ -422,6 +425,7 @@ 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=Homepage
 
 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.
@@ -446,3 +450,4 @@ Notification.ImageInsertFailed.Text=Les images suivantes ne peuvent être insér
 Notification.Mention.ShortText=Vous avez été mentionné.
 Notification.Mention.Text=Vous avez été mentionné dans les messages suivants:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
+#to update: 58, 99, 232 (?), 239, 241, 404-411, 413-415, 428, 452 (?)
\ No newline at end of file
index e076250..b27ce41 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=The number of images to display on a page before pagination controls are being shown.
 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=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=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=Number of images to show on a page
 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=Homepage
 
 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.
@@ -446,3 +450,4 @@ Notification.ImageInsertFailed.Text=De følgende bildene kunne ikke bli innsatt:
 Notification.Mention.ShortText=Du har blitt nevnt:
 Notification.Mention.Text=Du har blitt nevnt i følgende innlegg:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#sekund|1#sekund|1<sekunder}
+# to update: 58, 99, 413, 428
\ No newline at end of file
index c143cde..1392796 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=The number of images to display on a page before pagination controls are being shown.
+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=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=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=Number of images to show on a page
+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=Homepage
 
 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:
@@ -446,3 +450,4 @@ Notification.ImageInsertFailed.Text=Nie można załadowac następujących obraz
 Notification.Mention.ShortText=Zostałeś oznaczony.
 Notification.Mention.Text=Zostałeś oznaczony w następujących postach:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
+# to update: 58, 99, 413, 428
index bc43303..aa8cb16 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=The number of images to display on a page before pagination controls are being shown.
 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=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=Новые сообщения и ответы - Sone
 Page.New.Page.Title=Новые сообщения и ответы
@@ -408,6 +410,7 @@ WebInterface.DefaultText.UploadImage.Description=Описание изображ
 WebInterface.DefaultText.EditImage.Title=Название изображения
 WebInterface.DefaultText.EditImage.Description=Описание изображения
 WebInterface.DefaultText.Option.PostsPerPage=Количество сообщений, показываемых на странице
+WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
 WebInterface.DefaultText.Option.CharactersPerPost=Количество символов, которое должно быть у сообщения, чтобы оно было сокращено
 WebInterface.DefaultText.Option.PostCutOffLength=Количество символов в сокращенном варианте сообщения
 WebInterface.DefaultText.Option.PositiveTrust=Положительное доверие для назначения
@@ -422,6 +425,7 @@ WebInterface.SelectBox.No=Нет
 WebInterface.ClickToShow.Replies=Нажмите, чтобы показать скрытые ответы.
 WebInterface.VersionInformation.CurrentVersion=Текущая версия:
 WebInterface.VersionInformation.LatestVersion=Последняя версия:
+WebInterface.VersionInformation.Homepage=Homepage
 
 Notification.ClickHereToRead=Нажмите здесь, чтобы прочитать полный текст уведомления.
 Notification.FirstStart.Text=Видимо, вы запустили Sone в первый раз. Чтобы начать, создайте новый Sone из личность web of trust и начните подписываться на другие Sone.
@@ -446,3 +450,4 @@ Notification.ImageInsertFailed.Text=Следующие изображения н
 Notification.Mention.ShortText=Вас упомянули.
 Notification.Mention.Text=Вас упомянули в следующих сообщениях:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#секунд|1#секунда|2#секунды|4<секунд}
+# to update: 58, 99, 413, 428
\ No newline at end of file
index 7754528..1767090 100644 (file)
@@ -800,6 +800,10 @@ textarea {
        float: right;
 }
 
+#sone .clear {
+       clear: both;
+}
+
 #sone h1 {
        font-family: inherit;
        font-size: 200%;
index 2063576..49f372f 100644 (file)
@@ -1193,7 +1193,7 @@ function checkForRemovedReplies(oldNotification, newNotification) {
 }
 
 function getStatus() {
-       ajaxGet("getStatus.ajax", isViewSonePage() ? {"soneIds": getShownSoneId() } : {"loadAllSones": isKnownSonesPage()}, function(data, textStatus) {
+       ajaxGet("getStatus.ajax", isViewSonePage() ? {"soneIds": getShownSoneId() } : isKnownSonesPage() ? {"soneIds": getShownSoneIds() } : {}, function(data, textStatus) {
                if ((data != null) && data.success) {
                        /* process Sone information. */
                        $.each(data.sones, function(index, value) {
@@ -1359,6 +1359,20 @@ function getShownSoneId() {
 }
 
 /**
+ * Returns the ID of all currently visible Sones. This is mainly used on the
+ * “Known Sones” page.
+ *
+ * @returns The ID of the currently shown Sones
+ */
+function getShownSoneIds() {
+       var soneIds = new Array();
+       $("#sone #known-sones .sone .id").each(function() {
+               soneIds.push($(this).text());
+       });
+       return soneIds.join(",");
+}
+
+/**
  * Returns whether the current page is a “view post” page.
  *
  * @returns {Boolean} <code>true</code> if the current page is a “view post”
index 7465529..74ea9f0 100644 (file)
@@ -5,13 +5,13 @@
        <p>Sone – The Freenet Social Network Plugin, Version <% version|html>, © 2010–2012 by David ‘Bombe’ Roden.</p>
 
        <p>
-               <%= Page.About.Flattr.Description|l10n|html|replace needle="{link}" replacement='<a href="/?_CHECKED_HTTP_=https://www.flattr.com/" title="Flattr Homepage" target="_blank">'|replace needle="{/link}" replacement='</a>'>
+               <%= Page.About.Flattr.Description|l10n|html|replace needle=="{link}" replacement=='<a href="/?_CHECKED_HTTP_=https://www.flattr.com/" title="Flattr Homepage" target="_blank">'|replace needle=="{/link}" replacement=='</a>'>
        </p>
 
        <h2><%= Page.About.Homepage.Title|l10n|html></h2>
 
        <p>
-               <%= Page.About.Homepage.Description|l10n|html|replace needle="{link}" replacement='<a href="/USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/49/">'|replace needle="{/link}" replacement='</a>'>
+               <%= Page.About.Homepage.Description|l10n|html|replace needle=="{link}" replacement=='<a href="/USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/49/">'|replace needle=="{/link}" replacement=='</a>'>
        </p>
 
        <h2><%= Page.About.License.Title|l10n|html></h2>
index cf4e7ce..ef2cf68 100644 (file)
@@ -5,14 +5,13 @@
        <h1><%= Page.Bookmarks.Page.Title|l10n|html></h1>
 
        <div id="posts">
-               <%= page|store key=pageParameter>
-               <%include include/pagination.html>
+               <%include include/pagination.html pageParameter==page>
                <%foreach posts post>
                        <%include include/viewPost.html>
                <%/foreach>
-               <%include include/pagination.html>
+               <%include include/pagination.html pageParameter==page>
                <%if postsNotLoaded>
-                       <p><%= Page.Bookmarks.Text.PostsNotLoaded|l10n|html|replace needle='{link}' replacement='<a href="unbookmark.html?post=allNotLoaded">'|replace needle='{/link}' replacement='</a>'></p>
+                       <p><%= Page.Bookmarks.Text.PostsNotLoaded|l10n|html|replace needle=='{link}' replacement=='<a href="unbookmark.html?post=allNotLoaded">'|replace needle=='{/link}' replacement=='</a>'></p>
                <%elseif posts.empty>
                        <p><%= Page.Bookmarks.Text.NoBookmarks|l10n|html></p>
                <%/if>
index cb27c19..ea583fc 100644 (file)
@@ -2,7 +2,7 @@
 
        <h1><%= Page.DeleteAlbum.Page.Title|l10n|html></h1>
 
-       <p><%= Page.DeleteAlbum.Text.AlbumWillBeGone|l10n|replace needle="{title}" replacementKey=album.title|html></p>
+       <p><%= Page.DeleteAlbum.Text.AlbumWillBeGone|l10n|replace needle=="{title}" replacementKey==album.title|html></p>
 
        <form method="post">
                <input type="hidden" name="formPassword" value="<% formPassword|html>" />
index 68b403e..3fb89e3 100644 (file)
@@ -2,7 +2,7 @@
 
        <h1><%= Page.DeleteImage.Page.Title|l10n|html></h1>
 
-       <p><%= Page.DeleteImage.Text.ImageWillBeGone|l10n|replace needle="{image}" replacementKey=image.title|replace needle="{album}" replacementKey=image.album.title|html></p>
+       <p><%= Page.DeleteImage.Text.ImageWillBeGone|l10n|replace needle=="{image}" replacement=image.title|replace needle=="{album}" replacement=image.album.title|html></p>
 
        <form method="post">
                <input type="hidden" name="formPassword" value="<% formPassword|html>" />
index cc6023f..c84aeb0 100644 (file)
@@ -1,6 +1,6 @@
 <%include include/head.html>
 
-       <h1><%= Page.DeleteSone.Page.Title|l10n|replace needle="{sone}" replacementKey=currentSone.name|html></h1>
+       <h1><%= Page.DeleteSone.Page.Title|l10n|replace needle=="{sone}" replacement=currentSone.name|html></h1>
 
        <p><%= Page.DeleteSone.Page.Description|l10n|html></p>
 
index e140ac4..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>
                                <li>
                                        <input type="radio" name="avatar-id" value="<%image.id|html>"<%if avatar-id|match key=image.id> checked="checked"<%/if>/>
-                                       <div class="post-avatar"><% image|image-link max-width=48 max-height=48 mode=enlarge title==image.title></div>
+                                       <div class="post-avatar"><% image|image-link max-width==48 max-height==48 mode==enlarge title=image.title></div>
                                </li>
                        <%/foreach>
                </ul>
                <h2><%= Page.EditProfile.Fields.AddField.Title|l10n|html></h2>
 
                <%if duplicateFieldName>
-                       <p><%= Page.EditProfile.Error.DuplicateFieldName|l10n|replace needle="{fieldName}" replacementKey="fieldName"|html></p>
+                       <p><%= Page.EditProfile.Error.DuplicateFieldName|l10n|replace needle=="{fieldName}" replacement=fieldName|html></p>
                <%/if>
 
                <div id="new-field">
index 55705e1..7752ffb 100644 (file)
                                </script>
                        <%/if>
 
-                       <h1 class="backlink"><%= Page.ImageBrowser.Album.Title|l10n|replace needle='{album}' replacementKey=album.title|html></h1>
+                       <h1 class="backlink"><%= Page.ImageBrowser.Album.Title|l10n|replace needle=='{album}' replacement=album.title|html></h1>
 
                        <div class="backlinks">
                                <div class="backlink"><a href="imageBrowser.html?mode=gallery"><%= Page.ImageBrowser.Link.All|l10n|html></a></div>
                                                                <select name="album-image">
                                                                        <option disabled="disabled"><%= Page.ImageBrowser.Album.AlbumImage.Choose|l10n|html></option>
                                                                        <%foreach album.images image>
-                                                                               <option value="<% image.id|html>"<%if album.albumImage.id|match key=image.id> selected="selected"<%/if>><% image.title|html></option>
+                                                                               <option value="<% image.id|html>"<%if album.albumImage.id|match value=image.id> selected="selected"<%/if>><% image.title|html></option>
                                                                        <%/foreach>
                                                                </select>
                                                        </div>
                                </div>
                        <%/if>
 
-                       <%foreach album.images image>
-                               <%first><h2><%= Page.ImageBrowser.Header.Images|l10n|html></h2><%/first>
-                               <%if loop.count|mod divisor=3><div class="image-row"><%/if>
+                       <%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">
+                               <%/first>
+                               <%if loop.count|mod divisor==3><div class="image-row"><%/if>
                                <div id="image-<% image.id|html>" class="image">
                                        <div class="image-id hidden"><% image.id|html></div>
                                        <div class="image-container">
-                                               <a href="imageBrowser.html?image=<%image.id|html>"><% image|image-link max-width=250 max-height=250 mode=enlarge title==image.title></a>
+                                               <a href="imageBrowser.html?image=<%image.id|html>"><% image|image-link max-width==250 max-height==250 mode==enlarge title=image.title></a>
                                        </div>
                                        <div class="show-data">
                                                <div class="image-title"><% image.title|html></div>
                                                </form>
                                        <%/if>
                                </div>
-                               <%= false|store key=endRow>
-                               <%if loop.count|mod divisor=3 offset=1><%= true|store key=endRow><%/if>
-                               <%last><%= true|store key=endRow><%/last>
-                               <%if endRow></div><%/if>
+                               <%= false|store key==endRow>
+                               <%if loop.count|mod divisor==3 offset==1><%= true|store key==endRow><%/if>
+                               <%last><%= true|store key==endRow><%/last>
+                               <%if endRow>
+                                       </div>
+                                       <%include include/pagination.html pageParameter=="page">
+                               <%/if>
                        <%/foreach>
 
                        <%if album.sone.local>
-                               <div class="show-upload-image hidden toggle-link"><a class="small-link">» <%= View.UploadImage.Title|l10n|html></a></div>
-                               <div class="hide-upload-image hidden toggle-link"><a class="small-link">« <%= View.UploadImage.Title|l10n|html></a></div>
+                               <div class="clear show-upload-image hidden toggle-link"><a class="small-link">» <%= View.UploadImage.Title|l10n|html></a></div>
+                               <div class="clear hide-upload-image hidden toggle-link"><a class="small-link">« <%= View.UploadImage.Title|l10n|html></a></div>
                                <div class="upload-image">
                                        <%include include/uploadImage.html>
                                </div>
 
                        <div class="single-image">
                                <%ifnull !image.key>
-                                       <a href="/<%image.key|html>"><% image|image-link max-width=640 max-height=480></a>
+                                       <a href="/<%image.key|html>"><% image|image-link max-width==640 max-height==480></a>
                                <%else>
-                                       <a href="imageBrowser.html?image=<%image.id|html>"><% image|image-link max-width=640 max-height=480></a>
+                                       <a href="imageBrowser.html?image=<%image.id|html>"><% image|image-link max-width==640 max-height==480></a>
                                <%/if>
                        </div>
 
 
                <%else>
 
-                       <h1><%= Page.ImageBrowser.Sone.Title|l10n|replace needle='{sone}' replacementKey=sone.niceName|html></h1>
+                       <h1><%= Page.ImageBrowser.Sone.Title|l10n|replace needle=='{sone}' replacement=sone.niceName|html></h1>
 
                        <div class="backlinks">
                                <div class="backlink"><a href="imageBrowser.html?mode=gallery"><%= Page.ImageBrowser.Link.All|l10n|html></a></div>
 
        <%elseif galleryRequested>
 
-               <%foreach albums album>
-                       <%first><h2><%= Page.ImageBrowser.Header.Albums|l10n|html></h2><%/first>
-                       <%if loop.count|mod divisor=3><div class="album-row"><%/if>
+               <%foreach albums album|paginate pageSize=core.preferences.imagesPerPage pageParameter=request.page pagination=albumPagination>
+                       <%first>
+                               <h2><%= Page.ImageBrowser.Header.Albums|l10n|html></h2>
+                               <%include include/pagination.html pagination=albumPagination pageParameter=="page">
+                       <%/first>
+                       <%if loop.count|mod divisor==3><div class="album-row"><%/if>
                        <div id="album-<% album.id|html>" class="album">
                                <div class="album-id hidden"><% album.id|html></div>
                                <div class="album-container">
                                                <%ifnull album.albumImage>
                                                        <img src="images/unknown-image-0.png" width="333" height="250" alt="<% album.title|html> (<%album.sone.niceName|html>)" title="<% album.title|html> (<%album.sone.niceName|html>)" style="position: relative; top: 0px; left: -41px;" />
                                                <%else><!-- TODO -->
-                                                       <% album.albumImage|image-link max-width=250 max-height=250 mode=enlarge title==album.title>
+                                                       <% album.albumImage|image-link max-width==250 max-height==250 mode==enlarge title=album.title>
                                                <%/if>
                                        </a>
                                </div>
                                        <div class="album-description"><% album.description|parse sone=album.sone></div>
                                </div>
                        </div>
-                       <%= false|store key=endRow>
-                       <%if loop.count|mod divisor=3 offset=1><%= true|store key=endRow><%/if>
-                       <%last><%= true|store key=endRow><%/last>
-                       <%if endRow></div><%/if>
+                       <%= false|store key==endRow>
+                       <%if loop.count|mod divisor==3 offset==1><%= true|store key==endRow><%/if>
+                       <%last><%= true|store key==endRow><%/last>
+                       <%if endRow>
+                               </div>
+                       <%/if>
+                       <%last>
+                               <%include include/pagination.html pagination=albumPagination pageParameter=="page">
+                       <%/last>
                <%/foreach>
 
        <%/if>
index cb9abba..9aacad3 100644 (file)
@@ -1,6 +1,6 @@
 <%foreach albums album>
        <%first><h2><%= Page.ImageBrowser.Header.Albums|l10n|html></h2><%/first>
-       <%if loop.count|mod divisor=3><div class="album-row"><%/if>
+       <%if loop.count|mod divisor==3><div class="album-row"><%/if>
        <div id="album-<% album.id|html>" class="album">
                <div class="album-id hidden"><% album.id|html></div>
                <div class="album-container">
@@ -8,7 +8,7 @@
                                <%ifnull album.albumImage>
                                        <img src="images/unknown-image-0.png" width="333" height="250" alt="<% album.title|html>" title="<% album.title|html>" style="position: relative; top: 0px; left: -41px;" />
                                <%else><!-- TODO -->
-                                       <% album.albumImage|image-link max-width=250 max-height=250 mode=enlarge title==album.title>
+                                       <% album.albumImage|image-link max-width==250 max-height==250 mode==enlarge title=album.title>
                                <%/if>
                        </a>
                </div>
@@ -43,8 +43,8 @@
                        </form>
                <%/if>
        </div>
-       <%= false|store key=endRow>
-       <%if loop.count|mod divisor=3 offset=1><%= true|store key=endRow><%/if>
-       <%last><%= true|store key=endRow><%/last>
+       <%= false|store key==endRow>
+       <%if loop.count|mod divisor==3 offset==1><%= true|store key==endRow><%/if>
+       <%last><%= true|store key==endRow><%/last>
        <%if endRow></div><%/if>
 <%/foreach>
index c0df5c0..86543cc 100644 (file)
@@ -1,7 +1,7 @@
 <%if !identitiesWithoutSone.empty>
        <h1><%= Page.Login.CreateSone.Title|l10n|html></h1>
 
-       <p><%= View.CreateSone.Text.WotIdentityRequired|l10n|html|replace needle="{link}" replacement='<a href="/WebOfTrust/">'|replace needle="{/link}" replacement='</a>'></p>
+       <p><%= View.CreateSone.Text.WotIdentityRequired|l10n|html|replace needle=="{link}" replacement=='<a href="/WebOfTrust/">'|replace needle=="{/link}" replacement=='</a>'></p>
 
        <form id="create-sone" action="createSone.html" method="post">
                <input type="hidden" name="formPassword" value="<% formPassword|html>" />
@@ -23,8 +23,8 @@
        </form>
 <%else>
        <%if !sones.empty>
-               <p><%= View.CreateSone.Text.NoNonSoneIdentities|l10n|html|replace needle="{link}" replacement='<a href="/WebOfTrust/OwnIdentities">'|replace needle="{/link}" replacement="</a>"></p>
+               <p><%= View.CreateSone.Text.NoNonSoneIdentities|l10n|html|replace needle=="{link}" replacement=='<a href="/WebOfTrust/OwnIdentities">'|replace needle=="{/link}" replacement=="</a>"></p>
        <%else>
-               <p><%= View.CreateSone.Text.NoIdentities|l10n|html|replace needle="{link}" replacement='<a href="/WebOfTrust/OwnIdentities">'|replace needle="{/link}" replacement="</a>"></p>
+               <p><%= View.CreateSone.Text.NoIdentities|l10n|html|replace needle=="{link}" replacement=='<a href="/WebOfTrust/OwnIdentities">'|replace needle=="{/link}" replacement=="</a>"></p>
        <%/if>
 <%/if>
index 9988e68..13b564d 100644 (file)
@@ -44,7 +44,7 @@
                                <a class="picture" href="index.html">
                                        <%ifnull !currentSone>
                                                <%ifnull !currentSone.profile.avatar>
-                                                       <%currentSone.profile.avatar|image-link max-width=80 max-height=80 mode=enlarge title="Profile Avatar">
+                                                       <%currentSone.profile.avatar|image-link max-width==80 max-height==80 mode==enlarge title=="Profile Avatar">
                                                <%else>
                                                        <img src="/WebOfTrust/GetIdenticon?identity=<% currentSone.id|html>&amp;width=80&amp;height=80" width="80" height="80" alt="Profile Avatar" />
                                                <%/if>
@@ -55,8 +55,7 @@
                        </div>
                        <%ifnull ! currentSone>
                                <div id="home-sone">
-                                       <% currentSone|store key=sone>
-                                       <%include include/viewSone.html>
+                                       <%include include/viewSone.html sone=currentSone>
                                </div>
                        <%/if>
                        <form id="search" action="search.html" method="get">
index f8e6f11..80248d4 100644 (file)
@@ -1,10 +1,10 @@
 <%if pagination.necessary>
        <div class="navigation <%paginationName|html>">
-               <div class="first"><%if ! pagination.first><a href="<% request|change nameKey=pageParameter value=0>">«</a><%else><span>«</span><%/if></div>
-               <div class="previous"><%if ! pagination.first><a href="<% request|change nameKey=pageParameter key=pagination.previousPage>">‹</a><%else><span>‹</span><%/if></div>
+               <div class="first"><%if ! pagination.first><a href="<% request|change name=pageParameter value==0>">«</a><%else><span>«</span><%/if></div>
+               <div class="previous"><%if ! pagination.first><a href="<% request|change name=pageParameter value=pagination.previousPage>">‹</a><%else><span>‹</span><%/if></div>
                <div class="current-page"><% pagination.pageNumber></div>
                <div class="total-pages"><% pagination.pageCount></div>
-               <div class="last"><%if ! pagination.last><a href="<% request|change nameKey=pageParameter key=pagination.lastPage>">»</a><%else><span>»</span><%/if></div>
-               <div class="next"><%if ! pagination.last><a href="<% request|change nameKey=pageParameter key=pagination.nextPage>">›</a><%else><span>›</span><%/if></div>
+               <div class="last"><%if ! pagination.last><a href="<% request|change name=pageParameter value=pagination.lastPage>">»</a><%else><span>»</span><%/if></div>
+               <div class="next"><%if ! pagination.last><a href="<% request|change name=pageParameter value=pagination.nextPage>">›</a><%else><span>›</span><%/if></div>
        </div>
 <%/if>
index 9fa982f..f599eb4 100644 (file)
@@ -2,7 +2,7 @@
        <div class="sone-menu-id hidden"><%sone.id|html></div>
        <div class="avatar menu-avatar">
                <%ifnull !sone.profile.avatar>
-                       <%sone.profile.avatar|image-link max-width=64 max-height=64 mode=enlarge title==sone.niceName>
+                       <%sone.profile.avatar|image-link max-width==64 max-height==64 mode==enlarge title=sone.niceName>
                <%else>
                        <img src="/WebOfTrust/GetIdenticon?identity=<%sone.id|html>&amp;width=64&amp;height=64" width="64" height="64" alt="Avatar Image" />
                <%/if>
@@ -10,7 +10,7 @@
        <div class="inner-menu">
                <div>
                        <a class="author" href="viewSone.html?sone=<%sone.id|html>"><%sone.niceName|html></a>
-                       (<%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size><%if ! sone.allImages.size|match value=0>, <%= View.Sone.Stats.Images|l10n 0=sone.allImages.size><%/if>)
+                       (<%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size><%if ! sone.allImages.size|match value==0>, <%= View.Sone.Stats.Images|l10n 0=sone.allImages.size><%/if>)
                </div>
                <div><a href="/WebOfTrust/ShowIdentity?id=<%sone.id|html>">» <% =View.Post.WebOfTrustLink|l10n|html></a></div>
                <%foreach sone.albums album>
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 87a2656..7120d06 100644 (file)
@@ -7,9 +7,9 @@
        <div class="avatar post-avatar" >
                <%if post.loaded>
                        <%ifnull !post.sone.profile.avatar>
-                               <%post.sone.profile.avatar|image-link max-width=48 max-height=48 mode=enlarge title="Avatar Image">
+                               <%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" />
                                        <div class="recipient profile-link"><a href="viewSone.html?sone=<% post.recipient.id|html>"><% post.recipient.niceName|html></a></div>
                                <%/if>
                        <%/if>
-                       <% post.text|html|store key=originalText text=true>
-                       <% post.text|parse sone=post.sone|store key=parsedText text=true>
-                       <% post.text|parse sone=post.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|store key=shortText text=true>
+                       <% post.text|html|store key==originalText text==true>
+                       <% post.text|parse sone=post.sone|store key==parsedText text==true>
+                       <% post.text|parse sone=post.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|store key==shortText text==true>
                        <div class="post-text raw-text<%if !raw> hidden<%/if>"><% originalText></div>
                        <div class="post-text text<%if raw> hidden<%/if><%if !shortText|match key=parsedText> hidden<%/if>"><% parsedText></div>
                        <div class="post-text short-text<%if raw> hidden<%/if><%if shortText|match key=parsedText> hidden<%/if>"><% shortText></div>
-                       <%if !shortText|match key=parsedText><%if !raw><a class="expand-post-text" href="viewPost.html?post=<% post.id|html>&amp;raw=true"><%= View.Post.ShowMore|l10n|html></a><%/if><%/if>
-                       <%if !shortText|match key=parsedText><%if !raw><a class="shrink-post-text hidden"><%= View.Post.ShowLess|l10n|html></a><%/if><%/if>
+                       <%if !shortText|match value=parsedText><%if !raw><a class="expand-post-text" href="viewPost.html?post=<% post.id|html>&amp;raw=true"><%= View.Post.ShowMore|l10n|html></a><%/if><%/if>
+                       <%if !shortText|match value=parsedText><%if !raw><a class="shrink-post-text hidden"><%= View.Post.ShowLess|l10n|html></a><%/if><%/if>
                </div>
                <div class="post-status-line status-line<%if !post.loaded> hidden<%/if>">
                        <div class="bookmarks">
                                </form>
                        </div>
                        <span class='separator'>·</span>
-                       <div class="time"><a href="viewPost.html?post=<% post.id|html>"><% post.time|date format="MMM d, yyyy, HH:mm:ss"></a></div>
+                       <div class="time"><a href="viewPost.html?post=<% post.id|html>"><% post.time|date format=="MMM d, yyyy, HH:mm:ss"></a></div>
                        <span class='separator'>·</span>
                        <div class="permalink permalink-post"><a href="post://<%post.id|html>">[<%= View.Post.Permalink|l10n|html>]</a></div>
                        <span class='separator'>·</span>
                        <div class="permalink permalink-author"><a href="sone://<%post.sone.id|html>">[<%= View.Post.PermalinkAuthor|l10n|html>]</a></div>
-                       <%if ! originalText|match key=parsedText>
+                       <%if ! originalText|match value=parsedText>
                                <span class='separator'>·</span>
                                <div class="show-source"><a href="viewPost.html?post=<% post.id|html>&amp;raw=<%if raw>false<%else>true<%/if>"><%= View.Post.ShowSource|l10n|html></a></div>
                        <%/if>
-                       <div class="likes<%if post.likes.size|match value=0> hidden<%/if>">
+                       <div class="likes<%if post.likes.size|match value==0> hidden<%/if>">
                                <span class='separator'>·</span>
                                <span title="<% post.likes.soneNames|html>">↑<span class="like-count"><% post.likes.size></span></span>
                        </div>
                                        <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 ca07cf7..449a235 100644 (file)
@@ -6,7 +6,7 @@
        <%include include/soneMenu.html class=="sone-reply-menu" sone=reply.sone>
        <div class="avatar reply-avatar">
                <%ifnull !reply.sone.profile.avatar>
-                       <% reply.sone.profile.avatar|image-link max-width=36 max-height=36 mode=enlarge title="Avatar Image">
+                       <% reply.sone.profile.avatar|image-link max-width==36 max-height==36 mode==enlarge title=="Avatar Image">
                <%else>
                        <img src="/WebOfTrust/GetIdenticon?identity=<% reply.sone.id|html>&amp;width=36&height=36" width="36" height="36" alt="Avatar Image" />
                <%/if>
        <div class="inner-part">
                <div>
                        <div class="author profile-link"><a href="viewSone.html?sone=<% reply.sone.id|html>"><% reply.sone.niceName|html></a></div>
-                       <% reply.text|html|store key=originalText text=true>
-                       <% reply.text|parse sone=reply.sone|store key=parsedText text=true>
-                       <% reply.text|parse sone=reply.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|store key=shortText text=true>
+                       <% reply.text|html|store key==originalText text==true>
+                       <% reply.text|parse sone=reply.sone|store key==parsedText text==true>
+                       <% reply.text|parse sone=reply.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|store key==shortText text==true>
                        <div class="reply-text raw-text<%if !raw> hidden<%/if>"><% originalText></div>
                        <div class="reply-text text<%if raw> hidden<%/if><%if !shortText|match key=parsedText> hidden<%/if>"><% parsedText></div>
                        <div class="reply-text short-text<%if raw> hidden<%/if><%if shortText|match key=parsedText> hidden<%/if>"><% shortText></div>
-                       <%if !shortText|match key=parsedText><%if !raw><a class="expand-reply-text" href="viewPost.html?post=<% reply.post.id|html>&amp;raw=true"><%= View.Post.ShowMore|l10n|html></a><%/if><%/if>
-                       <%if !shortText|match key=parsedText><%if !raw><a class="shrink-reply-text hidden"><%= View.Post.ShowLess|l10n|html></a><%/if><%/if>
+                       <%if !shortText|match value=parsedText><%if !raw><a class="expand-reply-text" href="viewPost.html?post=<% reply.post.id|html>&amp;raw=true"><%= View.Post.ShowMore|l10n|html></a><%/if><%/if>
+                       <%if !shortText|match value=parsedText><%if !raw><a class="shrink-reply-text hidden"><%= View.Post.ShowLess|l10n|html></a><%/if><%/if>
                </div>
                <div class="reply-status-line status-line">
-                       <div class="time"><% reply.time|date format="MMM d, yyyy, HH:mm:ss"></div>
+                       <div class="time"><% reply.time|date format=="MMM d, yyyy, HH:mm:ss"></div>
                        <span class='separator'>·</span>
                        <div class="permalink permalink-author"><a href="sone://<%reply.sone.id|html>">[<%= View.Post.PermalinkAuthor|l10n|html>]</a></div>
-                       <%if ! originalText|match key=parsedText>
+                       <%if ! originalText|match value=parsedText>
                                <span class='separator'>·</span>
                                <div class="show-reply-source"><a href="viewPost.html?post=<% post.id|html>&amp;raw=<%if raw>false<%else>true<%/if>"><%= View.Post.ShowSource|l10n|html></a></div>
                        <%/if>
-                       <div class="likes<%if reply.likes.size|match value=0> hidden<%/if>">
+                       <div class="likes<%if reply.likes.size|match value==0> hidden<%/if>">
                                <span class='separator'>·</span>
                                <span title="<% reply.likes.soneNames|html>">↑<span class="like-count"><% reply.likes.size></span></span>
                        </div>
                                        <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 59ba792..1ca0023 100644 (file)
@@ -5,12 +5,12 @@
        <div class="download-marker" title="<%= View.Sone.Status.Downloading|l10n|html>">⬊</div>
        <div class="insert-marker" title="<%= View.Sone.Status.Inserting|l10n|html>">⬈</div>
        <div class="idle-marker" title="<%= View.Sone.Status.Idle|l10n|html>">✔</div>
-       <div class="last-update"><%= View.Sone.Label.LastUpdate|l10n|html> <span class="time" title="<% sone.time|unknown|date format="MMM d, yyyy, HH:mm:ss">"><%sone.lastUpdatedText|html></span></div>
+       <div class="last-update"><%= View.Sone.Label.LastUpdate|l10n|html> <span class="time" title="<% sone.time|unknown|date format=="MMM d, yyyy, HH:mm:ss">"><%sone.lastUpdatedText|html></span></div>
        <div>
                <div class="profile-link"><a href="viewSone.html?sone=<% sone.id|html>" title="<% sone.requestUri|html>"><% sone.niceName|html></a></div>
-               <div class="sone-stats">(<%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size><%if ! sone.allImages.size|match value=0>, <%= View.Sone.Stats.Images|l10n 0=sone.allImages.size><%/if>)</div>
+               <div class="sone-stats">(<%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size><%if ! sone.allImages.size|match value==0>, <%= View.Sone.Stats.Images|l10n 0=sone.allImages.size><%/if>)</div>
        </div>
-       <div class="short-request-uri"><% sone.requestUri|substring start=4 length=43|html></div>
+       <div class="short-request-uri"><% sone.requestUri|substring start==4 length==43|html></div>
        <div class="hidden"><% sone.blacklisted></div>
        <%if sone.local>
                <form class="lock<%if sone.locked> hidden<%/if>" action="lockSone.html" method="post">
index f79e52d..a5a57f6 100644 (file)
@@ -7,14 +7,14 @@
        <%include include/updateStatus.html>
 
        <div id="posts">
-               <%= page|store key=pageParameter>
-               <%include include/pagination.html paginationName==pagination-index>
+               <%include include/pagination.html pageParameter==page paginationName==pagination-index>
                <%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>
+               <%include include/pagination.html pageParameter==page>
        </div>
 
 <%include include/tail.html>
index ea97d00..3ac37cf 100644 (file)
@@ -3,13 +3,13 @@
        <h1><%= Page.Invalid.Page.Title|l10n|html></h1>
 
        <%foreach messages message>
-               <%if message|substring start=0 length=1|match value='!'>
-                       <p class="error"><% message|substring start=1|parse></p>
+               <%if message|substring start==0 length==1|match value=='!'>
+                       <p class="error"><% message|substring start==1|parse></p>
                <%else>
                        <p><% message|parse></p>
                <%/if>
        <%foreachelse>
-               <p><%= Page.Invalid.Text|l10n|html|replace needle="{link}" replacement='<a href="index.html">'|replace needle="{/link}" replacement='</a>'></p>
+               <p><%= Page.Invalid.Text|l10n|html|replace needle=="{link}" replacement=='<a href="index.html">'|replace needle=="{/link}" replacement=='</a>'></p>
        <%/foreach>
 
 <%include include/tail.html>
index afd40df..4ace659 100644 (file)
                        <div>
                                <%= Page.KnownSones.Label.Sort|l10n|html>
                                <select name="sort">
-                                       <option value="name"<%if sort|match value="name"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.Name|l10n|html></option>
-                                       <option value="activity"<%if sort|match value="activity"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.LastActivity|l10n|html></option>
-                                       <option value="posts"<%if sort|match value="posts"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.Posts|l10n|html></option>
-                                       <option value="images"<%if sort|match value="images"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.Images|l10n|html></option>
+                                       <option value="name"<%if sort|match value=="name"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.Name|l10n|html></option>
+                                       <option value="activity"<%if sort|match value=="activity"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.LastActivity|l10n|html></option>
+                                       <option value="posts"<%if sort|match value=="posts"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.Posts|l10n|html></option>
+                                       <option value="images"<%if sort|match value=="images"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Field.Images|l10n|html></option>
                                </select>
                                <select name="order">
-                                       <option value="asc"<%if order|match value="asc"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Order.Ascending|l10n|html></option>
-                                       <option value="desc"<%if order|match value="desc"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Order.Descending|l10n|html></option>
+                                       <option value="asc"<%if order|match value=="asc"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Order.Ascending|l10n|html></option>
+                                       <option value="desc"<%if order|match value=="desc"> selected="selected"<%/if>><%= Page.KnownSones.Sort.Order.Descending|l10n|html></option>
                                </select>
                        </div>
                        <%ifnull !currentSone>
                                        <select name="filter">
                                                <option value="none"></option>
                                                <%ifnull !currentSone>
-                                                       <option value="followed"<%if filter|match value="followed"> selected="selected"<%/if>><%= Page.KnownSones.Filter.Followed|l10n|html></option>
-                                                       <option value="not-followed"<%if filter|match value="not-followed"> selected="selected"<%/if>><%= Page.KnownSones.Filter.NotFollowed|l10n|html></option>
+                                                       <option value="followed"<%if filter|match value=="followed"> selected="selected"<%/if>><%= Page.KnownSones.Filter.Followed|l10n|html></option>
+                                                       <option value="not-followed"<%if filter|match value=="not-followed"> selected="selected"<%/if>><%= Page.KnownSones.Filter.NotFollowed|l10n|html></option>
                                                <%/if>
-                                               <option value="new"<%if filter|match value="new"> selected="selected"<%/if>><%= Page.KnownSones.Filter.New|l10n|html></option>
-                                               <option value="not-new"<%if filter|match value="not-new"> selected="selected"<%/if>><%= Page.KnownSones.Filter.NotNew|l10n|html></option>
+                                               <option value="new"<%if filter|match value=="new"> selected="selected"<%/if>><%= Page.KnownSones.Filter.New|l10n|html></option>
+                                               <option value="not-new"<%if filter|match value=="not-new"> selected="selected"<%/if>><%= Page.KnownSones.Filter.NotNew|l10n|html></option>
                                        </select>
                                </div>
                        <%/if>
        <%/if>
 
        <div id="known-sones">
-               <%= page|store key=pageParameter>
-               <%include include/pagination.html>
+               <%include include/pagination.html pageParameter==page>
                <%foreach pagination.items sone>
                        <%include include/viewSone.html>
                <%foreachelse>
                        <div><%= Page.KnownSones.Text.NoKnownSones|l10n|html></div>
                <%/foreach>
-               <%include include/pagination.html>
+               <%include include/pagination.html pageParameter==page>
        </div>
 
 <%include include/tail.html>
index 8e9adf6..d316dca 100644 (file)
@@ -7,14 +7,13 @@
        <%include include/updateStatus.html>
 
        <div id="posts">
-               <%= page|store key=pageParameter>
-               <%include include/pagination.html paginationName==pagination-index>
+               <%include include/pagination.html pageParameter==page paginationName==pagination-index>
                <%foreach pagination.items post>
                        <%include include/viewPost.html>
                <%foreachelse>
                        <div><%= Page.New.NothingNew|l10n|html></div>
                <%/foreach>
-               <%include include/pagination.html>
+               <%include include/pagination.html pageParameter==page>
        </div>
 
 <%include include/tail.html>
index 5dfc25a..599a7e6 100644 (file)
@@ -1 +1 @@
-<div class="text"><%= Notification.NewVersion.Text|l10n|replace needle="{version}" replacementKey=latestVersion|replace needle="{edition}" replacementKey=latestEdition|parse sone="nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"></div>
+<div class="text"><%= Notification.NewVersion.Text|l10n|replace needle=="{version}" replacement=latestVersion|replace needle=="{edition}" replacement=latestEdition|parse sone=="nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"></div>
index 27374ad..d760191 100644 (file)
@@ -1,7 +1,7 @@
-<%if soneStatus|match value="inserting">
+<%if soneStatus|match value=="inserting">
        Your Sone <a href="viewSone.html?sone=<%insertSone.id|html>"><%insertSone.niceName|html></a> is now being inserted.
-<%elseif soneStatus|match value="inserted">
+<%elseif soneStatus|match value=="inserted">
        Your Sone <a href="viewSone.html?sone=<%insertSone.id|html>"><%insertSone.niceName|html></a> has been inserted in <%= Notification.SoneInsert.Duration|l10n 0=insertDuration>.
-<%elseif soneStatus|match value="insert-aborted">
+<%elseif soneStatus|match value=="insert-aborted">
        Inserting your Sone <a href="viewSone.html?sone=<%insertSone.id|html>"><%insertSone.niceName|html></a> has failed.
 <%/if>
\ No newline at end of file
index 02fc451..ce8c927 100644 (file)
@@ -1,4 +1,4 @@
 <div class="text">
        <%= Page.WotPluginMissing.Text.WotRequired|l10n|html>
-       <%= Page.WotPluginMissing.Text.LoadPlugin|l10n|html|replace needle="{link}" replacement='<a href="/plugins/">'|replace needle="{/link}" replacement='</a>'>
+       <%= Page.WotPluginMissing.Text.LoadPlugin|l10n|html|replace needle=="{link}" replacement=='<a href="/plugins/">'|replace needle=="{/link}" replacement=='</a>'>
 </div>
index 94e01dc..4ec88cb 100644 (file)
@@ -8,6 +8,9 @@
                        getTranslation("WebInterface.DefaultText.Option.PostsPerPage", function(postsPerPageText) {
                                registerInputTextareaSwap("#sone #options input[name=posts-per-page]", postsPerPageText, "posts-per-page", true, true);
                        });
+                       getTranslation("WebInterface.DefaultText.Option.ImagesPerPage", function(imagesPerPageText) {
+                               registerInputTextareaSwap("#sone #options input[name=images-per-page]", imagesPerPageText, "images-per-page", true, true);
+                       });
                        getTranslation("WebInterface.DefaultText.Option.CharactersPerPost", function(postsPerPageText) {
                                registerInputTextareaSwap("#sone #options input[name=characters-per-post]", postsPerPageText, "characters-per-post", true, true);
                        });
@@ -36,7 +39,7 @@
                <h2><%= Page.Options.Section.SoneSpecificOptions.Title|l10n|html></h2>
 
                <%ifnull currentSone>
-                       <p><%= Page.Options.Section.SoneSpecificOptions.NotLoggedIn|l10n|html|replace needle="{link}" replacement='<a href="login.html">'|replace needle="{/link}" replacement='</a>'></p>
+                       <p><%= Page.Options.Section.SoneSpecificOptions.NotLoggedIn|l10n|html|replace needle=="{link}" replacement=='<a href="login.html">'|replace needle=="{/link}" replacement=='</a>'></p>
                <%else>
                        <p><%= Page.Options.Section.SoneSpecificOptions.LoggedIn|l10n|html></p>
                <%/if>
                <h2><%= Page.Options.Section.AvatarOptions.Title|l10n|html></h2>
 
                <p><%= Page.Options.Option.ShowAvatars.Description|l10n|html></p>
-               
+
                <ul>
                        <li>
-                               <input type="radio" name="show-custom-avatars" value="NEVER"<%if show-custom-avatars|match value=NEVER> checked="checked"<%/if>/>
+                               <input type="radio" name="show-custom-avatars" value="NEVER"<%if show-custom-avatars|match value==NEVER> checked="checked"<%/if>/>
                                <%=Page.Options.Option.ShowAvatars.Never.Description|l10n|html>
                        </li>
                        <li>
-                               <input type="radio" name="show-custom-avatars" value="FOLLOWED"<%if show-custom-avatars|match value=FOLLOWED> checked="checked"<%/if>/>
+                               <input type="radio" name="show-custom-avatars" value="FOLLOWED"<%if show-custom-avatars|match value==FOLLOWED> checked="checked"<%/if>/>
                                <%=Page.Options.Option.ShowAvatars.Followed.Description|l10n|html>
                        </li>
                        <li>
-                               <input type="radio" name="show-custom-avatars" value="MANUALLY_TRUSTED"<%if show-custom-avatars|match value=MANUALLY_TRUSTED> checked="checked"<%/if>/>
+                               <input type="radio" name="show-custom-avatars" value="MANUALLY_TRUSTED"<%if show-custom-avatars|match value==MANUALLY_TRUSTED> checked="checked"<%/if>/>
                                <%=Page.Options.Option.ShowAvatars.ManuallyTrusted.Description|l10n|html>
                        </li>
                        <li>
-                               <input type="radio" name="show-custom-avatars" value="TRUSTED"<%if show-custom-avatars|match value=TRUSTED> checked="checked"<%/if>/>
+                               <input type="radio" name="show-custom-avatars" value="TRUSTED"<%if show-custom-avatars|match value==TRUSTED> checked="checked"<%/if>/>
                                <%=Page.Options.Option.ShowAvatars.Trusted.Description|l10n|html>
                        </li>
                        <li>
-                               <input type="radio" name="show-custom-avatars" value="ALWAYS"<%if show-custom-avatars|match value=ALWAYS> checked="checked"<%/if>/>
+                               <input type="radio" name="show-custom-avatars" value="ALWAYS"<%if show-custom-avatars|match value==ALWAYS> checked="checked"<%/if>/>
                                <%=Page.Options.Option.ShowAvatars.Always.Description|l10n|html>
                        </li>
                </ul>
                <%/if>
                <p><input type="text" name="posts-per-page" value="<% posts-per-page|html>" /></p>
 
+               <p><%= Page.Options.Option.ImagesPerPage.Description|l10n|html></p>
+               <%if =images-per-page|in collection=fieldErrors>
+                       <p class="warning"><%= Page.Options.Warnings.ValueNotChanged|l10n|html></p>
+               <%/if>
+               <p><input type="text" name="images-per-page" value="<% images-per-page|html>" /></p>
+
                <p><%= Page.Options.Option.CharactersPerPost.Description|l10n|html></p>
                <%if =characters-per-post|in collection=fieldErrors>
                        <p class="warning"><%= Page.Options.Warnings.ValueNotChanged|l10n|html></p>
                <p><input type="checkbox" name="fcp-interface-active"<%if fcp-interface-active> checked="checked"<%/if> /> <%= Page.Options.Option.FcpInterfaceActive.Description|l10n|html></p>
 
                <p>
-                       <%= Page.Options.Option.FcpFullAccessRequired.Description|l10n|html|replace needle="{link}" replacement='<a href="/config/fcp">'|replace needle="{/link}" replacement='</a>'>
+                       <%= Page.Options.Option.FcpFullAccessRequired.Description|l10n|html|replace needle=="{link}" replacement=='<a href="/config/fcp">'|replace needle=="{/link}" replacement=='</a>'>
                        <select name="fcp-full-access-required">
-                               <option value="0"<%if fcp-full-access-required|match value="0"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.No|l10n|html></option>
-                               <option value="1"<%if fcp-full-access-required|match value="1"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.Writing|l10n|html></option>
-                               <option value="2"<%if fcp-full-access-required|match value="2"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.Always|l10n|html></option>
+                               <option value="0"<%if fcp-full-access-required|match value=="0"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.No|l10n|html></option>
+                               <option value="1"<%if fcp-full-access-required|match value=="1"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.Writing|l10n|html></option>
+                               <option value="2"<%if fcp-full-access-required|match value=="2"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.Always|l10n|html></option>
                        </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 836bf62..5269800 100644 (file)
@@ -8,7 +8,7 @@
 
                <p><%= Page.ViewPost.Text.UnknownPost|l10n|html></p>
        <%else>
-               <h1><%= Page.ViewPost.Page.Title|l10n|replace needle="{sone}" replacementKey=post.sone.niceName|html></h1>
+               <h1><%= Page.ViewPost.Page.Title|l10n|replace needle=="{sone}" replacement=post.sone.niceName|html></h1>
 
                <%include include/viewPost.html>
        <%/if>
index 6dd3912..4b88474 100644 (file)
@@ -8,8 +8,8 @@
                <h1><%= Page.ViewSone.Page.TitleWithoutSone|l10n|html></h1>
 
                <p>
-                       <%= Page.ViewSone.NoSone.Description|l10n|replace needle="{sone}" replacementKey=soneId|html>
-                       <a href="/WebOfTrust/ShowIdentity?id=<% sone.id|html>"><%= Page.ViewSone.Profile.Name.WoTLink|l10n|html></a>
+                       <%= Page.ViewSone.NoSone.Description|l10n|replace needle=="{sone}" replacement=soneId|html>
+                       <a href="/WebOfTrust/ShowIdentity?id=<% soneId|html>"><%= Page.ViewSone.Profile.Name.WoTLink|l10n|html></a>
                </p>
 
        <%elseifnull sone.name>
@@ -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>
 
@@ -79,7 +79,7 @@
                        <%/if>
                <%/if>
 
-               <h1><%= Page.ViewSone.PostList.Title|l10n|replace needle="{sone}" replacementKey=sone.niceName|html></h1>
+               <h1><%= Page.ViewSone.PostList.Title|l10n|replace needle=="{sone}" replacement=sone.niceName|html></h1>
 
                <%foreach posts post>
                        <%first>
@@ -97,7 +97,7 @@
 
                <%foreach repliedPosts post>
                        <%first>
-                               <h2><%= Page.ViewSone.Replies.Title|l10n|html|replace needle="{sone}" replacementKey=sone.niceName></h2>
+                               <h2><%= Page.ViewSone.Replies.Title|l10n|html|replace needle=="{sone}" replacement=sone.niceName></h2>
                                <div id="replied-posts">
                                        <%include include/pagination.html pagination=repliedPostPagination pageParameter==repliedPostPage paginationName==reply-navigation>
                        <%/first>