<modelVersion>4.0.0</modelVersion>
<groupId>net.pterodactylus</groupId>
<artifactId>sone</artifactId>
- <version>0.3.6-5</version>
+ <version>0.5.1</version>
<dependencies>
<dependency>
<groupId>net.pterodactylus</groupId>
<artifactId>utils</artifactId>
- <version>0.7.7</version>
+ <version>0.9.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<dependency>
<groupId>org.freenetproject</groupId>
<artifactId>fred</artifactId>
- <version>0.7.5.1311</version>
+ <version>0.7.5.1336</version>
<scope>provided</scope>
</dependency>
<dependency>
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
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;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.validation.Validation;
import net.pterodactylus.util.version.Version;
import freenet.keys.FreenetURI;
/** The options. */
private final Options options = new Options();
+ /** The preferences. */
+ private final Preferences preferences = new Preferences(options);
+
/** The core listener manager. */
private final CoreListenerManager coreListenerManager = new CoreListenerManager(this);
/** All known replies. */
private Set<String> knownReplies = new HashSet<String>();
+ /** All bookmarked posts. */
+ /* synchronize access on itself. */
+ private Set<String> bookmarkedPosts = new HashSet<String>();
+
+ /** Trusted identities, sorted by own identities. */
+ private Map<OwnIdentity, Set<Identity>> trustedIdentities = Collections.synchronizedMap(new HashMap<OwnIdentity, Set<Identity>>());
+
/** All known albums. */
private Map<String, Album> albums = new HashMap<String, Album>();
*
* @return The options of the core
*/
- public Options getOptions() {
- return options;
- }
-
- /**
- * Returns whether the “Sone rescue mode” is currently activated.
- *
- * @return {@code true} if the “Sone rescue mode” is currently activated,
- * {@code false} if it is not
- */
- public boolean isSoneRescueMode() {
- return options.getBooleanOption("SoneRescueMode").get();
+ public Preferences getPreferences() {
+ return preferences;
}
/**
if ((sone == null) && create) {
sone = new Sone(id);
localSones.put(id, sone);
+ setSoneStatus(sone, SoneStatus.unknown);
}
return sone;
}
if ((sone == null) && create) {
sone = new Sone(id);
remoteSones.put(id, sone);
+ setSoneStatus(sone, SoneStatus.unknown);
}
return sone;
}
}
/**
- * Returns whether the given Sone is a new Sone. After this check, the Sone
- * is marked as known, i.e. a second call with the same parameters will
- * always yield {@code false}.
+ * Returns whether the Sone with the given ID is a new Sone.
*
- * @param sone
- * The sone to check for
+ * @param soneId
+ * The ID of the sone to check for
* @return {@code true} if the given Sone is new, false otherwise
*/
- public boolean isNewSone(Sone sone) {
+ public boolean isNewSone(String soneId) {
synchronized (newSones) {
- boolean isNew = !knownSones.contains(sone.getId()) && newSones.remove(sone.getId());
- knownSones.add(sone.getId());
- if (isNew) {
- coreListenerManager.fireMarkSoneKnown(sone);
- }
- return isNew;
+ return !knownSones.contains(soneId) && newSones.contains(soneId);
}
}
}
/**
+ * Returns whether the target Sone is trusted by the origin Sone.
+ *
+ * @param origin
+ * The origin Sone
+ * @param target
+ * The target Sone
+ * @return {@code true} if the target Sone is trusted by the origin Sone
+ */
+ public boolean isSoneTrusted(Sone origin, Sone target) {
+ return trustedIdentities.containsKey(origin) && trustedIdentities.get(origin.getIdentity()).contains(target);
+ }
+
+ /**
* Returns the post with the given ID.
*
* @param postId
}
/**
- * Returns whether the given post ID is new. After this method returns it is
- * marked a known post ID.
+ * Returns whether the given post ID is new.
*
* @param postId
* The post ID
* otherwise
*/
public boolean isNewPost(String postId) {
- return isNewPost(postId, true);
- }
-
- /**
- * Returns whether the given post ID is new. If {@code markAsKnown} is
- * {@code true} then after this method returns the post ID is marked a known
- * post ID.
- *
- * @param postId
- * The post ID
- * @param markAsKnown
- * {@code true} to mark the post ID as known, {@code false} to
- * not to mark it as known
- * @return {@code true} if the post is considered to be new, {@code false}
- * otherwise
- */
- public boolean isNewPost(String postId, boolean markAsKnown) {
synchronized (newPosts) {
- boolean isNew = !knownPosts.contains(postId) && newPosts.contains(postId);
- if (markAsKnown) {
- Post post = getPost(postId, false);
- if (post != null) {
- markPostKnown(post);
- }
- }
- return isNew;
+ return !knownPosts.contains(postId) && newPosts.contains(postId);
}
}
* otherwise
*/
public boolean isNewReply(String replyId) {
- return isNewReply(replyId, true);
- }
-
- /**
- * Returns whether the reply with the given ID is new.
- *
- * @param replyId
- * The ID of the reply to check
- * @param markAsKnown
- * {@code true} to mark the reply as known, {@code false} to not
- * to mark it as known
- * @return {@code true} if the reply is considered to be new, {@code false}
- * otherwise
- */
- public boolean isNewReply(String replyId, boolean markAsKnown) {
synchronized (newReplies) {
- boolean isNew = !knownReplies.contains(replyId) && newReplies.contains(replyId);
- if (markAsKnown) {
- Reply reply = getReply(replyId, false);
- if (reply != null) {
- markReplyKnown(reply);
- }
- }
- return isNew;
+ return !knownReplies.contains(replyId) && newReplies.contains(replyId);
}
}
}
/**
+ * Returns whether the given post is bookmarked.
+ *
+ * @param post
+ * The post to check
+ * @return {@code true} if the given post is bookmarked, {@code false}
+ * otherwise
+ */
+ public boolean isBookmarked(Post post) {
+ return isPostBookmarked(post.getId());
+ }
+
+ /**
+ * Returns whether the post with the given ID is bookmarked.
+ *
+ * @param id
+ * The ID of the post to check
+ * @return {@code true} if the post with the given ID is bookmarked,
+ * {@code false} otherwise
+ */
+ public boolean isPostBookmarked(String id) {
+ synchronized (bookmarkedPosts) {
+ return bookmarkedPosts.contains(id);
+ }
+ }
+
+ /**
+ * Returns all currently known bookmarked posts.
+ *
+ * @return All bookmarked posts
+ */
+ public Set<Post> getBookmarkedPosts() {
+ Set<Post> posts = new HashSet<Post>();
+ synchronized (bookmarkedPosts) {
+ for (String bookmarkedPostId : bookmarkedPosts) {
+ Post post = getPost(bookmarkedPostId, false);
+ if (post != null) {
+ posts.add(post);
+ }
+ }
+ }
+ return posts;
+ }
+
+ /**
+ *
* Returns the album with the given ID, creating a new album if no album
* with the given ID can be found.
*
soneInserters.put(sone, soneInserter);
setSoneStatus(sone, SoneStatus.idle);
loadSone(sone);
- if (!isSoneRescueMode()) {
+ if (!preferences.isSoneRescueMode()) {
soneInserter.start();
}
new Thread(new Runnable() {
@Override
@SuppressWarnings("synthetic-access")
public void run() {
- if (!isSoneRescueMode()) {
+ if (!preferences.isSoneRescueMode()) {
soneDownloader.fetchSone(sone);
return;
}
coreListenerManager.fireRescuingSone(sone);
lockSone(sone);
long edition = sone.getLatestEdition();
- while (!stopped && (edition >= 0) && isSoneRescueMode()) {
+ while (!stopped && (edition >= 0) && preferences.isSoneRescueMode()) {
logger.log(Level.FINE, "Downloading edition " + edition + "…");
soneDownloader.fetchSone(sone, sone.getRequestUri().setKeyType("SSK").setDocName("Sone-" + edition));
--edition;
* @return The created Sone
*/
public Sone createSone(OwnIdentity ownIdentity) {
- identityManager.addContext(ownIdentity, "Sone");
+ try {
+ ownIdentity.addContext("Sone");
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.SEVERE, "Could not add “Sone” context to own identity: " + ownIdentity, wote1);
+ return null;
+ }
Sone sone = addLocalSone(ownIdentity);
return sone;
}
}
/**
+ * Retrieves the trust relationship from the origin to the target. If the
+ * trust relationship can not be retrieved, {@code null} is returned.
+ *
+ * @see Identity#getTrust(OwnIdentity)
+ * @param origin
+ * The origin of the trust tree
+ * @param target
+ * The target of the trust
+ * @return The trust relationship
+ */
+ public Trust getTrust(Sone origin, Sone target) {
+ if (!isLocalSone(origin)) {
+ logger.log(Level.WARNING, "Tried to get trust from remote Sone: %s", origin);
+ return null;
+ }
+ return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity());
+ }
+
+ /**
+ * Sets the trust value of the given origin Sone for the target Sone.
+ *
+ * @param origin
+ * The origin Sone
+ * @param target
+ * The target Sone
+ * @param trustValue
+ * The trust value (from {@code -100} to {@code 100})
+ */
+ 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, "Could not set trust for Sone: " + target, wote1);
+ }
+ }
+
+ /**
+ * Removes any trust assignment for the given target Sone.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ 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, "Could not remove trust for Sone: " + target, wote1);
+ }
+ }
+
+ /**
+ * Assigns the configured positive trust value for the given target.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ public void trustSone(Sone origin, Sone target) {
+ setTrust(origin, target, preferences.getPositiveTrust());
+ }
+
+ /**
+ * Assigns the configured negative trust value for the given target.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ public void distrustSone(Sone origin, Sone target) {
+ setTrust(origin, target, preferences.getNegativeTrust());
+ }
+
+ /**
+ * Removes the trust assignment for the given target.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ public void untrustSone(Sone origin, Sone target) {
+ removeTrust(origin, target);
+ }
+
+ /**
* Updates the stores Sone with the given Sone.
*
* @param sone
*/
public void updateSone(Sone sone) {
if (hasSone(sone.getId())) {
- boolean soneRescueMode = isLocalSone(sone) && isSoneRescueMode();
+ boolean soneRescueMode = isLocalSone(sone) && preferences.isSoneRescueMode();
Sone storedSone = getSone(sone.getId());
if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) {
logger.log(Level.FINE, "Downloaded Sone %s is not newer than stored Sone %s.", new Object[] { sone, storedSone });
}
}
}
+ List<Post> storedPosts = storedSone.getPosts();
synchronized (newPosts) {
for (Post post : sone.getPosts()) {
- post.setSone(getSone(post.getSone().getId()));
- if (!storedSone.getPosts().contains(post) && !knownPosts.contains(post.getId())) {
+ post.setSone(storedSone);
+ if (!storedPosts.contains(post) && !knownPosts.contains(post.getId())) {
newPosts.add(post.getId());
coreListenerManager.fireNewPostFound(post);
}
}
}
}
+ Set<Reply> storedReplies = storedSone.getReplies();
synchronized (newReplies) {
for (Reply reply : sone.getReplies()) {
- reply.setSone(getSone(reply.getSone().getId()));
- if (!storedSone.getReplies().contains(reply) && !knownReplies.contains(reply.getId())) {
+ reply.setSone(storedSone);
+ if (!storedReplies.contains(reply) && !knownReplies.contains(reply.getId())) {
newReplies.add(reply.getId());
coreListenerManager.fireNewReplyFound(reply);
}
localSones.remove(sone.getId());
soneInserters.remove(sone).stop();
}
- identityManager.removeContext((OwnIdentity) sone.getIdentity(), "Sone");
- identityManager.removeProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition");
+ try {
+ ((OwnIdentity) sone.getIdentity()).removeContext("Sone");
+ ((OwnIdentity) sone.getIdentity()).removeProperty("Sone.LatestEdition");
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not remove context and properties from Sone: " + sone, wote1);
+ }
try {
configuration.getLongValue("Sone/" + sone.getId() + "/Time").setValue(null);
} catch (ConfigurationException ce1) {
}
/**
+ * Marks the given Sone as known. If the Sone was {@link #isNewPost(String)
+ * new} before, a {@link CoreListener#markSoneKnown(Sone)} event is fired.
+ *
+ * @param sone
+ * The Sone to mark as known
+ */
+ public void markSoneKnown(Sone sone) {
+ synchronized (newSones) {
+ if (newSones.remove(sone.getId())) {
+ knownSones.add(sone.getId());
+ coreListenerManager.fireMarkSoneKnown(sone);
+ saveConfiguration();
+ }
+ }
+ }
+
+ /**
* Loads and updates the given Sone from the configuration. If any error is
* encountered, loading is aborted and the given Sone is not changed.
*
profile.setBirthMonth(configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").getValue(null));
profile.setBirthYear(configuration.getIntValue(sonePrefix + "/Profile/BirthYear").getValue(null));
+ /* load profile fields. */
+ while (true) {
+ String fieldPrefix = sonePrefix + "/Profile/Fields/" + profile.getFields().size();
+ String fieldName = configuration.getStringValue(fieldPrefix + "/Name").getValue(null);
+ if (fieldName == null) {
+ break;
+ }
+ String fieldValue = configuration.getStringValue(fieldPrefix + "/Value").getValue("");
+ profile.addField(fieldName).setValue(fieldValue);
+ }
+
/* load posts. */
Set<Post> posts = new HashSet<Post>();
while (true) {
}
logger.log(Level.INFO, "Saving Sone: %s", sone);
- identityManager.setProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition", String.valueOf(sone.getLatestEdition()));
try {
+ ((OwnIdentity) sone.getIdentity()).setProperty("Sone.LatestEdition", String.valueOf(sone.getLatestEdition()));
+
/* save Sone into configuration. */
String sonePrefix = "Sone/" + sone.getId();
configuration.getLongValue(sonePrefix + "/Time").setValue(sone.getTime());
configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").setValue(profile.getBirthMonth());
configuration.getIntValue(sonePrefix + "/Profile/BirthYear").setValue(profile.getBirthYear());
+ /* save profile fields. */
+ int fieldCounter = 0;
+ for (Field profileField : profile.getFields()) {
+ String fieldPrefix = sonePrefix + "/Profile/Fields/" + fieldCounter++;
+ configuration.getStringValue(fieldPrefix + "/Name").setValue(profileField.getName());
+ configuration.getStringValue(fieldPrefix + "/Value").setValue(profileField.getValue());
+ }
+ configuration.getStringValue(sonePrefix + "/Profile/Fields/" + fieldCounter + "/Name").setValue(null);
+
/* save posts. */
int postCounter = 0;
for (Post post : sone.getPosts()) {
logger.log(Level.INFO, "Sone %s saved.", sone);
} catch (ConfigurationException ce1) {
logger.log(Level.WARNING, "Could not save Sone: " + sone, ce1);
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not set WoT property for Sone: " + sone, wote1);
}
}
}
/**
+ * Bookmarks the given post.
+ *
+ * @param post
+ * The post to bookmark
+ */
+ public void bookmark(Post post) {
+ bookmarkPost(post.getId());
+ }
+
+ /**
+ * Bookmarks the post with the given ID.
+ *
+ * @param id
+ * The ID of the post to bookmark
+ */
+ public void bookmarkPost(String id) {
+ synchronized (bookmarkedPosts) {
+ bookmarkedPosts.add(id);
+ }
+ }
+
+ /**
+ * Removes the given post from the bookmarks.
+ *
+ * @param post
+ * The post to unbookmark
+ */
+ public void unbookmark(Post post) {
+ unbookmarkPost(post.getId());
+ }
+
+ /**
+ * Removes the post with the given ID from the bookmarks.
+ *
+ * @param id
+ * The ID of the post to unbookmark
+ */
+ public void unbookmarkPost(String id) {
+ synchronized (bookmarkedPosts) {
+ bookmarkedPosts.remove(id);
+ }
+ }
+
+ /**
* Creates a new reply.
*
* @param sone
try {
configuration.getIntValue("Option/ConfigurationVersion").setValue(0);
configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal());
+ configuration.getIntValue("Option/PositiveTrust").setValue(options.getIntegerOption("PositiveTrust").getReal());
+ configuration.getIntValue("Option/NegativeTrust").setValue(options.getIntegerOption("NegativeTrust").getReal());
+ configuration.getStringValue("Option/TrustComment").setValue(options.getStringOption("TrustComment").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());
configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null);
}
+ /* save bookmarked posts. */
+ int bookmarkedPostCounter = 0;
+ synchronized (bookmarkedPosts) {
+ for (String bookmarkedPostId : bookmarkedPosts) {
+ configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").setValue(bookmarkedPostId);
+ }
+ }
+ configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").setValue(null);
+
/* now save it. */
configuration.save();
}
}));
+ options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75));
+ options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-100));
+ options.addStringOption("TrustComment", new DefaultOption<String>("Set from Sone Web Interface"));
options.addBooleanOption("SoneRescueMode", new DefaultOption<Boolean>(false));
options.addBooleanOption("ClearOnNextRestart", new DefaultOption<Boolean>(false));
options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption<Boolean>(false));
}
options.getIntegerOption("InsertionDelay").set(configuration.getIntValue("Option/InsertionDelay").getValue(null));
+ options.getIntegerOption("PositiveTrust").set(configuration.getIntValue("Option/PositiveTrust").getValue(null));
+ options.getIntegerOption("NegativeTrust").set(configuration.getIntValue("Option/NegativeTrust").getValue(null));
+ options.getStringOption("TrustComment").set(configuration.getStringValue("Option/TrustComment").getValue(null));
options.getBooleanOption("SoneRescueMode").set(configuration.getBooleanValue("Option/SoneRescueMode").getValue(null));
/* load known Sones. */
}
}
+ /* load bookmarked posts. */
+ int bookmarkedPostCounter = 0;
+ while (true) {
+ String bookmarkedPostId = configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").getValue(null);
+ if (bookmarkedPostId == null) {
+ break;
+ }
+ synchronized (bookmarkedPosts) {
+ bookmarkedPosts.add(bookmarkedPostId);
+ }
+ }
+
}
/**
public void ownIdentityAdded(OwnIdentity ownIdentity) {
logger.log(Level.FINEST, "Adding OwnIdentity: " + ownIdentity);
if (ownIdentity.hasContext("Sone")) {
+ trustedIdentities.put(ownIdentity, Collections.synchronizedSet(new HashSet<Identity>()));
addLocalSone(ownIdentity);
}
}
@Override
public void ownIdentityRemoved(OwnIdentity ownIdentity) {
logger.log(Level.FINEST, "Removing OwnIdentity: " + ownIdentity);
+ trustedIdentities.remove(ownIdentity);
}
/**
* {@inheritDoc}
*/
@Override
- public void identityAdded(Identity identity) {
+ public void identityAdded(OwnIdentity ownIdentity, Identity identity) {
logger.log(Level.FINEST, "Adding Identity: " + identity);
+ trustedIdentities.get(ownIdentity).add(identity);
addRemoteSone(identity);
}
* {@inheritDoc}
*/
@Override
- public void identityUpdated(final Identity identity) {
+ public void identityUpdated(OwnIdentity ownIdentity, final Identity identity) {
new Thread(new Runnable() {
@Override
@SuppressWarnings("synthetic-access")
public void run() {
Sone sone = getRemoteSone(identity.getId());
+ sone.setIdentity(identity);
+ soneDownloader.addSone(sone);
soneDownloader.fetchSone(sone);
}
}).start();
* {@inheritDoc}
*/
@Override
- public void identityRemoved(Identity identity) {
- /* TODO */
+ public void identityRemoved(OwnIdentity ownIdentity, Identity identity) {
+ trustedIdentities.get(ownIdentity).remove(identity);
}
//
* {@inheritDoc}
*/
@Override
- public void updateFound(Version version, long releaseTime) {
- coreListenerManager.fireUpdateFound(version, releaseTime);
+ public void updateFound(Version version, long releaseTime, long latestEdition) {
+ coreListenerManager.fireUpdateFound(version, releaseTime, latestEdition);
+ }
+
+ /**
+ * Convenience interface for external classes that want to access the core’s
+ * configuration.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public static class Preferences {
+
+ /** The wrapped options. */
+ private final Options options;
+
+ /**
+ * Creates a new preferences object wrapped around the given options.
+ *
+ * @param options
+ * The options to wrap
+ */
+ public Preferences(Options options) {
+ this.options = options;
+ }
+
+ /**
+ * Returns the insertion delay.
+ *
+ * @return The insertion delay
+ */
+ public int getInsertionDelay() {
+ return options.getIntegerOption("InsertionDelay").get();
+ }
+
+ /**
+ * Sets the insertion delay
+ *
+ * @param insertionDelay
+ * The new insertion delay, or {@code null} to restore it to
+ * the default value
+ * @return This preferences
+ */
+ public Preferences setInsertionDelay(Integer insertionDelay) {
+ options.getIntegerOption("InsertionDelay").set(insertionDelay);
+ return this;
+ }
+
+ /**
+ * Returns the positive trust.
+ *
+ * @return The positive trust
+ */
+ public int getPositiveTrust() {
+ return options.getIntegerOption("PositiveTrust").get();
+ }
+
+ /**
+ * Sets the positive trust.
+ *
+ * @param positiveTrust
+ * The new positive trust, or {@code null} to restore it to
+ * the default vlaue
+ * @return This preferences
+ */
+ public Preferences setPositiveTrust(Integer positiveTrust) {
+ options.getIntegerOption("PositiveTrust").set(positiveTrust);
+ return this;
+ }
+
+ /**
+ * Returns the negative trust.
+ *
+ * @return The negative trust
+ */
+ public int getNegativeTrust() {
+ return options.getIntegerOption("NegativeTrust").get();
+ }
+
+ /**
+ * Sets the negative trust.
+ *
+ * @param negativeTrust
+ * The negative trust, or {@code null} to restore it to the
+ * default value
+ * @return The preferences
+ */
+ public Preferences setNegativeTrust(Integer negativeTrust) {
+ options.getIntegerOption("NegativeTrust").set(negativeTrust);
+ return this;
+ }
+
+ /**
+ * Returns the trust comment. This is the comment that is set in the web
+ * of trust when a trust value is assigned to an identity.
+ *
+ * @return The trust comment
+ */
+ public String getTrustComment() {
+ return options.getStringOption("TrustComment").get();
+ }
+
+ /**
+ * Sets the trust comment.
+ *
+ * @param trustComment
+ * The trust comment, or {@code null} to restore it to the
+ * default value
+ * @return This preferences
+ */
+ public Preferences setTrustComment(String trustComment) {
+ options.getStringOption("TrustComment").set(trustComment);
+ return this;
+ }
+
+ /**
+ * Returns whether the rescue mode is active.
+ *
+ * @return {@code true} if the rescue mode is active, {@code false}
+ * otherwise
+ */
+ public boolean isSoneRescueMode() {
+ return options.getBooleanOption("SoneRescueMode").get();
+ }
+
+ /**
+ * Sets whether the rescue mode is active.
+ *
+ * @param soneRescueMode
+ * {@code true} if the rescue mode is active, {@code false}
+ * otherwise
+ * @return This preferences
+ */
+ public Preferences setSoneRescueMode(Boolean soneRescueMode) {
+ options.getBooleanOption("SoneRescueMode").set(soneRescueMode);
+ 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;
+ }
+
}
}
* The version that was found
* @param releaseTime
* The release time of the new version
+ * @param latestEdition
+ * The latest edition of the Sone homepage
*/
- public void updateFound(Version version, long releaseTime);
+ public void updateFound(Version version, long releaseTime, long latestEdition);
}
/**
* Notifies all listeners that a new version was found.
*
- * @see CoreListener#updateFound(Version, long)
+ * @see CoreListener#updateFound(Version, long, long)
* @param version
* The new version
* @param releaseTime
* The release time of the new version
+ * @param latestEdition
+ * The latest edition of the Sone homepage
*/
- void fireUpdateFound(Version version, long releaseTime) {
+ void fireUpdateFound(Version version, long releaseTime, long latestEdition) {
for (CoreListener coreListener : getListeners()) {
- coreListener.updateFound(version, releaseTime);
+ coreListener.updateFound(version, releaseTime, latestEdition);
}
}
@SuppressWarnings("synthetic-access")
public void onFoundEdition(long edition, USK key, ObjectContainer objectContainer, ClientContext clientContext, boolean metadata, short codec, byte[] data, boolean newKnownGood, boolean newSlotToo) {
logger.log(Level.FINE, "Found USK update for Sone “%s” at %s, new known good: %s, new slot too: %s.", new Object[] { sone, key, newKnownGood, newSlotToo });
- if (newKnownGood) {
- sone.setLatestEdition(key.suggestedEdition);
+ if (edition > sone.getLatestEdition()) {
+ sone.setLatestEdition(edition);
new Thread(new Runnable() {
@Override
}
};
soneUskCallbacks.put(sone.getId(), uskCallback);
- node.clientCore.uskManager.subscribe(USK.create(sone.getRequestUri()), uskCallback, true, (HighLevelSimpleClientImpl) client);
+ node.clientCore.uskManager.subscribe(USK.create(sone.getRequestUri()), uskCallback, (System.currentTimeMillis() - sone.getTime()) < 7 * 24 * 60 * 60 * 1000, (HighLevelSimpleClientImpl) client);
} catch (MalformedURLException mue1) {
logger.log(Level.WARNING, "Could not subscribe USK “" + sone.getRequestUri() + "”!", mue1);
}
/** Holds all {@link Integer} {@link Option}s. */
private final Map<String, Option<Integer>> integerOptions = Collections.synchronizedMap(new HashMap<String, Option<Integer>>());
+ /** Holds all {@link String} {@link Option}s. */
+ private final Map<String, Option<String>> stringOptions = Collections.synchronizedMap(new HashMap<String, Option<String>>());
+
/**
* Adds a boolean option.
*
return integerOptions.get(name);
}
+ /**
+ * Adds a {@link String} {@link Option}.
+ *
+ * @param name
+ * The name of the option
+ * @param stringOption
+ * The option
+ * @return The given option
+ */
+ public Option<String> addStringOption(String name, Option<String> stringOption) {
+ stringOptions.put(name, stringOption);
+ return stringOption;
+ }
+
+ /**
+ * Returns a {@link String} {@link Option}.
+ *
+ * @param name
+ * The name of the string option to get
+ * @return The string option, or {@code null} if there is no option with the
+ * given name
+ */
+ public Option<String> getStringOption(String name) {
+ return stringOptions.get(name);
+ }
+
}
import java.util.logging.Level;
import java.util.logging.Logger;
+import net.pterodactylus.sone.core.Core.Preferences;
import net.pterodactylus.sone.core.Core.SoneStatus;
import net.pterodactylus.sone.data.Client;
import net.pterodactylus.sone.data.Post;
* The Sone to add
*/
public void addSone(Sone sone) {
- if (sones.add(sone)) {
- freenetInterface.registerUsk(sone, this);
+ if (!sones.add(sone)) {
+ freenetInterface.unregisterUsk(sone);
}
+ freenetInterface.registerUsk(sone, this);
}
/**
/**
* Fetches the updated Sone. This method can be used to fetch a Sone from a
- * specific URI (which happens when {@link Core#isSoneRescueMode() „Sone
- * rescue mode“} is active).
+ * specific URI (which happens when {@link Preferences#isSoneRescueMode()
+ * „Sone rescue mode“} is active).
*
* @param sone
* The Sone to fetch
* The URI to fetch the Sone from
*/
public void fetchSone(Sone sone, FreenetURI soneUri) {
- if (core.getSoneStatus(sone) == SoneStatus.downloading) {
- return;
- }
logger.log(Level.FINE, "Starting fetch for Sone “%s” from %s…", new Object[] { sone, soneUri });
FreenetURI requestUri = soneUri.setMetaString(new String[] { "sone.xml" });
core.setSoneStatus(sone, SoneStatus.downloading);
logger.log(Level.FINEST, "Got %d bytes back.", fetchResults.getRight().size());
Sone parsedSone = parseSone(sone, fetchResults.getRight(), fetchResults.getLeft());
if (parsedSone != null) {
+ addSone(parsedSone);
core.updateSone(parsedSone);
}
} finally {
if ((soneInsertUri != null) && (sone.getInsertUri() == null)) {
try {
sone.setInsertUri(new FreenetURI(soneInsertUri));
- sone.setLatestEdition(Math.max(sone.getRequestUri().getSuggestedEdition(), sone.getInsertUri().getSuggestedEdition()));
+ sone.setLatestEdition(Math.max(sone.getRequestUri().getEdition(), sone.getInsertUri().getEdition()));
} catch (MalformedURLException mue1) {
/* TODO - mark Sone as bad. */
logger.log(Level.WARNING, "Downloaded Sone " + sone + " has invalid insert URI: " + soneInsertUri, mue1);
Profile profile = new Profile().setFirstName(profileFirstName).setMiddleName(profileMiddleName).setLastName(profileLastName);
profile.setBirthDay(profileBirthDay).setBirthMonth(profileBirthMonth).setBirthYear(profileBirthYear);
+ /* parse profile fields. */
+ SimpleXML profileFieldsXml = profileXml.getNode("fields");
+ if (profileFieldsXml != null) {
+ for (SimpleXML fieldXml : profileFieldsXml.getNodes("field")) {
+ String fieldName = fieldXml.getValue("field-name", null);
+ String fieldValue = fieldXml.getValue("field-value", null);
+ if ((fieldName == null) || (fieldValue == null)) {
+ logger.log(Level.WARNING, "Downloaded profile field for Sone %s with missing data! Name: %s, Value: %s", new Object[] { sone, fieldName, fieldValue });
+ return null;
+ }
+ try {
+ profile.addField(fieldName).setValue(fieldValue);
+ } catch (IllegalArgumentException iae1) {
+ logger.log(Level.WARNING, "Duplicate field: " + fieldName, iae1);
+ return null;
+ }
+ }
+ }
+
/* parse posts. */
SimpleXML postsXml = soneXml.getNode("posts");
Set<Post> posts = new HashSet<Post>();
import net.pterodactylus.util.io.Closer;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.service.AbstractService;
-import net.pterodactylus.util.template.DefaultTemplateFactory;
+import net.pterodactylus.util.template.HtmlFilter;
import net.pterodactylus.util.template.ReflectionAccessor;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateContextFactory;
import net.pterodactylus.util.template.TemplateException;
+import net.pterodactylus.util.template.TemplateParser;
import net.pterodactylus.util.template.XmlFilter;
import freenet.client.async.ManifestElement;
import freenet.keys.FreenetURI;
private static volatile int insertionDelay = 60;
/** The template factory used to create the templates. */
- private static final DefaultTemplateFactory templateFactory = new DefaultTemplateFactory();
+ private static final TemplateContextFactory templateContextFactory = new TemplateContextFactory();
static {
- templateFactory.addAccessor(Object.class, new ReflectionAccessor());
- templateFactory.addFilter("xml", new XmlFilter());
+ templateContextFactory.addAccessor(Object.class, new ReflectionAccessor());
+ templateContextFactory.addFilter("xml", new XmlFilter());
+ templateContextFactory.addFilter("html", new HtmlFilter());
}
/** The UTF-8 charset. */
} else {
lastModificationTime = System.currentTimeMillis();
modified = true;
- sone.setTime(lastModificationTime);
logger.log(Level.FINE, "Sone %s has been modified, waiting %d seconds before inserting.", new Object[] { sone.getName(), insertionDelay });
}
lastFingerprint = fingerprint;
boolean success = false;
try {
core.setSoneStatus(sone, SoneStatus.inserting);
- FreenetURI finalUri = freenetInterface.insertDirectory(insertInformation.getInsertUri().setKeyType("USK").setSuggestedEdition(0), insertInformation.generateManifestEntries(), "index.html");
+ long insertTime = System.currentTimeMillis();
+ insertInformation.setTime(insertTime);
+ FreenetURI finalUri = freenetInterface.insertDirectory(insertInformation.getInsertUri(), insertInformation.generateManifestEntries(), "index.html");
/* at this point we might already be stopped. */
if (shouldStop()) {
/* if so, bail out, don’t change anything. */
break;
}
+ sone.setTime(insertTime);
sone.setLatestEdition(finalUri.getEdition());
+ core.saveSone(sone);
success = true;
logger.log(Level.INFO, "Inserted Sone “%s” at %s.", new Object[] { sone.getName(), finalUri });
} catch (SoneException se1) {
return (FreenetURI) soneProperties.get("insertUri");
}
+ /**
+ * Sets the time of the Sone at the time of the insert.
+ *
+ * @param time
+ * The time of the Sone
+ */
+ public void setTime(long time) {
+ soneProperties.put("time", time);
+ }
+
//
// ACTIONS
//
*/
@SuppressWarnings("synthetic-access")
private ManifestElement createManifestElement(String name, String contentType, String templateName) {
- InputStreamReader templateInputStreamReader;
- Template template = templateFactory.createTemplate(templateInputStreamReader = new InputStreamReader(getClass().getResourceAsStream(templateName), utf8Charset));
+ InputStreamReader templateInputStreamReader = null;
+ Template template;
try {
- template.parse();
+ templateInputStreamReader = new InputStreamReader(getClass().getResourceAsStream(templateName), utf8Charset);
+ template = TemplateParser.parse(templateInputStreamReader);
} catch (TemplateException te1) {
logger.log(Level.SEVERE, "Could not parse template “" + templateName + "”!", te1);
return null;
Closer.close(templateInputStreamReader);
}
- template.set("currentSone", soneProperties);
- template.set("version", SonePlugin.VERSION);
+ TemplateContext templateContext = templateContextFactory.createTemplateContext();
+ templateContext.set("currentSone", soneProperties);
+ templateContext.set("version", SonePlugin.VERSION);
StringWriter writer = new StringWriter();
StringBucket bucket = null;
try {
- template.render(writer);
+ template.render(templateContext, writer);
bucket = new StringBucket(writer.toString(), utf8Charset);
return new ManifestElement(name, bucket, contentType, bucket.size());
} catch (TemplateException te1) {
private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
/** The current latest known edition. */
- private static final int LATEST_EDITION = 23;
+ private static final int LATEST_EDITION = 25;
/** The Freenet interface. */
private final FreenetInterface freenetInterface;
/** The current URI of the homepage. */
private FreenetURI currentUri;
+ /** The latest known edition. */
+ private long latestEdition;
+
/** The current latest known version. */
private Version currentLatestVersion = SonePlugin.VERSION;
return latestVersionDate;
}
+ /**
+ * Returns the latest known edition of the Sone homepage.
+ *
+ * @return The latest edition of the Sone homepage
+ */
+ public long getLatestEdition() {
+ return latestEdition;
+ }
+
//
// ACTIONS
//
}
Bucket resultBucket = uriResult.getRight().asBucket();
try {
- parseProperties(resultBucket.getInputStream());
+ parseProperties(resultBucket.getInputStream(), edition);
+ latestEdition = edition;
} catch (IOException ioe1) {
logger.log(Level.WARNING, "Could not parse sone.properties of " + uri, ioe1);
} finally {
* Parses the properties of the latest version and fires events, if
* necessary.
*
- * @see UpdateListener#updateFound(Version, long)
- * @see UpdateListenerManager#fireUpdateFound(Version, long)
+ * @see UpdateListener#updateFound(Version, long, long)
+ * @see UpdateListenerManager#fireUpdateFound(Version, long, long)
* @param propertiesInputStream
* The input stream to parse
+ * @param edition
+ * The latest edition of the Sone homepage
* @throws IOException
* if an I/O error occured
*/
- private void parseProperties(InputStream propertiesInputStream) throws IOException {
+ private void parseProperties(InputStream propertiesInputStream, long edition) throws IOException {
Properties properties = new Properties();
InputStreamReader inputStreamReader = null;
try {
currentLatestVersion = version;
latestVersionDate = releaseTime;
logger.log(Level.INFO, "Found new version: %s (%tc)", new Object[] { version, new Date(releaseTime) });
- updateListenerManager.fireUpdateFound(version, releaseTime);
+ updateListenerManager.fireUpdateFound(version, releaseTime, edition);
}
}
* The version that was found
* @param releaseTime
* The release time of the version
+ * @param latestEdition
+ * The latest edition of the Sone homepage
*/
- public void updateFound(Version version, long releaseTime);
+ public void updateFound(Version version, long releaseTime, long latestEdition);
}
* The new version
* @param releaseTime
* The release time of the new version
+ * @param latestEdition
+ * The latest edition of the Sone homepage
*/
- void fireUpdateFound(Version version, long releaseTime) {
+ void fireUpdateFound(Version version, long releaseTime, long latestEdition) {
for (UpdateListener updateListener : getListeners()) {
- updateListener.updateFound(version, releaseTime);
+ updateListener.updateFound(version, releaseTime, latestEdition);
}
}
package net.pterodactylus.sone.data;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import net.pterodactylus.util.validation.Validation;
+
/**
* A profile stores personal information about a {@link Sone}. All information
* is optional and can be {@code null}.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Profile {
-
- /** Whether the profile was modified. */
- private volatile boolean modified;
+public class Profile implements Fingerprintable {
/** The first name. */
private volatile String firstName;
/** The year of the birth date. */
private volatile Integer birthYear;
+ /** Additional fields in the profile. */
+ private final List<Field> fields = Collections.synchronizedList(new ArrayList<Field>());
+
/**
* Creates a new empty profile.
*/
this.birthDay = profile.birthDay;
this.birthMonth = profile.birthMonth;
this.birthYear = profile.birthYear;
+ this.fields.addAll(profile.fields);
}
//
//
/**
- * Returns whether this profile was modified after creation. To clear the
- * “is modified” flag you need to create a new profile from this one using
- * the {@link #Profile(Profile)} constructor.
- *
- * @return {@code true} if this profile was modified after creation,
- * {@code false} otherwise
- */
- public boolean isModified() {
- return modified;
- }
-
- /**
* Returns the first name.
*
* @return The first name
* @return This profile (for method chaining)
*/
public Profile setFirstName(String firstName) {
- modified |= ((firstName != null) && (!firstName.equals(this.firstName))) || (this.firstName != null);
this.firstName = firstName;
return this;
}
* @return This profile (for method chaining)
*/
public Profile setMiddleName(String middleName) {
- modified |= ((middleName != null) && (!middleName.equals(this.middleName))) || (this.middleName != null);
this.middleName = middleName;
return this;
}
* @return This profile (for method chaining)
*/
public Profile setLastName(String lastName) {
- modified |= ((lastName != null) && (!lastName.equals(this.lastName))) || (this.lastName != null);
this.lastName = lastName;
return this;
}
* @return This profile (for method chaining)
*/
public Profile setBirthDay(Integer birthDay) {
- modified |= ((birthDay != null) && (!birthDay.equals(this.birthDay))) || (this.birthDay != null);
this.birthDay = birthDay;
return this;
}
* @return This profile (for method chaining)
*/
public Profile setBirthMonth(Integer birthMonth) {
- modified |= ((birthMonth != null) && (!birthMonth.equals(this.birthMonth))) || (this.birthMonth != null);
this.birthMonth = birthMonth;
return this;
}
* @return This profile (for method chaining)
*/
public Profile setBirthYear(Integer birthYear) {
- modified |= ((birthYear != null) && (!birthYear.equals(this.birthYear))) || (this.birthYear != null);
this.birthYear = birthYear;
return this;
}
+ /**
+ * Returns the fields of this profile.
+ *
+ * @return The fields of this profile
+ */
+ public List<Field> getFields() {
+ return new ArrayList<Field>(fields);
+ }
+
+ /**
+ * Returns whether this profile contains the given field.
+ *
+ * @param field
+ * The field to check for
+ * @return {@code true} if this profile contains the field, false otherwise
+ */
+ public boolean hasField(Field field) {
+ return fields.contains(field);
+ }
+
+ /**
+ * Returns the field with the given ID.
+ *
+ * @param fieldId
+ * The ID of the field to get
+ * @return The field, or {@code null} if this profile does not contain a
+ * field with the given ID
+ */
+ public Field getFieldById(String fieldId) {
+ Validation.begin().isNotNull("Field ID", fieldId).check();
+ for (Field field : fields) {
+ if (field.getId().equals(fieldId)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the field with the given name.
+ *
+ * @param fieldName
+ * The name of the field to get
+ * @return The field, or {@code null} if this profile does not contain a
+ * field with the given name
+ */
+ public Field getFieldByName(String fieldName) {
+ for (Field field : fields) {
+ if (field.getName().equals(fieldName)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Appends a new field to the list of fields.
+ *
+ * @param fieldName
+ * The name of the new field
+ * @return The new field
+ * @throws IllegalArgumentException
+ * if the name is not valid
+ */
+ public Field addField(String fieldName) throws IllegalArgumentException {
+ Validation.begin().isNotNull("Field Name", fieldName).check().isGreater("Field Name Length", fieldName.length(), 0).isNull("Field Name Unique", getFieldByName(fieldName)).check();
+ @SuppressWarnings("synthetic-access")
+ Field field = new Field().setName(fieldName);
+ fields.add(field);
+ return field;
+ }
+
+ /**
+ * Moves the given field up one position in the field list. The index of the
+ * field to move must be greater than {@code 0} (because you obviously can
+ * not move the first field further up).
+ *
+ * @param field
+ * The field to move up
+ */
+ public void moveFieldUp(Field field) {
+ Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).isGreater("Field Index", getFieldIndex(field), 0).check();
+ int fieldIndex = getFieldIndex(field);
+ fields.remove(field);
+ fields.add(fieldIndex - 1, field);
+ }
+
+ /**
+ * Moves the given field down one position in the field list. The index of
+ * the field to move must be less than the index of the last field (because
+ * you obviously can not move the last field further down).
+ *
+ * @param field
+ * The field to move down
+ */
+ public void moveFieldDown(Field field) {
+ Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).isLess("Field Index", getFieldIndex(field), fields.size() - 1).check();
+ int fieldIndex = getFieldIndex(field);
+ fields.remove(field);
+ fields.add(fieldIndex + 1, field);
+ }
+
+ /**
+ * Removes the given field.
+ *
+ * @param field
+ * The field to remove
+ */
+ public void removeField(Field field) {
+ Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).check();
+ fields.remove(field);
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Returns the index of the field with the given name.
+ *
+ * @param field
+ * The name of the field
+ * @return The index of the field, or {@code -1} if there is no field with
+ * the given name
+ */
+ private int getFieldIndex(Field field) {
+ return fields.indexOf(field);
+ }
+
+ //
+ // INTERFACE Fingerprintable
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getFingerprint() {
+ StringBuilder fingerprint = new StringBuilder();
+ fingerprint.append("Profile(");
+ if (firstName != null) {
+ fingerprint.append("FirstName(").append(firstName).append(')');
+ }
+ if (middleName != null) {
+ fingerprint.append("MiddleName(").append(middleName).append(')');
+ }
+ if (lastName != null) {
+ fingerprint.append("LastName(").append(lastName).append(')');
+ }
+ if (birthDay != null) {
+ fingerprint.append("BirthDay(").append(birthDay).append(')');
+ }
+ if (birthMonth != null) {
+ fingerprint.append("BirthMonth(").append(birthMonth).append(')');
+ }
+ if (birthYear != null) {
+ fingerprint.append("BirthYear(").append(birthYear).append(')');
+ }
+ fingerprint.append("ContactInformation(");
+ for (Field field : fields) {
+ fingerprint.append(field.getName()).append('(').append(field.getValue()).append(')');
+ }
+ fingerprint.append(")");
+ fingerprint.append(")");
+
+ return fingerprint.toString();
+ }
+
+ /**
+ * Container for a profile field.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public class Field {
+
+ /** The ID of the field. */
+ private final String id;
+
+ /** The name of the field. */
+ private String name;
+
+ /** The value of the field. */
+ private String value;
+
+ /**
+ * Creates a new field with a random ID.
+ */
+ private Field() {
+ this(UUID.randomUUID().toString());
+ }
+
+ /**
+ * Creates a new field with the given ID.
+ *
+ * @param id
+ * The ID of the field
+ */
+ private Field(String id) {
+ Validation.begin().isNotNull("Field ID", id).check();
+ this.id = id;
+ }
+
+ /**
+ * Returns the ID of this field.
+ *
+ * @return The ID of this field
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the name of this field.
+ *
+ * @return The name of this field
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name of this field. The name must not be {@code null} and
+ * must not match any other fields in this profile but my match the name
+ * of this field.
+ *
+ * @param name
+ * The new name of this field
+ * @return This field
+ */
+ public Field setName(String name) {
+ Validation.begin().isNotNull("Field Name", name).check().is("Field Unique", (getFieldByName(name) == null) || equals(getFieldByName(name))).check();
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Returns the value of this field.
+ *
+ * @return The value of this field
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value of this field. While {@code null} is allowed, no
+ * guarantees are made that {@code null} values are correctly persisted
+ * across restarts of the plugin!
+ *
+ * @param value
+ * The new value of this field
+ * @return This field
+ */
+ public Field setValue(String value) {
+ this.value = value;
+ return this;
+ }
+
+ //
+ // OBJECT METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Field)) {
+ return false;
+ }
+ Field field = (Field) object;
+ return id.equals(field.id);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ }
+
}
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Sone implements Fingerprintable {
+public class Sone implements Fingerprintable, Comparable<Sone> {
/** comparator that sorts Sones by their nice name. */
public static final Comparator<Sone> NICE_NAME_COMPARATOR = new Comparator<Sone>() {
*/
public Sone setRequestUri(FreenetURI requestUri) {
if (this.requestUri == null) {
- this.requestUri = requestUri.setDocName("Sone").setMetaString(new String[0]);
+ this.requestUri = requestUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]);
return this;
}
if (!this.requestUri.equalsKeypair(requestUri)) {
*/
public Sone setInsertUri(FreenetURI insertUri) {
if (this.insertUri == null) {
- this.insertUri = insertUri.setDocName("Sone").setMetaString(new String[0]);
+ this.insertUri = insertUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]);
return this;
}
if (!this.insertUri.equalsKeypair(insertUri)) {
@Override
public synchronized String getFingerprint() {
StringBuilder fingerprint = new StringBuilder();
- fingerprint.append("Profile(");
- if (profile.getFirstName() != null) {
- fingerprint.append("FirstName(").append(profile.getFirstName()).append(')');
- }
- if (profile.getMiddleName() != null) {
- fingerprint.append("MiddleName(").append(profile.getMiddleName()).append(')');
- }
- if (profile.getLastName() != null) {
- fingerprint.append("LastName(").append(profile.getLastName()).append(')');
- }
- if (profile.getBirthDay() != null) {
- fingerprint.append("BirthDay(").append(profile.getBirthDay()).append(')');
- }
- if (profile.getBirthMonth() != null) {
- fingerprint.append("BirthMonth(").append(profile.getBirthMonth()).append(')');
- }
- if (profile.getBirthYear() != null) {
- fingerprint.append("BirthYear(").append(profile.getBirthYear()).append(')');
- }
- fingerprint.append(")");
+ fingerprint.append(profile.getFingerprint());
fingerprint.append("Posts(");
for (Post post : getPosts()) {
}
//
+ // INTERFACE Comparable<Sone>
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int compareTo(Sone sone) {
+ return NICE_NAME_COMPARATOR.compare(this, sone);
+ }
+
+ //
// OBJECT METHODS
//
import java.util.Map;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
import freenet.l10n.BaseL10n;
/**
* {@inheritDoc}
*/
@Override
- public String format(DataProvider dataProvider, Object data, Map<String, String> parameters) {
+ public String format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
return l10n.getString(String.valueOf(data));
}
--- /dev/null
+/*
+ * Sone - ConnectorListener.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.plugin;
+
+import java.util.EventListener;
+
+
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Interface for objects that want to be notified if a {@link PluginConnector}
+ * receives a reply from a plugin. As a connection listener is always
+ * {@link PluginConnector#addConnectorListener(String, String, ConnectorListener)
+ * added} for a specific plugin, it will always be notified for replies from the
+ * correct plugin (unless you register the same listener for multiple
+ * plugins—which you subsequently should not do).
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface ConnectorListener extends EventListener {
+
+ /**
+ * A reply was received from the plugin this connection listener was added
+ * for.
+ *
+ * @param pluginConnector
+ * The plugin connector that received the reply
+ * @param fields
+ * The fields of the reply
+ * @param data
+ * The data of the reply (may be null)
+ */
+ public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data);
+
+}
--- /dev/null
+/*
+ * Sone - ConnectorListenerManager.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.plugin;
+
+import net.pterodactylus.util.event.AbstractListenerManager;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Manages {@link ConnectorListener}s and fire events.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ConnectorListenerManager extends AbstractListenerManager<PluginConnector, ConnectorListener> {
+
+ /**
+ * Creates a new manager for {@link ConnectorListener}s.
+ *
+ * @param pluginConnector
+ * The plugin connector that is the source for all events
+ */
+ public ConnectorListenerManager(PluginConnector pluginConnector) {
+ super(pluginConnector);
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Notifies all registered listeners that a reply from the plugin was
+ * received.
+ *
+ * @param fields
+ * The fields of the reply
+ * @param data
+ * The data of the reply (may be null)
+ */
+ public void fireReceivedReply(SimpleFieldSet fields, Bucket data) {
+ for (ConnectorListener connectorListener : getListeners()) {
+ connectorListener.receivedReply(getSource(), fields, data);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PluginConnector.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.plugin;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.pterodactylus.util.collection.Pair;
+import freenet.pluginmanager.FredPluginTalker;
+import freenet.pluginmanager.PluginNotFoundException;
+import freenet.pluginmanager.PluginRespirator;
+import freenet.pluginmanager.PluginTalker;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Interface for talking to other plugins. Other plugins are identified by their
+ * name and a unique connection identifier.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PluginConnector implements FredPluginTalker {
+
+ /** The plugin respirator. */
+ private final PluginRespirator pluginRespirator;
+
+ /** Connector listener managers for all plugin connections. */
+ private final Map<Pair<String, String>, ConnectorListenerManager> connectorListenerManagers = Collections.synchronizedMap(new HashMap<Pair<String, String>, ConnectorListenerManager>());
+
+ /**
+ * Creates a new plugin connector.
+ *
+ * @param pluginRespirator
+ * The plugin respirator
+ */
+ public PluginConnector(PluginRespirator pluginRespirator) {
+ this.pluginRespirator = pluginRespirator;
+ }
+
+ //
+ // LISTENER MANAGEMENT
+ //
+
+ /**
+ * Adds a connection listener for the given plugin connection.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param connectorListener
+ * The listener to add
+ */
+ public void addConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) {
+ getConnectorListenerManager(pluginName, identifier).addListener(connectorListener);
+ }
+
+ /**
+ * Removes a connection listener for the given plugin connection.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param connectorListener
+ * The listener to remove
+ */
+ public void removeConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) {
+ getConnectorListenerManager(pluginName, identifier).removeListener(connectorListener);
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Sends a request to the given plugin.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param fields
+ * The fields of the message
+ * @throws PluginException
+ * if the plugin can not be found
+ */
+ public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields) throws PluginException {
+ sendRequest(pluginName, identifier, fields, null);
+ }
+
+ /**
+ * Sends a request to the given plugin.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param fields
+ * The fields of the message
+ * @param data
+ * The payload of the message (may be null)
+ * @throws PluginException
+ * if the plugin can not be found
+ */
+ public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields, Bucket data) throws PluginException {
+ getPluginTalker(pluginName, identifier).send(fields, data);
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Returns the connection listener manager for the given plugin connection,
+ * creating a new one if none does exist yet.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @return The connection listener manager
+ */
+ private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier) {
+ return getConnectorListenerManager(pluginName, identifier, true);
+ }
+
+ /**
+ * Returns the connection listener manager for the given plugin connection,
+ * optionally creating a new one if none does exist yet.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param create
+ * {@code true} to create a new manager if there is none,
+ * {@code false} to return {@code null} in that case
+ * @return The connection listener manager, or {@code null} if none existed
+ * and {@code create} is {@code false}
+ */
+ private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier, boolean create) {
+ ConnectorListenerManager connectorListenerManager = connectorListenerManagers.get(new Pair<String, String>(pluginName, identifier));
+ if (create && (connectorListenerManager == null)) {
+ connectorListenerManager = new ConnectorListenerManager(this);
+ connectorListenerManagers.put(new Pair<String, String>(pluginName, identifier), connectorListenerManager);
+ }
+ return connectorListenerManager;
+ }
+
+ /**
+ * Returns the plugin talker for the given plugin connection.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @return The plugin talker
+ * @throws PluginException
+ * if the plugin can not be found
+ */
+ private PluginTalker getPluginTalker(String pluginName, String identifier) throws PluginException {
+ try {
+ return pluginRespirator.getPluginTalker(this, pluginName, identifier);
+ } catch (PluginNotFoundException pnfe1) {
+ throw new PluginException(pnfe1);
+ }
+ }
+
+ //
+ // INTERFACE FredPluginTalker
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onReply(String pluginName, String identifier, SimpleFieldSet params, Bucket data) {
+ ConnectorListenerManager connectorListenerManager = getConnectorListenerManager(pluginName, identifier, false);
+ if (connectorListenerManager == null) {
+ /* we don’t care about events for this plugin. */
+ return;
+ }
+ connectorListenerManager.fireReceivedReply(params, data);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PluginException.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.plugin;
+
+import net.pterodactylus.sone.freenet.wot.WebOfTrustException;
+
+/**
+ * Exception that signals an error when communicating with a plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PluginException extends WebOfTrustException {
+
+ /**
+ * Creates a new plugin exception.
+ */
+ public PluginException() {
+ super();
+ }
+
+ /**
+ * Creates a new plugin exception.
+ *
+ * @param message
+ * The message of the exception
+ */
+ public PluginException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new plugin exception.
+ *
+ * @param cause
+ * The cause of the exception
+ */
+ public PluginException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new plugin exception.
+ *
+ * @param message
+ * The message of the exception
+ * @param cause
+ * The cause of the exception
+ */
+ public PluginException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
+++ /dev/null
-/*
- * Sone - ConnectorListener.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import java.util.EventListener;
-
-import freenet.support.SimpleFieldSet;
-import freenet.support.api.Bucket;
-
-/**
- * Interface for objects that want to be notified if a {@link PluginConnector}
- * receives a reply from a plugin. As a connection listener is always
- * {@link PluginConnector#addConnectorListener(String, String, ConnectorListener)
- * added} for a specific plugin, it will always be notified for replies from the
- * correct plugin (unless you register the same listener for multiple
- * plugins—which you subsequently should not do).
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface ConnectorListener extends EventListener {
-
- /**
- * A reply was received from the plugin this connection listener was added
- * for.
- *
- * @param pluginConnector
- * The plugin connector that received the reply
- * @param fields
- * The fields of the reply
- * @param data
- * The data of the reply (may be null)
- */
- public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data);
-
-}
+++ /dev/null
-/*
- * Sone - ConnectorListenerManager.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import net.pterodactylus.util.event.AbstractListenerManager;
-import freenet.support.SimpleFieldSet;
-import freenet.support.api.Bucket;
-
-/**
- * Manages {@link ConnectorListener}s and fire events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class ConnectorListenerManager extends AbstractListenerManager<PluginConnector, ConnectorListener> {
-
- /**
- * Creates a new manager for {@link ConnectorListener}s.
- *
- * @param pluginConnector
- * The plugin connector that is the source for all events
- */
- public ConnectorListenerManager(PluginConnector pluginConnector) {
- super(pluginConnector);
- }
-
- //
- // ACTIONS
- //
-
- /**
- * Notifies all registered listeners that a reply from the plugin was
- * received.
- *
- * @param fields
- * The fields of the reply
- * @param data
- * The data of the reply (may be null)
- */
- public void fireReceivedReply(SimpleFieldSet fields, Bucket data) {
- for (ConnectorListener connectorListener : getListeners()) {
- connectorListener.receivedReply(getSource(), fields, data);
- }
- }
-
-}
--- /dev/null
+/*
+ * Sone - DefaultIdentity.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot;
+
+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.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+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;
+
+ /** The nickname of the identity. */
+ private final String nickname;
+
+ /** The request URI of the identity. */
+ private final String requestUri;
+
+ /** The contexts of the identity. */
+ private final Set<String> contexts = Collections.synchronizedSet(new HashSet<String>());
+
+ /** The properties of the identity. */
+ private final Map<String, String> properties = Collections.synchronizedMap(new HashMap<String, String>());
+
+ /** Cached trust. */
+ private final WritableCache<OwnIdentity, Trust> trustCache = new MemoryCache<OwnIdentity, Trust>(new ValueRetriever<OwnIdentity, Trust>() {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public CacheItem<Trust> retrieve(OwnIdentity ownIdentity) throws CacheException {
+ try {
+ return new DefaultCacheItem<Trust>(webOfTrustConnector.getTrust(ownIdentity, DefaultIdentity.this));
+ } catch (PluginException pe1) {
+ throw new CacheException("Could not retrieve trust for OwnIdentity: " + ownIdentity, pe1);
+ }
+ }
+
+ }, new TimedMap<OwnIdentity, CacheItem<Trust>>(60 * 60 * 1000));
+
+ /**
+ * Creates a new identity.
+ *
+ * @param webOfTrustConnector
+ * The web of trust connector
+ * @param id
+ * The ID of the identity
+ * @param nickname
+ * The nickname of the identity
+ * @param requestUri
+ * The request URI of the identity
+ */
+ public DefaultIdentity(WebOfTrustConnector webOfTrustConnector, String id, String nickname, String requestUri) {
+ this.webOfTrustConnector = webOfTrustConnector;
+ this.id = id;
+ this.nickname = nickname;
+ this.requestUri = requestUri;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getNickname() {
+ return nickname;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getRequestUri() {
+ return requestUri;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<String> getContexts() {
+ return Collections.unmodifiableSet(contexts);
+ }
+
+ /**
+ * Sets the contexts of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param contexts
+ * The contexts to set
+ */
+ void setContextsPrivate(Set<String> contexts) {
+ this.contexts.clear();
+ this.contexts.addAll(contexts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean hasContext(String context) {
+ return contexts.contains(context);
+ }
+
+ /**
+ * Adds the given context to this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to add
+ */
+ void addContextPrivate(String context) {
+ contexts.add(context);
+ }
+
+ /**
+ * Removes the given context from this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to remove
+ */
+ public void removeContextPrivate(String context) {
+ contexts.remove(context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map<String, String> getProperties() {
+ synchronized (properties) {
+ return Collections.unmodifiableMap(properties);
+ }
+ }
+
+ /**
+ * Sets all properties of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param properties
+ * The new properties of this identity
+ */
+ void setPropertiesPrivate(Map<String, String> properties) {
+ synchronized (this.properties) {
+ this.properties.clear();
+ this.properties.putAll(properties);
+ }
+ }
+
+ /**
+ * Sets the property with the given name to the given value.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param name
+ * The name of the property
+ * @param value
+ * The value of the property
+ */
+ void setPropertyPrivate(String name, String value) {
+ synchronized (properties) {
+ properties.put(name, value);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getProperty(String name) {
+ synchronized (properties) {
+ return properties.get(name);
+ }
+ }
+
+ /**
+ * Removes the property with the given name.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param name
+ * The name of the property to remove
+ */
+ void removePropertyPrivate(String name) {
+ synchronized (properties) {
+ properties.remove(name);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Trust getTrust(OwnIdentity ownIdentity) {
+ try {
+ return trustCache.get(ownIdentity);
+ } catch (CacheException ce1) {
+ logger.log(Level.WARNING, "Could not get trust for OwnIdentity: " + ownIdentity, ce1);
+ return null;
+ }
+ }
+
+ /**
+ * 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
+ */
+ void setTrustPrivate(OwnIdentity ownIdentity, Trust trust) {
+ trustCache.put(ownIdentity, trust);
+ }
+
+ //
+ // OBJECT METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof DefaultIdentity)) {
+ return false;
+ }
+ DefaultIdentity identity = (DefaultIdentity) object;
+ return identity.id.equals(id);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[id=" + id + ",nickname=" + nickname + ",contexts=" + contexts + ",properties=" + properties + "]";
+ }
+
+}
--- /dev/null
+/*
+ * Sone - DefaultOwnIdentity.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.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.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+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
+ * The nickname of the identity
+ * @param requestUri
+ * The request URI of the identity
+ * @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;
+ this.insertUri = insertUri;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getInsertUri() {
+ return insertUri;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addContext(String context) throws WebOfTrustException {
+ webOfTrustConnector.addContext(this, context);
+ addContextPrivate(context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeContext(String context) throws WebOfTrustException {
+ webOfTrustConnector.removeContext(this, context);
+ removeContextPrivate(context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setContexts(Set<String> contexts) throws WebOfTrustException {
+ for (String context : getContexts()) {
+ if (!contexts.contains(context)) {
+ webOfTrustConnector.removeContext(this, context);
+ }
+ }
+ for (String context : contexts) {
+ if (!getContexts().contains(context)) {
+ webOfTrustConnector.addContext(this, context);
+ }
+ }
+ setContextsPrivate(contexts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setProperty(String name, String value) throws WebOfTrustException {
+ webOfTrustConnector.setProperty(this, name, value);
+ setPropertyPrivate(name, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeProperty(String name) throws WebOfTrustException {
+ webOfTrustConnector.removeProperty(this, name);
+ removePropertyPrivate(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setProperties(Map<String, String> properties) throws WebOfTrustException {
+ for (Entry<String, String> oldProperty : getProperties().entrySet()) {
+ if (!properties.containsKey(oldProperty.getKey())) {
+ webOfTrustConnector.removeProperty(this, oldProperty.getKey());
+ } else {
+ webOfTrustConnector.setProperty(this, oldProperty.getKey(), properties.get(oldProperty.getKey()));
+ }
+ }
+ for (Entry<String, String> newProperty : properties.entrySet()) {
+ if (!getProperties().containsKey(newProperty.getKey())) {
+ webOfTrustConnector.setProperty(this, newProperty.getKey(), newProperty.getValue());
+ }
+ }
+ setPropertiesPrivate(properties);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException {
+ Validation.begin().isNotNull("Trust Target", target).isNotNull("Trust Comment", comment).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check();
+ webOfTrustConnector.setTrust(this, target, trustValue, comment);
+ if (target instanceof DefaultIdentity) {
+ ((DefaultIdentity) target).setTrustPrivate(this, new Trust(trustValue, trustValue, 0));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeTrust(Identity target) throws WebOfTrustException {
+ Validation.begin().isNotNull("Trust Target", target).check();
+ webOfTrustConnector.removeTrust(this, target);
+ if (target instanceof DefaultIdentity) {
+ ((DefaultIdentity) target).setTrustPrivate(this, new Trust(null, null, null));
+ }
+ }
+
+}
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.Set;
/**
- * A Web of Trust identity.
+ * 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.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Identity {
-
- /** The ID of the identity. */
- private final String id;
-
- /** The nickname of the identity. */
- private final String nickname;
-
- /** The request URI of the identity. */
- private final String requestUri;
-
- /** The contexts of the identity. */
- private final Set<String> contexts = Collections.synchronizedSet(new HashSet<String>());
-
- /** The properties of the identity. */
- private final Map<String, String> properties = Collections.synchronizedMap(new HashMap<String, String>());
-
- /**
- * Creates a new identity.
- *
- * @param id
- * The ID of the identity
- * @param nickname
- * The nickname of the identity
- * @param requestUri
- * The request URI of the identity
- */
- public Identity(String id, String nickname, String requestUri) {
- this.id = id;
- this.nickname = nickname;
- this.requestUri = requestUri;
- }
-
- //
- // ACCESSORS
- //
+public interface Identity {
/**
* Returns the ID of the identity.
*
* @return The ID of the identity
*/
- public String getId() {
- return id;
- }
+ public String getId();
/**
* Returns the nickname of the identity.
*
* @return The nickname of the identity
*/
- public String getNickname() {
- return nickname;
- }
+ public String getNickname();
/**
* Returns the request URI of the identity.
*
* @return The request URI of the identity
*/
- public String getRequestUri() {
- return requestUri;
- }
+ public String getRequestUri();
/**
* Returns all contexts of this identity.
*
* @return All contexts of this identity
*/
- public Set<String> getContexts() {
- return Collections.unmodifiableSet(contexts);
- }
-
- /**
- * Sets all contexts of this identity.
- * <p>
- * This method is only called by the {@link IdentityManager}.
- *
- * @param contexts
- * All contexts of the identity
- */
- void setContexts(Set<String> contexts) {
- this.contexts.clear();
- this.contexts.addAll(contexts);
- }
+ public Set<String> getContexts();
/**
* Returns whether this identity has the given context.
* @return {@code true} if this identity has the given context,
* {@code false} otherwise
*/
- public boolean hasContext(String context) {
- return contexts.contains(context);
- }
-
- /**
- * Adds the given context to this identity.
- * <p>
- * This method is only called by the {@link IdentityManager}.
- *
- * @param context
- * The context to add
- */
- void addContext(String context) {
- contexts.add(context);
- }
-
- /**
- * Removes the given context from this identity.
- * <p>
- * This method is only called by the {@link IdentityManager}.
- *
- * @param context
- * The context to remove
- */
- void removeContext(String context) {
- contexts.remove(context);
- }
+ public boolean hasContext(String context);
/**
* Returns all properties of this identity.
*
* @return All properties of this identity
*/
- public Map<String, String> getProperties() {
- synchronized (properties) {
- return Collections.unmodifiableMap(properties);
- }
- }
-
- /**
- * Sets all properties of this identity.
- * <p>
- * This method is only called by the {@link IdentityManager}.
- *
- * @param properties
- * The new properties of this identity
- */
- void setProperties(Map<String, String> properties) {
- synchronized (this.properties) {
- this.properties.clear();
- this.properties.putAll(properties);
- }
- }
-
- /**
- * Sets the property with the given name to the given value.
- * <p>
- * This method is only called by the {@link IdentityManager}.
- *
- * @param name
- * The name of the property
- * @param value
- * The value of the property
- */
- void setProperty(String name, String value) {
- synchronized (properties) {
- properties.put(name, value);
- }
- }
+ public Map<String, String> getProperties();
/**
* Returns the value of the property with the given name.
* The name of the property
* @return The value of the property
*/
- public String getProperty(String name) {
- synchronized (properties) {
- return properties.get(name);
- }
- }
+ public String getProperty(String name);
/**
- * Removes the property with the given name.
- * <p>
- * This method is only called by the {@link IdentityManager}.
+ * 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}.
+ * If the trust can not be retrieved, {@code null} is returned.
*
- * @param name
- * The name of the property to remove
- */
- void removeProperty(String name) {
- synchronized (properties) {
- properties.remove(name);
- }
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof Identity)) {
- return false;
- }
- Identity identity = (Identity) object;
- return identity.id.equals(id);
- }
-
- /**
- * {@inheritDoc}
+ * @param ownIdentity
+ * The own identity to get the trust for
+ * @return The trust assigned to this identity, or {@code null} if the trust
+ * could not be retrieved
*/
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[id=" + id + ",nickname=" + nickname + ",contexts=" + contexts + ",properties=" + properties + "]";
- }
+ public Trust getTrust(OwnIdentity ownIdentity);
}
/**
* Notifies a listener that a new identity was discovered.
*
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
* @param identity
* The new identity
*/
- public void identityAdded(Identity identity);
+ public void identityAdded(OwnIdentity ownIdentity, Identity identity);
/**
* Notifies a listener that some properties of the identity have changed.
*
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
* @param identity
* The updated identity
*/
- public void identityUpdated(Identity identity);
+ public void identityUpdated(OwnIdentity ownIdentity, Identity identity);
/**
* Notifies a listener that an identity has gone away.
*
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
* @param identity
* The disappeared identity
*/
- public void identityRemoved(Identity identity);
+ public void identityRemoved(OwnIdentity ownIdentity, Identity identity);
}
/**
* Notifies all listeners that a new identity was discovered.
*
- * @see IdentityListener#identityAdded(Identity)
+ * @see IdentityListener#identityAdded(OwnIdentity, Identity)
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
* @param identity
* The new identity
*/
- public void fireIdentityAdded(Identity identity) {
+ public void fireIdentityAdded(OwnIdentity ownIdentity, Identity identity) {
for (IdentityListener identityListener : getListeners()) {
- identityListener.identityAdded(identity);
+ identityListener.identityAdded(ownIdentity, identity);
}
}
/**
* Notifies all listeners that some properties of the identity have changed.
*
- * @see IdentityListener#identityUpdated(Identity)
+ * @see IdentityListener#identityUpdated(OwnIdentity, Identity)
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
* @param identity
* The updated identity
*/
- public void fireIdentityUpdated(Identity identity) {
+ public void fireIdentityUpdated(OwnIdentity ownIdentity, Identity identity) {
for (IdentityListener identityListener : getListeners()) {
- identityListener.identityUpdated(identity);
+ identityListener.identityUpdated(ownIdentity, identity);
}
}
/**
* Notifies all listeners that an identity has gone away.
*
- * @see IdentityListener#identityRemoved(Identity)
+ * @see IdentityListener#identityRemoved(OwnIdentity, Identity)
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
* @param identity
* The disappeared identity
*/
- public void fireIdentityRemoved(Identity identity) {
+ public void fireIdentityRemoved(OwnIdentity ownIdentity, Identity identity) {
for (IdentityListener identityListener : getListeners()) {
- identityListener.identityRemoved(identity);
+ identityListener.identityRemoved(ownIdentity, identity);
}
}
import java.util.logging.Level;
import java.util.logging.Logger;
+import net.pterodactylus.sone.freenet.plugin.PluginException;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.service.AbstractService;
}
checkOwnIdentities(newOwnIdentities);
return ownIdentities;
- } catch (PluginException pe1) {
- logger.log(Level.WARNING, "Could not load all own identities!", pe1);
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not load all own identities!", wote1);
return Collections.emptySet();
}
}
//
- // ACTIONS
- //
-
- /**
- * Adds a context to the given own identity.
- *
- * @param ownIdentity
- * The own identity
- * @param context
- * The context to add
- */
- public void addContext(OwnIdentity ownIdentity, String context) {
- if (ownIdentity.hasContext(context)) {
- return;
- }
- try {
- webOfTrustConnector.addContext(ownIdentity, context);
- ownIdentity.addContext(context);
- } catch (PluginException pe1) {
- logger.log(Level.WARNING, "Could not add context " + context + " to OwnIdentity " + ownIdentity + ".", pe1);
- }
- }
-
- /**
- * Removes a context from the given own identity.
- *
- * @param ownIdentity
- * The own identity
- * @param context
- * The context to remove
- */
- public void removeContext(OwnIdentity ownIdentity, String context) {
- if (!ownIdentity.hasContext(context)) {
- return;
- }
- try {
- webOfTrustConnector.removeContext(ownIdentity, context);
- ownIdentity.removeContext(context);
- } catch (PluginException pe1) {
- logger.log(Level.WARNING, "Could not remove context " + context + " from OwnIdentity " + ownIdentity + ".", pe1);
- }
- }
-
- /**
- * Sets the property with the given name to the given value.
- *
- * @param ownIdentity
- * The own identity
- * @param name
- * The name of the property
- * @param value
- * The value of the property
- */
- public void setProperty(OwnIdentity ownIdentity, String name, String value) {
- try {
- webOfTrustConnector.setProperty(ownIdentity, name, value);
- ownIdentity.setProperty(name, value);
- } catch (PluginException pe1) {
- logger.log(Level.WARNING, "Could not set property “" + name + "” to “" + value + "” for OwnIdentity: " + ownIdentity, pe1);
- }
- }
-
- /**
- * Removes the property with the given name.
- *
- * @param ownIdentity
- * The own identity
- * @param name
- * The name of the property to remove
- */
- public void removeProperty(OwnIdentity ownIdentity, String name) {
- try {
- webOfTrustConnector.removeProperty(ownIdentity, name);
- ownIdentity.removeProperty(name);
- } catch (PluginException pe1) {
- logger.log(Level.WARNING, "Could not remove property “" + name + "” from OwnIdentity: " + ownIdentity, pe1);
- }
- }
-
- //
// SERVICE METHODS
//
*/
@Override
protected void serviceRun() {
- Map<String, Identity> oldIdentities = Collections.emptyMap();
+ Map<OwnIdentity, Map<String, Identity>> oldIdentities = Collections.emptyMap();
while (!shouldStop()) {
- Map<String, Identity> currentIdentities = new HashMap<String, Identity>();
+ Map<OwnIdentity, Map<String, Identity>> currentIdentities = new HashMap<OwnIdentity, Map<String, Identity>>();
Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
- /* get all identities with the wanted context from WoT. */
- Set<OwnIdentity> ownIdentities;
try {
- ownIdentities = webOfTrustConnector.loadAllOwnIdentities();
+ /* get all identities with the wanted context from WoT. */
+ Set<OwnIdentity> ownIdentities = webOfTrustConnector.loadAllOwnIdentities();
/* check for changes. */
for (OwnIdentity ownIdentity : ownIdentities) {
checkOwnIdentities(currentOwnIdentities);
/* now filter for context and get all identities. */
- currentOwnIdentities.clear();
for (OwnIdentity ownIdentity : ownIdentities) {
if ((context != null) && !ownIdentity.hasContext(context)) {
continue;
}
- currentOwnIdentities.put(ownIdentity.getId(), ownIdentity);
- for (Identity identity : webOfTrustConnector.loadTrustedIdentities(ownIdentity, context)) {
- currentIdentities.put(identity.getId(), identity);
- }
- }
- /* find removed identities. */
- for (Identity oldIdentity : oldIdentities.values()) {
- if (!currentIdentities.containsKey(oldIdentity.getId())) {
- identityListenerManager.fireIdentityRemoved(oldIdentity);
+ Set<Identity> trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context);
+ Map<String, Identity> identities = new HashMap<String, Identity>();
+ currentIdentities.put(ownIdentity, identities);
+ for (Identity identity : trustedIdentities) {
+ identities.put(identity.getId(), identity);
}
- }
- /* find new identities. */
- for (Identity currentIdentity : currentIdentities.values()) {
- if (!oldIdentities.containsKey(currentIdentity.getId())) {
- identityListenerManager.fireIdentityAdded(currentIdentity);
+ /* find new identities. */
+ for (Identity currentIdentity : currentIdentities.get(ownIdentity).values()) {
+ if (!oldIdentities.containsKey(ownIdentity) || !oldIdentities.get(ownIdentity).containsKey(currentIdentity.getId())) {
+ identityListenerManager.fireIdentityAdded(ownIdentity, currentIdentity);
+ }
}
- }
- /* check for changes in the contexts. */
- for (Identity oldIdentity : oldIdentities.values()) {
- if (!currentIdentities.containsKey(oldIdentity.getId())) {
- continue;
- }
- Identity newIdentity = currentIdentities.get(oldIdentity.getId());
- Set<String> oldContexts = oldIdentity.getContexts();
- Set<String> newContexts = newIdentity.getContexts();
- if (oldContexts.size() != newContexts.size()) {
- identityListenerManager.fireIdentityUpdated(newIdentity);
- continue;
- }
- for (String oldContext : oldContexts) {
- if (!newContexts.contains(oldContext)) {
- identityListenerManager.fireIdentityUpdated(newIdentity);
- break;
+ /* find removed identities. */
+ if (oldIdentities.containsKey(ownIdentity)) {
+ for (Identity oldIdentity : oldIdentities.get(ownIdentity).values()) {
+ if (!currentIdentities.get(ownIdentity).containsKey(oldIdentity.getId())) {
+ identityListenerManager.fireIdentityRemoved(ownIdentity, oldIdentity);
+ }
}
- }
- }
- /* check for changes in the properties. */
- for (Identity oldIdentity : oldIdentities.values()) {
- if (!currentIdentities.containsKey(oldIdentity.getId())) {
- continue;
- }
- Identity newIdentity = currentIdentities.get(oldIdentity.getId());
- Map<String, String> oldProperties = oldIdentity.getProperties();
- Map<String, String> newProperties = newIdentity.getProperties();
- if (oldProperties.size() != newProperties.size()) {
- identityListenerManager.fireIdentityUpdated(newIdentity);
- continue;
- }
- for (Entry<String, String> oldProperty : oldProperties.entrySet()) {
- if (!newProperties.containsKey(oldProperty.getKey()) || !newProperties.get(oldProperty.getKey()).equals(oldProperty.getValue())) {
- identityListenerManager.fireIdentityUpdated(newIdentity);
- break;
+ /* check for changes in the contexts. */
+ for (Identity oldIdentity : oldIdentities.get(ownIdentity).values()) {
+ if (!currentIdentities.get(ownIdentity).containsKey(oldIdentity.getId())) {
+ continue;
+ }
+ Identity newIdentity = currentIdentities.get(ownIdentity).get(oldIdentity.getId());
+ Set<String> oldContexts = oldIdentity.getContexts();
+ Set<String> newContexts = newIdentity.getContexts();
+ if (oldContexts.size() != newContexts.size()) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ continue;
+ }
+ for (String oldContext : oldContexts) {
+ if (!newContexts.contains(oldContext)) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ break;
+ }
+ }
+ }
+
+ /* check for changes in the properties. */
+ for (Identity oldIdentity : oldIdentities.get(ownIdentity).values()) {
+ if (!currentIdentities.get(ownIdentity).containsKey(oldIdentity.getId())) {
+ continue;
+ }
+ Identity newIdentity = currentIdentities.get(ownIdentity).get(oldIdentity.getId());
+ Map<String, String> oldProperties = oldIdentity.getProperties();
+ Map<String, String> newProperties = newIdentity.getProperties();
+ if (oldProperties.size() != newProperties.size()) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ continue;
+ }
+ for (Entry<String, String> oldProperty : oldProperties.entrySet()) {
+ if (!newProperties.containsKey(oldProperty.getKey()) || !newProperties.get(oldProperty.getKey()).equals(oldProperty.getValue())) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ break;
+ }
+ }
}
}
- }
- /* remember the current set of identities. */
- oldIdentities = currentIdentities;
+ /* remember the current set of identities. */
+ oldIdentities = currentIdentities;
+ }
- } catch (PluginException pe1) {
- logger.log(Level.WARNING, "WoT has disappeared!", pe1);
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "WoT has disappeared!", wote1);
}
/* wait a minute before checking again. */
package net.pterodactylus.sone.freenet.wot;
+import java.util.Map;
+import java.util.Set;
+
/**
- * An own identity is an identity that the owner of the node has full control
- * over.
+ * Defines a local identity, an own identity.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class OwnIdentity extends Identity {
+public interface OwnIdentity extends Identity {
+
+ /**
+ * Returns the insert URI of the identity.
+ *
+ * @return The insert URI of the identity
+ */
+ public String getInsertUri();
- /** The insert URI of the identity. */
- private final String insertUri;
+ /**
+ * Adds the given context to this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to add
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void addContext(String context) throws WebOfTrustException;
/**
- * Creates a new own identity.
+ * Sets all contexts of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
*
- * @param id
- * The ID of the identity
- * @param nickname
- * The nickname of the identity
- * @param requestUri
- * The request URI of the identity
- * @param insertUri
- * The insert URI of the identity
+ * @param contexts
+ * All contexts of the identity
+ * @throws WebOfTrustException
+ * if an error occurs
*/
- public OwnIdentity(String id, String nickname, String requestUri, String insertUri) {
- super(id, nickname, requestUri);
- this.insertUri = insertUri;
- }
+ public void setContexts(Set<String> contexts) throws WebOfTrustException;
- //
- // ACCESSORS
- //
+ /**
+ * Removes the given context from this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to remove
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void removeContext(String context) throws WebOfTrustException;
/**
- * Returns the insert URI of the identity.
+ * Sets the property with the given name to the given value.
*
- * @return The insert URI of the identity
+ * @param name
+ * The name of the property
+ * @param value
+ * The value of the property
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void setProperty(String name, String value) throws WebOfTrustException;
+
+ /**
+ * Sets all properties of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param properties
+ * The new properties of this identity
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void setProperties(Map<String, String> properties) throws WebOfTrustException;
+
+ /**
+ * Removes the property with the given name.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param name
+ * The name of the property to remove
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void removeProperty(String name) throws WebOfTrustException;
+
+ /**
+ * Sets the trust for the given target identity.
+ *
+ * @param target
+ * The target to set the trust for
+ * @param trustValue
+ * The new trust value (from {@code -100} or {@code 100})
+ * @param comment
+ * The comment for the trust assignment
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException;
+
+ /**
+ * Removes any trust assignment for the given target identity.
+ *
+ * @param target
+ * The targe to remove the trust assignment for
+ * @throws WebOfTrustException
+ * if an error occurs
*/
- public String getInsertUri() {
- return insertUri;
- }
+ public void removeTrust(Identity target) throws WebOfTrustException;
}
+++ /dev/null
-/*
- * Sone - PluginConnector.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import net.pterodactylus.util.collection.Pair;
-import freenet.pluginmanager.FredPluginTalker;
-import freenet.pluginmanager.PluginNotFoundException;
-import freenet.pluginmanager.PluginRespirator;
-import freenet.pluginmanager.PluginTalker;
-import freenet.support.SimpleFieldSet;
-import freenet.support.api.Bucket;
-
-/**
- * Interface for talking to other plugins. Other plugins are identified by their
- * name and a unique connection identifier.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PluginConnector implements FredPluginTalker {
-
- /** The plugin respirator. */
- private final PluginRespirator pluginRespirator;
-
- /** Connector listener managers for all plugin connections. */
- private final Map<Pair<String, String>, ConnectorListenerManager> connectorListenerManagers = Collections.synchronizedMap(new HashMap<Pair<String, String>, ConnectorListenerManager>());
-
- /**
- * Creates a new plugin connector.
- *
- * @param pluginRespirator
- * The plugin respirator
- */
- public PluginConnector(PluginRespirator pluginRespirator) {
- this.pluginRespirator = pluginRespirator;
- }
-
- //
- // LISTENER MANAGEMENT
- //
-
- /**
- * Adds a connection listener for the given plugin connection.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @param connectorListener
- * The listener to add
- */
- public void addConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) {
- getConnectorListenerManager(pluginName, identifier).addListener(connectorListener);
- }
-
- /**
- * Removes a connection listener for the given plugin connection.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @param connectorListener
- * The listener to remove
- */
- public void removeConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) {
- getConnectorListenerManager(pluginName, identifier).removeListener(connectorListener);
- }
-
- //
- // ACTIONS
- //
-
- /**
- * Sends a request to the given plugin.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @param fields
- * The fields of the message
- * @throws PluginException
- * if the plugin can not be found
- */
- public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields) throws PluginException {
- sendRequest(pluginName, identifier, fields, null);
- }
-
- /**
- * Sends a request to the given plugin.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @param fields
- * The fields of the message
- * @param data
- * The payload of the message (may be null)
- * @throws PluginException
- * if the plugin can not be found
- */
- public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields, Bucket data) throws PluginException {
- getPluginTalker(pluginName, identifier).send(fields, data);
- }
-
- //
- // PRIVATE METHODS
- //
-
- /**
- * Returns the connection listener manager for the given plugin connection,
- * creating a new one if none does exist yet.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @return The connection listener manager
- */
- private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier) {
- return getConnectorListenerManager(pluginName, identifier, true);
- }
-
- /**
- * Returns the connection listener manager for the given plugin connection,
- * optionally creating a new one if none does exist yet.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @param create
- * {@code true} to create a new manager if there is none,
- * {@code false} to return {@code null} in that case
- * @return The connection listener manager, or {@code null} if none existed
- * and {@code create} is {@code false}
- */
- private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier, boolean create) {
- ConnectorListenerManager connectorListenerManager = connectorListenerManagers.get(new Pair<String, String>(pluginName, identifier));
- if (create && (connectorListenerManager == null)) {
- connectorListenerManager = new ConnectorListenerManager(this);
- connectorListenerManagers.put(new Pair<String, String>(pluginName, identifier), connectorListenerManager);
- }
- return connectorListenerManager;
- }
-
- /**
- * Returns the plugin talker for the given plugin connection.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @return The plugin talker
- * @throws PluginException
- * if the plugin can not be found
- */
- private PluginTalker getPluginTalker(String pluginName, String identifier) throws PluginException {
- try {
- return pluginRespirator.getPluginTalker(this, pluginName, identifier);
- } catch (PluginNotFoundException pnfe1) {
- throw new PluginException(pnfe1);
- }
- }
-
- //
- // INTERFACE FredPluginTalker
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onReply(String pluginName, String identifier, SimpleFieldSet params, Bucket data) {
- ConnectorListenerManager connectorListenerManager = getConnectorListenerManager(pluginName, identifier, false);
- if (connectorListenerManager == null) {
- /* we don’t care about events for this plugin. */
- return;
- }
- connectorListenerManager.fireReceivedReply(params, data);
- }
-
-}
+++ /dev/null
-/*
- * Sone - PluginException.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-/**
- * Exception that signals an error when communicating with a plugin.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PluginException extends Exception {
-
- /**
- * Creates a new plugin exception.
- */
- public PluginException() {
- super();
- }
-
- /**
- * Creates a new plugin exception.
- *
- * @param message
- * The message of the exception
- */
- public PluginException(String message) {
- super(message);
- }
-
- /**
- * Creates a new plugin exception.
- *
- * @param cause
- * The cause of the exception
- */
- public PluginException(Throwable cause) {
- super(cause);
- }
-
- /**
- * Creates a new plugin exception.
- *
- * @param message
- * The message of the exception
- * @param cause
- * The cause of the exception
- */
- public PluginException(String message, Throwable cause) {
- super(message, cause);
- }
-
-}
--- /dev/null
+/*
+ * Sone - Trust.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot;
+
+/**
+ * Container class for trust in the web of trust.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Trust {
+
+ /** Explicitely assigned trust. */
+ private final Integer explicit;
+
+ /** Implicitely calculated trust. */
+ private final Integer implicit;
+
+ /** The distance from the owner of the trust tree. */
+ private final Integer distance;
+
+ /**
+ * Creates a new trust container.
+ *
+ * @param explicit
+ * The explicit trust
+ * @param implicit
+ * The implicit trust
+ * @param distance
+ * The distance
+ */
+ public Trust(Integer explicit, Integer implicit, Integer distance) {
+ this.explicit = explicit;
+ this.implicit = implicit;
+ this.distance = distance;
+ }
+
+ /**
+ * Returns the trust explicitely assigned to an identity.
+ *
+ * @return The explicitely assigned trust, or {@code null} if the identity
+ * is not in the own identity’s trust tree
+ */
+ public Integer getExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Returns the implicitely assigned trust, or the calculated trust.
+ *
+ * @return The calculated trust, or {@code null} if the identity is not in
+ * the own identity’s trust tree
+ */
+ public Integer getImplicit() {
+ return implicit;
+ }
+
+ /**
+ * Returns the distance of the trusted identity from the trusting identity.
+ *
+ * @return The distance from the own identity, or {@code null} if the
+ * identity is not in the own identity’s trust tree
+ */
+ public Integer getDistance() {
+ return distance;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return getClass().getName() + "[explicit=" + explicit + ",implicit=" + implicit + ",distance=" + distance + "]";
+ }
+
+}
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.logging.Level;
import java.util.logging.Logger;
+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 freenet.support.SimpleFieldSet;
import freenet.support.api.Bucket;
/** A random connection identifier. */
private static final String PLUGIN_CONNECTION_IDENTIFIER = "Sone-WoT-Connector-" + Math.abs(Math.random());
- /** The current replies that we wait for. */
- private final Map<String, Reply> replies = Collections.synchronizedMap(new HashMap<String, Reply>());
+ /** The current reply. */
+ private Reply reply;
/** The plugin connector. */
private final PluginConnector pluginConnector;
* Loads all own identities from the Web of Trust plugin.
*
* @return All own identity
- * @throws PluginException
+ * @throws WebOfTrustException
* if the own identities can not be loaded
*/
- public Set<OwnIdentity> loadAllOwnIdentities() throws PluginException {
- Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get(), "OwnIdentities");
+ public Set<OwnIdentity> loadAllOwnIdentities() throws WebOfTrustException {
+ Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get());
SimpleFieldSet fields = reply.getFields();
int ownIdentityCounter = -1;
Set<OwnIdentity> ownIdentities = new HashSet<OwnIdentity>();
String requestUri = fields.get("RequestURI" + ownIdentityCounter);
String insertUri = fields.get("InsertURI" + ownIdentityCounter);
String nickname = fields.get("Nickname" + ownIdentityCounter);
- OwnIdentity ownIdentity = new OwnIdentity(id, nickname, requestUri, insertUri);
- ownIdentity.setContexts(parseContexts("Contexts" + ownIdentityCounter + ".", fields));
- ownIdentity.setProperties(parseProperties("Properties" + ownIdentityCounter + ".", fields));
+ DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(this, id, nickname, requestUri, insertUri);
+ ownIdentity.setContextsPrivate(parseContexts("Contexts" + ownIdentityCounter + ".", fields));
+ ownIdentity.setPropertiesPrivate(parseProperties("Properties" + ownIdentityCounter + ".", fields));
ownIdentities.add(ownIdentity);
}
return ownIdentities;
* if an error occured talking to the Web of Trust plugin
*/
public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity, String context) throws PluginException {
- Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("TreeOwner", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).get(), "Identities");
+ Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("TreeOwner", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).get());
SimpleFieldSet fields = reply.getFields();
Set<Identity> identities = new HashSet<Identity>();
int identityCounter = -1;
}
String nickname = fields.get("Nickname" + identityCounter);
String requestUri = fields.get("RequestURI" + identityCounter);
- Identity identity = new Identity(id, nickname, requestUri);
- identity.setContexts(parseContexts("Contexts" + identityCounter + ".", fields));
- identity.setProperties(parseProperties("Properties" + identityCounter + ".", fields));
+ DefaultIdentity identity = new DefaultIdentity(this, id, nickname, requestUri);
+ identity.setContextsPrivate(parseContexts("Contexts" + identityCounter + ".", fields));
+ identity.setPropertiesPrivate(parseProperties("Properties" + identityCounter + ".", fields));
identities.add(identity);
}
return identities;
* if an error occured talking to the Web of Trust plugin
*/
public void addContext(OwnIdentity ownIdentity, String context) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "AddContext").put("Identity", ownIdentity.getId()).put("Context", context).get(), "ContextAdded");
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "AddContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
}
/**
* if an error occured talking to the Web of Trust plugin
*/
public void removeContext(OwnIdentity ownIdentity, String context) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveContext").put("Identity", ownIdentity.getId()).put("Context", context).get(), "ContextRemoved");
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
}
/**
* if an error occured talking to the Web of Trust plugin
*/
public String getProperty(Identity identity, String name) throws PluginException {
- Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetProperty").put("Identity", identity.getId()).put("Property", name).get(), "PropertyValue");
+ Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetProperty").put("Identity", identity.getId()).put("Property", name).get());
return reply.getFields().get("Property");
}
* if an error occured talking to the Web of Trust plugin
*/
public void setProperty(OwnIdentity ownIdentity, String name, String value) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "SetProperty").put("Identity", ownIdentity.getId()).put("Property", name).put("Value", value).get(), "PropertyAdded");
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "SetProperty").put("Identity", ownIdentity.getId()).put("Property", name).put("Value", value).get());
}
/**
* if an error occured talking to the Web of Trust plugin
*/
public void removeProperty(OwnIdentity ownIdentity, String name) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveProperty").put("Identity", ownIdentity.getId()).put("Property", name).get(), "PropertyRemoved");
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveProperty").put("Identity", ownIdentity.getId()).put("Property", name).get());
+ }
+
+ /**
+ * Returns the trust for the given identity assigned to it by the given own
+ * identity.
+ *
+ * @param ownIdentity
+ * The own identity
+ * @param identity
+ * The identity to get the trust for
+ * @return The trust for the given identity
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public Trust getTrust(OwnIdentity ownIdentity, Identity identity) throws PluginException {
+ Reply getTrustReply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentity").put("TreeOwner", ownIdentity.getId()).put("Identity", identity.getId()).get());
+ String trust = getTrustReply.getFields().get("Trust");
+ String score = getTrustReply.getFields().get("Score");
+ String rank = getTrustReply.getFields().get("Rank");
+ Integer explicit = null;
+ Integer implicit = null;
+ Integer distance = null;
+ try {
+ explicit = Integer.valueOf(trust);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ try {
+ implicit = Integer.valueOf(score);
+ distance = Integer.valueOf(rank);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ return new Trust(explicit, implicit, distance);
+ }
+
+ /**
+ * Sets the trust for the given identity.
+ *
+ * @param ownIdentity
+ * The trusting identity
+ * @param identity
+ * The trusted identity
+ * @param trust
+ * The amount of trust (-100 thru 100)
+ * @param comment
+ * The comment or explanation of the trust value
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public void setTrust(OwnIdentity ownIdentity, Identity identity, int trust, String comment) throws PluginException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "SetTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).put("Value", String.valueOf(trust)).put("Comment", comment).get());
+ }
+
+ /**
+ * Removes any trust assignment of the given own identity for the given
+ * identity.
+ *
+ * @param ownIdentity
+ * The own identity
+ * @param identity
+ * The identity to remove all trust for
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void removeTrust(OwnIdentity ownIdentity, Identity identity) throws WebOfTrustException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).get());
}
/**
* if the plugin is not loaded
*/
public void ping() throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "Ping").get(), "Pong");
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "Ping").get());
}
//
*
* @param fields
* The fields of the message
- * @param targetMessages
- * The messages of the reply to wait for
* @return The reply message
* @throws PluginException
* if the request could not be sent
*/
- private Reply performRequest(SimpleFieldSet fields, String... targetMessages) throws PluginException {
- return performRequest(fields, null, targetMessages);
+ private Reply performRequest(SimpleFieldSet fields) throws PluginException {
+ return performRequest(fields, null);
}
/**
* The fields of the message
* @param data
* The payload of the message
- * @param targetMessages
- * The messages of the reply to wait for
* @return The reply message
* @throws PluginException
* if the request could not be sent
*/
- private Reply performRequest(SimpleFieldSet fields, Bucket data, String... targetMessages) throws PluginException {
- @SuppressWarnings("synthetic-access")
- Reply reply = new Reply();
- for (String targetMessage : targetMessages) {
- replies.put(targetMessage, reply);
- }
- replies.put("Error", reply);
+ private synchronized Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException {
+ reply = new Reply();
+ logger.log(Level.FINE, "Sending FCP Request: " + fields.get("Message"));
synchronized (reply) {
pluginConnector.sendRequest(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, fields, data);
try {
- long now = System.currentTimeMillis();
- while ((reply.getFields() == null) && ((System.currentTimeMillis() - now) < 60000)) {
- reply.wait(60000 - (System.currentTimeMillis() - now));
- }
- if (reply.getFields() == null) {
- for (String targetMessage : targetMessages) {
- replies.remove(targetMessage);
- }
- replies.remove("Error");
- throw new PluginException("Timeout waiting for " + targetMessages[0] + "!");
- }
+ reply.wait();
} catch (InterruptedException ie1) {
- logger.log(Level.WARNING, "Got interrupted while waiting for reply on " + targetMessages[0] + ".", ie1);
+ logger.log(Level.WARNING, "Got interrupted while waiting for reply on " + fields.get("Message") + ".", ie1);
}
}
- for (String targetMessage : targetMessages) {
- replies.remove(targetMessage);
- }
- replies.remove("Error");
- if ((reply.getFields() != null) && reply.getFields().get("Message").equals("Error")) {
- throw new PluginException("Could not perform request for " + targetMessages[0]);
+ logger.log(Level.FINEST, "Received FCP Response for %s: %s", new Object[] { fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null });
+ if ((reply.getFields() == null) || "Error".equals(reply.getFields().get("Message"))) {
+ throw new PluginException("Could not perform request for " + fields.get("Message"));
}
return reply;
}
public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data) {
String messageName = fields.get("Message");
logger.log(Level.FINEST, "Received Reply from Plugin: " + messageName);
- Reply reply = replies.remove(messageName);
- if (reply == null) {
- logger.log(Level.FINE, "Not waiting for a “%s” message.", messageName);
- return;
- }
synchronized (reply) {
reply.setFields(fields);
reply.setData(data);
/** The payload of the reply. */
private Bucket data;
+ /** Empty constructor. */
+ public Reply() {
+ /* do nothing. */
+ }
+
/**
* Returns the fields of the reply.
*
--- /dev/null
+/*
+ * Sone - WebOfTrustException.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot;
+
+/**
+ * Exception that signals an error processing web of trust identities, mostly
+ * when communicating with the web of trust plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class WebOfTrustException extends Exception {
+
+ /**
+ * Creates a new web of trust exception.
+ */
+ public WebOfTrustException() {
+ super();
+ }
+
+ /**
+ * Creates a new web of trust exception.
+ *
+ * @param message
+ * The message of the exception
+ */
+ public WebOfTrustException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new web of trust exception.
+ *
+ * @param cause
+ * The cause of the exception
+ */
+ public WebOfTrustException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new web of trust exception.
+ *
+ * @param message
+ * The message of the exception
+ * @param cause
+ * The cause of the exception
+ */
+ public WebOfTrustException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.core.FreenetInterface;
import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend;
+import net.pterodactylus.sone.freenet.plugin.PluginConnector;
import net.pterodactylus.sone.freenet.wot.IdentityManager;
-import net.pterodactylus.sone.freenet.wot.PluginConnector;
import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.config.Configuration;
}
/** The version. */
- public static final Version VERSION = new Version(0, 3, 6, 5);
+ public static final Version VERSION = new Version(0, 5, 1);
/** The logger. */
private static final Logger logger = Logging.getLogger(SonePlugin.class);
*/
public ListNotification(String id, String key, Template template) {
super(id, template);
- template.set(key, elements);
- template.set("notification", this);
+ template.getInitialContext().set(key, elements);
}
//
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.util.template.Accessor;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
/**
* {@link Accessor} implementation for {@link Album}s. A property named
* {@inheritDoc}
*/
@Override
- public Object get(DataProvider dataProvider, Object object, String member) {
+ public Object get(TemplateContext templateContext, Object object, String member) {
Album album = (Album) object;
if ("backlinks".equals(member)) {
List<Map<String, String>> backlinks = new ArrayList<Map<String, String>>();
backlinks.add(0, createLink("viewSone.html?sone=" + album.getSone().getId(), SoneAccessor.getNiceName(album.getSone())));
return backlinks;
}
- return super.get(dataProvider, object, member);
+ return super.get(templateContext, object, member);
}
//
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.util.template.Accessor;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
/**
* {@link Accessor} for {@link Collection}s that adds a couple of specialized
* {@inheritDoc}
*/
@Override
- public Object get(DataProvider dataProvider, Object object, String member) {
+ public Object get(TemplateContext templateContext, Object object, String member) {
if (object == null) {
return null;
}
}
return soneNames.toString();
}
- return super.get(dataProvider, object, member);
+ return super.get(templateContext, object, member);
}
}
import java.util.Map;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Converts the {@link String} {@link String#valueOf(Object) representation} of
* {@inheritDoc}
*/
@Override
- public Object format(DataProvider dataProvider, Object data, Map<String, String> parameters) {
+ public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
return String.valueOf(data).replaceAll("[^a-zA-Z0-9-]", "_");
}
import java.util.Map;
import net.pterodactylus.sone.web.page.Page.Request;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Plugin;
+import net.pterodactylus.util.template.TemplateContext;
/**
- * Extracts a page number from a {@link Request}’s parameters and stores it in a
- * {@link DataProvider}.
+ * Extracts a page number from a {@link Request}’s parameters and stores it in
+ * the {@link TemplateContext}.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
* {@inheritDoc}
*/
@Override
- public void execute(DataProvider dataProvider, Map<String, String> parameters) {
+ public void execute(TemplateContext templateContext, Map<String, String> parameters) {
String requestKey = parameters.get("request");
String parameter = parameters.get("parameter");
String pageKey = parameters.get("key");
pageKey = "page";
}
- Request request = (Request) dataProvider.getData(requestKey);
+ Request request = (Request) templateContext.get(requestKey);
String pageString = request.getHttpRequest().getParam(parameter);
int page = 0;
try {
} catch (NumberFormatException nfe1) {
/* ignore. */
}
- dataProvider.setData(pageKey, page);
+ templateContext.set(pageKey, page);
}
}
import net.pterodactylus.sone.freenet.wot.Identity;
import net.pterodactylus.sone.freenet.wot.OwnIdentity;
import net.pterodactylus.util.template.Accessor;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
/**
* {@link Accessor} implementation that adds a “uniqueNickname” member to an
* {@inheritDoc}
*/
@Override
- public Object get(DataProvider dataProvider, Object object, String member) {
+ public Object get(TemplateContext templateContext, Object object, String member) {
Identity identity = (Identity) object;
if ("uniqueNickname".equals(member)) {
int minLength = -1;
} while (!found && (minLength < 43));
return getAbbreviatedNickname(identity, minLength);
}
- return super.get(dataProvider, object, member);
+ return super.get(templateContext, object, member);
}
//
--- /dev/null
+/*
+ * Sone - JavascriptFilter.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template;
+
+import java.util.Map;
+
+import net.pterodactylus.util.number.Hex;
+import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Escapes double quotes, backslashes, carriage returns and line feeds, and
+ * additionally encloses a given string with double quotes to make it possible
+ * to use a string in Javascript.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class JavascriptFilter implements Filter {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+ StringBuilder javascriptString = new StringBuilder();
+ javascriptString.append('"');
+ for (char c : String.valueOf(data).toCharArray()) {
+ if (c == '\r') {
+ javascriptString.append("\\r");
+ continue;
+ }
+ if (c == '\n') {
+ javascriptString.append("\\n");
+ continue;
+ }
+ if (c == '\t') {
+ javascriptString.append("\\t");
+ continue;
+ }
+ if ((c == '"') || (c == '\\')) {
+ javascriptString.append('\\');
+ javascriptString.append(c);
+ } else if (c < 32) {
+ javascriptString.append("\\x").append(Hex.toHex((byte) c));
+ } else {
+ javascriptString.append(c);
+ }
+ }
+ javascriptString.append('"');
+ return javascriptString.toString();
+ }
+
+}
import net.pterodactylus.util.notify.Notification;
import net.pterodactylus.util.notify.NotificationManager;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Adds additional properties to a {@link NotificationManager}.
* {@inheritDoc}
*/
@Override
- public Object get(DataProvider dataProvider, Object object, String member) {
+ public Object get(TemplateContext templateContext, Object object, String member) {
NotificationManager notificationManager = (NotificationManager) object;
if ("all".equals(member)) {
List<Notification> notifications = new ArrayList<Notification>(notificationManager.getNotifications());
Collections.sort(notifications, Notification.LAST_UPDATED_TIME_SORTER);
return notifications;
}
- return super.get(dataProvider, object, member);
+ return super.get(templateContext, object, member);
}
}
--- /dev/null
+/*
+ * Sone - ParserFilter.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Map;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.text.FreenetLinkParser;
+import net.pterodactylus.sone.text.FreenetLinkParserContext;
+import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateContextFactory;
+
+/**
+ * Filter that filters a given text through a {@link FreenetLinkParser}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ParserFilter implements Filter {
+
+ /** The core. */
+ private final Core core;
+
+ /** The link parser. */
+ private final FreenetLinkParser linkParser;
+
+ /**
+ * Creates a new filter that runs its input through a
+ * {@link FreenetLinkParser}.
+ *
+ * @param core
+ * The core
+ * @param templateContextFactory
+ * The context factory for rendering the parts
+ */
+ public ParserFilter(Core core, TemplateContextFactory templateContextFactory) {
+ this.core = core;
+ linkParser = new FreenetLinkParser(templateContextFactory);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+ String text = String.valueOf(data);
+ String soneKey = parameters.get("sone");
+ if (soneKey == null) {
+ soneKey = "sone";
+ }
+ Sone sone = (Sone) templateContext.get(soneKey);
+ if (sone == null) {
+ sone = core.getSone(soneKey, false);
+ }
+ FreenetLinkParserContext context = new FreenetLinkParserContext(sone);
+ try {
+ return linkParser.parse(context, new StringReader(text));
+ } catch (IOException ioe1) {
+ /* no exceptions in a StringReader, ignore. */
+ }
+ return null;
+ }
+
+}
package net.pterodactylus.sone.template;
-import java.io.IOException;
-import java.io.StringReader;
-
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.text.FreenetLinkParser;
-import net.pterodactylus.sone.text.FreenetLinkParserContext;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.ReflectionAccessor;
-import net.pterodactylus.util.template.TemplateFactory;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Accessor for {@link Post} objects that adds additional properties:
*/
public class PostAccessor extends ReflectionAccessor {
- /** Parser for Freenet links. */
- private final FreenetLinkParser linkParser;
-
/** The core to get the replies from. */
private final Core core;
*
* @param core
* The core to get the replies from
- * @param templateFactory
- * The template factory for the text parser
*/
- public PostAccessor(Core core, TemplateFactory templateFactory) {
+ public PostAccessor(Core core) {
this.core = core;
- linkParser = new FreenetLinkParser(templateFactory);
}
/**
* {@inheritDoc}
*/
@Override
- public Object get(DataProvider dataProvider, Object object, String member) {
+ public Object get(TemplateContext templateContext, Object object, String member) {
Post post = (Post) object;
if ("replies".equals(member)) {
return core.getReplies(post);
} else if (member.equals("likes")) {
return core.getLikes(post);
} else if (member.equals("liked")) {
- Sone currentSone = (Sone) dataProvider.getData("currentSone");
+ Sone currentSone = (Sone) templateContext.get("currentSone");
return (currentSone != null) && (currentSone.isLikedPostId(post.getId()));
} else if (member.equals("new")) {
- return core.isNewPost(post.getId(), false);
- } else if (member.equals("text")) {
- String text = post.getText();
- if (text == null) {
- return null;
- }
- try {
- return linkParser.parse(new FreenetLinkParserContext(post.getSone()), new StringReader(text));
- } catch (IOException ioe1) {
- /* ignore. */
- }
+ return core.isNewPost(post.getId());
+ } else if (member.equals("bookmarked")) {
+ return core.isBookmarked(post);
+ } else if (member.equals("loaded")) {
+ return post.getSone() != null;
}
- return super.get(dataProvider, object, member);
+ return super.get(templateContext, object, member);
}
}
package net.pterodactylus.sone.template;
-import java.io.IOException;
-import java.io.StringReader;
-
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.text.FreenetLinkParser;
-import net.pterodactylus.sone.text.FreenetLinkParserContext;
import net.pterodactylus.util.template.Accessor;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.ReflectionAccessor;
-import net.pterodactylus.util.template.TemplateFactory;
+import net.pterodactylus.util.template.TemplateContext;
/**
* {@link Accessor} implementation that adds a couple of properties to
*/
public class ReplyAccessor extends ReflectionAccessor {
- /** Parser for Freenet links. */
- private final FreenetLinkParser linkParser;
-
/** The core. */
private final Core core;
*
* @param core
* The core
- * @param templateFactory
- * The template factory for the text parser
*/
- public ReplyAccessor(Core core, TemplateFactory templateFactory) {
+ public ReplyAccessor(Core core) {
this.core = core;
- linkParser = new FreenetLinkParser(templateFactory);
}
/**
* {@inheritDoc}
*/
@Override
- public Object get(DataProvider dataProvider, Object object, String member) {
+ public Object get(TemplateContext templateContext, Object object, String member) {
Reply reply = (Reply) object;
if ("likes".equals(member)) {
return core.getLikes(reply);
} else if (member.equals("liked")) {
- Sone currentSone = (Sone) dataProvider.getData("currentSone");
+ Sone currentSone = (Sone) templateContext.get("currentSone");
return (currentSone != null) && (currentSone.isLikedReplyId(reply.getId()));
} else if (member.equals("new")) {
- return core.isNewReply(reply.getId(), false);
- } else if (member.equals("text")) {
- String text = reply.getText();
- try {
- return linkParser.parse(new FreenetLinkParserContext(reply.getSone()), new StringReader(text));
- } catch (IOException ioe1) {
- /* ignore. */
- }
+ return core.isNewReply(reply.getId());
+ } else if (member.equals("loaded")) {
+ return reply.getSone() != null;
}
- return super.get(dataProvider, object, member);
+ return super.get(templateContext, object, member);
}
}
import java.util.Map.Entry;
import net.pterodactylus.sone.web.page.Page.Request;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This filter expects a {@link Request} as input and outputs a {@link URI} that
* {@inheritDoc}
*/
@Override
- public Object format(DataProvider dataProvider, Object data, Map<String, String> parameters) {
+ public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
Request request = (Request) data;
String name = parameters.get("name");
String nameKey = parameters.get("nameKey");
if (nameKey != null) {
- name = String.valueOf(dataProvider.getData(nameKey));
+ name = String.valueOf(templateContext.get(nameKey));
}
String key = parameters.get("key");
String value = null;
if (key != null) {
- value = String.valueOf(dataProvider.getData(key));
+ value = String.valueOf(templateContext.get(key));
}
if (value == null) {
value = parameters.get("value");
package net.pterodactylus.sone.template;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.core.Core.SoneStatus;
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.wot.Trust;
+import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.template.Accessor;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
/**
* {@link Accessor} for {@link Sone}s that adds a couple of properties to Sones.
* <dt>friend</dt>
* <dd>Will return {@code true} if the sone in question is a friend of the
* currently logged in Sone (as determined by accessing the “currentSone”
- * variable of the given {@link DataProvider}).</dd>
+ * variable of the given {@link TemplateContext}).</dd>
* <dt>current</dt>
* <dd>Will return {@code true} if the sone in question is the currently logged
* in Sone.</dd>
*/
public class SoneAccessor extends ReflectionAccessor {
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(SoneAccessor.class);
+
/** The core. */
private final Core core;
* {@inheritDoc}
*/
@Override
- public Object get(DataProvider dataProvider, Object object, String member) {
+ public Object get(TemplateContext templateContext, Object object, String member) {
Sone sone = (Sone) object;
if (member.equals("niceName")) {
return getNiceName(sone);
} else if (member.equals("local")) {
- return sone.getInsertUri() != null;
+ return core.isLocalSone(sone);
} else if (member.equals("friend")) {
- Sone currentSone = (Sone) dataProvider.getData("currentSone");
+ Sone currentSone = (Sone) templateContext.get("currentSone");
return (currentSone != null) && currentSone.hasFriend(sone.getId());
} else if (member.equals("current")) {
- Sone currentSone = (Sone) dataProvider.getData("currentSone");
+ Sone currentSone = (Sone) templateContext.get("currentSone");
return (currentSone != null) && currentSone.equals(sone);
} else if (member.equals("modified")) {
return core.isModifiedSone(sone);
} else if (member.equals("downloading")) {
return core.getSoneStatus(sone) == SoneStatus.downloading;
} else if (member.equals("new")) {
- return core.isNewSone(sone);
+ return core.isNewSone(sone.getId());
} else if (member.equals("locked")) {
return core.isLocked(sone);
+ } else if (member.equals("trust")) {
+ Sone currentSone = (Sone) templateContext.get("currentSone");
+ if (currentSone == null) {
+ return null;
+ }
+ Trust trust = core.getTrust(currentSone, sone);
+ logger.log(Level.FINEST, "Trust for %s by %s: %s", new Object[] { sone, currentSone, trust });
+ if (trust == null) {
+ return new Trust(null, null, null);
+ }
+ return trust;
}
- return super.get(dataProvider, object, member);
+ return super.get(templateContext, object, member);
}
//
import java.util.Map;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
/**
* {@link Filter} implementation that executes
* {@inheritDoc}
*/
@Override
- public Object format(DataProvider dataProvider, Object data, Map<String, String> parameters) {
+ public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
String startString = parameters.get("start");
String lengthString = parameters.get("length");
int start = 0;
--- /dev/null
+/*
+ * Sone - TrustAccessor.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template;
+
+import net.pterodactylus.sone.freenet.wot.Trust;
+import net.pterodactylus.util.template.Accessor;
+import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * {@link Accessor} implementation for {@link Trust} values, adding the
+ * following properties:
+ * <dl>
+ * <dt>assigned</dt>
+ * <dd>{@link Boolean} that indicates whether this trust relationship has an
+ * explicit value assigned to it.</dd>
+ * </dl>
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class TrustAccessor extends ReflectionAccessor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(TemplateContext templateContext, Object object, String member) {
+ Trust trust = (Trust) object;
+ if ("assigned".equals(member)) {
+ return trust.getExplicit() != null;
+ } else if ("maximum".equals(member)) {
+ return ((trust.getExplicit() != null) && (trust.getExplicit() >= 100)) || ((trust.getImplicit() != null) && (trust.getImplicit() >= 100));
+ } else if ("hasDistance".equals(member)) {
+ return (trust.getDistance() != null) && (trust.getDistance() != Integer.MAX_VALUE);
+ }
+ return super.get(templateContext, object, member);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - UnknownDateFilter.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template;
+
+import java.util.Map;
+
+import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
+import freenet.l10n.BaseL10n;
+
+/**
+ * {@link Filter} implementation that replaces a {@link Long} with a value of
+ * {@code 0} by a {@link String} from an {@link BaseL10n l10n handler}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UnknownDateFilter implements Filter {
+
+ /** The l10n handler. */
+ private BaseL10n l10nHandler;
+
+ /** The key for the text to show. */
+ private final String unknownKey;
+
+ /**
+ * Creates a new unknown date filter.
+ *
+ * @param l10nHandler
+ * The l10n handler
+ * @param unknownKey
+ * The key of the text to show
+ */
+ public UnknownDateFilter(BaseL10n l10nHandler, String unknownKey) {
+ this.l10nHandler = l10nHandler;
+ this.unknownKey = unknownKey;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+ if (data instanceof Long) {
+ if ((Long) data == 0) {
+ return l10nHandler.getString(unknownKey);
+ }
+ }
+ return data;
+ }
+
+}
import java.util.regex.Pattern;
import net.pterodactylus.util.logging.Logging;
-import net.pterodactylus.util.template.TemplateFactory;
+import net.pterodactylus.util.template.TemplateContextFactory;
+import net.pterodactylus.util.template.TemplateParser;
import freenet.keys.FreenetURI;
/**
}
/** The template factory. */
- private final TemplateFactory templateFactory;
+ private final TemplateContextFactory templateContextFactory;
/**
* Creates a new freenet link parser.
*
- * @param templateFactory
- * The template factory
+ * @param templateContextFactory
+ * The template context factory
*/
- public FreenetLinkParser(TemplateFactory templateFactory) {
- this.templateFactory = templateFactory;
+ public FreenetLinkParser(TemplateContextFactory templateContextFactory) {
+ this.templateContextFactory = templateContextFactory;
}
//
String name = link;
logger.log(Level.FINER, "Found link: %s", link);
logger.log(Level.FINEST, "Next: %d, CHK: %d, SSK: %d, USK: %d", new Object[] { next, nextChk, nextSsk, nextUsk });
- if (linkType == LinkType.KSK) {
- name = link.substring(4);
- } else if ((linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
- if (name.indexOf('/') > -1) {
- if (!name.endsWith("/")) {
- name = name.substring(name.lastIndexOf('/') + 1);
- } else {
- if (name.indexOf('/') != name.lastIndexOf('/')) {
- name = name.substring(name.lastIndexOf('/', name.lastIndexOf('/') - 1));
- } else {
- /* shorten to 5 chars. */
- name = name.substring(4, Math.min(9, name.length()));
- }
- }
- }
+
+ if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
+ FreenetURI uri;
if (name.indexOf('?') > -1) {
name = name.substring(0, name.indexOf('?'));
}
- boolean fromPostingSone = false;
- if ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
- try {
- new FreenetURI(link);
- fromPostingSone = link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
- parts.add(fromPostingSone ? createTrustedFreenetLinkPart(link, name) : createFreenetLinkPart(link, name));
- } catch (MalformedURLException mue1) {
- /* it’s not a valid link. */
- parts.add(createPlainTextPart(link));
+ if (name.endsWith("/")) {
+ name = name.substring(0, name.length() - 1);
+ }
+ try {
+ uri = new FreenetURI(name);
+ name = uri.lastMetaString();
+ if (name == null) {
+ name = uri.getDocName();
+ }
+ if (name == null) {
+ name = link.substring(0, Math.min(9, link.length()));
}
- } else {
+ boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
parts.add(fromPostingSone ? createTrustedFreenetLinkPart(link, name) : createFreenetLinkPart(link, name));
+ } catch (MalformedURLException mue1) {
+ /* not a valid link, insert as plain text. */
+ parts.add(createPlainTextPart(link));
+ } catch (NullPointerException npe1) {
+ /* FreenetURI sometimes throws these, too. */
+ parts.add(createPlainTextPart(link));
+ } catch (ArrayIndexOutOfBoundsException aioobe1) {
+ /* oh, and these, too. */
+ parts.add(createPlainTextPart(link));
}
} else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
* @return The part that displays the given text
*/
private Part createPlainTextPart(String text) {
- return new TemplatePart(templateFactory.createTemplate(new StringReader("<% text|html>"))).set("text", text);
+ return new TemplatePart(templateContextFactory, TemplateParser.parse(new StringReader("<% text|html>"))).set("text", text);
}
/**
* @return The part that displays the link
*/
private Part createInternetLinkPart(String link, String name) {
- return new TemplatePart(templateFactory.createTemplate(new StringReader("<a class=\"internet\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
+ return new TemplatePart(templateContextFactory, TemplateParser.parse(new StringReader("<a class=\"internet\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
}
/**
* @return The part that displays the link
*/
private Part createFreenetLinkPart(String link, String name) {
- return new TemplatePart(templateFactory.createTemplate(new StringReader("<a class=\"freenet\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
+ return new TemplatePart(templateContextFactory, TemplateParser.parse(new StringReader("<a class=\"freenet\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
}
/**
* @return The part that displays the link
*/
private Part createTrustedFreenetLinkPart(String link, String name) {
- return new TemplatePart(templateFactory.createTemplate(new StringReader("<a class=\"freenet-trusted\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
+ return new TemplatePart(templateContextFactory, TemplateParser.parse(new StringReader("<a class=\"freenet-trusted\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
}
}
import java.io.Writer;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateContextFactory;
+import net.pterodactylus.util.template.TemplateException;
/**
* {@link Part} implementation that is rendered using a {@link Template}.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class TemplatePart implements Part {
+public class TemplatePart implements Part, net.pterodactylus.util.template.Part {
+
+ /** The template context factory. */
+ private final TemplateContextFactory templateContextFactory;
/** The template to render for this part. */
private final Template template;
/**
* Creates a new template part.
*
+ * @param templateContextFactory
+ * The template context factory
* @param template
* The template to render
*/
- public TemplatePart(Template template) {
+ public TemplatePart(TemplateContextFactory templateContextFactory, Template template) {
+ this.templateContextFactory = templateContextFactory;
this.template = template;
}
* @return This template part (for method chaining)
*/
public TemplatePart set(String key, Object value) {
- template.set(key, value);
+ template.getInitialContext().set(key, value);
return this;
}
*/
@Override
public void render(Writer writer) throws IOException {
- template.render(writer);
+ template.render(templateContextFactory.createTemplateContext().mergeContext(template.getInitialContext()), writer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void render(TemplateContext templateContext, Writer writer) throws TemplateException {
+ template.render(templateContext.mergeContext(template.getInitialContext()), writer);
}
}
package net.pterodactylus.sone.web;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.version.Version;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
- dataProvider.set("version", version);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ templateContext.set("version", version);
}
}
--- /dev/null
+/*
+ * Sone - BookmarkPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user bookmark a post.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class BookmarkPage extends SoneTemplatePage {
+
+ /**
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public BookmarkPage(Template template, WebInterface webInterface) {
+ super("bookmark.html", template, "Page.Bookmark.Title", webInterface);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String id = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
+ String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
+ webInterface.getCore().bookmarkPost(id);
+ throw new RedirectException(returnPage);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - BookmarksPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.util.collection.Pagination;
+import net.pterodactylus.util.filter.Filter;
+import net.pterodactylus.util.filter.Filters;
+import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user browse all his bookmarked posts.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class BookmarksPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new bookmarks page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public BookmarksPage(Template template, WebInterface webInterface) {
+ super("bookmarks.html", template, "Page.Bookmarks.Title", webInterface);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ Set<Post> allPosts = webInterface.getCore().getBookmarkedPosts();
+ Set<Post> loadedPosts = Filters.filteredSet(allPosts, new Filter<Post>() {
+
+ @Override
+ public boolean filterObject(Post post) {
+ return post.getSone() != null;
+ }
+ });
+ List<Post> sortedPosts = new ArrayList<Post>(loadedPosts);
+ Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+ Pagination<Post> pagination = new Pagination<Post>(sortedPosts, 25).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
+ templateContext.set("pagination", pagination);
+ templateContext.set("posts", pagination.getItems());
+ templateContext.set("postsNotLoaded", allPosts.size() != loadedPosts.size());
+ }
+
+}
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Page that lets the user create a new album.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String name = request.getHttpRequest().getPartAsStringFailsafe("name", 64).trim();
if (name.length() == 0) {
- dataProvider.set("nameMissing", true);
+ templateContext.set("nameMissing", true);
return;
}
Sone currentSone = getCurrentSone(request.getToadletContext());
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user create a new {@link Post}.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
if (request.getMethod() == Method.POST) {
String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
if (text.length() != 0) {
+ String senderId = request.getHttpRequest().getPartAsStringFailsafe("sender", 43);
String recipientId = request.getHttpRequest().getPartAsStringFailsafe("recipient", 43);
- Sone recipient = webInterface.getCore().getSone(recipientId, false);
Sone currentSone = getCurrentSone(request.getToadletContext());
- webInterface.getCore().createPost(currentSone, recipient, System.currentTimeMillis(), text);
+ Sone sender = webInterface.getCore().getLocalSone(senderId, false);
+ if (sender == null) {
+ sender = currentSone;
+ }
+ Sone recipient = webInterface.getCore().getSone(recipientId, false);
+ webInterface.getCore().createPost(sender, recipient, System.currentTimeMillis(), text);
throw new RedirectException(returnPage);
}
- dataProvider.set("errorTextEmpty", true);
+ templateContext.set("errorTextEmpty", true);
}
- dataProvider.set("returnPage", returnPage);
+ templateContext.set("returnPage", returnPage);
}
}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user post a reply to a post.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
if (request.getMethod() == Method.POST) {
Post post = webInterface.getCore().getPost(postId);
if (text.length() > 0) {
- Sone currentSone = getCurrentSone(request.getToadletContext());
- webInterface.getCore().createReply(currentSone, post, text);
+ String senderId = request.getHttpRequest().getPartAsStringFailsafe("sender", 43);
+ Sone sender = webInterface.getCore().getLocalSone(senderId, false);
+ if (sender == null) {
+ sender = getCurrentSone(request.getToadletContext());
+ }
+ webInterface.getCore().createReply(sender, post, text);
throw new RedirectException(returnPage);
}
- dataProvider.set("errorTextEmpty", true);
+ templateContext.set("errorTextEmpty", true);
}
- dataProvider.set("postId", postId);
- dataProvider.set("text", text);
- dataProvider.set("returnPage", returnPage);
+ templateContext.set("postId", postId);
+ templateContext.set("text", text);
+ templateContext.set("returnPage", returnPage);
}
}
import net.pterodactylus.sone.freenet.wot.OwnIdentity;
import net.pterodactylus.sone.web.page.Page.Request.Method;
import net.pterodactylus.util.logging.Logging;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import freenet.clients.http.ToadletContext;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
List<OwnIdentity> ownIdentitiesWithoutSone = getOwnIdentitiesWithoutSone(webInterface.getCore());
- dataProvider.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
+ templateContext.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
if (request.getMethod() == Method.POST) {
String id = request.getHttpRequest().getPartAsStringFailsafe("identity", 44);
OwnIdentity selectedIdentity = null;
}
}
if (selectedIdentity == null) {
- dataProvider.set("errorNoIdentity", true);
+ templateContext.set("errorNoIdentity", true);
return;
}
/* create Sone. */
- webInterface.getCore().getIdentityManager().addContext(selectedIdentity, "Sone");
Sone sone = webInterface.getCore().createSone(selectedIdentity);
if (sone == null) {
logger.log(Level.SEVERE, "Could not create Sone for OwnIdentity: %s", selectedIdentity);
*/
@Override
public boolean isEnabled(ToadletContext toadletContext) {
- return getCurrentSone(toadletContext) == null;
+ return (getCurrentSone(toadletContext, false) == null) || (webInterface.getCore().getLocalSones().size() == 1);
}
}
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Lets the user delete a post they made.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
if (request.getMethod() == Method.GET) {
String postId = request.getHttpRequest().getParam("post");
String returnPage = request.getHttpRequest().getParam("returnPage");
Post post = webInterface.getCore().getPost(postId);
- dataProvider.set("post", post);
- dataProvider.set("returnPage", returnPage);
+ templateContext.set("post", post);
+ templateContext.set("returnPage", returnPage);
return;
} else if (request.getMethod() == Method.POST) {
String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
Post post = webInterface.getCore().getPost(postId);
- Sone currentSone = getCurrentSone(request.getToadletContext());
- if (!post.getSone().equals(currentSone)) {
+ if (!webInterface.getCore().isLocalSone(post.getSone())) {
throw new RedirectException("noPermission.html");
}
if (request.getHttpRequest().isPartSet("confirmDelete")) {
- currentSone.removePost(post);
+ webInterface.getCore().deletePost(post);
throw new RedirectException(returnPage);
} else if (request.getHttpRequest().isPartSet("abortDelete")) {
throw new RedirectException(returnPage);
}
- dataProvider.set("post", post);
- dataProvider.set("returnPage", returnPage);
+ templateContext.set("post", post);
+ templateContext.set("returnPage", returnPage);
}
}
--- /dev/null
+/*
+ * Sone - DeleteProfileFieldPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Profile.Field;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user confirm the deletion of a profile field.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeleteProfileFieldPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “delete profile field” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DeleteProfileFieldPage(Template template, WebInterface webInterface) {
+ super("deleteProfileField.html", template, "Page.DeleteProfileField.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Profile profile = currentSone.getProfile();
+
+ /* get parameters from request. */
+ String fieldId = request.getHttpRequest().getParam("field");
+ Field field = profile.getFieldById(fieldId);
+ if (field == null) {
+ throw new RedirectException("invalid.html");
+ }
+
+ /* process POST request. */
+ if (request.getMethod() == Method.POST) {
+ if (request.getHttpRequest().getPartAsStringFailsafe("confirm", 4).equals("true")) {
+ fieldId = request.getHttpRequest().getParam("field");
+ field = profile.getFieldById(fieldId);
+ if (field == null) {
+ throw new RedirectException("invalid.html");
+ }
+ profile.removeField(field);
+ currentSone.setProfile(profile);
+ }
+ throw new RedirectException("editProfile.html#profile-fields");
+ }
+
+ /* set current values in template. */
+ templateContext.set("field", field);
+ }
+
+}
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Reply;
-import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user delete a reply.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", 36);
Reply reply = webInterface.getCore().getReply(replyId);
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
if (request.getMethod() == Method.POST) {
- Sone currentSone = getCurrentSone(request.getToadletContext());
- if (!reply.getSone().equals(currentSone)) {
+ if (!webInterface.getCore().isLocalSone(reply.getSone())) {
throw new RedirectException("noPermission.html");
}
if (request.getHttpRequest().isPartSet("confirmDelete")) {
throw new RedirectException(returnPage);
}
}
- dataProvider.set("reply", reply);
- dataProvider.set("returnPage", returnPage);
+ templateContext.set("reply", reply);
+ templateContext.set("returnPage", returnPage);
}
}
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Lets the user delete a Sone. Of course the Sone is not really deleted from
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
if (request.getHttpRequest().isPartSet("deleteSone")) {
Sone currentSone = getCurrentSone(request.getToadletContext());
package net.pterodactylus.sone.web;
import net.pterodactylus.util.notify.Notification;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Page that lets the user dismiss a notification.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String notificationId = request.getHttpRequest().getPartAsStringFailsafe("notification", 36);
Notification notification = webInterface.getNotifications().getNotification(notificationId);
if ((notification != null) && notification.isDismissable()) {
--- /dev/null
+/*
+ * Sone - TrustPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user distrust another Sone. This will assign a
+ * configurable (negative) amount of trust to an identity.
+ *
+ * @see Core#distrustSone(Sone, Sone)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DistrustPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “distrust Sone” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DistrustPage(Template template, WebInterface webInterface) {
+ super("distrust.html", template, "Page.Distrust.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
+ String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Sone sone = webInterface.getCore().getSone(identity, false);
+ if (sone != null) {
+ webInterface.getCore().distrustSone(currentSone, sone);
+ }
+ throw new RedirectException(returnPage);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - EditProfileFieldPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Profile.Field;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user edit the name of a profile field.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditProfileFieldPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “edit profile field” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public EditProfileFieldPage(Template template, WebInterface webInterface) {
+ super("editProfileField.html", template, "Page.EditProfileField.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Profile profile = currentSone.getProfile();
+
+ /* get parameters from request. */
+ String fieldId = request.getHttpRequest().getParam("field");
+ Field field = profile.getFieldById(fieldId);
+ if (field == null) {
+ throw new RedirectException("invalid.html");
+ }
+
+ /* process the POST request. */
+ if (request.getMethod() == Method.POST) {
+ if (request.getHttpRequest().getPartAsStringFailsafe("cancel", 4).equals("true")) {
+ throw new RedirectException("editProfile.html#profile-fields");
+ }
+ fieldId = request.getHttpRequest().getPartAsStringFailsafe("field", 36);
+ field = profile.getFieldById(fieldId);
+ if (field == null) {
+ throw new RedirectException("invalid.html");
+ }
+ String name = request.getHttpRequest().getPartAsStringFailsafe("name", 256);
+ Field existingField = profile.getFieldByName(name);
+ if ((existingField == null) || (existingField.equals(field))) {
+ field.setName(name);
+ currentSone.setProfile(profile);
+ throw new RedirectException("editProfile.html#profile-fields");
+ }
+ templateContext.set("duplicateFieldName", true);
+ }
+
+ /* store current values in template. */
+ templateContext.set("field", field);
+ }
+
+}
package net.pterodactylus.sone.web;
+import java.util.List;
+
import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
import net.pterodactylus.util.number.Numbers;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import freenet.clients.http.ToadletContext;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
ToadletContext toadletContenxt = request.getToadletContext();
Sone currentSone = getCurrentSone(toadletContenxt);
Profile profile = currentSone.getProfile();
Integer birthDay = profile.getBirthDay();
Integer birthMonth = profile.getBirthMonth();
Integer birthYear = profile.getBirthYear();
+ List<Field> fields = profile.getFields();
if (request.getMethod() == Method.POST) {
- firstName = request.getHttpRequest().getPartAsStringFailsafe("first-name", 256).trim();
- middleName = request.getHttpRequest().getPartAsStringFailsafe("middle-name", 256).trim();
- lastName = request.getHttpRequest().getPartAsStringFailsafe("last-name", 256).trim();
- birthDay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-day", 256).trim());
- birthMonth = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-month", 256).trim());
- birthYear = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-year", 256).trim());
- profile.setFirstName(firstName.length() > 0 ? firstName : null);
- profile.setMiddleName(middleName.length() > 0 ? middleName : null);
- profile.setLastName(lastName.length() > 0 ? lastName : null);
- profile.setBirthDay(birthDay).setBirthMonth(birthMonth).setBirthYear(birthYear);
- if (profile.isModified()) {
+ if (request.getHttpRequest().getPartAsStringFailsafe("save-profile", 4).equals("true")) {
+ firstName = request.getHttpRequest().getPartAsStringFailsafe("first-name", 256).trim();
+ middleName = request.getHttpRequest().getPartAsStringFailsafe("middle-name", 256).trim();
+ lastName = request.getHttpRequest().getPartAsStringFailsafe("last-name", 256).trim();
+ birthDay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-day", 256).trim());
+ birthMonth = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-month", 256).trim());
+ birthYear = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-year", 256).trim());
+ profile.setFirstName(firstName.length() > 0 ? firstName : null);
+ profile.setMiddleName(middleName.length() > 0 ? middleName : null);
+ profile.setLastName(lastName.length() > 0 ? lastName : null);
+ profile.setBirthDay(birthDay).setBirthMonth(birthMonth).setBirthYear(birthYear);
+ for (Field field : fields) {
+ String value = request.getHttpRequest().getPartAsStringFailsafe("field-" + field.getId(), 400);
+ field.setValue(value);
+ }
currentSone.setProfile(profile);
+ webInterface.getCore().saveSone(currentSone);
+ throw new RedirectException("editProfile.html");
+ } else if (request.getHttpRequest().getPartAsStringFailsafe("add-field", 4).equals("true")) {
+ String fieldName = request.getHttpRequest().getPartAsStringFailsafe("field-name", 256).trim();
+ try {
+ profile.addField(fieldName);
+ currentSone.setProfile(profile);
+ fields = profile.getFields();
+ webInterface.getCore().saveSone(currentSone);
+ throw new RedirectException("editProfile.html#profile-fields");
+ } catch (IllegalArgumentException iae1) {
+ templateContext.set("fieldName", fieldName);
+ templateContext.set("duplicateFieldName", true);
+ }
+ } else {
+ String id = getFieldId(request, "delete-field-");
+ if (id != null) {
+ throw new RedirectException("deleteProfileField.html?field=" + id);
+ }
+ id = getFieldId(request, "move-up-field-");
+ if (id != null) {
+ Field field = profile.getFieldById(id);
+ if (field == null) {
+ throw new RedirectException("invalid.html");
+ }
+ profile.moveFieldUp(field);
+ currentSone.setProfile(profile);
+ throw new RedirectException("editProfile.html#profile-fields");
+ }
+ id = getFieldId(request, "move-down-field-");
+ if (id != null) {
+ Field field = profile.getFieldById(id);
+ if (field == null) {
+ throw new RedirectException("invalid.html");
+ }
+ profile.moveFieldDown(field);
+ currentSone.setProfile(profile);
+ throw new RedirectException("editProfile.html#profile-fields");
+ }
+ id = getFieldId(request, "edit-field-");
+ if (id != null) {
+ throw new RedirectException("editProfileField.html?field=" + id);
+ }
}
- throw new RedirectException("index.html");
}
- dataProvider.set("firstName", firstName);
- dataProvider.set("middleName", middleName);
- dataProvider.set("lastName", lastName);
- dataProvider.set("birthDay", birthDay);
- dataProvider.set("birthMonth", birthMonth);
- dataProvider.set("birthYear", birthYear);
+ templateContext.set("firstName", firstName);
+ templateContext.set("middleName", middleName);
+ templateContext.set("lastName", lastName);
+ templateContext.set("birthDay", birthDay);
+ templateContext.set("birthMonth", birthMonth);
+ templateContext.set("birthYear", birthYear);
+ templateContext.set("fields", fields);
}
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Searches for a part whose names starts with the given {@code String} and
+ * extracts the ID from the located name.
+ *
+ * @param request
+ * The request to get the parts from
+ * @param partNameStart
+ * The start of the name of the requested part
+ * @return The parsed ID, or {@code null} if there was no part matching the
+ * given string
+ */
+ private String getFieldId(Request request, String partNameStart) {
+ for (String partName : request.getHttpRequest().getParts()) {
+ if (partName.startsWith(partNameStart)) {
+ return partName.substring(partNameStart.length());
+ }
+ }
+ return null;
+ }
}
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user follow another Sone.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* The image browser page is the entry page for the image management.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String albumId = request.getHttpRequest().getParam("album", null);
if (albumId != null) {
Album album = webInterface.getCore().getAlbum(albumId, false);
- dataProvider.set("albumRequested", true);
- dataProvider.set("album", album);
+ templateContext.set("albumRequested", true);
+ templateContext.set("album", album);
return;
}
String imageId = request.getHttpRequest().getParam("image", null);
if (imageId != null) {
Image image = webInterface.getCore().getImage(imageId, false);
- dataProvider.set("imageRequested", true);
- dataProvider.set("image", image);
+ templateContext.set("imageRequested", true);
+ templateContext.set("image", image);
}
}
}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.util.template.DataProvider;
+import net.pterodactylus.util.collection.Pagination;
+import net.pterodactylus.util.number.Numbers;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* The index page shows the main page of Sone. This page will contain the posts
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
Sone currentSone = getCurrentSone(request.getToadletContext());
List<Post> allPosts = new ArrayList<Post>();
allPosts.addAll(currentSone.getPosts());
}
}
Collections.sort(allPosts, Post.TIME_COMPARATOR);
- dataProvider.set("posts", allPosts);
+ Pagination<Post> pagination = new Pagination<Post>(allPosts, 25).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
+ templateContext.set("pagination", pagination);
+ templateContext.set("posts", pagination.getItems());
}
/**
* {@inheritDoc}
*/
@Override
- protected void postProcess(Request request, DataProvider dataProvider) {
+ protected void postProcess(Request request, TemplateContext templateContext) {
@SuppressWarnings("unchecked")
- List<Post> posts = (List<Post>) dataProvider.get("posts");
+ List<Post> posts = (List<Post>) templateContext.get("posts");
for (Post post : posts) {
webInterface.getCore().markPostKnown(post);
for (Reply reply : webInterface.getCore().getReplies(post)) {
import java.util.List;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.util.template.DataProvider;
+import net.pterodactylus.util.collection.Pagination;
+import net.pterodactylus.util.number.Numbers;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page shows all known Sones.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
List<Sone> knownSones = new ArrayList<Sone>(webInterface.getCore().getSones());
Collections.sort(knownSones, Sone.NICE_NAME_COMPARATOR);
- dataProvider.set("knownSones", knownSones);
+ Pagination<Sone> sonePagination = new Pagination<Sone>(knownSones, 25).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
+ templateContext.set("pagination", sonePagination);
+ templateContext.set("knownSones", sonePagination.getItems());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void postProcess(Request request, TemplateContext templateContext) {
+ super.postProcess(request, templateContext);
+ @SuppressWarnings("unchecked")
+ List<Sone> sones = (List<Sone>) templateContext.get("knownSones");
+ for (Sone sone : sones) {
+ webInterface.getCore().markSoneKnown(sone);
+ }
}
}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Page that lets the user like a {@link Post}.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String type=request.getHttpRequest().getPartAsStringFailsafe("type", 16);
String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36);
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user lock a {@link Sone} to prevent it from being
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
Sone sone = webInterface.getCore().getLocalSone(soneId, false);
if (sone != null) {
import net.pterodactylus.sone.freenet.wot.OwnIdentity;
import net.pterodactylus.sone.web.page.Page.Request.Method;
import net.pterodactylus.util.logging.Logging;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import freenet.clients.http.ToadletContext;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
/* get all own identities. */
List<Sone> localSones = new ArrayList<Sone>(webInterface.getCore().getLocalSones());
Collections.sort(localSones, Sone.NICE_NAME_COMPARATOR);
- dataProvider.set("sones", localSones);
+ templateContext.set("sones", localSones);
if (request.getMethod() == Method.POST) {
String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone-id", 100);
Sone selectedSone = webInterface.getCore().getLocalSone(soneId, false);
if (selectedSone != null) {
setCurrentSone(request.getToadletContext(), selectedSone);
- throw new RedirectException("index.html");
+ String target = request.getHttpRequest().getParam("target");
+ if ((target == null) || (target.length() == 0)) {
+ target = "index.html";
+ }
+ throw new RedirectException(target);
}
}
List<OwnIdentity> ownIdentitiesWithoutSone = CreateSonePage.getOwnIdentitiesWithoutSone(webInterface.getCore());
- dataProvider.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
+ templateContext.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
}
/**
*/
@Override
protected String getRedirectTarget(Request request) {
- if (getCurrentSone(request.getToadletContext()) != null) {
+ if (getCurrentSone(request.getToadletContext(), false) != null) {
return "index.html";
}
return null;
*/
@Override
public boolean isEnabled(ToadletContext toadletContext) {
- return getCurrentSone(toadletContext) == null;
+ return getCurrentSone(toadletContext, false) == null;
}
}
package net.pterodactylus.sone.web;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import freenet.clients.http.ToadletContext;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
setCurrentSone(request.getToadletContext(), null);
- super.processTemplate(request, dataProvider);
+ super.processTemplate(request, templateContext);
throw new RedirectException("index.html");
}
*/
@Override
public boolean isEnabled(ToadletContext toadletContext) {
- return getCurrentSone(toadletContext) != null;
+ return (getCurrentSone(toadletContext, false) != null) && (webInterface.getCore().getLocalSones().size() != 1);
}
}
--- /dev/null
+/*
+ * Sone - MarkReadPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import java.util.StringTokenizer;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user mark a number of {@link Sone}s, {@link Post}s, or
+ * {@link Reply Replie}s as known.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class MarkAsKnownPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “mark as known” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public MarkAsKnownPage(Template template, WebInterface webInterface) {
+ super("markAsKnown.html", template, "Page.MarkAsKnown.Title", webInterface);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ String type = request.getHttpRequest().getPartAsStringFailsafe("type", 5);
+ if (!type.equals("sone") && !type.equals("post") && !type.equals("reply")) {
+ throw new RedirectException("invalid.html");
+ }
+ String ids = request.getHttpRequest().getPartAsStringFailsafe("id", 65536);
+ for (StringTokenizer idTokenizer = new StringTokenizer(ids); idTokenizer.hasMoreTokens();) {
+ String id = idTokenizer.nextToken();
+ if (type.equals("post")) {
+ Post post = webInterface.getCore().getPost(id, false);
+ if (post == null) {
+ continue;
+ }
+ webInterface.getCore().markPostKnown(post);
+ } else if (type.equals("reply")) {
+ Reply reply = webInterface.getCore().getReply(id, false);
+ if (reply == null) {
+ continue;
+ }
+ webInterface.getCore().markReplyKnown(reply);
+ } else if (type.equals("sone")) {
+ Sone sone = webInterface.getCore().getSone(id, false);
+ if (sone == null) {
+ continue;
+ }
+ webInterface.getCore().markSoneKnown(sone);
+ }
+ }
+ String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
+ throw new RedirectException(returnPage);
+ }
+
+}
package net.pterodactylus.sone.web;
-import net.pterodactylus.sone.core.Options;
+import net.pterodactylus.sone.core.Core.Preferences;
import net.pterodactylus.sone.web.page.Page.Request.Method;
import net.pterodactylus.util.number.Numbers;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user edit the options of the Sone plugin.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
- Options options = webInterface.getCore().getOptions();
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ Preferences preferences = webInterface.getCore().getPreferences();
if (request.getMethod() == Method.POST) {
Integer insertionDelay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("insertion-delay", 16));
- options.getIntegerOption("InsertionDelay").set(insertionDelay);
+ preferences.setInsertionDelay(insertionDelay);
+ Integer positiveTrust = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("positive-trust", 3));
+ preferences.setPositiveTrust(positiveTrust);
+ Integer negativeTrust = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("negative-trust", 4));
+ preferences.setNegativeTrust(negativeTrust);
+ String trustComment = request.getHttpRequest().getPartAsStringFailsafe("trust-comment", 256);
+ if (trustComment.trim().length() == 0) {
+ trustComment = null;
+ }
+ preferences.setTrustComment(trustComment);
boolean soneRescueMode = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("sone-rescue-mode", 5));
- options.getBooleanOption("SoneRescueMode").set(soneRescueMode);
+ preferences.setSoneRescueMode(soneRescueMode);
boolean clearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("clear-on-next-restart", 5));
- options.getBooleanOption("ClearOnNextRestart").set(clearOnNextRestart);
+ preferences.setClearOnNextRestart(clearOnNextRestart);
boolean reallyClearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("really-clear-on-next-restart", 5));
- options.getBooleanOption("ReallyClearOnNextRestart").set(reallyClearOnNextRestart);
+ preferences.setReallyClearOnNextRestart(reallyClearOnNextRestart);
webInterface.getCore().saveConfiguration();
throw new RedirectException(getPath());
}
- dataProvider.set("insertion-delay", options.getIntegerOption("InsertionDelay").get());
- dataProvider.set("sone-rescue-mode", options.getBooleanOption("SoneRescueMode").get());
- dataProvider.set("clear-on-next-restart", options.getBooleanOption("ClearOnNextRestart").get());
- dataProvider.set("really-clear-on-next-restart", options.getBooleanOption("ReallyClearOnNextRestart").get());
+ templateContext.set("insertion-delay", preferences.getInsertionDelay());
+ templateContext.set("positive-trust", preferences.getPositiveTrust());
+ templateContext.set("negative-trust", preferences.getNegativeTrust());
+ templateContext.set("trust-comment", preferences.getTrustComment());
+ templateContext.set("sone-rescue-mode", preferences.isSoneRescueMode());
+ templateContext.set("clear-on-next-restart", preferences.isClearOnNextRestart());
+ templateContext.set("really-clear-on-next-restart", preferences.isReallyClearOnNextRestart());
}
}
package net.pterodactylus.sone.web;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Collection;
import net.pterodactylus.sone.main.SonePlugin;
import net.pterodactylus.sone.web.page.Page;
import net.pterodactylus.sone.web.page.TemplatePage;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import freenet.clients.http.SessionManager.Session;
import freenet.clients.http.ToadletContext;
+import freenet.support.api.HTTPRequest;
/**
* Base page for the Freetalk web interface.
* Whether this page requires a login
*/
public SoneTemplatePage(String path, Template template, String pageTitleKey, WebInterface webInterface, boolean requireLogin) {
- super(path, template, webInterface.getL10n(), pageTitleKey, "noPermission.html");
+ super(path, webInterface.getTemplateContextFactory(), template, webInterface.getL10n(), pageTitleKey, "noPermission.html");
this.webInterface = webInterface;
this.requireLogin = requireLogin;
- template.set("webInterface", webInterface);
+ template.getInitialContext().set("webInterface", webInterface);
}
//
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
- dataProvider.set("currentSone", getCurrentSone(request.getToadletContext(), false));
- dataProvider.set("request", request);
- dataProvider.set("currentVersion", SonePlugin.VERSION);
- dataProvider.set("hasLatestVersion", webInterface.getCore().getUpdateChecker().hasLatestVersion());
- dataProvider.set("latestVersion", webInterface.getCore().getUpdateChecker().getLatestVersion());
- dataProvider.set("latestVersionTime", webInterface.getCore().getUpdateChecker().getLatestVersionDate());
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ templateContext.set("currentSone", getCurrentSone(request.getToadletContext(), false));
+ templateContext.set("localSones", webInterface.getCore().getLocalSones());
+ templateContext.set("request", request);
+ templateContext.set("currentVersion", SonePlugin.VERSION);
+ templateContext.set("hasLatestVersion", webInterface.getCore().getUpdateChecker().hasLatestVersion());
+ templateContext.set("latestEdition", webInterface.getCore().getUpdateChecker().getLatestEdition());
+ templateContext.set("latestVersion", webInterface.getCore().getUpdateChecker().getLatestVersion());
+ templateContext.set("latestVersionTime", webInterface.getCore().getUpdateChecker().getLatestVersionDate());
}
/**
@Override
protected String getRedirectTarget(Page.Request request) {
if (requiresLogin() && (getCurrentSone(request.getToadletContext(), false) == null)) {
- return "login.html";
+ HTTPRequest httpRequest = request.getHttpRequest();
+ String originalUrl = httpRequest.getPath();
+ if (httpRequest.hasParameters()) {
+ StringBuilder requestParameters = new StringBuilder();
+ for (String parameterName : httpRequest.getParameterNames()) {
+ if (requestParameters.length() > 0) {
+ requestParameters.append("%26");
+ }
+ String[] parameterValues = httpRequest.getMultipleParam(parameterName);
+ for (String parameterValue : parameterValues) {
+ try {
+ requestParameters.append(URLEncoder.encode(parameterName, "UTF-8")).append("%3d").append(URLEncoder.encode(parameterValue, "UTF-8"));
+ } catch (UnsupportedEncodingException uee1) {
+ /* A JVM without UTF-8? I don’t think so. */
+ }
+ }
+ }
+ originalUrl += "?" + requestParameters.toString();
+ }
+ return "login.html?target=" + originalUrl;
}
return null;
}
--- /dev/null
+/*
+ * Sone - TrustPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user trust another Sone. This will assign a configurable
+ * amount of trust to an identity.
+ *
+ * @see Core#trustSone(Sone, Sone)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class TrustPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “trust Sone” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public TrustPage(Template template, WebInterface webInterface) {
+ super("trust.html", template, "Page.Trust.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
+ String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Sone sone = webInterface.getCore().getSone(identity, false);
+ if (sone != null) {
+ webInterface.getCore().trustSone(currentSone, sone);
+ }
+ throw new RedirectException(returnPage);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - BookmarkPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import java.util.Set;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user unbookmark a post.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UnbookmarkPage extends SoneTemplatePage {
+
+ /**
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public UnbookmarkPage(Template template, WebInterface webInterface) {
+ super("unbookmark.html", template, "Page.Unbookmark.Title", webInterface);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String id = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
+ String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
+ webInterface.getCore().unbookmarkPost(id);
+ throw new RedirectException(returnPage);
+ }
+ String id = request.getHttpRequest().getParam("post");
+ if (id.equals("allNotLoaded")) {
+ Set<Post> posts = webInterface.getCore().getBookmarkedPosts();
+ for (Post post : posts) {
+ if (post.getSone() == null) {
+ webInterface.getCore().unbookmark(post);
+ }
+ }
+ throw new RedirectException("bookmarks.html");
+ }
+ }
+
+}
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user unfollow another Sone.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Page that lets the user unlike a {@link Post}.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16);
String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36);
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user unlock a {@link Sone} to allow its insertion.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
Sone sone = webInterface.getCore().getLocalSone(soneId, false);
if (sone != null) {
--- /dev/null
+/*
+ * Sone - TrustPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * Page that lets the user untrust another Sone. This will remove all trust
+ * assignments for an identity.
+ *
+ * @see Core#untrustSone(Sone, Sone)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UntrustPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “untrust Sone” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public UntrustPage(Template template, WebInterface webInterface) {
+ super("untrust.html", template, "Page.Untrust.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
+ String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Sone sone = webInterface.getCore().getSone(identity, false);
+ if (sone != null) {
+ webInterface.getCore().untrustSone(currentSone, sone);
+ }
+ throw new RedirectException(returnPage);
+ }
+ }
+
+}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* This page lets the user view a post and all its replies.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String postId = request.getHttpRequest().getParam("post");
+ boolean raw = request.getHttpRequest().getParam("raw").equals("true");
Post post = webInterface.getCore().getPost(postId);
- dataProvider.set("post", post);
+ templateContext.set("post", post);
+ templateContext.set("raw", raw);
}
/**
* {@inheritDoc}
*/
@Override
- protected void postProcess(Request request, DataProvider dataProvider) {
- Post post = (Post) dataProvider.get("post");
+ protected void postProcess(Request request, TemplateContext templateContext) {
+ Post post = (Post) templateContext.get("post");
+ if (post == null) {
+ return;
+ }
webInterface.getCore().markPostKnown(post);
for (Reply reply : webInterface.getCore().getReplies(post)) {
webInterface.getCore().markReplyKnown(reply);
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
/**
* Lets the user browser another Sone.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
- super.processTemplate(request, dataProvider);
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
String soneId = request.getHttpRequest().getParam("sone");
Sone sone = webInterface.getCore().getSone(soneId, false);
- dataProvider.set("sone", sone);
+ templateContext.set("sone", sone);
}
/**
* {@inheritDoc}
*/
@Override
- protected void postProcess(Request request, DataProvider dataProvider) {
- Sone sone = (Sone) dataProvider.get("sone");
+ protected void postProcess(Request request, TemplateContext templateContext) {
+ Sone sone = (Sone) templateContext.get("sone");
+ if (sone == null) {
+ return;
+ }
+ webInterface.getCore().markSoneKnown(sone);
List<Post> posts = sone.getPosts();
for (Post post : posts) {
webInterface.getCore().markPostKnown(post);
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.L10nFilter;
import net.pterodactylus.sone.freenet.wot.Identity;
+import net.pterodactylus.sone.freenet.wot.Trust;
import net.pterodactylus.sone.main.SonePlugin;
import net.pterodactylus.sone.notify.ListNotification;
import net.pterodactylus.sone.template.AlbumAccessor;
import net.pterodactylus.sone.template.CssClassNameFilter;
import net.pterodactylus.sone.template.GetPagePlugin;
import net.pterodactylus.sone.template.IdentityAccessor;
+import net.pterodactylus.sone.template.JavascriptFilter;
import net.pterodactylus.sone.template.NotificationManagerAccessor;
+import net.pterodactylus.sone.template.ParserFilter;
import net.pterodactylus.sone.template.PostAccessor;
import net.pterodactylus.sone.template.ReplyAccessor;
import net.pterodactylus.sone.template.RequestChangeFilter;
import net.pterodactylus.sone.template.SoneAccessor;
import net.pterodactylus.sone.template.SubstringFilter;
+import net.pterodactylus.sone.template.TrustAccessor;
+import net.pterodactylus.sone.template.UnknownDateFilter;
+import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage;
import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage;
import net.pterodactylus.sone.web.ajax.CreateReplyAjaxPage;
import net.pterodactylus.sone.web.ajax.DeletePostAjaxPage;
+import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage;
import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage;
import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage;
+import net.pterodactylus.sone.web.ajax.DistrustAjaxPage;
+import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
import net.pterodactylus.sone.web.ajax.GetPostAjaxPage;
import net.pterodactylus.sone.web.ajax.GetTranslationPage;
import net.pterodactylus.sone.web.ajax.LikeAjaxPage;
import net.pterodactylus.sone.web.ajax.LockSoneAjaxPage;
-import net.pterodactylus.sone.web.ajax.MarkPostAsKnownPage;
-import net.pterodactylus.sone.web.ajax.MarkReplyAsKnownPage;
+import net.pterodactylus.sone.web.ajax.MarkAsKnownAjaxPage;
+import net.pterodactylus.sone.web.ajax.MoveProfileFieldAjaxPage;
+import net.pterodactylus.sone.web.ajax.TrustAjaxPage;
+import net.pterodactylus.sone.web.ajax.UnbookmarkAjaxPage;
import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage;
import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage;
import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
+import net.pterodactylus.sone.web.ajax.UntrustAjaxPage;
import net.pterodactylus.sone.web.page.PageToadlet;
import net.pterodactylus.sone.web.page.PageToadletFactory;
import net.pterodactylus.sone.web.page.StaticPage;
+import net.pterodactylus.util.cache.Cache;
+import net.pterodactylus.util.cache.CacheException;
+import net.pterodactylus.util.cache.CacheItem;
+import net.pterodactylus.util.cache.DefaultCacheItem;
+import net.pterodactylus.util.cache.MemoryCache;
+import net.pterodactylus.util.cache.ValueRetriever;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.notify.Notification;
import net.pterodactylus.util.notify.NotificationManager;
import net.pterodactylus.util.notify.TemplateNotification;
+import net.pterodactylus.util.template.CollectionSortFilter;
import net.pterodactylus.util.template.DateFilter;
-import net.pterodactylus.util.template.DefaultTemplateFactory;
+import net.pterodactylus.util.template.FormatFilter;
+import net.pterodactylus.util.template.HtmlFilter;
import net.pterodactylus.util.template.MatchFilter;
import net.pterodactylus.util.template.PaginationPlugin;
+import net.pterodactylus.util.template.Provider;
import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.ReplaceFilter;
+import net.pterodactylus.util.template.StoreFilter;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateContextFactory;
import net.pterodactylus.util.template.TemplateException;
-import net.pterodactylus.util.template.TemplateFactory;
-import net.pterodactylus.util.template.TemplateProvider;
+import net.pterodactylus.util.template.TemplateParser;
import net.pterodactylus.util.template.XmlFilter;
import net.pterodactylus.util.thread.Ticker;
import net.pterodactylus.util.version.Version;
/** The form password. */
private final String formPassword;
- /** The template factory. */
- private DefaultTemplateFactory templateFactory;
+ /** The template context factory. */
+ private final TemplateContextFactory templateContextFactory;
/** The “new Sone” notification. */
private final ListNotification<Sone> newSoneNotification;
* @param sonePlugin
* The Sone plugin
*/
+ @SuppressWarnings("synthetic-access")
public WebInterface(SonePlugin sonePlugin) {
this.sonePlugin = sonePlugin;
formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
- templateFactory = new DefaultTemplateFactory();
- templateFactory.addAccessor(Object.class, new ReflectionAccessor());
- templateFactory.addAccessor(Collection.class, new CollectionAccessor());
- templateFactory.addAccessor(Sone.class, new SoneAccessor(getCore()));
- templateFactory.addAccessor(Post.class, new PostAccessor(getCore(), templateFactory));
- templateFactory.addAccessor(Reply.class, new ReplyAccessor(getCore(), templateFactory));
- templateFactory.addAccessor(Album.class, new AlbumAccessor());
- templateFactory.addAccessor(Identity.class, new IdentityAccessor(getCore()));
- templateFactory.addAccessor(NotificationManager.class, new NotificationManagerAccessor());
- templateFactory.addFilter("date", new DateFilter());
- templateFactory.addFilter("l10n", new L10nFilter(getL10n()));
- templateFactory.addFilter("substring", new SubstringFilter());
- templateFactory.addFilter("xml", new XmlFilter());
- templateFactory.addFilter("change", new RequestChangeFilter());
- templateFactory.addFilter("match", new MatchFilter());
- templateFactory.addFilter("css", new CssClassNameFilter());
- templateFactory.addPlugin("getpage", new GetPagePlugin());
- templateFactory.addPlugin("paginate", new PaginationPlugin());
- templateFactory.setTemplateProvider(new ClassPathTemplateProvider(templateFactory));
- templateFactory.addTemplateObject("formPassword", formPassword);
+ templateContextFactory = new TemplateContextFactory();
+ templateContextFactory.addAccessor(Object.class, new ReflectionAccessor());
+ templateContextFactory.addAccessor(Collection.class, new CollectionAccessor());
+ templateContextFactory.addAccessor(Sone.class, new SoneAccessor(getCore()));
+ templateContextFactory.addAccessor(Post.class, new PostAccessor(getCore()));
+ templateContextFactory.addAccessor(Reply.class, new ReplyAccessor(getCore()));
+ templateContextFactory.addAccessor(Album.class, new AlbumAccessor());
+ templateContextFactory.addAccessor(Identity.class, new IdentityAccessor(getCore()));
+ templateContextFactory.addAccessor(NotificationManager.class, new NotificationManagerAccessor());
+ templateContextFactory.addAccessor(Trust.class, new TrustAccessor());
+ templateContextFactory.addFilter("date", new DateFilter());
+ templateContextFactory.addFilter("html", new HtmlFilter());
+ templateContextFactory.addFilter("replace", new ReplaceFilter());
+ templateContextFactory.addFilter("store", new StoreFilter());
+ templateContextFactory.addFilter("l10n", new L10nFilter(getL10n()));
+ templateContextFactory.addFilter("substring", new SubstringFilter());
+ templateContextFactory.addFilter("xml", new XmlFilter());
+ templateContextFactory.addFilter("change", new RequestChangeFilter());
+ templateContextFactory.addFilter("match", new MatchFilter());
+ templateContextFactory.addFilter("css", new CssClassNameFilter());
+ templateContextFactory.addFilter("js", new JavascriptFilter());
+ templateContextFactory.addFilter("parse", new ParserFilter(getCore(), templateContextFactory));
+ templateContextFactory.addFilter("unknown", new UnknownDateFilter(getL10n(), "View.Sone.Text.UnknownDate"));
+ templateContextFactory.addFilter("format", new FormatFilter());
+ templateContextFactory.addFilter("sort", new CollectionSortFilter());
+ templateContextFactory.addPlugin("getpage", new GetPagePlugin());
+ templateContextFactory.addPlugin("paginate", new PaginationPlugin());
+ templateContextFactory.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER);
+ templateContextFactory.addProvider(new ClassPathTemplateProvider());
+ templateContextFactory.addTemplateObject("formPassword", formPassword);
/* create notifications. */
- Template newSoneNotificationTemplate = templateFactory.createTemplate(createReader("/templates/notify/newSoneNotification.html"));
+ Template newSoneNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/newSoneNotification.html"));
newSoneNotification = new ListNotification<Sone>("new-sone-notification", "sones", newSoneNotificationTemplate);
- Template newPostNotificationTemplate = templateFactory.createTemplate(createReader("/templates/notify/newPostNotification.html"));
+ Template newPostNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/newPostNotification.html"));
newPostNotification = new ListNotification<Post>("new-post-notification", "posts", newPostNotificationTemplate);
- Template newReplyNotificationTemplate = templateFactory.createTemplate(createReader("/templates/notify/newReplyNotification.html"));
+ Template newReplyNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/newReplyNotification.html"));
newReplyNotification = new ListNotification<Reply>("new-replies-notification", "replies", newReplyNotificationTemplate);
- Template rescuingSonesTemplate = templateFactory.createTemplate(createReader("/templates/notify/rescuingSonesNotification.html"));
+ Template rescuingSonesTemplate = TemplateParser.parse(createReader("/templates/notify/rescuingSonesNotification.html"));
rescuingSonesNotification = new ListNotification<Sone>("sones-being-rescued-notification", "sones", rescuingSonesTemplate);
- Template sonesRescuedTemplate = templateFactory.createTemplate(createReader("/templates/notify/sonesRescuedNotification.html"));
+ Template sonesRescuedTemplate = TemplateParser.parse(createReader("/templates/notify/sonesRescuedNotification.html"));
sonesRescuedNotification = new ListNotification<Sone>("sones-rescued-notification", "sones", sonesRescuedTemplate);
- Template lockedSonesTemplate = templateFactory.createTemplate(createReader("/templates/notify/lockedSonesNotification.html"));
+ Template lockedSonesTemplate = TemplateParser.parse(createReader("/templates/notify/lockedSonesNotification.html"));
lockedSonesNotification = new ListNotification<Sone>("sones-locked-notification", "sones", lockedSonesTemplate);
- Template newVersionTemplate = templateFactory.createTemplate(createReader("/templates/notify/newVersionNotification.html"));
+ Template newVersionTemplate = TemplateParser.parse(createReader("/templates/notify/newVersionNotification.html"));
newVersionNotification = new TemplateNotification("new-version-notification", newVersionTemplate);
}
}
/**
+ * Returns the template context factory of the web interface.
+ *
+ * @return The template context factory
+ */
+ public TemplateContextFactory getTemplateContextFactory() {
+ return templateContextFactory;
+ }
+
+ /**
* Returns the current session, creating a new session if there is no
* current session.
*
* currently logged in
*/
public Sone getCurrentSone(ToadletContext toadletContext, boolean create) {
+ Set<Sone> localSones = getCore().getLocalSones();
+ if (localSones.size() == 1) {
+ return localSones.iterator().next();
+ }
return getCurrentSone(getCurrentSession(toadletContext, create));
}
*/
public void setFirstStart(boolean firstStart) {
if (firstStart) {
- Template firstStartNotificationTemplate = templateFactory.createTemplate(createReader("/templates/notify/firstStartNotification.html"));
+ Template firstStartNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/firstStartNotification.html"));
Notification firstStartNotification = new TemplateNotification("first-start-notification", firstStartNotificationTemplate);
notificationManager.addNotification(firstStartNotification);
}
*/
public void setNewConfig(boolean newConfig) {
if (newConfig && !hasFirstStartNotification()) {
- Template configNotReadNotificationTemplate = templateFactory.createTemplate(createReader("/templates/notify/configNotReadNotification.html"));
+ Template configNotReadNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/configNotReadNotification.html"));
Notification configNotReadNotification = new TemplateNotification("config-not-read-notification", configNotReadNotificationTemplate);
notificationManager.addNotification(configNotReadNotification);
}
registerToadlets();
/* notification templates. */
- Template startupNotificationTemplate = templateFactory.createTemplate(createReader("/templates/notify/startupNotification.html"));
+ Template startupNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/startupNotification.html"));
final TemplateNotification startupNotification = new TemplateNotification("startup-notification", startupNotificationTemplate);
notificationManager.addNotification(startupNotification);
}
}, "Sone Startup Notification Remover");
- Template wotMissingNotificationTemplate = templateFactory.createTemplate(createReader("/templates/notify/wotMissingNotification.html"));
+ Template wotMissingNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/wotMissingNotification.html"));
final TemplateNotification wotMissingNotification = new TemplateNotification("wot-missing-notification", wotMissingNotificationTemplate);
Ticker.getInstance().registerEvent(System.currentTimeMillis() + (15 * 1000), new Runnable() {
* Register all toadlets.
*/
private void registerToadlets() {
- Template emptyTemplate = templateFactory.createTemplate(new StringReader(""));
- Template loginTemplate = templateFactory.createTemplate(createReader("/templates/login.html"));
- Template indexTemplate = templateFactory.createTemplate(createReader("/templates/index.html"));
- Template knownSonesTemplate = templateFactory.createTemplate(createReader("/templates/knownSones.html"));
- Template createSoneTemplate = templateFactory.createTemplate(createReader("/templates/createSone.html"));
- Template createPostTemplate = templateFactory.createTemplate(createReader("/templates/createPost.html"));
- Template createReplyTemplate = templateFactory.createTemplate(createReader("/templates/createReply.html"));
- Template editProfileTemplate = templateFactory.createTemplate(createReader("/templates/editProfile.html"));
- Template viewSoneTemplate = templateFactory.createTemplate(createReader("/templates/viewSone.html"));
- Template viewPostTemplate = templateFactory.createTemplate(createReader("/templates/viewPost.html"));
- Template deletePostTemplate = templateFactory.createTemplate(createReader("/templates/deletePost.html"));
- Template deleteReplyTemplate = templateFactory.createTemplate(createReader("/templates/deleteReply.html"));
- Template deleteSoneTemplate = templateFactory.createTemplate(createReader("/templates/deleteSone.html"));
- Template imageBrowserTemplate = templateFactory.createTemplate(createReader("/templates/imageBrowser.html"));
- Template createAlbumTemplate = templateFactory.createTemplate(createReader("/templates/createAlbum.html"));
- Template noPermissionTemplate = templateFactory.createTemplate(createReader("/templates/noPermission.html"));
- Template optionsTemplate = templateFactory.createTemplate(createReader("/templates/options.html"));
- Template aboutTemplate = templateFactory.createTemplate(createReader("/templates/about.html"));
- Template postTemplate = templateFactory.createTemplate(createReader("/templates/include/viewPost.html"));
- Template replyTemplate = templateFactory.createTemplate(createReader("/templates/include/viewReply.html"));
+ Template emptyTemplate = TemplateParser.parse(new StringReader(""));
+ Template loginTemplate = TemplateParser.parse(createReader("/templates/login.html"));
+ Template indexTemplate = TemplateParser.parse(createReader("/templates/index.html"));
+ Template knownSonesTemplate = TemplateParser.parse(createReader("/templates/knownSones.html"));
+ Template createSoneTemplate = TemplateParser.parse(createReader("/templates/createSone.html"));
+ Template createPostTemplate = TemplateParser.parse(createReader("/templates/createPost.html"));
+ Template createReplyTemplate = TemplateParser.parse(createReader("/templates/createReply.html"));
+ Template bookmarksTemplate = TemplateParser.parse(createReader("/templates/bookmarks.html"));
+ Template editProfileTemplate = TemplateParser.parse(createReader("/templates/editProfile.html"));
+ Template editProfileFieldTemplate = TemplateParser.parse(createReader("/templates/editProfileField.html"));
+ Template deleteProfileFieldTemplate = TemplateParser.parse(createReader("/templates/deleteProfileField.html"));
+ Template viewSoneTemplate = TemplateParser.parse(createReader("/templates/viewSone.html"));
+ Template viewPostTemplate = TemplateParser.parse(createReader("/templates/viewPost.html"));
+ Template deletePostTemplate = TemplateParser.parse(createReader("/templates/deletePost.html"));
+ Template deleteReplyTemplate = TemplateParser.parse(createReader("/templates/deleteReply.html"));
+ Template deleteSoneTemplate = TemplateParser.parse(createReader("/templates/deleteSone.html"));
+ Template imageBrowserTemplate = TemplateParser.parse(createReader("/templates/imageBrowser.html"));
+ Template createAlbumTemplate = TemplateParser.parse(createReader("/templates/createAlbum.html"));
+ Template noPermissionTemplate = TemplateParser.parse(createReader("/templates/noPermission.html"));
+ Template optionsTemplate = TemplateParser.parse(createReader("/templates/options.html"));
+ Template aboutTemplate = TemplateParser.parse(createReader("/templates/about.html"));
+ Template invalidTemplate = TemplateParser.parse(createReader("/templates/invalid.html"));
+ Template postTemplate = TemplateParser.parse(createReader("/templates/include/viewPost.html"));
+ Template replyTemplate = TemplateParser.parse(createReader("/templates/include/viewReply.html"));
PageToadletFactory pageToadletFactory = new PageToadletFactory(sonePlugin.pluginRespirator().getHLSimpleClient(), "/Sone/");
pageToadlets.add(pageToadletFactory.createPageToadlet(new IndexPage(indexTemplate, this), "Index"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateSonePage(createSoneTemplate, this), "CreateSone"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new KnownSonesPage(knownSonesTemplate, this), "KnownSones"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new EditProfilePage(editProfileTemplate, this), "EditProfile"));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new EditProfileFieldPage(editProfileFieldTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteProfileFieldPage(deleteProfileFieldTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreatePostPage(createPostTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateReplyPage(createReplyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new ViewSonePage(viewSoneTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnfollowSonePage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new ImageBrowserPage(imageBrowserTemplate, this), "ImageBrowser"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateAlbumPage(createAlbumTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new MarkAsKnownPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new BookmarkPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new UnbookmarkPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new BookmarksPage(bookmarksTemplate, this), "Bookmarks"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteSonePage(deleteSoneTemplate, this), "DeleteSone"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new LoginPage(loginTemplate, this), "Login"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new LogoutPage(emptyTemplate, this), "Logout"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new AboutPage(aboutTemplate, this, SonePlugin.VERSION), "About"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("noPermission.html", noPermissionTemplate, "Page.NoPermission.Title", this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DismissNotificationPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("invalid.html", invalidTemplate, "Page.Invalid.Title", this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("css/", "/static/css/", "text/css")));
pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("javascript/", "/static/javascript/", "text/javascript")));
pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("images/", "/static/images/", "image/png")));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateReplyAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetReplyAjaxPage(this, replyTemplate)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetPostAjaxPage(this, postTemplate)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new MarkPostAsKnownPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new MarkReplyAsKnownPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new MarkAsKnownAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DeletePostAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteReplyAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new LockSoneAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlockSoneAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new FollowSoneAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnfollowSoneAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new LikeAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlikeAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetLikesAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new BookmarkAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new UnbookmarkAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new EditProfileFieldAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteProfileFieldAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new MoveProfileFieldAjaxPage(this)));
ToadletContainer toadletContainer = sonePlugin.pluginRespirator().getToadletContainer();
toadletContainer.getPageMaker().addNavigationCategory("/Sone/index.html", "Navigation.Menu.Name", "Navigation.Menu.Tooltip", sonePlugin);
try {
return new InputStreamReader(getClass().getResourceAsStream(resourceName), "UTF-8");
} catch (UnsupportedEncodingException uee1) {
+ System.out.println(" fail.");
return null;
}
}
* {@inheritDoc}
*/
@Override
- public void updateFound(Version version, long releaseTime) {
- newVersionNotification.set("version", version);
- newVersionNotification.set("releaseTime", releaseTime);
+ public void updateFound(Version version, long releaseTime, long latestEdition) {
+ newVersionNotification.getTemplateContext().set("latestVersion", version);
+ newVersionNotification.getTemplateContext().set("latestEdition", latestEdition);
+ newVersionNotification.getTemplateContext().set("releaseTime", releaseTime);
notificationManager.addNotification(newVersionNotification);
}
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
- private class ClassPathTemplateProvider implements TemplateProvider {
+ private class ClassPathTemplateProvider implements Provider {
- /** The template factory. */
- @SuppressWarnings("hiding")
- private final TemplateFactory templateFactory;
+ /** Cache for templates. */
+ private final Cache<String, Template> templateCache = new MemoryCache<String, Template>(new ValueRetriever<String, Template>() {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public CacheItem<Template> retrieve(String key) throws CacheException {
+ Template template = findTemplate(key);
+ if (template != null) {
+ return new DefaultCacheItem<Template>(template);
+ }
+ return null;
+ }
+ });
/**
- * Creates a new template provider that locates templates on the
- * classpath.
- *
- * @param templateFactory
- * The template factory to create the templates
+ * {@inheritDoc}
*/
- public ClassPathTemplateProvider(TemplateFactory templateFactory) {
- this.templateFactory = templateFactory;
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public Template getTemplate(TemplateContext templateContext, String templateName) {
+ try {
+ return templateCache.get(templateName);
+ } catch (CacheException ce1) {
+ logger.log(Level.WARNING, "Could not get template for " + templateName + "!", ce1);
+ return null;
+ }
}
/**
- * {@inheritDoc}
+ * Locates a template in the class path.
+ *
+ * @param templateName
+ * The name of the template to load
+ * @return The loaded template, or {@code null} if no template could be
+ * found
*/
- @Override
@SuppressWarnings("synthetic-access")
- public Template getTemplate(String templateName) {
+ private Template findTemplate(String templateName) {
Reader templateReader = createReader("/templates/" + templateName);
if (templateReader == null) {
return null;
}
- Template template = templateFactory.createTemplate(templateReader);
+ Template template = null;
try {
- template.parse();
+ template = TemplateParser.parse(templateReader);
} catch (TemplateException te1) {
logger.log(Level.WARNING, "Could not parse template “" + templateName + "” for inclusion!", te1);
}
--- /dev/null
+/*
+ * Sone - BookmarkAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user bookmark a post.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class BookmarkAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new bookmark AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public BookmarkAjaxPage(WebInterface webInterface) {
+ super("bookmark.ajax", webInterface);
+ }
+
+ //
+ // JSONPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ String id = request.getHttpRequest().getParam("post", null);
+ if ((id == null) || (id.length() == 0)) {
+ return createErrorJsonObject("invalid-post-id");
+ }
+ webInterface.getCore().bookmarkPost(id);
+ return createSuccessJsonObject();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
+}
}
String recipientId = request.getHttpRequest().getParam("recipient");
Sone recipient = webInterface.getCore().getSone(recipientId, false);
+ String senderId = request.getHttpRequest().getParam("sender");
+ Sone sender = webInterface.getCore().getLocalSone(senderId, false);
+ if (sender == null) {
+ sender = sone;
+ }
String text = request.getHttpRequest().getParam("text");
if ((text == null) || (text.trim().length() == 0)) {
return createErrorJsonObject("text-required");
}
- Post newPost = webInterface.getCore().createPost(sone, recipient, text);
- return createSuccessJsonObject().put("postId", newPost.getId());
+ Post newPost = webInterface.getCore().createPost(sender, recipient, text);
+ return createSuccessJsonObject().put("postId", newPost.getId()).put("sone", sender.getId()).put("recipient", (newPost.getRecipient() != null) ? newPost.getRecipient().getId() : null);
}
}
protected JsonObject createJsonObject(Request request) {
String postId = request.getHttpRequest().getParam("post");
String text = request.getHttpRequest().getParam("text").trim();
- Sone currentSone = getCurrentSone(request.getToadletContext());
- if (currentSone == null) {
- return createErrorJsonObject("auth-required");
+ String senderId = request.getHttpRequest().getParam("sender");
+ Sone sender = webInterface.getCore().getLocalSone(senderId, false);
+ if (sender == null) {
+ sender = getCurrentSone(request.getToadletContext());
}
Post post = webInterface.getCore().getPost(postId);
if ((post == null) || (post.getSone() == null)) {
return createErrorJsonObject("invalid-post-id");
}
- Reply reply = webInterface.getCore().createReply(currentSone, post, text);
- return createSuccessJsonObject().put("reply", reply.getId());
+ Reply reply = webInterface.getCore().createReply(sender, post, text);
+ return createSuccessJsonObject().put("reply", reply.getId()).put("sone", sender.getId());
}
}
package net.pterodactylus.sone.web.ajax;
import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.json.JsonObject;
protected JsonObject createJsonObject(Request request) {
String postId = request.getHttpRequest().getParam("post");
Post post = webInterface.getCore().getPost(postId, false);
- Sone currentSone = getCurrentSone(request.getToadletContext());
if ((post == null) || (post.getSone() == null)) {
return createErrorJsonObject("invalid-post-id");
}
- if (currentSone == null) {
- return createErrorJsonObject("auth-required");
- }
- if (!post.getSone().equals(currentSone)) {
+ if (!webInterface.getCore().isLocalSone(post.getSone())) {
return createErrorJsonObject("not-authorized");
}
webInterface.getCore().deletePost(post);
--- /dev/null
+/*
+ * Sone - DeleteProfileFieldAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Profile.Field;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user delete a profile field.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeleteProfileFieldAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new “delete profile field” AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DeleteProfileFieldAjaxPage(WebInterface webInterface) {
+ super("deleteProfileField.ajax", webInterface);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ String fieldId = request.getHttpRequest().getParam("field");
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Profile profile = currentSone.getProfile();
+ Field field = profile.getFieldById(fieldId);
+ if (field == null) {
+ return createErrorJsonObject("invalid-field-id");
+ }
+ profile.removeField(field);
+ currentSone.setProfile(profile);
+ webInterface.getCore().saveSone(currentSone);
+ return createSuccessJsonObject().put("field", new JsonObject().put("id", field.getId()));
+ }
+
+}
package net.pterodactylus.sone.web.ajax;
import net.pterodactylus.sone.data.Reply;
-import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.json.JsonObject;
protected JsonObject createJsonObject(Request request) {
String replyId = request.getHttpRequest().getParam("reply");
Reply reply = webInterface.getCore().getReply(replyId);
- Sone currentSone = getCurrentSone(request.getToadletContext());
if (reply == null) {
return createErrorJsonObject("invalid-reply-id");
}
- if (currentSone == null) {
- return createErrorJsonObject("auth-required");
- }
- if (!reply.getSone().equals(currentSone)) {
+ if (!webInterface.getCore().isLocalSone(reply.getSone())) {
return createErrorJsonObject("not-authorized");
}
webInterface.getCore().deleteReply(reply);
return createSuccessJsonObject();
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
}
--- /dev/null
+/*
+ * Sone - TrustAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.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.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user distrust a Sone.
+ *
+ * @see Core#distrustSone(Sone, Sone)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DistrustAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new “distrust Sone” AJAX handler.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DistrustAjaxPage(WebInterface webInterface) {
+ super("distrustSone.ajax", webInterface);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ Sone currentSone = getCurrentSone(request.getToadletContext(), false);
+ if (currentSone == null) {
+ return createErrorJsonObject("auth-required");
+ }
+ String soneId = request.getHttpRequest().getParam("sone");
+ Sone sone = webInterface.getCore().getSone(soneId, false);
+ if (sone == null) {
+ 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());
+ }
+
+}
--- /dev/null
+/*
+ * Sone - EditProfileFieldAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Profile.Field;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user rename a profile field.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditProfileFieldAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new “edit profile field” AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public EditProfileFieldAjaxPage(WebInterface webInterface) {
+ super("editProfileField.ajax", webInterface);
+ }
+
+ //
+ // JSONPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ String fieldId = request.getHttpRequest().getParam("field");
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Profile profile = currentSone.getProfile();
+ Field field = profile.getFieldById(fieldId);
+ if (field == null) {
+ return createErrorJsonObject("invalid-field-id");
+ }
+ String name = request.getHttpRequest().getParam("name", "").trim();
+ if (name.length() == 0) {
+ return createErrorJsonObject("invalid-parameter-name");
+ }
+ Field existingField = profile.getFieldByName(name);
+ if ((existingField != null) && !existingField.equals(field)) {
+ return createErrorJsonObject("duplicate-field-name");
+ }
+ field.setName(name);
+ currentSone.setProfile(profile);
+ return createSuccessJsonObject();
+ }
+
+}
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.io.Closer;
import net.pterodactylus.util.json.JsonObject;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.template.TemplateException;
/**
jsonPost.put("recipient", (post.getRecipient() == null) ? null : post.getRecipient().getId());
jsonPost.put("time", post.getTime());
StringWriter stringWriter = new StringWriter();
- DataProvider dataProvider = postTemplate.createDataProvider();
- dataProvider.setData("post", post);
- dataProvider.setData("currentSone", currentSone);
+ TemplateContext templateContext = webInterface.getTemplateContextFactory().createTemplateContext();
+ templateContext.set("post", post);
+ templateContext.set("currentSone", currentSone);
+ templateContext.set("localSones", webInterface.getCore().getLocalSones());
try {
- postTemplate.render(dataProvider, stringWriter);
+ postTemplate.render(templateContext, stringWriter);
} catch (TemplateException te1) {
/* TODO - shouldn’t happen. */
} finally {
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.io.Closer;
import net.pterodactylus.util.json.JsonObject;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.template.TemplateException;
/**
jsonReply.put("soneId", reply.getSone().getId());
jsonReply.put("time", reply.getTime());
StringWriter stringWriter = new StringWriter();
- DataProvider dataProvider = replyTemplate.createDataProvider();
- dataProvider.setData("reply", reply);
- dataProvider.setData("currentSone", currentSone);
+ TemplateContext templateContext = webInterface.getTemplateContextFactory().createTemplateContext();
+ templateContext.set("reply", reply);
+ templateContext.set("currentSone", currentSone);
try {
- replyTemplate.render(dataProvider, stringWriter);
+ replyTemplate.render(templateContext, stringWriter);
} catch (TemplateException te1) {
/* TODO - shouldn’t happen. */
} finally {
package net.pterodactylus.sone.web.ajax;
+import java.io.IOException;
+import java.io.StringWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import net.pterodactylus.util.json.JsonArray;
import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.notify.Notification;
+import net.pterodactylus.util.notify.TemplateNotification;
+import net.pterodactylus.util.template.TemplateContext;
/**
* The “get status” AJAX handler returns all information that is necessary to
Set<Post> newPosts = webInterface.getNewPosts();
JsonArray jsonPosts = new JsonArray();
for (Post post : newPosts) {
- jsonPosts.add(post.getId());
+ JsonObject jsonPost = new JsonObject();
+ jsonPost.put("id", post.getId());
+ jsonPost.put("sone", post.getSone().getId());
+ jsonPost.put("recipient", (post.getRecipient() != null) ? post.getRecipient().getId() : null);
+ jsonPost.put("time", post.getTime());
+ jsonPosts.add(jsonPost);
}
/* load new replies. */
Set<Reply> newReplies = webInterface.getNewReplies();
JsonArray jsonReplies = new JsonArray();
for (Reply reply : newReplies) {
- jsonReplies.add(reply.getId());
+ JsonObject jsonReply = new JsonObject();
+ jsonReply.put("id", reply.getId());
+ jsonReply.put("sone", reply.getSone().getId());
+ jsonReply.put("post", reply.getPost().getId());
+ jsonReply.put("postSone", reply.getPost().getSone().getId());
+ jsonReplies.add(jsonReply);
}
return createSuccessJsonObject().put("sones", jsonSones).put("notifications", jsonNotifications).put("removedNotifications", jsonRemovedNotifications).put("newPosts", jsonPosts).put("newReplies", jsonReplies);
}
return false;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
//
// PRIVATE METHODS
//
jsonSone.put("status", webInterface.getCore().getSoneStatus(sone).name());
jsonSone.put("modified", webInterface.getCore().isModifiedSone(sone));
jsonSone.put("locked", webInterface.getCore().isLocked(sone));
+ jsonSone.put("lastUpdatedUnknown", sone.getTime() == 0);
synchronized (dateFormat) {
jsonSone.put("lastUpdated", dateFormat.format(new Date(sone.getTime())));
}
* The notification to create a JSON object
* @return The JSON object
*/
- private static JsonObject createJsonNotification(Notification notification) {
+ private JsonObject createJsonNotification(Notification notification) {
JsonObject jsonNotification = new JsonObject();
jsonNotification.put("id", notification.getId());
- jsonNotification.put("text", notification.toString());
+ StringWriter notificationWriter = new StringWriter();
+ try {
+ if (notification instanceof TemplateNotification) {
+ TemplateContext templateContext = webInterface.getTemplateContextFactory().createTemplateContext().mergeContext(((TemplateNotification) notification).getTemplateContext());
+ templateContext.set("notification", notification);
+ ((TemplateNotification) notification).render(templateContext, notificationWriter);
+ } else {
+ notification.render(notificationWriter);
+ }
+ } catch (IOException ioe1) {
+ /* StringWriter never throws, ignore. */
+ }
+ jsonNotification.put("text", notificationWriter.toString());
jsonNotification.put("createdTime", notification.getCreatedTime());
jsonNotification.put("lastUpdatedTime", notification.getLastUpdatedTime());
jsonNotification.put("dismissable", notification.isDismissable());
return false;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
}
return true;
}
+ /**
+ * Returns whether this page requires the user to be logged in.
+ *
+ * @return {@code true} if the user needs to be logged in to use this page,
+ * {@code false} otherwise
+ */
+ protected boolean requiresLogin() {
+ return true;
+ }
+
//
// PROTECTED METHODS
//
return new Response(401, "Not authorized", "application/json", JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
}
}
+ if (requiresLogin()) {
+ if (getCurrentSone(request.getToadletContext(), false) == null) {
+ return new Response(401, "Not authorized", "application/json", JsonUtils.format(createErrorJsonObject("auth-required")));
+ }
+ }
JsonObject jsonObject = createJsonObject(request);
return new Response(200, "OK", "application/json", JsonUtils.format(jsonObject));
}
return createSuccessJsonObject();
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
}
--- /dev/null
+/*
+ * Sone - MarkAsKnownAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user mark a number of {@link Sone}s, {@link Post}s,
+ * or {@link Reply}s as known.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class MarkAsKnownAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new “mark as known” AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public MarkAsKnownAjaxPage(WebInterface webInterface) {
+ super("markAsKnown.ajax", webInterface);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ String type = request.getHttpRequest().getParam("type");
+ if (!type.equals("sone") && !type.equals("post") && !type.equals("reply")) {
+ return createErrorJsonObject("invalid-type");
+ }
+ String[] ids = request.getHttpRequest().getParam("id").split(" ");
+ Core core = webInterface.getCore();
+ for (String id : ids) {
+ if (type.equals("post")) {
+ Post post = core.getPost(id, false);
+ if (post == null) {
+ continue;
+ }
+ core.markPostKnown(post);
+ } else if (type.equals("reply")) {
+ Reply reply = core.getReply(id, false);
+ if (reply == null) {
+ continue;
+ }
+ core.markReplyKnown(reply);
+ } else if (type.equals("sone")) {
+ Sone sone = core.getSone(id, false);
+ if (sone == null) {
+ continue;
+ }
+ core.markSoneKnown(sone);
+ }
+ }
+ return createSuccessJsonObject();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
+}
+++ /dev/null
-/*
- * Sone - MarkPostAsKnownPage.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.ajax;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.web.WebInterface;
-import net.pterodactylus.util.json.JsonObject;
-
-/**
- * AJAX handler that marks a {@link Post} as known.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class MarkPostAsKnownPage extends JsonPage {
-
- /**
- * Creates a new “mark post as known” AJAX handler.
- *
- * @param webInterface
- * The Sone web interface
- */
- public MarkPostAsKnownPage(WebInterface webInterface) {
- super("markPostAsKnown.ajax", webInterface);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected JsonObject createJsonObject(Request request) {
- String postId = request.getHttpRequest().getParam("post");
- Post post = webInterface.getCore().getPost(postId, false);
- if (post == null) {
- return createErrorJsonObject("invalid-post-id");
- }
- webInterface.getCore().markPostKnown(post);
- return createSuccessJsonObject();
- }
-
-}
+++ /dev/null
-/*
- * Sone - MarkPostAsKnownPage.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.ajax;
-
-import net.pterodactylus.sone.data.Reply;
-import net.pterodactylus.sone.web.WebInterface;
-import net.pterodactylus.util.json.JsonObject;
-
-/**
- * AJAX handler that marks a {@link Reply} as known.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class MarkReplyAsKnownPage extends JsonPage {
-
- /**
- * Creates a new “mark reply as known” AJAX handler.
- *
- * @param webInterface
- * The Sone web interface
- */
- public MarkReplyAsKnownPage(WebInterface webInterface) {
- super("markReplyAsKnown.ajax", webInterface);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected JsonObject createJsonObject(Request request) {
- String replyId = request.getHttpRequest().getParam("reply");
- Reply reply = webInterface.getCore().getReply(replyId, false);
- if (reply == null) {
- return createErrorJsonObject("invalid-reply-id");
- }
- webInterface.getCore().markReplyKnown(reply);
- return createSuccessJsonObject();
- }
-
-}
--- /dev/null
+/*
+ * Sone - MoveProfileFieldAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Profile.Field;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user move a profile field up or down.
+ *
+ * @see Profile#moveFieldUp(Field)
+ * @see Profile#moveFieldDown(Field)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class MoveProfileFieldAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new “move profile field” AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public MoveProfileFieldAjaxPage(WebInterface webInterface) {
+ super("moveProfileField.ajax", webInterface);
+ }
+
+ //
+ // JSONPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ Profile profile = currentSone.getProfile();
+ String fieldId = request.getHttpRequest().getParam("field");
+ Field field = profile.getFieldById(fieldId);
+ if (field == null) {
+ return createErrorJsonObject("invalid-field-id");
+ }
+ String direction = request.getHttpRequest().getParam("direction");
+ try {
+ if ("up".equals(direction)) {
+ profile.moveFieldUp(field);
+ } else if ("down".equals(direction)) {
+ profile.moveFieldDown(field);
+ } else {
+ return createErrorJsonObject("invalid-direction");
+ }
+ } catch (IllegalArgumentException iae1) {
+ return createErrorJsonObject("not-possible");
+ }
+ currentSone.setProfile(profile);
+ webInterface.getCore().saveSone(currentSone);
+ return createSuccessJsonObject();
+ }
+
+}
--- /dev/null
+/*
+ * Sone - TrustAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.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.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user trust a Sone.
+ *
+ * @see Core#trustSone(Sone, Sone)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class TrustAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new “trust Sone” AJAX handler.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public TrustAjaxPage(WebInterface webInterface) {
+ super("trustSone.ajax", webInterface);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ Sone currentSone = getCurrentSone(request.getToadletContext(), false);
+ if (currentSone == null) {
+ return createErrorJsonObject("auth-required");
+ }
+ String soneId = request.getHttpRequest().getParam("sone");
+ Sone sone = webInterface.getCore().getSone(soneId, false);
+ if (sone == null) {
+ 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());
+ }
+
+}
--- /dev/null
+/*
+ * Sone - BookmarkAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user unbookmark a post.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UnbookmarkAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new unbookmark AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public UnbookmarkAjaxPage(WebInterface webInterface) {
+ super("unbookmark.ajax", webInterface);
+ }
+
+ //
+ // JSONPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ String id = request.getHttpRequest().getParam("post", null);
+ if ((id == null) || (id.length() == 0)) {
+ return createErrorJsonObject("invalid-post-id");
+ }
+ webInterface.getCore().unbookmarkPost(id);
+ return createSuccessJsonObject();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
+}
return createSuccessJsonObject();
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean requiresLogin() {
+ return false;
+ }
+
}
--- /dev/null
+/*
+ * Sone - TrustAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.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.util.json.JsonObject;
+
+/**
+ * AJAX page that lets the user untrust a Sone.
+ *
+ * @see Core#untrustSone(Sone, Sone)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UntrustAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new “untrust Sone” AJAX handler.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public UntrustAjaxPage(WebInterface webInterface) {
+ super("untrustSone.ajax", webInterface);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(Request request) {
+ Sone currentSone = getCurrentSone(request.getToadletContext(), false);
+ if (currentSone == null) {
+ return createErrorJsonObject("auth-required");
+ }
+ String soneId = request.getHttpRequest().getParam("sone");
+ Sone sone = webInterface.getCore().getSone(soneId, false);
+ if (sone == null) {
+ 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());
+ }
+
+}
import net.pterodactylus.sone.web.page.Page.Request.Method;
import net.pterodactylus.util.logging.Logging;
-import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateContextFactory;
import freenet.clients.http.LinkEnabledCallback;
import freenet.clients.http.PageMaker;
import freenet.clients.http.PageNode;
/** The path of the page. */
private final String path;
+ /** The template context factory. */
+ private final TemplateContextFactory templateContextFactory;
+
/** The template to render. */
private final Template template;
*
* @param path
* The path of the page
+ * @param templateContextFactory
+ * The template context factory
* @param template
* The template to render
* @param l10n
* The target to redirect to if a POST request does not contain
* the correct form password
*/
- public TemplatePage(String path, Template template, BaseL10n l10n, String pageTitleKey, String invalidFormPasswordRedirectTarget) {
+ public TemplatePage(String path, TemplateContextFactory templateContextFactory, Template template, BaseL10n l10n, String pageTitleKey, String invalidFormPasswordRedirectTarget) {
this.path = path;
+ this.templateContextFactory = templateContextFactory;
this.template = template;
this.l10n = l10n;
this.pageTitleKey = pageTitleKey;
pageNode.addForwardLink("icon", shortcutIcon);
}
- DataProvider dataProvider = template.createDataProvider();
+ TemplateContext templateContext = templateContextFactory.createTemplateContext();
+ templateContext.mergeContext(template.getInitialContext());
try {
long start = System.nanoTime();
- processTemplate(request, dataProvider);
+ processTemplate(request, templateContext);
long finish = System.nanoTime();
logger.log(Level.FINEST, "Template was rendered in " + ((finish - start) / 1000) / 1000.0 + "ms.");
} catch (RedirectException re1) {
}
StringWriter stringWriter = new StringWriter();
- template.render(dataProvider, stringWriter);
+ template.render(templateContext, stringWriter);
pageNode.content.addChild("%", stringWriter.toString());
- postProcess(request, dataProvider);
+ postProcess(request, templateContext);
return new Response(200, "OK", "text/html", pageNode.outer.generate());
}
*
* @param request
* The request that is rendered
- * @param dataProvider
- * The data provider to set variables in
+ * @param templateContext
+ * The template context to set variables in
* @throws RedirectException
* if the processing page wants to redirect after processing
*/
- protected void processTemplate(Request request, DataProvider dataProvider) throws RedirectException {
+ protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
/* do nothing. */
}
/**
* This method will be called after
- * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, DataProvider)}
+ * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, TemplateContext)}
* has processed the template and the template was rendered. This method
* will not be called if
- * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, DataProvider)}
+ * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, TemplateContext)}
* throws a {@link RedirectException}!
*
* @param request
* The request being processed
- * @param dataProvider
- * The data provider that supplied the rendered data
+ * @param templateContext
+ * The template context that supplied the rendered data
*/
- protected void postProcess(Request request, DataProvider dataProvider) {
+ protected void postProcess(Request request, TemplateContext templateContext) {
/* do nothing. */
}
/**
* Exception that can be thrown to signal that a subclassed {@link Page}
* wants to redirect the user during the
- * {@link TemplatePage#processTemplate(net.pterodactylus.sone.web.page.Page.Request, DataProvider)}
+ * {@link TemplatePage#processTemplate(net.pterodactylus.sone.web.page.Page.Request, TemplateContext)}
* method call.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
Navigation.Menu.Item.CreateSone.Tooltip=Create a new Sone
Navigation.Menu.Item.KnownSones.Name=Known Sones
Navigation.Menu.Item.KnownSones.Tooltip=Shows all known Sones
+Navigation.Menu.Item.Bookmarks.Name=Bookmarks
+Navigation.Menu.Item.Bookmarks.Tooltip=Show bookmarked posts
Navigation.Menu.Item.EditProfile.Name=Edit Profile
Navigation.Menu.Item.EditProfile.Tooltip=Edit the Profile of your Sone
Navigation.Menu.Item.ImageBrowser.Name=Images
Page.Options.Page.Description=These options influence the runtime behaviour of the Sone plugin.
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.Section.TrustOptions.Title=Trust Settings
+Page.Options.Option.PositiveTrust.Description=The amount of positive trust you want to assign to other Sones by clicking the checkmark below a post or reply.
+Page.Options.Option.NegativeTrust.Description=The amount of trust you want to assign to other Sones by clicking the red X below a post or reply. This value should be negative.
+Page.Options.Option.TrustComment.Description=The comment that will be set in the web of trust for any trust you assign from Sone.
Page.Options.Section.RescueOptions.Title=Rescue Settings
Page.Options.Option.SoneRescueMode.Description=Try to rescue your Sones at the next start of the Sone plugin. This will read your all your old Sones from Freenet and ignore any disappearing postings and replies. You have to unlock your local Sones after they have been restored and you have to manually disable the rescue mode once you are satisfied with what has been restored!
Page.Options.Section.Cleaning.Title=Clean Up
Page.Index.Title=Your Sone - Sone
Page.Index.Label.Text=Post text:
+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.EditProfile.Birthday.Label.Day=Day:
Page.EditProfile.Birthday.Label.Month=Month:
Page.EditProfile.Birthday.Label.Year=Year:
-Page.EditProfile.Page.Status.Changed=Your changes have been saved and will be inserted shortly.
+Page.EditProfile.Fields.Title=Custom Fields
+Page.EditProfile.Fields.Description=Here you can enter custom fields into your profile. These fields can contain anything you want and be as terse or as verbose as you wish. Just remember that when it comes to anonymity, sometimes less is more.
+Page.EditProfile.Fields.Button.Edit=edit
+Page.EditProfile.Fields.Button.MoveUp=move up
+Page.EditProfile.Fields.Button.MoveDown=move down
+Page.EditProfile.Fields.Button.Delete=delete
+Page.EditProfile.Fields.Button.ReallyDelete=really delete
+Page.EditProfile.Fields.AddField.Title=Add Field
+Page.EditProfile.Fields.AddField.Label.Name=Name:
+Page.EditProfile.Fields.AddField.Button.AddField=Add Field
Page.EditProfile.Button.Save=Save Profile
+Page.EditProfile.Error.DuplicateFieldName=The field name “{fieldName}” does already exist.
+
+Page.EditProfileField.Title=Edit Profile Field - Sone
+Page.EditProfileField.Page.Title=Edit Profile Field
+Page.EditProfileField.Text=Enter a new name for this profile field.
+Page.EditProfileField.Error.DuplicateFieldName=The field name you entered does already exist.
+Page.EditProfileField.Button.Save=Change
+Page.EditProfileField.Button.Reset=Revert to old name
+Page.EditProfileField.Button.Cancel=Do not change name
+
+Page.DeleteProfileField.Title=Delete Profile Field - Sone
+Page.DeleteProfileField.Page.Title=Delete Profile Field
+Page.DeleteProfileField.Text=Do you really want to delete this profile field?
+Page.DeleteProfileField.Button.Yes=Yes, delete
+Page.DeleteProfileField.Button.No=No, do not delete
Page.CreatePost.Title=Create Post - Sone
Page.CreatePost.Page.Title=Create Post
Page.ViewSone.WriteAMessage=You can write a message to this Sone here. Please note that everybody will be able to read this message!
Page.ViewSone.PostList.Title=Posts by {sone}
Page.ViewSone.PostList.Text.NoPostYet=This Sone has not yet posted anything.
+Page.ViewSone.Profile.Title=Profile
+Page.ViewSone.Profile.Label.Name=Name
Page.ViewPost.Title=View Post - Sone
Page.ViewPost.Page.Title=View Post by {sone}
Page.CreateAlbum.Page.Title=Create Album
Page.CreateAlbum.Error.NameMissing=You seem to have forgotten to enter a name for your new album.
+Page.Trust.Title=Trust Sone - Sone
+
+Page.Distrust.Title=Distrust Sone - Sone
+
+Page.Untrust.Title=Untrust Sone - Sone
+
+Page.MarkAsKnown.Title=Mark as Known - Sone
+
+Page.Bookmark.Title=Bookmark - Sone
+Page.Unbookmark.Title=Remove Bookmark - Sone
+Page.Bookmarks.Title=Bookmarks - Sone
+Page.Bookmarks.Page.Title=Bookmarks
+Page.Bookmarks.Text.NoBookmarks=You don’t have any bookmarks defined right now. You can bookmark posts by clicking the star below the post.
+Page.Bookmarks.Text.PostsNotLoaded=Some of your bookmarked posts have not been shown because they could not be loaded. This can happen if you restarted Sone recently or if the originating Sone has deleted the post. If you are reasonable sure that these posts do not exist anymore, you can {link}unbookmark them{/link}.
+
Page.NoPermission.Title=Unauthorized Access - Sone
Page.NoPermission.Page.Title=Unauthorized Access
Page.NoPermission.Text.NoPermission=You tried to do something that you do not have sufficient authorization for. Please refrain from such actions in the future or we will be forced to take counter-measures!
Page.Logout.Title=Logout - Sone
+Page.Invalid.Title=Invalid Action Performed
+Page.Invalid.Page.Title=Invalid Action Performed
+Page.Invalid.Text=An invalid action was performed, or the action was valid but the parameters were not. Please go back to the {link}index page{/link} and try again. If the error persists you have probably found a bug.
+
View.CreateSone.Text.WotIdentityRequired=To create a Sone you need an identity from the {link}Web of Trust plugin{/link}.
View.CreateSone.Select.Default=Select an identity
View.CreateSone.Text.NoIdentities=You do not have any Web of Trust identities. Please head over to the {link}Web of Trust plugin{/link} and create an identity.
View.CreateSone.Text.Error.NoIdentity=You have not selected an identity.
View.Sone.Label.LastUpdate=Last update:
+View.Sone.Text.UnknownDate=unknown
View.Sone.Button.UnlockSone=unlock
View.Sone.Button.UnlockSone.Tooltip=Allow this Sone to be inserted now
View.Sone.Button.LockSone=lock
View.Sone.Status.Inserting=This Sone is currently being inserted.
View.Post.UnknownAuthor=(unknown)
+View.Post.Bookmarks.PostIsBookmarked=Post is bookmarked, click to remove from bookmarks
+View.Post.Bookmarks.PostIsNotBookmarked=Post is not bookmarked, click to bookmark
View.Post.DeleteLink=Delete
View.Post.SendReply=Post Reply!
View.Post.Reply.DeleteLink=Delete
View.Post.LikeLink=Like
View.Post.UnlikeLink=Unlike
+View.Post.ShowSource=Toggle Parser
+
+View.UpdateStatus.Text.ChooseSenderIdentity=Choose the sender identity
+
+View.Trust.Tooltip.Trust=Trust this person
+View.Trust.Tooltip.Distrust=Assign negative trust to this person
+View.Trust.Tooltip.Untrust=Remove your trust assignment for this person
View.CreateAlbum.Title=Create Album
View.CreateAlbum.Label.Name=Name:
WebInterface.DefaultText.BirthDay=Day
WebInterface.DefaultText.BirthMonth=Month
WebInterface.DefaultText.BirthYear=Year
+WebInterface.DefaultText.FieldName=Field name
WebInterface.DefaultText.Option.InsertionDelay=Time to wait after a Sone is modified before insert (in seconds)
WebInterface.Confirmation.DeletePostButton=Yes, delete!
WebInterface.Confirmation.DeleteReplyButton=Yes, delete!
Notification.NewSone.Text=New Sones have been discovered:
Notification.NewPost.ShortText=New posts have been discovered.
Notification.NewPost.Text=New posts have been discovered by the following Sones:
+Notification.NewPost.Button.MarkRead=Mark as read
Notification.NewReply.ShortText=New replies have been discovered.
Notification.NewReply.Text=New replies have been discovered by the following Sones:
Notification.SoneIsBeingRescued.Text=The following Sones are currently being rescued:
Notification.SoneRescued.Text=The following Sones have been rescued:
Notification.SoneRescued.Text.RememberToUnlock=Please remember to control the posts and replies you have given and don’t forget to unlock your Sones!
Notification.LockedSones.Text=The following Sones have been locked for more than 5 minutes. Please check if you really want to keep these Sones locked:
-Notification.NewVersion.Text=A new version of the Sone plugin was found: Version {version}.
+Notification.NewVersion.Text=Version {version} of the Sone plugin was found. Download it from USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}!
/* Sone Main CSS File */
/* first, override some fproxy rules. */
-#sone .post .reply div,#sone .post .time,#sone .post .delete,#sone .post .show-reply-form, input[type=text], textarea {
+#sone div, #sone span, #sone .post .time,#sone .post .delete,#sone .post .show-reply-form, input[type=text], textarea {
font: inherit;
}
outline: none;
}
+input[type=text].short {
+ width: 25em;
+}
+
textarea {
height: 4em;
}
+#sone button {
+ background-color: #ddd;
+ border-width: 1px;
+ color: #444;
+ padding: 0.5ex 1.5ex;
+}
+
#sone form {
margin: 0px;
}
+#sone select {
+ color: #444;
+ padding: 0.5ex 1.5ex;
+}
+
/* now for the real stuff. */
#sone {
min-height: 3.5ex;
}
+#sone #notification-area .notification button {
+ margin-left: 1ex;
+}
+
#sone #notification-area .notification .dismiss {
float: right;
}
margin-left: 1ex;
}
+#sone #notification-area .notification .mark-as-read {
+ float: right;
+}
+
#sone #plugin-warning {
border: solid 0.5em red;
padding: 0.5em;
padding-left: 1ex;
}
-#sone #update-status label {
+#sone #update-status {
+ margin-bottom: 1em;
+}
+
+#sone #update-status label, #sone #post-message label {
display: none;
}
float: right;
}
+#sone #update-status .select-sender, #sone .create-reply .select-sender, #sone #post-message .select-sender {
+ display: none;
+}
+
+#sone #update-status .select-sender button, #sone #post-message .select-sender button {
+ display: inline;
+ float: left;
+}
+
#sone .nice-name {
font-weight: bold;
}
font-weight: bold;
}
-#sone .post .text {
+#sone .post .text, #sone .post .raw-text {
display: inline;
white-space: pre-wrap;
}
+#sone .post .text.hidden, #sone .post .raw-text.hidden {
+ display: none;
+}
+
#sone .post .status-line {
margin-top: 0.5ex;
font-size: 85%;
}
+#sone .separator {
+ font: inherit;
+ color: rgb(28, 131, 191);
+}
+
+#sone .show-source, #sone .show-reply-source {
+ display: inline;
+}
+
+#sone .post .bookmarks {
+ display: inline;
+ color: rgb(28, 131, 191);
+}
+
+#sone .post .bookmark, #sone .post .unbookmark {
+ display: inline;
+ font: inherit;
+ margin: 0px;
+}
+
#sone .post .time {
display: inline;
color: #666;
}
-#sone .post .delete, #sone .post .likes, #sone .post .like, #sone .post .unlike {
+#sone .post .delete, #sone .post .likes, #sone .post .like, #sone .post .unlike, #sone .post .trust, #sone .post .distrust, #sone .post .untrust {
display: inline;
font: inherit;
+ margin: 0px;
}
#sone .post .likes.hidden {
display: none;
}
-#sone .post .like.hidden, #sone .post .unlike.hidden {
+#sone .post .like.hidden, #sone .post .unlike.hidden, #sone .post .trust.hidden, #sone .post .distrust.hidden, #sone .post .untrust.hidden, #sone .post .bookmark.hidden, #sone .post .unbookmark.hidden {
display: none;
}
-#sone .post .delete button, #sone .post .like button, #sone .post .unlike button {
+#sone .post .delete button, #sone .post .like button, #sone .post .unlike button, #sone .post .trust button, #sone .post .distrust button, #sone .post .untrust button, #sone .post .bookmark button, #sone .post .unbookmark button {
border: 0px;
background: none;
padding: 0px;
color: rgb(28, 131, 191);
font: inherit;
+ margin: 0px;
}
-#sone .post .delete button:hover, #sone .post .like button:hover, #sone .post .unlike button:hover {
+#sone .post .delete button.confirm {
+ color: red;
+ font-weight: bold;
+}
+
+#sone .post .trust button {
+ color: rgb(0, 128, 0);
+}
+
+#sone .post .distrust button {
+ color: rgb(255, 0, 0);
+}
+
+#sone .post .untrust button {
+ color: rgb(64, 64, 64);
+}
+
+#sone .post .delete button:hover, #sone .post .like button:hover, #sone .post .unlike button:hover, #sone .post .trust button:hover, #sone .post .distrust button:hover, #sone .post .untrust button:hover, #sone .post .bookmark button:hover, #sone .post .unbookmark button:hover {
border: 0px;
background: none;
padding: 0px;
cursor: pointer;
}
-#sone .post .delete:before, #sone .post .likes:before, #sone .post .like:before, #sone .post .unlike:before {
- content: ' ‧ ';
-}
-
#sone .post .likes span {
font: inherit;
color: green;
color: rgb(255, 172, 0);
}
-#sone .post .show-reply-form:before {
- content: ' ‧ ';
-}
-
#sone .post .create-reply {
clear: both;
background-color: #f0f0ff;
#sone .post .create-reply input[type=text] {
margin-left: 0.5ex;
- width: 44em;
+ width: 42em;
}
#sone .post .create-reply textarea {
margin-left: 0.5ex;
- width: 44em;
+ width: 42em;
height: 4em;
}
float: right;
}
+#sone .create-reply .select-sender button, #sone #post-message .select-sender button {
+ display: inline;
+ float: left;
+}
+
#sone .sone {
clear: both;
background-color: #f0f0ff;
display: inline;
}
-#sone #create-sone {
+#sone .profile-field, #sone #edit-profile button[type=submit], #sone #delete-profile-field {
+ margin-top: 1em;
+}
+#sone .profile-field .name {
+ display: inline;
+ font-weight: bold;
+}
+
+#sone .profile-field .name.hidden {
+ display: none;
+}
+
+#sone .profile-field button.confirm.edit {
+ font-weight: bold;
+ color: #080;
+}
+
+#sone .profile-field button.cancel {
+ font-weight: bold;
+ color: red;
+}
+
+#sone .profile-field .value {
+ margin-left: 2em;
+}
+
+#sone #edit-profile .profile-field .value {
+ margin-left: inherit;
+}
+
+#sone .profile-field .edit-field-name, #sone .profile-field .move-up-field, #sone .profile-field .move-down-field, #sone .profile-field .delete-field-name {
+ float: right;
+ margin-top: -1ex;
+ position: relative;
}
#sone #tail {
}
#sone .confirm {
- font-weight: bold !important;
- color: red !important;
+ font-weight: bold;
+ color: red;
}
--- /dev/null
+// JQuery URL Parser
+// Written by Mark Perkins, mark@allmarkedup.com
+// License: http://unlicense.org/ (i.e. do what you want with it!)
+
+jQuery.url = function()
+{
+ var segments = {};
+
+ var parsed = {};
+
+ /**
+ * Options object. Only the URI and strictMode values can be changed via the setters below.
+ */
+ var options = {
+
+ url : window.location, // default URI is the page in which the script is running
+
+ strictMode: false, // 'loose' parsing by default
+
+ key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], // keys available to query
+
+ q: {
+ name: "queryKey",
+ parser: /(?:^|&)([^&=]*)=?([^&]*)/g
+ },
+
+ parser: {
+ strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, //less intuitive, more accurate to the specs
+ loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
+ }
+
+ };
+
+ /**
+ * Deals with the parsing of the URI according to the regex above.
+ * Written by Steven Levithan - see credits at top.
+ */
+ var parseUri = function()
+ {
+ str = decodeURI( options.url );
+
+ var m = options.parser[ options.strictMode ? "strict" : "loose" ].exec( str );
+ var uri = {};
+ var i = 14;
+
+ while ( i-- ) {
+ uri[ options.key[i] ] = m[i] || "";
+ }
+
+ uri[ options.q.name ] = {};
+ uri[ options.key[12] ].replace( options.q.parser, function ( $0, $1, $2 ) {
+ if ($1) {
+ uri[options.q.name][$1] = $2;
+ }
+ });
+
+ return uri;
+ };
+
+ /**
+ * Returns the value of the passed in key from the parsed URI.
+ *
+ * @param string key The key whose value is required
+ */
+ var key = function( key )
+ {
+ if ( jQuery.isEmptyObject(parsed) )
+ {
+ setUp(); // if the URI has not been parsed yet then do this first...
+ }
+ if ( key == "base" )
+ {
+ if ( parsed.port !== null && parsed.port !== "" )
+ {
+ return parsed.protocol+"://"+parsed.host+":"+parsed.port+"/";
+ }
+ else
+ {
+ return parsed.protocol+"://"+parsed.host+"/";
+ }
+ }
+
+ return ( parsed[key] === "" ) ? null : parsed[key];
+ };
+
+ /**
+ * Returns the value of the required query string parameter.
+ *
+ * @param string item The parameter whose value is required
+ */
+ var param = function( item )
+ {
+ if ( jQuery.isEmptyObject(parsed) )
+ {
+ setUp(); // if the URI has not been parsed yet then do this first...
+ }
+ return ( parsed.queryKey[item] === null ) ? null : parsed.queryKey[item];
+ };
+
+ /**
+ * 'Constructor' (not really!) function.
+ * Called whenever the URI changes to kick off re-parsing of the URI and splitting it up into segments.
+ */
+ var setUp = function()
+ {
+ parsed = parseUri();
+
+ getSegments();
+ };
+
+ /**
+ * Splits up the body of the URI into segments (i.e. sections delimited by '/')
+ */
+ var getSegments = function()
+ {
+ var p = parsed.path;
+ segments = []; // clear out segments array
+ segments = parsed.path.length == 1 ? {} : ( p.charAt( p.length - 1 ) == "/" ? p.substring( 1, p.length - 1 ) : path = p.substring( 1 ) ).split("/");
+ };
+
+ return {
+
+ /**
+ * Sets the parsing mode - either strict or loose. Set to loose by default.
+ *
+ * @param string mode The mode to set the parser to. Anything apart from a value of 'strict' will set it to loose!
+ */
+ setMode : function( mode )
+ {
+ options.strictMode = mode == "strict" ? true : false;
+ return this;
+ },
+
+ /**
+ * Sets URI to parse if you don't want to to parse the current page's URI.
+ * Calling the function with no value for newUri resets it to the current page's URI.
+ *
+ * @param string newUri The URI to parse.
+ */
+ setUrl : function( newUri )
+ {
+ options.url = newUri === undefined ? window.location : newUri;
+ setUp();
+ return this;
+ },
+
+ /**
+ * Returns the value of the specified URI segment. Segments are numbered from 1 to the number of segments.
+ * For example the URI http://test.com/about/company/ segment(1) would return 'about'.
+ *
+ * If no integer is passed into the function it returns the number of segments in the URI.
+ *
+ * @param int pos The position of the segment to return. Can be empty.
+ */
+ segment : function( pos )
+ {
+ if ( jQuery.isEmptyObject(parsed) )
+ {
+ setUp(); // if the URI has not been parsed yet then do this first...
+ }
+ if ( pos === undefined )
+ {
+ return segments.length;
+ }
+ return ( segments[pos] === "" || segments[pos] === undefined ) ? null : segments[pos];
+ },
+
+ attr : key, // provides public access to private 'key' function - see above
+
+ param : param // provides public access to private 'param' function - see above
+
+ };
+
+}();
\ No newline at end of file
textarea.show();
}
$(inputField.get(0).form).submit(function() {
+ inputField.attr("disabled", "disabled");
if (!optional && (textarea.val() == "")) {
return false;
}
* The element to add a “comment” link to
*/
function addCommentLink(postId, element, insertAfterThisElement) {
- if ($(element).find(".show-reply-form").length > 0) {
+ if (($(element).find(".show-reply-form").length > 0) || (getPostElement(element).find(".create-reply").length == 0)) {
return;
}
commentElement = (function(postId) {
+ separator = $("<span> · </span>").addClass("separator");
var commentElement = $("<div><span>Comment</span></div>").addClass("show-reply-form").click(function() {
- markPostAsKnown(getPostElement(this));
replyElement = $("#sone .post#" + postId + " .create-reply");
replyElement.removeClass("hidden");
replyElement.removeClass("light");
return commentElement;
})(postId);
$(insertAfterThisElement).after(commentElement.clone(true));
+ $(insertAfterThisElement).after(separator);
}
var translations = {};
toggleClass("modified", modified);
$("#sone .sone." + filterSoneId(soneId) + " .lock").toggleClass("hidden", locked);
$("#sone .sone." + filterSoneId(soneId) + " .unlock").toggleClass("hidden", !locked);
- $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(lastUpdated);
+ if (lastUpdated != null) {
+ $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(lastUpdated);
+ } else {
+ getTranslation("View.Sone.Text.UnknownDate", function(unknown) {
+ $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(unknown);
+ });
+ }
$("#sone .sone." + filterSoneId(soneId) + " .profile-link a").text(name);
}
return getSoneElement(element).find(".id").text();
}
+/**
+ * Returns the element of the post with the given ID.
+ *
+ * @param postId
+ * The ID of the post
+ * @returns The element of the post
+ */
+function getPost(postId) {
+ return $("#sone .post#" + postId);
+}
+
function getPostElement(element) {
return $(element).closest(".post");
}
return getPostElement(element).find(".post-time").text();
}
+/**
+ * Returns the author of the post the given element belongs to.
+ *
+ * @param element
+ * The element whose post to get the author for
+ * @returns The ID of the authoring Sone
+ */
+function getPostAuthor(element) {
+ return getPostElement(element).find(".post-author").text();
+}
+
function getReplyElement(element) {
return $(element).closest(".reply");
}
return getReplyElement(element).find(".reply-time").text();
}
+/**
+ * Returns the author of the reply the given element belongs to.
+ *
+ * @param element
+ * The element whose reply to get the author for
+ * @returns The ID of the authoring Sone
+ */
+function getReplyAuthor(element) {
+ return getReplyElement(element).find(".reply-author").text();
+}
+
function likePost(postId) {
$.getJSON("like.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
if ((data == null) || !data.success) {
});
}
+/**
+ * Trusts the Sone with the given ID.
+ *
+ * @param soneId
+ * The ID of the Sone to trust
+ */
+function trustSone(soneId) {
+ $.getJSON("trustSone.ajax", { "formPassword" : getFormPassword(), "sone" : soneId }, function(data, textStatus) {
+ if ((data != null) && data.success) {
+ updateTrustControls(soneId, data.trustValue);
+ }
+ });
+}
+
+/**
+ * Distrusts the Sone with the given ID, i.e. assigns a negative trust value.
+ *
+ * @param soneId
+ * The ID of the Sone to distrust
+ */
+function distrustSone(soneId) {
+ $.getJSON("distrustSone.ajax", { "formPassword" : getFormPassword(), "sone" : soneId }, function(data, textStatus) {
+ if ((data != null) && data.success) {
+ updateTrustControls(soneId, data.trustValue);
+ }
+ });
+}
+
+/**
+ * Untrusts the Sone with the given ID, i.e. removes any trust assignment.
+ *
+ * @param soneId
+ * The ID of the Sone to untrust
+ */
+function untrustSone(soneId) {
+ $.getJSON("untrustSone.ajax", { "formPassword" : getFormPassword(), "sone" : soneId }, function(data, textStatus) {
+ if ((data != null) && data.success) {
+ updateTrustControls(soneId, data.trustValue);
+ }
+ });
+}
+
+/**
+ * Updates the trust controls for all posts and replies of the given Sone,
+ * according to the given trust value.
+ *
+ * @param soneId
+ * The ID of the Sone to update all trust controls for
+ * @param trustValue
+ * The trust value for the Sone
+ */
+function updateTrustControls(soneId, trustValue) {
+ $("#sone .post").each(function() {
+ if (getPostAuthor(this) == soneId) {
+ getPostElement(this).find(".post-trust").toggleClass("hidden", trustValue != null);
+ getPostElement(this).find(".post-distrust").toggleClass("hidden", trustValue != null);
+ getPostElement(this).find(".post-untrust").toggleClass("hidden", trustValue == null);
+ }
+ });
+ $("#sone .reply").each(function() {
+ if (getReplyAuthor(this) == soneId) {
+ getReplyElement(this).find(".reply-trust").toggleClass("hidden", trustValue != null);
+ getReplyElement(this).find(".reply-distrust").toggleClass("hidden", trustValue != null);
+ getReplyElement(this).find(".reply-untrust").toggleClass("hidden", trustValue == null);
+ }
+ });
+}
+
+/**
+ * Bookmarks the post with the given ID.
+ *
+ * @param postId
+ * The ID of the post to bookmark
+ */
+function bookmarkPost(postId) {
+ (function(postId) {
+ $.getJSON("bookmark.ajax", {"formPassword": getFormPassword(), "type": "post", "post": postId}, function(data, textStatus) {
+ if ((data != null) && data.success) {
+ getPost(postId).find(".bookmark").toggleClass("hidden", true);
+ getPost(postId).find(".unbookmark").toggleClass("hidden", false);
+ }
+ });
+ })(postId);
+}
+
+/**
+ * Unbookmarks the post with the given ID.
+ *
+ * @param postId
+ * The ID of the post to unbookmark
+ */
+function unbookmarkPost(postId) {
+ $.getJSON("unbookmark.ajax", {"formPassword": getFormPassword(), "type": "post", "post": postId}, function(data, textStatus) {
+ if ((data != null) && data.success) {
+ getPost(postId).find(".bookmark").toggleClass("hidden", false);
+ getPost(postId).find(".unbookmark").toggleClass("hidden", true);
+ }
+ });
+}
+
function updateReplyLikes(replyId) {
$.getJSON("getLikes.ajax", { "type": "reply", "reply": replyId }, function(data, textStatus) {
if ((data != null) && data.success) {
/**
* Posts a reply and calls the given callback when the request finishes.
*
+ * @param sender
+ * The ID of the sender
* @param postId
* The ID of the post the reply refers to
* @param text
* The callback function to call when the request finishes (takes 3
* parameters: success, error, replyId)
*/
-function postReply(postId, text, callbackFunction) {
- $.getJSON("createReply.ajax", { "formPassword" : getFormPassword(), "post" : postId, "text": text }, function(data, textStatus) {
+function postReply(sender, postId, text, callbackFunction) {
+ $.getJSON("createReply.ajax", { "formPassword" : getFormPassword(), "sender": sender, "post" : postId, "text": text }, function(data, textStatus) {
if (data == null) {
/* TODO - show error */
return;
}
if (data.success) {
- callbackFunction(true, null, data.reply);
+ callbackFunction(true, null, data.reply, data.sone);
} else {
callbackFunction(false, data.error);
}
}
/**
+ * Ajaxifies the given Sone by enhancing all eligible elements with AJAX.
+ *
+ * @param soneElement
+ * The Sone to ajaxify
+ */
+function ajaxifySone(soneElement) {
+ /*
+ * convert all “follow”, “unfollow”, “lock”, and “unlock” links to something
+ * nicer.
+ */
+ $(".follow", soneElement).submit(function() {
+ var followElement = this;
+ $.getJSON("followSone.ajax", { "sone": getSoneId(this), "formPassword": getFormPassword() }, function() {
+ $(followElement).addClass("hidden");
+ $(followElement).parent().find(".unfollow").removeClass("hidden");
+ });
+ return false;
+ });
+ $(".unfollow", soneElement).submit(function() {
+ var unfollowElement = this;
+ $.getJSON("unfollowSone.ajax", { "sone": getSoneId(this), "formPassword": getFormPassword() }, function() {
+ $(unfollowElement).addClass("hidden");
+ $(unfollowElement).parent().find(".follow").removeClass("hidden");
+ });
+ return false;
+ });
+ $(".lock", soneElement).submit(function() {
+ var lockElement = this;
+ $.getJSON("lockSone.ajax", { "sone" : getSoneId(this), "formPassword" : getFormPassword() }, function() {
+ $(lockElement).addClass("hidden");
+ $(lockElement).parent().find(".unlock").removeClass("hidden");
+ });
+ return false;
+ });
+ $(".unlock", soneElement).submit(function() {
+ var unlockElement = this;
+ $.getJSON("unlockSone.ajax", { "sone" : getSoneId(this), "formPassword" : getFormPassword() }, function() {
+ $(unlockElement).addClass("hidden");
+ $(unlockElement).parent().find(".lock").removeClass("hidden");
+ });
+ return false;
+ });
+
+ /* mark Sone as known when clicking it. */
+ $(soneElement).click(function() {
+ markSoneAsKnown(soneElement);
+ });
+}
+
+/**
* Ajaxifies the given post by enhancing all eligible elements with AJAX.
*
* @param postElement
return false;
});
$(postElement).find(".create-reply button:submit").click(function() {
- inputField = $(this.form).find(":input:enabled").get(0);
+ sender = $(this.form).find(":input[name=sender]").val();
+ inputField = $(this.form).find(":input[name=text]:enabled").get(0);
postId = getPostId(this);
text = $(inputField).val();
- (function(postId, text, inputField) {
- postReply(postId, text, function(success, error, replyId) {
+ (function(sender, postId, text, inputField) {
+ postReply(sender, postId, text, function(success, error, replyId, soneId) {
if (success) {
$(inputField).val("");
- loadNewReply(replyId);
- markPostAsKnown(getPostElement(inputField));
+ loadNewReply(replyId, soneId, postId);
$("#sone .post#" + postId + " .create-reply").addClass("hidden");
+ $("#sone .post#" + postId + " .create-reply .sender").hide();
+ $("#sone .post#" + postId + " .create-reply .select-sender").show();
+ $("#sone .post#" + postId + " .create-reply :input[name=sender]").val(getCurrentSoneId());
} else {
alert(error);
}
});
- })(postId, text, inputField);
+ })(sender, postId, text, inputField);
return false;
});
/* convert all “like” buttons to javascript functions. */
$(postElement).find(".like-post").submit(function() {
likePost(getPostId(this));
- markPostAsKnown(getPostElement(this));
return false;
});
$(postElement).find(".unlike-post").submit(function() {
unlikePost(getPostId(this));
- markPostAsKnown(getPostElement(this));
return false;
});
+ /* convert trust control buttons to javascript functions. */
+ $(postElement).find(".post-trust").submit(function() {
+ trustSone(getPostAuthor(this));
+ return false;
+ });
+ $(postElement).find(".post-distrust").submit(function() {
+ distrustSone(getPostAuthor(this));
+ return false;
+ });
+ $(postElement).find(".post-untrust").submit(function() {
+ untrustSone(getPostAuthor(this));
+ return false;
+ });
+
+ /* convert bookmark/unbookmark buttons to javascript functions. */
+ $(postElement).find(".bookmark").submit(function() {
+ bookmarkPost(getPostId(this));
+ return false;
+ });
+ $(postElement).find(".unbookmark").submit(function() {
+ unbookmarkPost(getPostId(this));
+ return false;
+ });
+
+ /* convert “show source” link into javascript function. */
+ $(postElement).find(".show-source").each(function() {
+ $("a", this).click(function() {
+ $(".post-text.text", getPostElement(this)).toggleClass("hidden");
+ $(".post-text.raw-text", getPostElement(this)).toggleClass("hidden");
+ return false;
+ });
+ });
+
/* add “comment” link. */
addCommentLink(getPostId(postElement), postElement, $(postElement).find(".post-status-line .time"));
});
});
+ /* process sender selection. */
+ $(".select-sender", postElement).css("display", "inline");
+ $(".sender", postElement).hide();
+ $(".select-sender button", postElement).click(function() {
+ $(".sender", postElement).show();
+ $(".select-sender", postElement).hide();
+ return false;
+ });
+
/* mark everything as known on click. */
- $(postElement).click(function() {
+ $(postElement).click(function(event) {
+ if ($(event.target).hasClass("click-to-show")) {
+ return false;
+ }
markPostAsKnown(this);
});
function ajaxifyReply(replyElement) {
$(replyElement).find(".like-reply").submit(function() {
likeReply(getReplyId(this));
- markPostAsKnown(getPostElement(this));
return false;
});
$(replyElement).find(".unlike-reply").submit(function() {
unlikeReply(getReplyId(this));
- markPostAsKnown(getPostElement(this));
return false;
});
(function(replyElement) {
})(replyElement);
addCommentLink(getPostId(replyElement), replyElement, $(replyElement).find(".reply-status-line .time"));
- /* mark post and all replies as known on click. */
- $(replyElement).click(function() {
- markPostAsKnown(getPostElement(this));
+ /* convert “show source” link into javascript function. */
+ $(replyElement).find(".show-reply-source").each(function() {
+ $("a", this).click(function() {
+ $(".reply-text.text", getReplyElement(this)).toggleClass("hidden");
+ $(".reply-text.raw-text", getReplyElement(this)).toggleClass("hidden");
+ return false;
+ });
+ });
+
+ /* convert trust control buttons to javascript functions. */
+ $(replyElement).find(".reply-trust").submit(function() {
+ trustSone(getReplyAuthor(this));
+ return false;
+ });
+ $(replyElement).find(".reply-distrust").submit(function() {
+ distrustSone(getReplyAuthor(this));
+ return false;
+ });
+ $(replyElement).find(".reply-untrust").submit(function() {
+ untrustSone(getReplyAuthor(this));
+ return false;
});
}
* jQuery object representing the notification.
*/
function ajaxifyNotification(notification) {
- notification.find("form.dismiss").submit(function() {
+ notification.find("form").submit(function() {
return false;
});
+ notification.find("input[name=returnPage]").val($.url.attr("relative"));
+ if (notification.find(".short-text").length > 0) {
+ notification.find(".short-text").removeClass("hidden");
+ notification.find(".text").addClass("hidden");
+ }
+ notification.find("form.mark-as-read button").click(function() {
+ $.getJSON("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": $(":input[name=type]", this.form).val(), "id": $(":input[name=id]", this.form).val()});
+ });
+ notification.find("a[class^='link-']").each(function() {
+ linkElement = $(this);
+ if (linkElement.is("[href^='viewPost']")) {
+ id = linkElement.attr("class").substr(5);
+ if (hasPost(id)) {
+ linkElement.attr("href", "#post-" + id);
+ }
+ }
+ });
notification.find("form.dismiss button").click(function() {
$.getJSON("dismissNotification.ajax", { "formPassword" : getFormPassword(), "notification" : notification.attr("id") }, function(data, textStatus) {
/* dismiss in case of error, too. */
if ((data != null) && data.success) {
/* process Sone information. */
$.each(data.sones, function(index, value) {
- updateSoneStatus(value.id, value.name, value.status, value.modified, value.locked, value.lastUpdated);
+ updateSoneStatus(value.id, value.name, value.status, value.modified, value.locked, value.lastUpdatedUnknown ? null : value.lastUpdated);
});
/* process notifications. */
$.each(data.notifications, function(index, value) {
oldNotification = $("#sone #notification-area .notification#" + value.id);
notification = ajaxifyNotification(createNotification(value.id, value.text, value.dismissable)).hide();
if (oldNotification.length != 0) {
+ if ((oldNotification.find(".short-text").length > 0) && (notification.find(".short-text").length > 0)) {
+ opened = oldNotification.is(":visible") && oldNotification.find(".short-text").hasClass("hidden");
+ notification.find(".short-text").toggleClass("hidden", opened);
+ notification.find(".text").toggleClass("hidden", !opened);
+ }
oldNotification.replaceWith(notification.show());
} else {
$("#sone #notification-area").append(notification);
});
/* process new posts. */
$.each(data.newPosts, function(index, value) {
- loadNewPost(value);
+ loadNewPost(value.id, value.sone, value.recipient, value.time);
});
/* process new replies. */
$.each(data.newReplies, function(index, value) {
- loadNewReply(value);
+ loadNewReply(value.id, value.sone, value.post, value.postSone);
});
/* do it again in 5 seconds. */
setTimeout(getStatus, 5000);
}
/**
+ * Returns the ID of the currently logged in Sone.
+ *
+ * @return The ID of the current Sone, or an empty string if no Sone is logged
+ * in
+ */
+function getCurrentSoneId() {
+ return $("#currentSoneId").text();
+}
+
+/**
* Returns the content of the page-id attribute.
*
* @returns The page ID
return $("#sone .reply#" + replyId).length > 0;
}
-function loadNewPost(postId) {
+function loadNewPost(postId, soneId, recipientId, time) {
if (hasPost(postId)) {
return;
}
+ if (!isIndexPage()) {
+ if (!isViewPostPage() || (getShownPostId() != postId)) {
+ if (!isViewSonePage() || ((getShownSoneId() != soneId) && (getShownSoneId() != recipientId))) {
+ return;
+ }
+ }
+ }
+ if (getPostTime($("#sone .post").last()) > time) {
+ return;
+ }
$.getJSON("getPost.ajax", { "post" : postId }, function(data, textStatus) {
if ((data != null) && data.success) {
if (hasPost(data.post.id)) {
newPost = $(data.post.html).addClass("hidden");
if (firstOlderPost != null) {
newPost.insertBefore(firstOlderPost);
- } else {
- $("#sone #posts").append(newPost);
}
ajaxifyPost(newPost);
newPost.slideDown();
});
}
-function loadNewReply(replyId) {
+function loadNewReply(replyId, soneId, postId, postSoneId) {
if (hasReply(replyId)) {
return;
}
+ if (!hasPost(postId)) {
+ return;
+ }
$.getJSON("getReply.ajax", { "reply": replyId }, function(data, textStatus) {
/* find post. */
if ((data != null) && data.success) {
});
}
+/**
+ * Marks the given Sone as known if it is still new.
+ *
+ * @param soneElement
+ * The Sone to mark as known
+ */
+function markSoneAsKnown(soneElement) {
+ if ($(".new", soneElement).length > 0) {
+ $.getJSON("maskAsKnown.ajax", {"formPassword": getFormPassword(), "type": "sone", "id": getSoneId(soneElement)}, function(data, textStatus) {
+ $(soneElement).removeClass("new");
+ });
+ }
+}
+
function markPostAsKnown(postElements) {
$(postElements).each(function() {
postElement = this;
if ($(postElement).hasClass("new")) {
(function(postElement) {
- $.getJSON("markPostAsKnown.ajax", {"formPassword": getFormPassword(), "post": getPostId(postElement)}, function(data, textStatus) {
- $(postElement).removeClass("new");
- });
+ $(postElement).removeClass("new");
+ $(".click-to-show", postElement).removeClass("new");
+ $.getJSON("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": "post", "id": getPostId(postElement)});
})(postElement);
}
});
replyElement = this;
if ($(replyElement).hasClass("new")) {
(function(replyElement) {
- $.getJSON("markReplyAsKnown.ajax", {"formPassword": getFormPassword(), "reply": getReplyId(replyElement)}, function(data, textStatus) {
- $(replyElement).removeClass("new");
- });
+ $(replyElement).removeClass("new");
+ $.getJSON("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": "reply", "id": getReplyId(replyElement)});
})(replyElement);
}
});
function resetActivity() {
title = document.title;
if (title.indexOf('(') == 0) {
- document.title = title.substr(title.indexOf(' ') + 1);
+ setTitle(title.substr(title.indexOf(' ') + 1));
}
}
if (!focus) {
title = document.title;
if (title.indexOf('(') != 0) {
- document.title = "(!) " + title;
+ setTitle("(!) " + title);
+ }
+ if (!iconBlinking) {
+ setTimeout(toggleIcon, 1500);
+ iconBlinking = true;
+ }
+ }
+}
+
+/**
+ * Sets the window title after a small delay to prevent race-condition issues.
+ *
+ * @param title
+ * The title to set
+ */
+function setTitle(title) {
+ setTimeout(function() {
+ document.title = title;
+ }, 50);
+}
+
+/** Whether the icon is currently showing activity. */
+var iconActive = false;
+
+/** Whether the icon is currently supposed to blink. */
+var iconBlinking = false;
+
+/**
+ * Toggles the icon. If the window has gained focus and the icon is still
+ * showing the activity state, it is returned to normal.
+ */
+function toggleIcon() {
+ if (focus) {
+ if (iconActive) {
+ changeIcon("images/icon.png");
+ iconActive = false;
}
+ iconBlinking = false;
+ } else {
+ iconActive = !iconActive;
+ console.log("showing icon: " + iconActive);
+ changeIcon(iconActive ? "images/icon-activity.png" : "images/icon.png");
+ setTimeout(toggleIcon, 1500);
}
}
/**
+ * Changes the icon of the page.
+ *
+ * @param iconUrl
+ * The new URL of the icon
+ */
+function changeIcon(iconUrl) {
+ $("link[rel=icon]").remove();
+ $("head").append($("<link>").attr("rel", "icon").attr("type", "image/png").attr("href", iconUrl));
+ $("iframe[id=icon-update]")[0].src += "";
+}
+
+/**
* Creates a new notification.
*
* @param id
* The ID of the notification
*/
function showNotificationDetails(notificationId) {
- $("#sone .notification#" + notificationId + " .text").show();
- $("#sone .notification#" + notificationId + " .short-text").hide();
+ $("#sone .notification#" + notificationId + " .text").removeClass("hidden");
+ $("#sone .notification#" + notificationId + " .short-text").addClass("hidden");
+}
+
+/**
+ * Deletes the field with the given ID from the profile.
+ *
+ * @param fieldId
+ * The ID of the field to delete
+ */
+function deleteProfileField(fieldId) {
+ $.getJSON("deleteProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId}, function(data, textStatus) {
+ if (data && data.success) {
+ $("#sone .profile-field#" + data.field.id).slideUp();
+ }
+ });
+}
+
+/**
+ * Renames a profile field.
+ *
+ * @param fieldId
+ * The ID of the field to rename
+ * @param newName
+ * The new name of the field
+ * @param successFunction
+ * Called when the renaming was successful
+ */
+function editProfileField(fieldId, newName, successFunction) {
+ $.getJSON("editProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "name": newName}, function(data, textStatus) {
+ if (data && data.success) {
+ successFunction();
+ }
+ });
+}
+
+/**
+ * Moves the profile field with the given ID one slot in the given direction.
+ *
+ * @param fieldId
+ * The ID of the field to move
+ * @param direction
+ * The direction to move in (“up” or “down”)
+ * @param successFunction
+ * Function to call on success
+ */
+function moveProfileField(fieldId, direction, successFunction) {
+ $.getJSON("moveProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "direction": direction}, function(data, textStatus) {
+ if (data && data.success) {
+ successFunction();
+ }
+ });
+}
+
+/**
+ * Moves the profile field with the given ID up one slot.
+ *
+ * @param fieldId
+ * The ID of the field to move
+ * @param successFunction
+ * Function to call on success
+ */
+function moveProfileFieldUp(fieldId, successFunction) {
+ moveProfileField(fieldId, "up", successFunction);
+}
+
+/**
+ * Moves the profile field with the given ID down one slot.
+ *
+ * @param fieldId
+ * The ID of the field to move
+ * @param successFunction
+ * Function to call on success
+ */
+function moveProfileFieldDown(fieldId, successFunction) {
+ moveProfileField(fieldId, "down", successFunction);
}
//
/* this initializes the status update input field. */
getTranslation("WebInterface.DefaultText.StatusUpdate", function(defaultText) {
registerInputTextareaSwap("#sone #update-status .status-input", defaultText, "text", false, false);
+ $("#sone #update-status .select-sender").css("display", "inline");
+ $("#sone #update-status .sender").hide();
+ $("#sone #update-status .select-sender button").click(function() {
+ $("#sone #update-status .sender").show();
+ $("#sone #update-status .select-sender").hide();
+ return false;
+ });
$("#sone #update-status").submit(function() {
if ($(this).find(":input.default:enabled").length > 0) {
return false;
}
- text = $(this).find(":input:enabled").val();
- $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "text": text }, function(data, textStatus) {
+ sender = $(this).find(":input[name=sender]").val();
+ text = $(this).find(":input[name=text]:enabled").val();
+ $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "sender": sender, "text": text }, function(data, textStatus) {
if ((data != null) && data.success) {
- loadNewPost(data.postId);
+ loadNewPost(data.postId, data.sone, data.recipient);
}
});
- $(this).find(":input:enabled").val("").blur();
+ $(this).find(":input[name=sender]").val(getCurrentSoneId());
+ $(this).find(":input[name=text]:enabled").val("").blur();
+ $(this).find(".sender").hide();
+ $(this).find(".select-sender").show();
return false;
});
});
/* ajaxify input field on “view Sone” page. */
getTranslation("WebInterface.DefaultText.Message", function(defaultText) {
registerInputTextareaSwap("#sone #post-message input[name=text]", defaultText, "text", false, false);
+ $("#sone #post-message .select-sender").css("display", "inline");
+ $("#sone #post-message .sender").hide();
+ $("#sone #post-message .select-sender button").click(function() {
+ $("#sone #post-message .sender").show();
+ $("#sone #post-message .select-sender").hide();
+ return false;
+ });
$("#sone #post-message").submit(function() {
- text = $(this).find(":input:enabled").val();
- $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "recipient": getShownSoneId(), "text": text }, function(data, textStatus) {
+ sender = $(this).find(":input[name=sender]").val();
+ text = $(this).find(":input[name=text]:enabled").val();
+ $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "recipient": getShownSoneId(), "sender": sender, "text": text }, function(data, textStatus) {
if ((data != null) && data.success) {
- loadNewPost(data.postId);
+ loadNewPost(data.postId, getCurrentSoneId());
}
});
- $(this).find(":input:enabled").val("").blur();
+ $(this).find(":input[name=sender]").val(getCurrentSoneId());
+ $(this).find(":input[name=text]:enabled").val("").blur();
+ $(this).find(".sender").hide();
+ $(this).find(".select-sender").show();
return false;
});
});
});
}
- /*
- * convert all “follow”, “unfollow”, “lock”, and “unlock” links to something
- * nicer.
- */
- $("#sone .follow").submit(function() {
- var followElement = this;
- $.getJSON("followSone.ajax", { "sone": getSoneId(this), "formPassword": getFormPassword() }, function() {
- $(followElement).addClass("hidden");
- $(followElement).parent().find(".unfollow").removeClass("hidden");
- });
- return false;
- });
- $("#sone .unfollow").submit(function() {
- var unfollowElement = this;
- $.getJSON("unfollowSone.ajax", { "sone": getSoneId(this), "formPassword": getFormPassword() }, function() {
- $(unfollowElement).addClass("hidden");
- $(unfollowElement).parent().find(".follow").removeClass("hidden");
- });
- return false;
- });
- $("#sone .lock").submit(function() {
- var lockElement = this;
- $.getJSON("lockSone.ajax", { "sone" : getSoneId(this), "formPassword" : getFormPassword() }, function() {
- $(lockElement).addClass("hidden");
- $(lockElement).parent().find(".unlock").removeClass("hidden");
- });
- return false;
- });
- $("#sone .unlock").submit(function() {
- var unlockElement = this;
- $.getJSON("unlockSone.ajax", { "sone" : getSoneId(this), "formPassword" : getFormPassword() }, function() {
- $(unlockElement).addClass("hidden");
- $(unlockElement).parent().find(".lock").removeClass("hidden");
- });
- return false;
+ $("#sone .sone").each(function() {
+ ajaxifySone($(this));
});
/* process all existing notifications, ajaxify dismiss buttons. */
--- /dev/null
+<%include include/head.html>
+
+ <div class="page-id hidden">bookmarks</div>
+
+ <h1><%= Page.Bookmarks.Page.Title|l10n|html></h1>
+
+ <div id="posts">
+ <%include include/pagination.html>
+ <%foreach posts post>
+ <%include include/viewPost.html>
+ <%/foreach>
+ <%include include/pagination.html>
+ <%if postsNotLoaded>
+ <p><%= Page.Bookmarks.Text.PostsNotLoaded|l10n|html|replace needle='{link}' replacement='<a href="unbookmark.html?post=allNotLoaded">'|replace needle='{/link}' replacement='</a>'></p>
+ <%elseif posts.empty>
+ <p><%= Page.Bookmarks.Text.NoBookmarks|l10n|html></p>
+ <%/if>
+ </div>
+
+<%include include/tail.html>
--- /dev/null
+<%include include/head.html>
+
+ <h1><%= Page.DeleteProfileField.Page.Title|l10n|html></h1>
+
+ <p><%= Page.DeleteProfileField.Text|l10n|html></p>
+
+ <div class="profile-field">
+ <div class="name"><% field.name|html></div>
+ <div class="value"><% field.value|html></div>
+ </div>
+
+ <form id="delete-profile-field" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="field" value="<% field.id|html>" />
+ <button type="submit" name="confirm" value="true"><%= Page.DeleteProfileField.Button.Yes|l10n|html></button>
+ <button type="submit" name="cancel" value="true"><%= Page.DeleteProfileField.Button.No|l10n|html></button>
+ </form>
+
+<%include include/tail.html>
<%include include/head.html>
<script language="javascript">
- $(document).ready(function() {
+ function recheckMoveButtons() {
+ $("#sone .profile-field").each(function() {
+ $(".move-up-field", this).toggleClass("hidden", $(this).prev(".profile-field").length == 0);
+ $(".move-down-field", this).toggleClass("hidden", $(this).next(".profile-field").length == 0);
+ });
+ }
+
+ $(function() {
getTranslation("WebInterface.DefaultText.FirstName", function(firstNameDefaultText) {
registerInputTextareaSwap("#sone #edit-profile input[name=first-name]", firstNameDefaultText, "first-name", true, true);
});
getTranslation("WebInterface.DefaultText.BirthYear", function(birthYearDefaultText) {
registerInputTextareaSwap("#sone #edit-profile input[name=birth-year]", birthYearDefaultText, "birth-year", true, true);
});
+ getTranslation("WebInterface.DefaultText.FieldName", function(fieldNameDefaultText) {
+ registerInputTextareaSwap("#sone #add-profile-field input[name=field-name]", fieldNameDefaultText, "field-name", true, true);
+ });
+
+ <%foreach fields field>
+ registerInputTextareaSwap("#sone #edit-profile input[name=field-<% loop.count>]", <% field.key|js>, "field-<% loop.count>", true, true);
+ <%/foreach>
/* hide all the labels. */
- $("#sone #edit-profile label").hide();
+ $("#sone #edit-profile label, #sone #add-profile-field label").hide();
+
+ /* ajaxify the delete buttons. */
+ getTranslation("Page.EditProfile.Fields.Button.ReallyDelete", function(reallyDeleteText) {
+ $("#sone #edit-profile .delete-field-name button").each(function() {
+ confirmButton = $(this).clone().addClass("hidden").addClass("confirm").text(reallyDeleteText).insertAfter(this);
+ (function(deleteButton, confirmButton) {
+ deleteButton.click(function() {
+ deleteButton.fadeOut("slow", function() {
+ confirmButton.fadeIn("slow");
+ $(document).one("click", function() {
+ if (this != confirmButton.get(0)) {
+ confirmButton.fadeOut("slow", function() {
+ deleteButton.fadeIn("slow");
+ });
+ }
+ return false;
+ });
+ });
+ return false;
+ });
+ confirmButton.click(function() {
+ confirmButton.fadeOut("slow");
+ buttonName = confirmButton.attr("name");
+ fieldId = buttonName.substring("delete-field-".length);
+ deleteProfileField(fieldId);
+ recheckMoveButtons();
+ return false;
+ });
+ })($(this), confirmButton);
+ });
+ });
+
+ /* ajaxify the edit button. */
+ $("#sone #edit-profile .edit-field-name button").each(function() {
+ profileField = $(this).parents(".profile-field");
+ fieldNameElement = profileField.find(".name");
+ inputField = $("input[type=text].short", profileField);
+ confirmButton = $("button.confirm", profileField);
+ cancelButton = $("button.cancel", profileField);
+ (function(editButton, inputField, confirmButton, cancelButton, fieldNameElement) {
+ cleanUp = function(editButton, inputField, confirmButton, cancelButton, fieldNameElement) {
+ editButton.removeAttr("disabled");
+ inputField.addClass("hidden");
+ confirmButton.addClass("hidden");
+ cancelButton.addClass("hidden");
+ fieldNameElement.removeClass("hidden");
+ };
+ confirmButton.click(function() {
+ inputField.attr("disabled", "disabled");
+ confirmButton.attr("disabled", "disabled");
+ cancelButton.attr("disabled", "disabled");
+ editProfileField(confirmButton.parents(".profile-field").attr("id"), inputField.val(), function() {
+ fieldNameElement.text(inputField.val());
+ cleanUp(editButton, inputField, confirmButton, cancelButton, fieldNameElement);
+ });
+ return false;
+ });
+ cancelButton.click(function() {
+ cleanUp(editButton, inputField, confirmButton, cancelButton, fieldNameElement);
+ return false;
+ });
+ inputField.keypress(function(event) {
+ if (event.which == 13) {
+ confirmButton.click();
+ return false;
+ } else if (event.which == 27) {
+ cancelButton.click();
+ return false;
+ }
+ });
+ editButton.click(function() {
+ editButton.attr("disabled", "disabled");
+ fieldNameElement.addClass("hidden");
+ inputField.removeAttr("disabled").val(fieldNameElement.text()).removeClass("hidden").focus().select();
+ confirmButton.removeAttr("disabled").removeClass("hidden");
+ cancelButton.removeAttr("disabled").removeClass("hidden");
+ return false;
+ });
+ })($(this), inputField, confirmButton, cancelButton, fieldNameElement);
+ });
+
+ /* ajaxify “move up” and “move down” buttons. */
+ $("#sone .profile-field .move-down-field button").click(function() {
+ profileField = $(this).parents(".profile-field");
+ moveProfileFieldDown(profileField.attr("id"), function() {
+ next = profileField.next();
+ current = profileField.insertAfter(next);
+ recheckMoveButtons();
+ });
+ return false;
+ });
+ $("#sone .profile-field .move-up-field button").click(function() {
+ profileField = $(this).parents(".profile-field");
+ moveProfileFieldUp(profileField.attr("id"), function() {
+ previous = profileField.prev();
+ current = profileField.insertBefore(previous);
+ recheckMoveButtons();
+ });
+ return false;
+ });
});
</script>
<p><%= Page.EditProfile.Page.Description|l10n|html></p>
<p><%= Page.EditProfile.Page.Hint.Optionality|l10n|html></p>
- <%if changed>
- <p><%= Page.EditProfile.Page.Status.Changed|l10n|html></p>
- <%/if>
-
<form id="edit-profile" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
</div>
<div>
- <button type="submit"><%= Page.EditProfile.Button.Save|l10n|html></button>
+ <button type="submit" name="save-profile" value="true"><%= Page.EditProfile.Button.Save|l10n|html></button>
+ </div>
+
+ <h1><%= Page.EditProfile.Fields.Title|l10n|html></h1>
+
+ <p><%= Page.EditProfile.Fields.Description|l10n|html></p>
+
+ <%foreach fields field fieldLoop>
+ <div class="profile-field" id="<% field.id|html>">
+ <div class="name"><% field.name|html></div>
+ <input class="short hidden" type="text"><button class="edit confirm hidden" type="button">✔</button><button class="cancel hidden" type="button">✘</button>
+ <div class="edit-field-name"><button type="submit" name="edit-field-<% field.id|html>" value="true"><%= Page.EditProfile.Fields.Button.Edit|l10n|html></button></div>
+ <div class="delete-field-name"><button type="submit" name="delete-field-<% field.id|html>" value="true"><%= Page.EditProfile.Fields.Button.Delete|l10n|html></button></div>
+ <div class="<%if fieldLoop.last>hidden <%/if>move-down-field"><button type="submit" name="move-down-field-<% field.id|html>" value="true"><%= Page.EditProfile.Fields.Button.MoveDown|l10n|html></button></div>
+ <div class="<%if fieldLoop.first>hidden <%/if>move-up-field"><button type="submit" name="move-up-field-<% field.id|html>" value="true"><%= Page.EditProfile.Fields.Button.MoveUp|l10n|html></button></div>
+ <div class="value"><input type="text" name="field-<% field.id|html>" value="<% field.value|html>" /></div>
+ </div>
+
+ <%if fieldLoop.last>
+ <div>
+ <button type="submit" name="save-profile" value="true"><%= Page.EditProfile.Button.Save|l10n|html></button>
+ </div>
+ <%/if>
+ <%/foreach>
+
+ </form>
+
+ <form id="add-profile-field" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+
+ <a name="profile-fields"></a>
+ <h2><%= Page.EditProfile.Fields.AddField.Title|l10n|html></h2>
+
+ <%if duplicateFieldName>
+ <p><%= Page.EditProfile.Error.DuplicateFieldName|l10n|replace needle="{fieldName}" replacementKey="fieldName"|html></p>
+ <%/if>
+
+ <div id="new-field">
+ <label for="new-field"><%= Page.EditProfile.Fields.AddField.Label.Name|l10n|html></label>
+ <input type="text" name="field-name" value="" />
+ <button type="submit" name="add-field" value="true"><%= Page.EditProfile.Fields.AddField.Button.AddField|l10n|html></button>
</div>
</form>
--- /dev/null
+<%include include/head.html>
+
+ <h1><%= Page.EditProfileField.Page.Title|l10n|html></h1>
+
+ <p><%= Page.EditProfileField.Text|l10n|html></p>
+
+ <%if duplicateFieldName>
+ <p><%= Page.EditProfileField.Error.DuplicateFieldName|l10n|html></p>
+ <%/if>
+
+ <form method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="field" value="<% field.id|html>" />
+ <div>
+ <input type="text" name="name" value="<% field.name|html>" />
+ <button type="submit" name="save" value="true"><%= Page.EditProfileField.Button.Save|l10n|html></button>
+ </div>
+ <p>
+ <button type="reset"><%= Page.EditProfileField.Button.Reset|l10n|html></button>
+ <button type="submit" name="cancel" value="true"><%= Page.EditProfileField.Button.Cancel|l10n|html></button>
+ </p>
+ </form>
+
+<%include include/tail.html>
<div id="sone" class="<%ifnull ! currentSone>online<%else>offline<%/if>">
<div id="formPassword"><% formPassword|html></div>
+ <div id="currentSoneId" class="hidden"><% currentSone.id|html></div>
<script src="javascript/jquery-1.4.2.js" language="javascript"></script>
+ <script src="javascript/jquery.url.js" language="javascript"></script>
<script src="javascript/sone.js" language="javascript"></script>
<div id="main">
<button type="submit"><%= Notification.Button.Dismiss|l10n|html></button>
</form>
<%/if>
- <% notification>
+ <%include notification>
</div>
<%/foreach>
</div>
<div id="home-sone">
<% currentSone|store key=sone>
<%include include/viewSone.html>
- <%include include/updateStatus.html>
</div>
<%/if>
</div>
</div>
</div>
+ <iframe id="icon-update" class="hidden" src="about:blank"></iframe>
+
</div>
<form id="update-status" action="createPost.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
- <label for="text"><%= Page.Index.Label.Text|l10n|html></label>
+ <label for="sender"><%= Page.Index.Label.Sender|l10n|html></label>
+ <div class="sender">
+ <select name="sender" title="<%= View.UpdateStatus.Text.ChooseSenderIdentity|l10n|html>">
+ <%foreach localSones localSone>
+ <option value="<% localSone.id|html>"<%if localSone.current> selected="selected"<%/if>><% localSone.niceName|html></option>
+ <%/foreach>
+ </select>
+ </div>
+ <div class="select-sender"><button type="button" title="<%= View.UpdateStatus.Text.ChooseSenderIdentity|l10n|html>">+</button></div><label for="text"><%= Page.Index.Label.Text|l10n|html></label>
<input type="text" class="status-input" name="text" value="" />
<button type="submit"><%= Page.Index.Button.Post|l10n|html></button>
</form>
-<a name="post-<% post.id|html>"></a>
<div id="<% post.id|html>" class="post<%if loop.last> last<%/if><%if post.new> new<%/if>">
+ <a name="post-<% post.id|html>"></a>
<div class="post-time hidden"><% post.time|html></div>
+ <div class="post-author hidden"><% post.sone.id|html></div>
<div class="avatar">
<img src="/WoT/GetIdenticon?identity=<% post.sone.id|html>&width=48&height=48" width="48" height="48" alt="Avatar Image" />
</div>
<div class="recipient profile-link"><a href="viewSone.html?sone=<% post.recipient.id|html>"><% post.recipient.niceName|html></a></div>
<%/if>
<%/if>
- <div class="text"><% post.text></div>
+ <div class="post-text raw-text<%if !raw> hidden<%/if>"><% post.text|html></div>
+ <div class="post-text text<%if raw> hidden<%/if>"><% post.text|parse sone=post.sone></div>
</div>
<div class="post-status-line status-line">
+ <div class="bookmarks">
+ <form class="unbookmark<%if !post.bookmarked> hidden<%/if>" action="unbookmark.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="post" value="<% post.id|html>" />
+ <button type="submit" title="<%= View.Post.Bookmarks.PostIsBookmarked|l10n|html>">★</button>
+ </form>
+ <form class="bookmark<%if post.bookmarked> hidden<%/if>" action="bookmark.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="post" value="<% post.id|html>" />
+ <button type="submit" title="<%= View.Post.Bookmarks.PostIsNotBookmarked|l10n|html>">☆</button>
+ </form>
+ </div>
+ <span class='separator'>·</span>
<div class="time"><a href="viewPost.html?post=<% post.id|html>"><% post.time|date format="MMM d, yyyy, HH:mm:ss"></a></div>
- <div class="likes<%if post.likes.size|match value=0> hidden<%/if>"><span title="<% post.likes.soneNames|html>">↑<span class="like-count"><% post.likes.size></span></span></div>
+ <span class='separator'>·</span>
+ <div class="show-source"><a href="viewPost.html?post=<% post.id|html>&raw=<%if raw>false<%else>true<%/if>"><%= View.Post.ShowSource|l10n|html></a></div>
+ <div class="likes<%if post.likes.size|match value=0> hidden<%/if>">
+ <span class='separator'>·</span>
+ <span title="<% post.likes.soneNames|html>">↑<span class="like-count"><% post.likes.size></span></span>
+ </div>
<%ifnull ! currentSone>
+ <span class='separator'>·</span>
<form class="like like-post<%if post.liked> hidden<%/if>" action="like.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="post" value="<% post.id|html>" />
<button type="submit" value="1"><%= View.Post.UnlikeLink|l10n|html></button>
</form>
+ <%if !post.sone.current>
+ <span class='separator'>·</span>
+ <form class="trust post-trust<%if post.sone.trust.assigned> hidden<%/if>" action="trust.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="sone" value="<% post.sone.id|html>" />
+ <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
+ </form>
+ <form class="distrust post-distrust<%if post.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="sone" value="<% post.sone.id|html>" />
+ <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
+ </form>
+ <form class="untrust post-untrust<%if !post.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="sone" value="<% post.sone.id|html>" />
+ <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">↶</button>
+ </form>
+ <%/if>
<%/if>
- <%if post.sone.current>
+ <%if post.sone.local>
+ <span class='separator'>·</span>
<form class="delete delete-post" action="deletePost.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="post" value="<% post.id|html>" />
+ <div class="sender">
+ <select name="sender" title="<%= View.UpdateStatus.Text.ChooseSenderIdentity|l10n|html>">
+ <%foreach localSones localSone|sort>
+ <option value="<% localSone.id|html>"<%if localSone.current> selected="selected"<%/if>><% localSone.niceName|html></option>
+ <%/foreach>
+ </select>
+ </div>
+ <div class="select-sender"><button type="button" title="<%= View.UpdateStatus.Text.ChooseSenderIdentity|l10n|html>">+</button></div>
<input type="text" class="reply-input" name="text" value="" />
<button type="submit"><%= View.Post.SendReply|l10n|html></button>
</form>
-<a name="reply-<% reply.id|html>"></a>
<div id="<% reply.id|html>" class="reply<%if reply.new> new<%/if>">
+ <a name="reply-<% reply.id|html>"></a>
<div class="reply-time hidden"><% reply.time|html></div>
+ <div class="reply-author hidden"><% reply.sone.id|html></div>
<div class="avatar">
<img src="/WoT/GetIdenticon?identity=<% reply.sone.id|html>&width=36&height=36" width="36" height="36" alt="Avatar Image" />
</div>
<div class="inner-part">
<div>
<div class="author profile-link"><a href="viewSone.html?sone=<% reply.sone.id|html>"><% reply.sone.niceName|html></a></div>
- <div class="text"><% reply.text></div>
+ <div class="reply-text raw-text<%if !raw> hidden<%/if>"><% reply.text|html></div>
+ <div class="reply-text text<%if raw> hidden<%/if>"><% reply.text|parse sone=reply.sone></div>
</div>
<div class="reply-status-line status-line">
<div class="time"><% reply.time|date format="MMM d, yyyy, HH:mm:ss"></div>
- <div class="likes<%if reply.likes.size|match value=0> hidden<%/if>"><span title="<% reply.likes.soneNames|html>">↑<span class="like-count"><% reply.likes.size></span></span></div>
+ <span class='separator'>·</span>
+ <div class="show-reply-source"><a href="viewPost.html?post=<% post.id|html>&raw=<%if raw>false<%else>true<%/if>"><%= View.Post.ShowSource|l10n|html></a></div>
+ <div class="likes<%if reply.likes.size|match value=0> hidden<%/if>">
+ <span class='separator'>·</span>
+ <span title="<% reply.likes.soneNames|html>">↑<span class="like-count"><% reply.likes.size></span></span>
+ </div>
<%ifnull ! currentSone>
+ <span class='separator'>·</span>
<form class="like like-reply<%if reply.liked> hidden<%/if>" action="like.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="reply" value="<% reply.id|html>" />
<button type="submit" value="1"><%= View.Post.UnlikeLink|l10n|html></button>
</form>
+ <%if !reply.sone.current>
+ <span class='separator'>·</span>
+ <form class="trust reply-trust<%if reply.sone.trust.assigned> hidden<%/if>" action="trust.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
+ <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
+ </form>
+ <form class="distrust reply-distrust<%if reply.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
+ <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
+ </form>
+ <form class="untrust reply-untrust<%if !reply.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="sone" value="<% reply.sone.id|html>" />
+ <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">↶</button>
+ </form>
+ <%/if>
<%/if>
- <%if reply.sone.current>
+ <%if reply.sone.local>
+ <span class='separator'>·</span>
<form class="delete delete-reply" action="deleteReply.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<div class="download-marker" title="<%= View.Sone.Status.Downloading|l10n|html>">⬊</div>
<div class="insert-marker" title="<%= View.Sone.Status.Inserting|l10n|html>">⬈</div>
<div class="idle-marker" title="<%= View.Sone.Status.Idle|l10n|html>">✔</div>
- <div class="last-update"><%= View.Sone.Label.LastUpdate|l10n|html> <span class="time"><% sone.time|date format="MMM d, yyyy, HH:mm:ss"></span></div>
+ <div class="last-update"><%= View.Sone.Label.LastUpdate|l10n|html> <span class="time"><% sone.time|unknown|date format="MMM d, yyyy, HH:mm:ss"></span></div>
<div class="profile-link"><a href="viewSone.html?sone=<% sone.id|html>" title="<% sone.requestUri|html>"><% sone.niceName|html></a></div>
<div class="short-request-uri"><% sone.requestUri|substring start=4 length=43|html></div>
<div class="hidden"><% sone.blacklisted></div>
<h1><%= Page.Index.PostList.Title|l10n|html></h1>
+ <%include include/updateStatus.html>
+
<div id="posts">
- <%getpage>
- <%paginate list=posts pagesize=25>
<%= page|store key=pageParameter>
<%include include/pagination.html>
<%foreach pagination.items post>
<birth-day><% currentSone.profile.birthDay|xml></birth-day>
<birth-month><% currentSone.profile.birthMonth|xml></birth-month>
<birth-year><% currentSone.profile.birthYear|xml></birth-year>
+ <fields>
+ <%foreach currentSone.profile.fields field>
+ <field>
+ <field-name><% field.name|xml></field-name>
+ <field-value><% field.value|xml></field-value>
+ </field>
+ <%/foreach>
+ </fields>
</profile>
<posts>
--- /dev/null
+<%include include/head.html>
+
+ <h1><%= Page.Invalid.Page.Title|l10n|html></h1>
+
+ <p><%= Page.Invalid.Text|l10n|html|replace needle="{link}" replacement='<a href="index.html">'|replace needle="{/link}" replacement='</a>'></p>
+
+<%include include/tail.html>
<h1><%= Page.KnownSones.Page.Title|l10n|html></h1>
<div id="known-sones">
- <%getpage parameter=page>
- <%paginate list=knownSones pagesize=25>
<%= page|store key=pageParameter>
<%include include/pagination.html>
<%foreach pagination.items sone>
-<div class="short-text">
+<div class="short-text hidden">
<%= Notification.NewPost.ShortText|l10n|html>
<a class="link" onclick="showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
</div>
-<div class="text hidden">
+<div class="text">
+ <form class="mark-as-read" action="markAsKnown.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="type" value="post" />
+ <input type="hidden" name="id" value="<%foreach posts post><% post.id|html><%notlast> <%/notlast><%/foreach>" />
+ <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
+ </form>
<%= Notification.NewPost.Text|l10n|html>
<%foreach posts post>
- <a href="viewPost.html?post=<% post.id|html>"><% post.sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
+ <a class="link-<% post.id|html>" href="viewPost.html?post=<% post.id|html>"><% post.sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
<%/foreach>
</div>
-<div class="short-text">
+<div class="short-text hidden">
<%= Notification.NewReply.ShortText|l10n|html>
<a class="link" onclick="showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
</div>
-<div class="text hidden">
+<div class="text">
+ <form class="mark-as-read" action="markAsKnown.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="type" value="reply" />
+ <input type="hidden" name="id" value="<%foreach replies reply><% reply.id|html><%notlast> <%/notlast><%/foreach>" />
+ <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
+ </form>
<%= Notification.NewReply.Text|l10n|html>
<%foreach replies reply>
- <a href="viewPost.html?post=<% reply.post.id|html>"><% reply.sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
+ <a class="link-<% reply.post.id|html>" href="viewPost.html?post=<% reply.post.id|html>"><% reply.sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
<%/foreach>
</div>
-<div class="short-text">
+<div class="short-text hidden">
<%= Notification.NewSone.ShortText|l10n|html>
<a class="link" onclick="showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
</div>
-<div class="text hidden">
+<div class="text">
+ <form class="mark-as-read" action="markAsKnown.html" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="type" value="sone" />
+ <input type="hidden" name="id" value="<%foreach sones sone><% sone.id|html><%notlast> <%/notlast><%/foreach>" />
+ <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
+ </form>
<%= Notification.NewSone.Text|l10n|html>
<%foreach sones sone>
<a href="viewSone.html?sone=<% sone.id|html>" title="<% sone.requestUri|html>"><% sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
-<div class="text"><%= Notification.NewVersion.Text|l10n|html|replace needle="{version}" replacementKey=version></div>
+<div class="text"><%= Notification.NewVersion.Text|l10n|replace needle="{version}" replacementKey=latestVersion|replace needle="{edition}" replacementKey=latestEdition|parse sone="nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"></div>
<p><%= Page.Options.Option.InsertionDelay.Description|l10n|html></p>
<p><input type="text" name="insertion-delay" value="<% insertion-delay|html>" /></p>
+ <h2><%= Page.Options.Section.TrustOptions.Title|l10n|html></h2>
+
+ <p><%= Page.Options.Option.PositiveTrust.Description|l10n|html></p>
+ <p><input type="text" name="positive-trust" value="<% positive-trust|html>" /></p>
+
+ <p><%= Page.Options.Option.NegativeTrust.Description|l10n|html></p>
+ <p><input type="text" name="negative-trust" value="<% negative-trust|html>" /></p>
+
+ <p><%= Page.Options.Option.TrustComment.Description|l10n|html></p>
+ <p><input type="text" name="trust-comment" value="<% trust-comment|html>" /></p>
+
<h2><%= Page.Options.Section.RescueOptions.Title|l10n|html></h2>
<p><%= Page.Options.Option.SoneRescueMode.Description|l10n|html></p>
<p><%= Page.ViewPost.Text.UnknownPost|l10n|html></p>
<%else>
- <h1><%= Page.ViewPost.Page.Title|l10n|insert needle="{sone}" key=post.sone.niceName|html></h1>
+ <h1><%= Page.ViewPost.Page.Title|l10n|replace needle="{sone}" replacementKey=post.sone.niceName|html></h1>
<%include include/viewPost.html>
<%/if>
<h1><%= Page.ViewSone.Page.TitleWithoutSone|l10n|html></h1>
- <p><%= Page.ViewSone.NoSone.Description|l10n|insert needle="{sone}" key=sone.id|html></p>
+ <p><%= Page.ViewSone.NoSone.Description|l10n|replace needle="{sone}" replacementKey=sone.id|html></p>
<%elseifnull sone.name>
<%if ! sone.current>
<%include include/viewSone.html>
+ <%/if>
- <p><%= Page.ViewSone.WriteAMessage|l10n|html></p>
+ <h1><%= Page.ViewSone.Profile.Title|l10n|html></h1>
- <form action="createPost.html" id="post-message" method="post">
- <input type="hidden" name="formPassword" value="<% formPassword|html>" />
- <input type="hidden" name="returnPage" value="<% request.uri|html>" />
- <input type="hidden" name="recipient" value="<% sone.id|html>" />
- <input type="text" name="text" value="" />
- <button type="submit"><%= Page.CreatePost.Button.Post|l10n|html></button>
- </form>
- <%/if>
+ <div class="profile-field">
+ <div class="name"><%= Page.ViewSone.Profile.Label.Name|l10n|html></div>
+ <div class="value"><a href="/WoT/ShowIdentity?id=<% sone.id|html>"><% sone.niceName|html></a></div>
+ </div>
+ <%foreach sone.profile.fields field>
+ <div class="profile-field">
+ <div class="name"><% field.name|html></div>
+ <div class="value"><% field.value|parse></div>
+ </div>
+ <%/foreach>
+
+ <%ifnull ! currentSone>
+ <%if ! sone.current>
+ <p><%= Page.ViewSone.WriteAMessage|l10n|html></p>
+
+ <form action="createPost.html" id="post-message" method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+ <input type="hidden" name="recipient" value="<% sone.id|html>" />
+ <label for="sender"><%= Page.Index.Label.Sender|l10n|html></label>
+ <div class="sender">
+ <select name="sender" title="<%= View.UpdateStatus.Text.ChooseSenderIdentity|l10n|html>">
+ <%foreach localSones localSone>
+ <option value="<% localSone.id|html>"<%if localSone.current> selected="selected"<%/if>><% localSone.niceName|html></option>
+ <%/foreach>
+ </select>
+ </div>
+ <div class="select-sender"><button type="button" title="<%= View.UpdateStatus.Text.ChooseSenderIdentity|l10n|html>">+</button></div><label for="text"><%= Page.Index.Label.Text|l10n|html></label>
+ <input type="text" name="text" value="" />
+ <button type="submit"><%= Page.CreatePost.Button.Post|l10n|html></button>
+ </form>
+ <%/if>
+ <%/if>
- <h1><%= Page.ViewSone.PostList.Title|l10n|insert needle="{sone}" key=sone.niceName|html></h1>
+ <h1><%= Page.ViewSone.PostList.Title|l10n|replace needle="{sone}" replacementKey=sone.niceName|html></h1>
<div id="posts">
- <%getpage parameter=postPage>
- <%paginate list=sone.posts pagesize=25>
+ <%:getpage parameter=postPage>
+ <%:paginate list=sone.posts pagesize=25>
<%= postPage|store key=pageParameter>
<%include include/pagination.html>
<%foreach pagination.items post>