From: David ‘Bombe’ Roden Date: Thu, 13 Sep 2012 12:34:58 +0000 (+0200) Subject: Merge branch 'next' into new-database-38 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=927de326e0af0f11a27b3444f4e25b0796c877db;hp=c1fd1bc55bd809fd4ee447b2679bb8aafe4e06fc Merge branch 'next' into new-database-38 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 --- diff --git a/pom.xml b/pom.xml index 3825dfc..9ea02ae 100644 --- a/pom.xml +++ b/pom.xml @@ -2,12 +2,12 @@ 4.0.0 net.pterodactylus sone - 0.8.1 + 0.8.2 net.pterodactylus utils - 0.12-SNAPSHOT + 0.12 junit diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 922f0d1..0b1379a 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -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 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(10, new IntegerRangeValidator(1, Integer.MAX_VALUE))); + options.addIntegerOption("ImagesPerPage", new DefaultOption(9, new IntegerRangeValidator(1, Integer.MAX_VALUE))); options.addIntegerOption("CharactersPerPost", new DefaultOption(400, new OrValidator(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator(-1)))); options.addIntegerOption("PostCutOffLength", new DefaultOption(200, new OrValidator(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator(-1)))); options.addBooleanOption("RequireFullAccess", new DefaultOption(false)); @@ -2156,23 +2106,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } })); - options.addBooleanOption("SoneRescueMode", new DefaultOption(false)); - options.addBooleanOption("ClearOnNextRestart", new DefaultOption(false)); - options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption(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 -1 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; - } - } } diff --git a/src/main/java/net/pterodactylus/sone/core/Options.java b/src/main/java/net/pterodactylus/sone/core/Options.java index af81d0f..0ea895a 100644 --- a/src/main/java/net/pterodactylus/sone/core/Options.java +++ b/src/main/java/net/pterodactylus/sone/core/Options.java @@ -329,6 +329,8 @@ public class Options { /** * Adds an {@link Enum} {@link Option}. * + * @param + * The enum type * @param name * The name of the option * @param enumOption @@ -350,6 +352,8 @@ public class Options { * options.<SomeEnum> getEnumOption("SomeEnumOption").get(); * * + * @param + * 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 index 0000000..bdfc1ae --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +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 updateJobs = new LinkedBlockingQueue(); + + /** + * 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 David ‘Bombe’ Roden + */ + 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. + *

+ * 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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); + } + } + + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/Post.java b/src/main/java/net/pterodactylus/sone/data/Post.java index 334a944..f171475 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -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 David ‘Bombe’ Roden */ -public class Post { +public interface Post { /** Comparator for posts, sorts descending by time. */ public static final Comparator TIME_COMPARATOR = new Comparator() { @@ -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 index 0000000..2269738 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +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 + "]"; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemorySone.java b/src/main/java/net/pterodactylus/sone/database/memory/MemorySone.java index 1c5912f..da487b3 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemorySone.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemorySone.java @@ -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); + } } /** diff --git a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java index 490a05c..1944570 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java @@ -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 sones, String prefix) { + protected static SimpleFieldSet encodeSones(Collection 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 replies, String prefix) { + protected static SimpleFieldSet encodeReplies(Collection 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 likes, String prefix) { + protected static SimpleFieldSet encodeLikes(Collection likes, String prefix) { SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder(); int likeIndex = 0; diff --git a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java index 128d46a..b3222be 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java +++ b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java @@ -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); diff --git a/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java b/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java index 1cde976..7fffd5c 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java +++ b/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java @@ -52,7 +52,7 @@ public class L10nFilter implements Filter { * {@inheritDoc} */ @Override - public String format(TemplateContext templateContext, Object data, Map parameters) { + public String format(TemplateContext templateContext, Object data, Map 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; } diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java index 6e13a32..b73fb1d 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java @@ -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); } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java index 6fe4101..2ef33c3 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java @@ -17,23 +17,12 @@ 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 properties = Collections.synchronizedMap(new HashMap()); /** Cached trust. */ - /* synchronize on itself. */ - private final WritableCache trustCache = new MemoryCache(new ValueRetriever() { - - @Override - @SuppressWarnings("synthetic-access") - public CacheItem retrieve(OwnIdentity ownIdentity) throws CacheException { - try { - return new DefaultCacheItem(webOfTrustConnector.getTrust(ownIdentity, DefaultIdentity.this)); - } catch (PluginException pe1) { - throw new CacheException("Could not retrieve trust for OwnIdentity: " + ownIdentity, pe1); - } - } - - }, new TimedMap>(60 * 60 * 1000)); + private final Map trustCache = Collections.synchronizedMap(new HashMap()); /** * 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. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param contexts - * The contexts to set + * {@inheritDoc} */ - void setContextsPrivate(Set 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 contexts) { + this.contexts.clear(); + this.contexts.addAll(contexts); } /** - * Adds the given context to this identity. - *

- * 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. - *

- * 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 getProperties() { - synchronized (properties) { - return Collections.unmodifiableMap(properties); - } + return Collections.unmodifiableMap(properties); } /** - * Sets all properties of this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param properties - * The new properties of this identity + * {@inheritDoc} */ - void setPropertiesPrivate(Map properties) { - synchronized (this.properties) { - this.properties.clear(); - this.properties.putAll(properties); - } + @Override + public void setProperties(Map properties) { + this.properties.clear(); + this.properties.putAll(properties); } /** - * Sets the property with the given name to the given value. - *

- * 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. - *

- * 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); } // diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java index c461c8b..451dd5f 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java @@ -17,12 +17,6 @@ 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 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 properties) throws WebOfTrustException { - for (Entry 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 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); - } - } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java index 0a9fb93..2849da9 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java @@ -17,13 +17,14 @@ 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 David ‘Bombe’ Roden */ @@ -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 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 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); + } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java index 32c0d27..3c3c9f9 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java @@ -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 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 getAllOwnIdentities() { - try { - Set ownIdentities = webOfTrustConnector.loadAllOwnIdentities(); - Map newOwnIdentities = new HashMap(); - for (OwnIdentity ownIdentity : ownIdentities) { - newOwnIdentities.put(ownIdentity.getId(), ownIdentity); - } - checkOwnIdentities(newOwnIdentities); - return Mappers.mappedSet(ownIdentities, new Mapper() { - - /** - * {@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(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)); } } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java index 6c6224f..4272669 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java @@ -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. - *

- * 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. - *

- * 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 contexts) throws WebOfTrustException; - - /** - * Removes the given context from this identity. - *

- * 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. - *

- * 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 properties) throws WebOfTrustException; - - /** - * Removes the property with the given name. - *

- * 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; - } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java index a7c6524..53a45b1 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java @@ -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 David ‘Bombe’ Roden */ -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 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 identities = new HashSet(); 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 parseContexts(String prefix, SimpleFieldSet fields) { + private static Set parseContexts(String prefix, SimpleFieldSet fields) { Set contexts = new HashSet(); 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 parseProperties(String prefix, SimpleFieldSet fields) { + private static Map parseProperties(String prefix, SimpleFieldSet fields) { Map properties = new HashMap(); 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. * diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index da50101..42e2fe1 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -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); diff --git a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java index 51bb1c0..6efee27 100644 --- a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java +++ b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java @@ -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; diff --git a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java index 7a5d6d3..e22e00b 100644 --- a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java @@ -68,7 +68,7 @@ public class AlbumAccessor extends ReflectionAccessor { * The name of the link * @return The created map containing the mappings */ - private Map createLink(String target, String name) { + private static Map createLink(String target, String name) { Map link = new HashMap(); link.put("target", target); link.put("name", name); diff --git a/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java b/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java index 7db7be0..0282156 100644 --- a/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java @@ -35,7 +35,7 @@ public class CssClassNameFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { + public Object format(TemplateContext templateContext, Object data, Map parameters) { return String.valueOf(data).replaceAll("[^a-zA-Z0-9-]", "_"); } diff --git a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java index 4602188..4fbe74c 100644 --- a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java @@ -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) : ""); } diff --git a/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java index 50ce8fd..06a11d7 100644 --- a/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java @@ -65,7 +65,7 @@ public class ImageLinkFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { + public Object format(TemplateContext templateContext, Object data, Map 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); diff --git a/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java b/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java index 966b81f..2dc963f 100644 --- a/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java @@ -36,7 +36,7 @@ public class JavascriptFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { + public Object format(TemplateContext templateContext, Object data, Map parameters) { StringBuilder javascriptString = new StringBuilder(); javascriptString.append('"'); for (char c : String.valueOf(data).toCharArray()) { diff --git a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java index 1b46fa0..1936656 100644 --- a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java @@ -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("\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html>")); + private static final Template linkTemplate = TemplateParser.parse(new StringReader("\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html>")); /** * 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 parameters) { + public Object format(TemplateContext templateContext, Object data, Map 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 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 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; - } - } diff --git a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java index ceb3e22..19177c3 100644 --- a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java @@ -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; } diff --git a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java index 37d4175..ff4b64e 100644 --- a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java @@ -42,7 +42,7 @@ public class ReplyGroupFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { + public Object format(TemplateContext templateContext, Object data, Map parameters) { @SuppressWarnings("unchecked") List allReplies = (List) data; Map> postSones = new HashMap>(); diff --git a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java index 45d6ba7..3a89021 100644 --- a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java @@ -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 David ‘Bombe’ Roden */ @@ -44,24 +43,10 @@ public class RequestChangeFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { + public Object format(TemplateContext templateContext, Object data, Map 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 values = new HashMap(); Collection parameterNames = request.getHttpRequest().getParameterNames(); diff --git a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java index ffad018..9d4bdc8 100644 --- a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java @@ -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); diff --git a/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java b/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java index b60f4d3..005eb1f 100644 --- a/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java @@ -37,9 +37,9 @@ public class SubstringFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { - String startString = parameters.get("start"); - String lengthString = parameters.get("length"); + public Object format(TemplateContext templateContext, Object data, Map parameters) { + String startString = String.valueOf(parameters.get("start")); + String lengthString = String.valueOf(parameters.get("length")); int start = 0; try { start = Integer.parseInt(startString); diff --git a/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java b/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java index 45c1cb8..e3ddd59 100644 --- a/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java @@ -36,7 +36,7 @@ public class UniqueElementFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { + public Object format(TemplateContext templateContext, Object data, Map parameters) { if (!(data instanceof Collection)) { return data; } diff --git a/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java b/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java index fa68c6f..c87a97b 100644 --- a/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java @@ -54,7 +54,7 @@ public class UnknownDateFilter implements Filter { * {@inheritDoc} */ @Override - public Object format(TemplateContext templateContext, Object data, Map parameters) { + public Object format(TemplateContext templateContext, Object data, Map parameters) { if (data instanceof Long) { if ((Long) data == 0) { return l10nHandler.getString(unknownKey); diff --git a/src/main/java/net/pterodactylus/sone/text/LinkPart.java b/src/main/java/net/pterodactylus/sone/text/LinkPart.java index 1b47080..202b9db 100644 --- a/src/main/java/net/pterodactylus/sone/text/LinkPart.java +++ b/src/main/java/net/pterodactylus/sone/text/LinkPart.java @@ -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; } } diff --git a/src/main/java/net/pterodactylus/sone/text/Part.java b/src/main/java/net/pterodactylus/sone/text/Part.java index 76e80ef..79c59dc 100644 --- a/src/main/java/net/pterodactylus/sone/text/Part.java +++ b/src/main/java/net/pterodactylus/sone/text/Part.java @@ -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(); } diff --git a/src/main/java/net/pterodactylus/sone/text/PartContainer.java b/src/main/java/net/pterodactylus/sone/text/PartContainer.java index e456cd9..a8a7e85 100644 --- a/src/main/java/net/pterodactylus/sone/text/PartContainer.java +++ b/src/main/java/net/pterodactylus/sone/text/PartContainer.java @@ -81,6 +81,22 @@ public class PartContainer implements Part, Iterable { } // + // PART METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + StringBuilder partText = new StringBuilder(); + for (Part part : parts) { + partText.append(part.getText()); + } + return partText.toString(); + } + + // // ITERABLE METHODS // diff --git a/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java b/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java index 09c1fba..2c29ee2 100644 --- a/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java @@ -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; } diff --git a/src/main/java/net/pterodactylus/sone/text/PostPart.java b/src/main/java/net/pterodactylus/sone/text/PostPart.java index c416c57..6241b7a 100644 --- a/src/main/java/net/pterodactylus/sone/text/PostPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PostPart.java @@ -52,4 +52,16 @@ public class PostPart implements Part { return post; } + // + // PART METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + return post.getText(); + } + } diff --git a/src/main/java/net/pterodactylus/sone/text/SonePart.java b/src/main/java/net/pterodactylus/sone/text/SonePart.java index 475c091..37f098b 100644 --- a/src/main/java/net/pterodactylus/sone/text/SonePart.java +++ b/src/main/java/net/pterodactylus/sone/text/SonePart.java @@ -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); + } + } diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index 558edb0..19db65b 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -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 { public Iterable 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); diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java index 30c90bc..a1b759b 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java @@ -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()); diff --git a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java index 93d35b9..da48ec5 100644 --- a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java @@ -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 albumPagination = new Pagination(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); diff --git a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java index fbc8cf9..c770da3 100644 --- a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java +++ b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java @@ -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()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/SearchPage.java b/src/main/java/net/pterodactylus/sone/web/SearchPage.java index 22e5b57..691453d 100644 --- a/src/main/java/net/pterodactylus/sone/web/SearchPage.java +++ b/src/main/java/net/pterodactylus/sone/web/SearchPage.java @@ -168,7 +168,7 @@ public class SearchPage extends SoneTemplatePage { * The string generator for the objects * @return The hits for the given phrases */ - private Set> getHits(Collection objects, List phrases, StringGenerator stringGenerator) { + private static Set> getHits(Collection objects, List phrases, StringGenerator stringGenerator) { Set> hits = new HashSet>(); 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 parseSearchPhrases(String query) { + private static List parseSearchPhrases(String query) { List 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 phrases, String expression) { + private static double calculateScore(List phrases, String expression) { logger.log(Level.FINEST, String.format("Calculating Score for “%s”…", expression)); double optionalHits = 0; double requiredHits = 0; diff --git a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java index 9045cdf..559a8ff 100644 --- a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java @@ -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); diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 7654776..b5f6dfb 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -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); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java index f25b19c..6e72a5a 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java @@ -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()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java index b549718..4a1689b 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java @@ -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().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().put("sone", image.getSone()).get())); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java index 7c4bda4..34018af 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java @@ -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 sones) { + private static JsonArray getSones(Set sones) { JsonArray soneArray = new JsonArray(); List sortedSones = new ArrayList(sones); Collections.sort(sortedSones, Sone.NICE_NAME_COMPARATOR); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java index 4e9a879..98a204b 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java @@ -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()); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java index 223a79f..4c61f89 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java @@ -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 sones = new HashSet(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()); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java index 84badf2..f6bc05d 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java @@ -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); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java index c6b7738..22eeec8 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java @@ -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); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java index 9b86c5e..5b27d1b 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java @@ -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()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java index 932be6c..916b6e9 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java @@ -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); } } diff --git a/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java b/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java index 30071f5..97ee53b 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java +++ b/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java @@ -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 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> 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; } diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties index 709b307..e253ad5 100644 --- a/src/main/resources/i18n/sone.de.properties +++ b/src/main/resources/i18n/sone.de.properties @@ -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! diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties index 3fe965e..5c4367a 100644 --- a/src/main/resources/i18n/sone.en.properties +++ b/src/main/resources/i18n/sone.en.properties @@ -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. diff --git a/src/main/resources/i18n/sone.fr.properties b/src/main/resources/i18n/sone.fr.properties index 294a483..7da1634 100644 --- a/src/main/resources/i18n/sone.fr.properties +++ b/src/main/resources/i18n/sone.fr.properties @@ -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|1true if the current page is a “view post” diff --git a/src/main/resources/templates/about.html b/src/main/resources/templates/about.html index 7465529..74ea9f0 100644 --- a/src/main/resources/templates/about.html +++ b/src/main/resources/templates/about.html @@ -5,13 +5,13 @@

Sone – The Freenet Social Network Plugin, Version <% version|html>, © 2010–2012 by David ‘Bombe’ Roden.

- <%= Page.About.Flattr.Description|l10n|html|replace needle="{link}" replacement=''|replace needle="{/link}" replacement=''> + <%= Page.About.Flattr.Description|l10n|html|replace needle=="{link}" replacement==''|replace needle=="{/link}" replacement==''>

<%= Page.About.Homepage.Title|l10n|html>

- <%= Page.About.Homepage.Description|l10n|html|replace needle="{link}" replacement=''|replace needle="{/link}" replacement=''> + <%= Page.About.Homepage.Description|l10n|html|replace needle=="{link}" replacement==''|replace needle=="{/link}" replacement==''>

<%= Page.About.License.Title|l10n|html>

diff --git a/src/main/resources/templates/bookmarks.html b/src/main/resources/templates/bookmarks.html index cf4e7ce..ef2cf68 100644 --- a/src/main/resources/templates/bookmarks.html +++ b/src/main/resources/templates/bookmarks.html @@ -5,14 +5,13 @@

<%= Page.Bookmarks.Page.Title|l10n|html>

- <%= 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> -

<%= Page.Bookmarks.Text.PostsNotLoaded|l10n|html|replace needle='{link}' replacement=''|replace needle='{/link}' replacement=''>

+

<%= Page.Bookmarks.Text.PostsNotLoaded|l10n|html|replace needle=='{link}' replacement==''|replace needle=='{/link}' replacement==''>

<%elseif posts.empty>

<%= Page.Bookmarks.Text.NoBookmarks|l10n|html>

<%/if> diff --git a/src/main/resources/templates/deleteAlbum.html b/src/main/resources/templates/deleteAlbum.html index cb27c19..ea583fc 100644 --- a/src/main/resources/templates/deleteAlbum.html +++ b/src/main/resources/templates/deleteAlbum.html @@ -2,7 +2,7 @@

<%= Page.DeleteAlbum.Page.Title|l10n|html>

-

<%= Page.DeleteAlbum.Text.AlbumWillBeGone|l10n|replace needle="{title}" replacementKey=album.title|html>

+

<%= Page.DeleteAlbum.Text.AlbumWillBeGone|l10n|replace needle=="{title}" replacementKey==album.title|html>

diff --git a/src/main/resources/templates/deleteImage.html b/src/main/resources/templates/deleteImage.html index 68b403e..3fb89e3 100644 --- a/src/main/resources/templates/deleteImage.html +++ b/src/main/resources/templates/deleteImage.html @@ -2,7 +2,7 @@

<%= Page.DeleteImage.Page.Title|l10n|html>

-

<%= Page.DeleteImage.Text.ImageWillBeGone|l10n|replace needle="{image}" replacementKey=image.title|replace needle="{album}" replacementKey=image.album.title|html>

+

<%= Page.DeleteImage.Text.ImageWillBeGone|l10n|replace needle=="{image}" replacement=image.title|replace needle=="{album}" replacement=image.album.title|html>

diff --git a/src/main/resources/templates/deleteSone.html b/src/main/resources/templates/deleteSone.html index cc6023f..c84aeb0 100644 --- a/src/main/resources/templates/deleteSone.html +++ b/src/main/resources/templates/deleteSone.html @@ -1,6 +1,6 @@ <%include include/head.html> -

<%= Page.DeleteSone.Page.Title|l10n|replace needle="{sone}" replacementKey=currentSone.name|html>

+

<%= Page.DeleteSone.Page.Title|l10n|replace needle=="{sone}" replacement=currentSone.name|html>

<%= Page.DeleteSone.Page.Description|l10n|html>

diff --git a/src/main/resources/templates/editProfile.html b/src/main/resources/templates/editProfile.html index e140ac4..f0b8b56 100644 --- a/src/main/resources/templates/editProfile.html +++ b/src/main/resources/templates/editProfile.html @@ -186,13 +186,13 @@
  • - checked="checked"<%/if>/> + checked="checked"<%/if>/> <%= Page.EditProfile.Avatar.Delete|l10n|html>
  • <%foreach currentSone.allImages image>
  • checked="checked"<%/if>/> -
    <% image|image-link max-width=48 max-height=48 mode=enlarge title==image.title>
    +
    <% image|image-link max-width==48 max-height==48 mode==enlarge title=image.title>
  • <%/foreach>
@@ -232,7 +232,7 @@

<%= Page.EditProfile.Fields.AddField.Title|l10n|html>

<%if duplicateFieldName> -

<%= Page.EditProfile.Error.DuplicateFieldName|l10n|replace needle="{fieldName}" replacementKey="fieldName"|html>

+

<%= Page.EditProfile.Error.DuplicateFieldName|l10n|replace needle=="{fieldName}" replacement=fieldName|html>

<%/if>
diff --git a/src/main/resources/templates/imageBrowser.html b/src/main/resources/templates/imageBrowser.html index 55705e1..7752ffb 100644 --- a/src/main/resources/templates/imageBrowser.html +++ b/src/main/resources/templates/imageBrowser.html @@ -312,7 +312,7 @@ <%/if> -

<%= Page.ImageBrowser.Album.Title|l10n|replace needle='{album}' replacementKey=album.title|html>

+

<%= Page.ImageBrowser.Album.Title|l10n|replace needle=='{album}' replacement=album.title|html>

@@ -373,13 +373,16 @@
<%/if> - <%foreach album.images image> - <%first>

<%= Page.ImageBrowser.Header.Images|l10n|html>

<%/first> - <%if loop.count|mod divisor=3>
<%/if> + <%foreach album.images image|paginate pageSize=core.preferences.imagesPerPage page=page> + <%first> +

<%= Page.ImageBrowser.Header.Images|l10n|html>

+ <%include include/pagination.html pageParameter=="page"> + <%/first> + <%if loop.count|mod divisor==3>
<%/if>
<% image.title|html>
@@ -412,15 +415,18 @@ <%/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>
<%/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> +
+ <%include include/pagination.html pageParameter=="page"> + <%/if> <%/foreach> <%if album.sone.local> - - + +
<%include include/uploadImage.html>
@@ -486,9 +492,9 @@ @@ -568,7 +574,7 @@ <%else> -

<%= Page.ImageBrowser.Sone.Title|l10n|replace needle='{sone}' replacementKey=sone.niceName|html>

+

<%= Page.ImageBrowser.Sone.Title|l10n|replace needle=='{sone}' replacement=sone.niceName|html>

+ <%/if> + <%last> + <%include include/pagination.html pagination=albumPagination pageParameter=="page"> + <%/last> <%/foreach> <%/if> diff --git a/src/main/resources/templates/include/browseAlbums.html b/src/main/resources/templates/include/browseAlbums.html index cb9abba..9aacad3 100644 --- a/src/main/resources/templates/include/browseAlbums.html +++ b/src/main/resources/templates/include/browseAlbums.html @@ -1,6 +1,6 @@ <%foreach albums album> <%first>

<%= Page.ImageBrowser.Header.Albums|l10n|html>

<%/first> - <%if loop.count|mod divisor=3>
<%/if> + <%if loop.count|mod divisor==3>
<%/if>
@@ -8,7 +8,7 @@ <%ifnull album.albumImage> <% album.title|html> <%else> - <% 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>
@@ -43,8 +43,8 @@ <%/if>
- <%= 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>
<%/if> <%/foreach> diff --git a/src/main/resources/templates/include/createSone.html b/src/main/resources/templates/include/createSone.html index c0df5c0..86543cc 100644 --- a/src/main/resources/templates/include/createSone.html +++ b/src/main/resources/templates/include/createSone.html @@ -1,7 +1,7 @@ <%if !identitiesWithoutSone.empty>

<%= Page.Login.CreateSone.Title|l10n|html>

-

<%= View.CreateSone.Text.WotIdentityRequired|l10n|html|replace needle="{link}" replacement=''|replace needle="{/link}" replacement=''>

+

<%= View.CreateSone.Text.WotIdentityRequired|l10n|html|replace needle=="{link}" replacement==''|replace needle=="{/link}" replacement==''>

@@ -23,8 +23,8 @@
<%else> <%if !sones.empty> -

<%= View.CreateSone.Text.NoNonSoneIdentities|l10n|html|replace needle="{link}" replacement=''|replace needle="{/link}" replacement="">

+

<%= View.CreateSone.Text.NoNonSoneIdentities|l10n|html|replace needle=="{link}" replacement==''|replace needle=="{/link}" replacement=="">

<%else> -

<%= View.CreateSone.Text.NoIdentities|l10n|html|replace needle="{link}" replacement=''|replace needle="{/link}" replacement="">

+

<%= View.CreateSone.Text.NoIdentities|l10n|html|replace needle=="{link}" replacement==''|replace needle=="{/link}" replacement=="">

<%/if> <%/if> diff --git a/src/main/resources/templates/include/head.html b/src/main/resources/templates/include/head.html index 9988e68..13b564d 100644 --- a/src/main/resources/templates/include/head.html +++ b/src/main/resources/templates/include/head.html @@ -44,7 +44,7 @@ <%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> Profile Avatar <%/if> @@ -55,8 +55,7 @@
<%ifnull ! currentSone>
- <% currentSone|store key=sone> - <%include include/viewSone.html> + <%include include/viewSone.html sone=currentSone>
<%/if>