<modelVersion>4.0.0</modelVersion>
<groupId>net.pterodactylus</groupId>
<artifactId>sone</artifactId>
- <version>0.8.6</version>
+ <version>0.8.7</version>
<dependencies>
<dependency>
<groupId>net.pterodactylus</groupId>
<artifactId>utils</artifactId>
- <version>0.12.3</version>
+ <version>${version.utils}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>net.pterodactylus</groupId>
- <artifactId>utils.json</artifactId>
- <version>0.1</version>
- </dependency>
- <dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <version>2.0.1</version>
+ </dependency>
</dependencies>
<repositories>
<repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <version.utils>0.12.4</version.utils>
+ <findbugs.timeout>600000</findbugs.timeout>
</properties>
<build>
<plugins>
<plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>2.5.2</version>
+ <configuration>
+ <timeout>${findbugs.timeout}</timeout>
+ </configuration>
+ </plugin>
+ <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
database.storePostReplies(sone, sone.getReplies());
synchronized (albums) {
synchronized (images) {
- for (Album album : storedSone.get().getAlbums()) {
+ for (Album album : storedSone.get().getRootAlbum().getAlbums()) {
albums.remove(album.getId());
for (Image image : album.getImages()) {
images.remove(image.getId());
}
}
- for (Album album : sone.getAlbums()) {
+ for (Album album : sone.getRootAlbum().getAlbums()) {
albums.put(album.getId(), album);
for (Image image : album.getImages()) {
images.put(image.getId(), image);
synchronized (sones) {
sone.setOptions(storedSone.get().getOptions());
sone.setKnown(storedSone.get().isKnown());
+ sone.setStatus((sone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle);
+ if (sone.isLocal()) {
+ soneInserters.get(storedSone.get()).setSone(sone);
+ touchConfiguration();
+ }
sones.put(sone.getId(), sone);
}
}
for (String friendId : friends) {
followSone(sone, friendId);
}
- sone.setAlbums(topLevelAlbums);
+ for (Album album : sone.getRootAlbum().getAlbums()) {
+ sone.getRootAlbum().removeAlbum(album);
+ }
+ for (Album album : topLevelAlbums) {
+ sone.getRootAlbum().addAlbum(album);
+ }
soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint);
}
synchronized (knownSones) {
* @return The new album
*/
public Album createAlbum(Sone sone) {
- return createAlbum(sone, null);
+ return createAlbum(sone, sone.getRootAlbum());
}
/**
albums.put(album.getId(), album);
}
album.setSone(sone);
- if (parent != null) {
- parent.addAlbum(album);
- } else {
- sone.addAlbum(album);
- }
+ parent.addAlbum(album);
return album;
}
if (!album.isEmpty()) {
return;
}
- if (album.getParent() == null) {
- album.getSone().removeAlbum(album);
- } else {
- album.getParent().removeAlbum(album);
- }
+ album.getParent().removeAlbum(album);
synchronized (albums) {
albums.remove(album.getId());
}
configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null);
/* save albums. first, collect in a flat structure, top-level first. */
- List<Album> albums = FluentIterable.from(sone.getAlbums()).transformAndConcat(Album.FLATTENER).toList();
+ List<Album> albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList();
int albumCounter = 0;
for (Album album : albums) {
configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId());
configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle());
configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription());
- configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent() == null ? null : album.getParent().getId());
+ configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId());
configuration.getStringValue(albumPrefix + "/AlbumImage").setValue(album.getAlbumImage() == null ? null : album.getAlbumImage().getId());
}
configuration.getStringValue(sonePrefix + "/Albums/" + albumCounter + "/ID").setValue(null);
return null;
}
- Sone sone = new Sone(originalSone.getId(), false).setIdentity(originalSone.getIdentity());
+ Sone sone = new Sone(originalSone.getId(), originalSone.isLocal()).setIdentity(originalSone.getIdentity());
SimpleXML soneXml;
try {
sone.setReplies(replies);
sone.setLikePostIds(likedPostIds);
sone.setLikeReplyIds(likedReplyIds);
- sone.setAlbums(topLevelAlbums);
+ for (Album album : topLevelAlbums) {
+ sone.getRootAlbum().addAlbum(album);
+ }
}
return sone;
package net.pterodactylus.sone.core;
+import static com.google.common.base.Preconditions.checkArgument;
+import static net.pterodactylus.sone.data.Album.NOT_EMPTY;
+
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
private final FreenetInterface freenetInterface;
/** The Sone to insert. */
- private final Sone sone;
+ private volatile Sone sone;
/** Whether a modification has been detected. */
private volatile boolean modified = false;
//
/**
- * Changes the insertion delay, i.e. the time the Sone inserter waits after
- * it has noticed a Sone modification before it starts the insert.
+ * Sets the Sone to insert.
+ *
+ * @param sone
+ * The Sone to insert
+ * @return This Sone inserter
+ */
+ public SoneInserter setSone(Sone sone) {
+ checkArgument((this.sone == null) || sone.equals(this.sone), "Sone to insert can not be set to a different Sone");
+ this.sone = sone;
+ return this;
+ }
+
+ /**
+ * Changes the insertion delay, i.e. the time the Sone inserter waits after it
+ * has noticed a Sone modification before it starts the insert.
*
* @param insertionDelay
* The insertion delay (in seconds)
long lastModificationTime = 0;
String lastInsertedFingerprint = lastInsertFingerprint;
String lastFingerprint = "";
+ Sone sone;
while (!shouldStop()) {
try {
/* check every seconds. */
sleep(1000);
/* don’t insert locked Sones. */
+ sone = this.sone;
if (core.isLocked(sone)) {
/* trigger redetection when the Sone is unlocked. */
synchronized (sone) {
soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies()));
soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
- soneProperties.put("albums", FluentIterable.from(sone.getAlbums()).transformAndConcat(Album.FLATTENER).toList());
+ soneProperties.put("albums", FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).filter(NOT_EMPTY).toList());
}
//
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 = 58;
+ private static final int LATEST_EDITION = 60;
/** The event bus. */
private final EventBus eventBus;
* Creates a new trust updater.
*
* @param webOfTrustConnector
- * The web of trust connector
+ * The web of trust connector
*/
@Inject
public WebOfTrustUpdater(WebOfTrustConnector webOfTrustConnector) {
//
/**
- * Updates the trust relation between the truster and the trustee. This
- * method will return immediately and perform a trust update in the
- * background.
+ * Updates the trust relation between the truster and the trustee. This method
+ * will return immediately and perform a trust update in the background.
*
* @param truster
- * The identity giving the trust
+ * The identity giving the trust
* @param trustee
- * The identity receiving the trust
+ * The identity receiving the trust
* @param score
- * The new level of trust (from -100 to 100, may be {@code null}
- * to remove the trust completely)
+ * The new level of trust (from -100 to 100, may be {@code null} to remove
+ * the trust completely)
* @param comment
- * The comment of the trust relation
+ * The comment of the trust relation
*/
public void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment) {
SetTrustJob setTrustJob = new SetTrustJob(truster, trustee, score, comment);
* Adds the given context to the given own identity.
*
* @param ownIdentity
- * The own identity to add the context to
+ * The own identity to add the context to
* @param context
- * The context to add
+ * The context to add
*/
public void addContext(OwnIdentity ownIdentity, String context) {
addContextWait(ownIdentity, context, false);
}
/**
- * Adds the given context to the given own identity, waiting for completion
- * of the operation.
+ * Adds the given context to the given own identity, waiting for completion of
+ * the operation.
*
* @param ownIdentity
- * The own identity to add the context to
+ * The own identity to add the context to
* @param context
- * The context to add
+ * The context to add
* @return {@code true} if the context was added successfully, {@code false}
* otherwise
*/
}
/**
- * Adds the given context to the given own identity, waiting for completion
- * of the operation.
+ * Adds the given context to the given own identity, waiting for completion of
+ * the operation.
*
* @param ownIdentity
- * The own identity to add the context to
+ * The own identity to add the context to
* @param context
- * The context to add
+ * The context to add
* @param wait
- * {@code true} to wait for the end of the operation,
- * {@code false} to return immediately
- * @return {@code true} if the context was added successfully, {@code false}
- * if the context was not added successfully, or if the job should
- * not wait for completion
+ * {@code true} to wait for the end of the operation, {@code false} to return
+ * immediately
+ * @return {@code true} if the context was added successfully, {@code false} if
+ * the context was not added successfully, or if the job should not
+ * wait for completion
*/
private boolean addContextWait(OwnIdentity ownIdentity, String context, boolean wait) {
AddContextJob addContextJob = new AddContextJob(ownIdentity, context);
* Removes the given context from the given own identity.
*
* @param ownIdentity
- * The own identity to remove the context from
+ * The own identity to remove the context from
* @param context
- * The context to remove
+ * The context to remove
*/
public void removeContext(OwnIdentity ownIdentity, String context) {
RemoveContextJob removeContextJob = new RemoveContextJob(ownIdentity, context);
* Sets a property on the given own identity.
*
* @param ownIdentity
- * The own identity to set the property on
+ * The own identity to set the property on
* @param propertyName
- * The name of the property to set
+ * The name of the property to set
* @param propertyValue
- * The value of the property to set
+ * The value of the property to set
*/
public void setProperty(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
SetPropertyJob setPropertyJob = new SetPropertyJob(ownIdentity, propertyName, propertyValue);
* Removes a property from the given own identity.
*
* @param ownIdentity
- * The own identity to remove the property from
+ * The own identity to remove the property from
* @param propertyName
- * The name of the property to remove
+ * The name of the property to remove
*/
public void removeProperty(OwnIdentity ownIdentity, String propertyName) {
setProperty(ownIdentity, propertyName, null);
// SERVICE METHODS
//
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
protected void serviceRun() {
while (!shouldStop()) {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
protected void serviceStop() {
try {
/**
* Performs the actual update operation.
- * <p>
+ * <p/>
* The implementation of this class does nothing.
*/
public void run() {
}
/**
- * Waits for completion of this job or stopping of the WebOfTrust
- * updater.
+ * Waits for completion of this job or stopping of the WebOfTrust updater.
*
* @return {@code true} if this job finished successfully, {@code false}
* otherwise
* Signals that this job has finished.
*
* @param success
- * {@code true} if this job finished successfully,
- * {@code false} otherwise
+ * {@code true} if this job finished successfully, {@code false} otherwise
*/
protected void finish(boolean success) {
synchronized (syncObject) {
}
/**
- * Base class for WebOfTrust trust update jobs.
+ * Update job that sets the trust relation between two identities.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
- private class WebOfTrustTrustUpdateJob extends WebOfTrustUpdateJob {
+ private class SetTrustJob extends WebOfTrustUpdateJob {
/** The identity giving the trust. */
- protected final OwnIdentity truster;
+ private final OwnIdentity truster;
/** The identity receiving the trust. */
- protected final Identity trustee;
-
- /**
- * Creates a new trust update job.
- *
- * @param truster
- * The identity giving the trust
- * @param trustee
- * The identity receiving the trust
- */
- @SuppressWarnings("synthetic-access")
- public WebOfTrustTrustUpdateJob(OwnIdentity truster, Identity trustee) {
- this.truster = truster;
- this.trustee = trustee;
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object object) {
- if ((object == null) || !object.getClass().equals(getClass())) {
- return false;
- }
- WebOfTrustTrustUpdateJob updateJob = (WebOfTrustTrustUpdateJob) object;
- return ((truster == null) ? (updateJob.truster == null) : updateJob.truster.equals(truster)) && ((trustee == null) ? (updateJob.trustee == null) : updateJob.trustee.equals(trustee));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return getClass().hashCode() ^ ((truster == null) ? 0 : truster.hashCode()) ^ ((trustee == null) ? 0 : trustee.hashCode());
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), (truster == null) ? null : truster.getId(), (trustee == null) ? null : trustee.getId());
- }
-
- }
-
- /**
- * Update job that sets the trust relation between two identities.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- private class SetTrustJob extends WebOfTrustTrustUpdateJob {
+ private final Identity trustee;
/** The score of the relation. */
private final Integer score;
* Creates a new set trust job.
*
* @param truster
- * The identity giving the trust
+ * The identity giving the trust
* @param trustee
- * The identity receiving the trust
+ * The identity receiving the trust
* @param score
- * The score of the trust (from -100 to 100, may be
- * {@code null} to remote the trust relation completely)
+ * The score of the trust (from -100 to 100, may be {@code null} to remote
+ * the trust relation completely)
* @param comment
- * The comment of the trust relation
+ * The comment of the trust relation
*/
public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) {
- super(truster, trustee);
+ this.truster = truster;
+ this.trustee = trustee;
this.score = score;
this.comment = comment;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
@SuppressWarnings("synthetic-access")
public void run() {
}
}
+ //
+ // OBJECT METHODS
+ //
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object object) {
+ if ((object == null) || !object.getClass().equals(getClass())) {
+ return false;
+ }
+ SetTrustJob updateJob = (SetTrustJob) object;
+ return ((truster == null) ? (updateJob.truster == null) : updateJob.truster.equals(truster)) && ((trustee == null) ? (updateJob.trustee == null) : updateJob.trustee.equals(trustee));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return getClass().hashCode() ^ ((truster == null) ? 0 : truster.hashCode()) ^ ((trustee == null) ? 0 : trustee.hashCode());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), (truster == null) ? null : truster.getId(), (trustee == null) ? null : trustee.getId());
+ }
+
}
/**
* Creates a new context update job.
*
* @param ownIdentity
- * The own identity to update
+ * The own identity to update
* @param context
- * The context to update
+ * The context to update
*/
@SuppressWarnings("synthetic-access")
public WebOfTrustContextUpdateJob(OwnIdentity ownIdentity, String context) {
// OBJECT METHODS
//
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
public boolean equals(Object object) {
if ((object == null) || !object.getClass().equals(getClass())) {
return updateJob.ownIdentity.equals(ownIdentity) && updateJob.context.equals(context);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
public int hashCode() {
return getClass().hashCode() ^ ownIdentity.hashCode() ^ context.hashCode();
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
public String toString() {
return String.format("%s[ownIdentity=%s,context=%s]", getClass().getSimpleName(), ownIdentity, context);
* Creates a new add-context job.
*
* @param ownIdentity
- * The own identity whose contexts to manage
+ * The own identity whose contexts to manage
* @param context
- * The context to add
+ * The context to add
*/
public AddContextJob(OwnIdentity ownIdentity, String context) {
super(ownIdentity, context);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
@SuppressWarnings("synthetic-access")
public void run() {
* Creates a new remove-context job.
*
* @param ownIdentity
- * The own identity whose contexts to manage
+ * The own identity whose contexts to manage
* @param context
- * The context to remove
+ * The context to remove
*/
public RemoveContextJob(OwnIdentity ownIdentity, String context) {
super(ownIdentity, context);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
@SuppressWarnings("synthetic-access")
public void run() {
}
/**
- * Base class for update jobs that deal with properties.
+ * WebOfTrust update job that sets a property on an {@link OwnIdentity}.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
- private class WebOfTrustPropertyUpdateJob extends WebOfTrustUpdateJob {
+ private class SetPropertyJob extends WebOfTrustUpdateJob {
/** The own identity to update properties on. */
- protected final OwnIdentity ownIdentity;
+ private final OwnIdentity ownIdentity;
/** The name of the property to update. */
- protected final String propertyName;
-
- /**
- * Creates a new property update job.
- *
- * @param ownIdentity
- * The own identity to update the property on
- * @param propertyName
- * The name of the property to update
- */
- @SuppressWarnings("synthetic-access")
- public WebOfTrustPropertyUpdateJob(OwnIdentity ownIdentity, String propertyName) {
- this.ownIdentity = ownIdentity;
- this.propertyName = propertyName;
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object object) {
- if ((object == null) || !object.getClass().equals(getClass())) {
- return false;
- }
- WebOfTrustPropertyUpdateJob updateJob = (WebOfTrustPropertyUpdateJob) object;
- return updateJob.ownIdentity.equals(ownIdentity) && updateJob.propertyName.equals(propertyName);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return getClass().hashCode() ^ ownIdentity.hashCode() ^ propertyName.hashCode();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return String.format("%s[ownIdentity=%s,propertyName=%s]", getClass().getSimpleName(), ownIdentity, propertyName);
- }
-
- }
-
- /**
- * WebOfTrust update job that sets a property on an {@link OwnIdentity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- private class SetPropertyJob extends WebOfTrustPropertyUpdateJob {
+ private final String propertyName;
/** The value of the property to set. */
private final String propertyValue;
* Creates a new set-property job.
*
* @param ownIdentity
- * The own identity to set the property on
+ * The own identity to set the property on
* @param propertyName
- * The name of the property to set
+ * The name of the property to set
* @param propertyValue
- * The value of the property to set
+ * The value of the property to set
*/
public SetPropertyJob(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
- super(ownIdentity, propertyName);
+ this.ownIdentity = ownIdentity;
+ this.propertyName = propertyName;
this.propertyValue = propertyValue;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
@SuppressWarnings("synthetic-access")
public void run() {
}
}
+ //
+ // OBJECT METHODS
+ //
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object object) {
+ if ((object == null) || !object.getClass().equals(getClass())) {
+ return false;
+ }
+ SetPropertyJob updateJob = (SetPropertyJob) object;
+ return updateJob.ownIdentity.equals(ownIdentity) && updateJob.propertyName.equals(propertyName);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return getClass().hashCode() ^ ownIdentity.hashCode() ^ propertyName.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return String.format("%s[ownIdentity=%s,propertyName=%s]", getClass().getSimpleName(), ownIdentity, propertyName);
+ }
+
}
}
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.annotation.Nonnull;
import com.google.common.base.Function;
import com.google.common.base.Optional;
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Album implements Fingerprintable {
+public class Album implements Identified, Fingerprintable {
/** Compares two {@link Album}s by {@link #getTitle()}. */
public static final Comparator<Album> TITLE_COMPARATOR = new Comparator<Album>() {
public static final Function<Album, List<Album>> FLATTENER = new Function<Album, List<Album>>() {
@Override
+ @Nonnull
public List<Album> apply(Album album) {
+ if (album == null) {
+ return emptyList();
+ }
List<Album> albums = new ArrayList<Album>();
albums.add(album);
for (Album subAlbum : album.getAlbums()) {
}
};
+ /** Function that transforms an album into the images it contains. */
+ public static final Function<Album, List<Image>> IMAGES = new Function<Album, List<Image>>() {
+
+ @Override
+ @Nonnull
+ public List<Image> apply(Album album) {
+ return (album != null) ? album.getImages() : Collections.<Image>emptyList();
+ }
+ };
+
/**
* Filter that removes all albums that do not have any images in any album
* below it.
@Override
public boolean apply(Album album) {
+ /* so, we flatten all albums below the given one and check whether at least one album… */
return FluentIterable.from(asList(album)).transformAndConcat(FLATTENER).anyMatch(new Predicate<Album>() {
@Override
public boolean apply(Album album) {
- return !album.getImages().isEmpty();
+ /* …contains any inserted images. */
+ return !album.getImages().isEmpty() && FluentIterable.from(album.getImages()).allMatch(new Predicate<Image>() {
+
+ @Override
+ public boolean apply(Image input) {
+ return input.isInserted();
+ }
+ });
}
});
}
public void addAlbum(Album album) {
checkNotNull(album, "album must not be null");
checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
- checkState((this.parent == null) || (this.parent.equals(album.parent)), "album must not already be set to some other Sone");
album.setParent(this);
if (!albums.contains(album)) {
albums.add(album);
}
/**
+ * Returns whether this album is an identitiy’s root album.
+ *
+ * @return {@code true} if this album is an identity’s root album, {@code
+ * false} otherwise
+ */
+ public boolean isRoot() {
+ return parent == null;
+ }
+
+ /**
* Returns the parent album of this album.
*
* @return The parent album of this album, or {@code null} if this album
--- /dev/null
+/*
+ * Sone - Identified.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+
+/**
+ * Interface for all objects that expose an ID.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface Identified {
+
+ /** Function to extract the ID from an optional. */
+ public static final Function<Optional<? extends Identified>, Optional<String>> GET_ID = new Function<Optional<? extends Identified>, Optional<String>>() {
+
+ @Override
+ @Nonnull
+ public Optional<String> apply(Optional<? extends Identified> identified) {
+ return (identified == null) ? Optional.<String>absent() : (identified.isPresent() ? Optional.of(identified.get().getId()) : Optional.<String>absent());
+ }
+ };
+
+ /**
+ * Returns the ID of this element.
+ *
+ * @return The ID of this element
+ */
+ public String getId();
+
+}
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Image implements Fingerprintable {
+public class Image implements Identified, Fingerprintable {
/** The ID of the image. */
private final String id;
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public interface Post {
+public interface Post extends Identified {
/** Comparator for posts, sorts descending by time. */
public static final Comparator<Post> TIME_COMPARATOR = new Comparator<Post>() {
@Override
public boolean apply(Post post) {
- return post.getTime() <= System.currentTimeMillis();
+ return (post == null) ? false : post.getTime() <= System.currentTimeMillis();
}
};
public Optional<String> getRecipientId();
/**
- * Returns the recipient of this post, if any. As this method can return
- * {@link Optional#absent()} if the post has a recipient which has not yet
- * been loaded, it is recommended to use {@link #hasRecipient()} to check
- * for the presence of a recipient.
+ * Returns the recipient of this post, if any.
*
* @return The recipient of this post, or {@link Optional#absent()} if there
* is no recipient
@Override
public boolean apply(PostReply postReply) {
- return postReply.getPost().isPresent();
+ return (postReply == null) ? false : postReply.getPost().isPresent();
}
};
* The type of the reply
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public interface Reply<T extends Reply<T>> {
+public interface Reply<T extends Reply<T>> extends Identified {
/** Comparator that sorts replies ascending by time. */
public static final Comparator<? super Reply<?>> TIME_COMPARATOR = new Comparator<Reply<?>>() {
*/
@Override
public boolean apply(Reply<?> reply) {
- return reply.getTime() <= System.currentTimeMillis();
+ return (reply == null) ? false : reply.getTime() <= System.currentTimeMillis();
}
};
package net.pterodactylus.sone.data;
-import static com.google.common.base.Preconditions.*;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.FluentIterable.from;
+import static java.util.Arrays.asList;
+import static net.pterodactylus.sone.data.Album.FLATTENER;
+import static net.pterodactylus.sone.data.Album.IMAGES;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Level;
import java.util.logging.Logger;
import freenet.keys.FreenetURI;
import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
+import com.google.common.primitives.Ints;
/**
* A Sone defines everything about a user: her profile, her status updates, her
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Sone implements Fingerprintable, Comparable<Sone> {
+public class Sone implements Identified, Fingerprintable, Comparable<Sone> {
/**
* Enumeration for the possible states of a {@link Sone}.
*/
@Override
public int compare(Sone leftSone, Sone rightSone) {
- return rightSone.getAllImages().size() - leftSone.getAllImages().size();
+ int rightSoneImageCount = from(asList(rightSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size();
+ int leftSoneImageCount = from(asList(leftSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size();
+ /* sort descending. */
+ return Ints.compare(rightSoneImageCount, leftSoneImageCount);
}
};
@Override
public boolean apply(Sone sone) {
- return sone.getTime() != 0;
+ return (sone == null) ? false : sone.getTime() != 0;
}
};
@Override
public boolean apply(Sone sone) {
- return sone.getIdentity() instanceof OwnIdentity;
+ return (sone == null) ? false : sone.getIdentity() instanceof OwnIdentity;
}
};
@Override
public boolean apply(Sone sone) {
- return !sone.getAlbums().isEmpty();
+ return (sone == null) ? false : !sone.getRootAlbum().getAlbums().isEmpty();
}
};
/** The IDs of all liked replies. */
private final Set<String> likedReplyIds = new CopyOnWriteArraySet<String>();
- /** The albums of this Sone. */
- private final List<Album> albums = new CopyOnWriteArrayList<Album>();
+ /** The root album containing all albums. */
+ private final Album rootAlbum = new Album().setSone(this);
/** Sone-specific options. */
private Options options = new Options();
}
/**
- * Returns the albums of this Sone.
+ * Returns the root album that contains all visible albums of this Sone.
*
- * @return The albums of this Sone
+ * @return The root album of this Sone
*/
- public List<Album> getAlbums() {
- return Collections.unmodifiableList(albums);
- }
-
- /**
- * Returns all images of a Sone. Images of a album are inserted into this list
- * before images of all child albums.
- *
- * @return The list of all images
- */
- public List<Image> getAllImages() {
- List<Image> allImages = new ArrayList<Image>();
- for (Album album : FluentIterable.from(getAlbums()).transformAndConcat(Album.FLATTENER).toList()) {
- allImages.addAll(album.getImages());
- }
- return allImages;
- }
-
- /**
- * Adds an album to this Sone.
- *
- * @param album
- * The album to add
- */
- public void addAlbum(Album album) {
- checkNotNull(album, "album must not be null");
- checkArgument(album.getSone().equals(this), "album must belong to this Sone");
- if (!albums.contains(album)) {
- albums.add(album);
- }
- }
-
- /**
- * Sets the albums of this Sone.
- *
- * @param albums
- * The albums of this Sone
- */
- public void setAlbums(Collection<? extends Album> albums) {
- checkNotNull(albums, "albums must not be null");
- this.albums.clear();
- for (Album album : albums) {
- addAlbum(album);
- }
- }
-
- /**
- * Removes an album from this Sone.
- *
- * @param album
- * The album to remove
- */
- public void removeAlbum(Album album) {
- checkNotNull(album, "album must not be null");
- checkArgument(album.getSone().equals(this), "album must belong to this Sone");
- albums.remove(album);
- }
-
- /**
- * Moves the given album up in this album’s albums. If the album is already the
- * first album, nothing happens.
- *
- * @param album
- * The album to move up
- * @return The album that the given album swapped the place with, or
- * <code>null</code> if the album did not change its place
- */
- public Album moveAlbumUp(Album album) {
- checkNotNull(album, "album must not be null");
- checkArgument(album.getSone().equals(this), "album must belong to this Sone");
- checkArgument(album.getParent() == null, "album must not have a parent");
- int oldIndex = albums.indexOf(album);
- if (oldIndex <= 0) {
- return null;
- }
- albums.remove(oldIndex);
- albums.add(oldIndex - 1, album);
- return albums.get(oldIndex);
- }
-
- /**
- * Moves the given album down in this album’s albums. If the album is already
- * the last album, nothing happens.
- *
- * @param album
- * The album to move down
- * @return The album that the given album swapped the place with, or
- * <code>null</code> if the album did not change its place
- */
- public Album moveAlbumDown(Album album) {
- checkNotNull(album, "album must not be null");
- checkArgument(album.getSone().equals(this), "album must belong to this Sone");
- checkArgument(album.getParent() == null, "album must not have a parent");
- int oldIndex = albums.indexOf(album);
- if ((oldIndex < 0) || (oldIndex >= (albums.size() - 1))) {
- return null;
- }
- albums.remove(oldIndex);
- albums.add(oldIndex + 1, album);
- return albums.get(oldIndex);
+ public Album getRootAlbum() {
+ return rootAlbum;
}
/**
hash.putString(")");
hash.putString("Albums(");
- for (Album album : albums) {
+ for (Album album : rootAlbum.getAlbums()) {
+ if (!Album.NOT_EMPTY.apply(album)) {
+ continue;
+ }
hash.putString(album.getFingerprint());
}
hash.putString(")");
/** {@inheritDoc} */
@Override
public String toString() {
- return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + ")]";
+ return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + "),albums(" + getRootAlbum().getAlbums().size() + ")]";
}
}
}
return new MessageFormat(webInterface.getL10n().getString(String.valueOf(data)), new Locale(webInterface.getL10n().getSelectedLanguage().shortCode)).format(parameterValues.toArray());
}
+
}
return insertUri;
}
+ //
+ // OBJECT METHODS
+ //
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ return super.equals(object);
+ }
+
}
}
/** The version. */
- public static final Version VERSION = new Version(0, 8, 6);
+ public static final Version VERSION = new Version(0, 8, 7);
/** The logger. */
private static final Logger logger = Logging.getLogger(SonePlugin.class);
package net.pterodactylus.sone.template;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.util.template.Accessor;
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>>();
+ List<Link> backlinks = new ArrayList<Link>();
Album currentAlbum = album;
- while (currentAlbum != null) {
- backlinks.add(0, createLink("imageBrowser.html?album=" + currentAlbum.getId(), currentAlbum.getTitle()));
+ while (!currentAlbum.equals(album.getSone().getRootAlbum())) {
+ backlinks.add(0, new Link("imageBrowser.html?album=" + currentAlbum.getId(), currentAlbum.getTitle()));
currentAlbum = currentAlbum.getParent();
}
- backlinks.add(0, createLink("imageBrowser.html?sone=" + album.getSone().getId(), SoneAccessor.getNiceName(album.getSone())));
+ backlinks.add(0, new Link("imageBrowser.html?sone=" + album.getSone().getId(), SoneAccessor.getNiceName(album.getSone())));
return backlinks;
}
return super.get(templateContext, object, member);
}
- //
- // PRIVATE METHODS
- //
-
/**
- * Creates a map containing mappings for “target” and “link.”
+ * Container for links.
*
- * @param target
- * The target to link to
- * @param name
- * The name of the link
- * @return The created map containing the mappings
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
- private static Map<String, String> createLink(String target, String name) {
- Map<String, String> link = new HashMap<String, String>();
- link.put("target", target);
- link.put("name", name);
- return link;
+ private static class Link {
+
+ /** The target of the link. */
+ private final String target;
+
+ /** The name of the link. */
+ private final String name;
+
+ /**
+ * Creates a new link.
+ *
+ * @param target
+ * The target of the link
+ * @param name
+ * The name of the link
+ */
+ private Link(String target, String name) {
+ this.target = target;
+ this.name = name;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the target of the link.
+ *
+ * @return The target of the link
+ */
+ public String getTarget() {
+ return target;
+ }
+
+ /**
+ * Returns the name of the link.
+ *
+ * @return The name of the link
+ */
+ public String getName() {
+ return name;
+ }
+
}
}
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import net.pterodactylus.sone.data.Post;
replies.add(reply);
}
Map<Post, Map<String, Set<?>>> result = new HashMap<Post, Map<String, Set<?>>>();
- for (Post post : postSones.keySet()) {
- if (result.containsKey(post)) {
+ for (Entry<Post, Set<Sone>> postEntry : postSones.entrySet()) {
+ if (result.containsKey(postEntry.getKey())) {
continue;
}
Map<String, Set<?>> postResult = new HashMap<String, Set<?>>();
- postResult.put("sones", postSones.get(post));
- postResult.put("replies", postReplies.get(post));
- result.put(post, postResult);
+ postResult.put("sones", postEntry.getValue());
+ postResult.put("replies", postReplies.get(postEntry.getKey()));
+ result.put(postEntry.getKey(), postResult);
}
return result;
}
package net.pterodactylus.sone.template;
+import static com.google.common.collect.FluentIterable.from;
+import static java.util.Arrays.asList;
+import static net.pterodactylus.sone.data.Album.FLATTENER;
+import static net.pterodactylus.sone.data.Album.IMAGES;
+
import java.util.logging.Level;
import java.util.logging.Logger;
return new Trust(null, null, null);
}
return trust;
+ } else if (member.equals("allImages")) {
+ return from(asList(sone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES);
}
return super.get(templateContext, object, member);
}
}
String dataString = String.valueOf(data);
int dataLength = dataString.length();
- if (lengthString == null) {
- if (start < 0) {
- return dataString.substring(dataLength + start);
- }
- return dataString.substring(start);
- }
int length = Integer.MAX_VALUE;
try {
length = Integer.parseInt(lengthString);
Sone currentSone = getCurrentSone(request.getToadletContext());
String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
Album parent = webInterface.getCore().getAlbum(parentId, false);
+ if (parentId.equals("")) {
+ parent = currentSone.getRootAlbum();
+ }
Album album = webInterface.getCore().createAlbum(currentSone, parent);
album.setTitle(name).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description));
webInterface.getCore().touchConfiguration();
@Override
protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
+ List<Sone> localSones = new ArrayList<Sone>(webInterface.getCore().getLocalSones());
+ Collections.sort(localSones, Sone.NICE_NAME_COMPARATOR);
+ templateContext.set("sones", localSones);
List<OwnIdentity> ownIdentitiesWithoutSone = getOwnIdentitiesWithoutSone(webInterface.getCore());
templateContext.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
if (request.getMethod() == Method.POST) {
}
Album parentAlbum = album.getParent();
webInterface.getCore().deleteAlbum(album);
- if (parentAlbum == null) {
+ if (parentAlbum.equals(album.getSone().getRootAlbum())) {
throw new RedirectException("imageBrowser.html?sone=" + album.getSone().getId());
}
throw new RedirectException("imageBrowser.html?album=" + parentAlbum.getId());
protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
- Sone currentSone = getCurrentSone(request.getToadletContext());
String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
Album album = webInterface.getCore().getAlbum(albumId, false);
if (album == null) {
throw new RedirectException("noPermission.html");
}
if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveLeft", 4))) {
- if (album.getParent() == null) {
- currentSone.moveAlbumUp(album);
- webInterface.getCore().touchConfiguration();
- throw new RedirectException("imageBrowser.html?sone=" + currentSone.getId());
- }
album.getParent().moveAlbumUp(album);
webInterface.getCore().touchConfiguration();
throw new RedirectException("imageBrowser.html?album=" + album.getParent().getId());
} else if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveRight", 4))) {
- if (album.getParent() == null) {
- currentSone.moveAlbumDown(album);
- webInterface.getCore().touchConfiguration();
- throw new RedirectException("imageBrowser.html?sone=" + currentSone.getId());
- }
album.getParent().moveAlbumDown(album);
webInterface.getCore().touchConfiguration();
throw new RedirectException("imageBrowser.html?album=" + album.getParent().getId());
package net.pterodactylus.sone.web;
+import static com.google.common.collect.FluentIterable.from;
import static net.pterodactylus.sone.data.Album.FLATTENER;
import static net.pterodactylus.sone.data.Album.NOT_EMPTY;
import static net.pterodactylus.sone.data.Album.TITLE_COMPARATOR;
import java.util.List;
import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Image;
templateContext.set("galleryRequested", true);
List<Album> albums = new ArrayList<Album>();
for (Sone sone : webInterface.getCore().getSones()) {
- albums.addAll(FluentIterable.from(sone.getAlbums()).transformAndConcat(FLATTENER).filter(NOT_EMPTY).toList());
+ albums.addAll(from(sone.getRootAlbum().getAlbums()).transformAndConcat(FLATTENER).filter(NOT_EMPTY).toList());
}
Collections.sort(albums, TITLE_COMPARATOR);
Pagination<Album> albumPagination = new Pagination<Album>(albums, 12).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
@Override
public boolean apply(Hit<?> hit) {
- return hit.getScore() > 0;
+ return (hit == null) ? false : hit.getScore() > 0;
}
};
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user bookmark a post.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String id = request.getHttpRequest().getParam("post", null);
if ((id == null) || (id.length() == 0)) {
return createErrorJsonObject("invalid-post-id");
import net.pterodactylus.sone.text.TextFilter;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
import com.google.common.base.Optional;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
Sone sone = getCurrentSone(request.getToadletContext());
if (sone == null) {
return createErrorJsonObject("auth-required");
import net.pterodactylus.sone.text.TextFilter;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* This AJAX page create a reply.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String postId = request.getHttpRequest().getParam("post");
String text = request.getHttpRequest().getParam("text").trim();
String senderId = request.getHttpRequest().getParam("sender");
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* This AJAX page deletes a post.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String postId = request.getHttpRequest().getParam("post");
Optional<Post> post = webInterface.getCore().getPost(postId);
if (!post.isPresent()) {
package net.pterodactylus.sone.web.ajax;
+import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
+
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.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
/**
* AJAX page that lets the user delete a profile field.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String fieldId = request.getHttpRequest().getParam("field");
Sone currentSone = getCurrentSone(request.getToadletContext());
Profile profile = currentSone.getProfile();
profile.removeField(field);
currentSone.setProfile(profile);
webInterface.getCore().touchConfiguration();
- return createSuccessJsonObject().put("field", new JsonObject().put("id", field.getId()));
+ return createSuccessJsonObject().put("field", new ObjectNode(instance).put("id", new TextNode(field.getId())));
}
}
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* This AJAX page deletes a reply.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String replyId = request.getHttpRequest().getParam("reply");
Optional<PostReply> reply = webInterface.getCore().getPostReply(replyId);
if (!reply.isPresent()) {
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.notify.Notification;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String notificationId = request.getHttpRequest().getParam("notification");
Notification notification = webInterface.getNotifications().getNotification(notificationId);
if (notification == null) {
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user distrust a Sone.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
if (currentSone == null) {
return createErrorJsonObject("auth-required");
import net.pterodactylus.sone.text.TextFilter;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* Page that stores a user’s album modifications.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String albumId = request.getHttpRequest().getParam("album");
Album album = webInterface.getCore().getAlbum(albumId, false);
if (album == null) {
return createErrorJsonObject("not-authorized");
}
if ("true".equals(request.getHttpRequest().getParam("moveLeft"))) {
- Album swappedAlbum = (album.getParent() != null) ? album.getParent().moveAlbumUp(album) : album.getSone().moveAlbumUp(album);
+ Album swappedAlbum = album.getParent().moveAlbumUp(album);
webInterface.getCore().touchConfiguration();
return createSuccessJsonObject().put("sourceAlbumId", album.getId()).put("destinationAlbumId", swappedAlbum.getId());
}
if ("true".equals(request.getHttpRequest().getParam("moveRight"))) {
- Album swappedAlbum = (album.getParent() != null) ? album.getParent().moveAlbumDown(album) : album.getSone().moveAlbumDown(album);
+ Album swappedAlbum = album.getParent().moveAlbumDown(album);
webInterface.getCore().touchConfiguration();
return createSuccessJsonObject().put("sourceAlbumId", album.getId()).put("destinationAlbumId", swappedAlbum.getId());
}
import net.pterodactylus.sone.text.TextFilter;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.template.TemplateContext;
import com.google.common.collect.ImmutableMap;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String imageId = request.getHttpRequest().getParam("image");
Image image = webInterface.getCore().getImage(imageId, false);
if (image == null) {
String description = request.getHttpRequest().getParam("description").trim();
image.setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description));
webInterface.getCore().touchConfiguration();
- return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", (String) parserFilter.format(new TemplateContext(), image.getDescription(), ImmutableMap.<String, Object> builder().put("sone", image.getSone()).build()));
+ return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", (String) parserFilter.format(new TemplateContext(), image.getDescription(), ImmutableMap.<String, Object>builder().put("sone", image.getSone()).build()));
}
}
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user rename a profile field.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String fieldId = request.getHttpRequest().getParam("field");
Sone currentSone = getCurrentSone(request.getToadletContext());
Profile profile = currentSone.getProfile();
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets a Sone follow another Sone.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
Optional<Sone> sone = webInterface.getCore().getSone(soneId);
if (!sone.isPresent()) {
package net.pterodactylus.sone.web.ajax;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
+import static net.pterodactylus.sone.data.Sone.NICE_NAME_COMPARATOR;
+
import java.util.Set;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.template.SoneAccessor;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonArray;
-import net.pterodactylus.util.json.JsonObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Optional;
+import com.google.common.collect.FluentIterable;
/**
* AJAX page that retrieves the number of “likes” a {@link Post} has.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String type = request.getHttpRequest().getParam("type", null);
String id = request.getHttpRequest().getParam(type, null);
if ((id == null) || (id.length() == 0)) {
* The Sones to convert to an array
* @return The Sones, sorted by name
*/
- private static JsonArray getSones(Set<Sone> sones) {
- JsonArray soneArray = new JsonArray();
- List<Sone> sortedSones = new ArrayList<Sone>(sones);
- Collections.sort(sortedSones, Sone.NICE_NAME_COMPARATOR);
- for (Sone sone : sortedSones) {
- soneArray.add(new JsonObject().put("id", sone.getId()).put("name", SoneAccessor.getNiceName(sone)));
+ private static JsonNode getSones(Set<Sone> sones) {
+ ArrayNode soneArray = new ArrayNode(instance);
+ for (Sone sone : FluentIterable.from(sones).toSortedList(NICE_NAME_COMPARATOR)) {
+ soneArray.add(new ObjectNode(instance).put("id", sone.getId()).put("name", SoneAccessor.getNiceName(sone)));
}
return soneArray;
}
package net.pterodactylus.sone.web.ajax;
+import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
+
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collection;
import net.pterodactylus.sone.notify.ListNotificationFilters;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-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;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
/**
* AJAX handler to return all current notifications.
*
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
Collection<Notification> notifications = webInterface.getNotifications().getNotifications();
List<Notification> filteredNotifications = ListNotificationFilters.filterNotifications(notifications, currentSone);
Collections.sort(filteredNotifications, Notification.CREATED_TIME_SORTER);
- JsonArray jsonNotifications = new JsonArray();
+ ArrayNode jsonNotifications = new ArrayNode(instance);
for (Notification notification : filteredNotifications) {
jsonNotifications.add(createJsonNotification(request, notification));
}
* The notification to create a JSON object
* @return The JSON object
*/
- private JsonObject createJsonNotification(FreenetRequest request, Notification notification) {
- JsonObject jsonNotification = new JsonObject();
+ private JsonNode createJsonNotification(FreenetRequest request, Notification notification) {
+ ObjectNode jsonNotification = new ObjectNode(instance);
jsonNotification.put("id", notification.getId());
StringWriter notificationWriter = new StringWriter();
try {
* The current Sone (may be {@code null})
* @return The current options
*/
- private static JsonObject createJsonOptions(Sone currentSone) {
- JsonObject options = new JsonObject();
+ private static JsonNode createJsonOptions(Sone currentSone) {
+ ObjectNode options = new ObjectNode(instance);
if (currentSone != null) {
options.put("ShowNotification/NewSones", currentSone.getOptions().getBooleanOption("ShowNotification/NewSones").get());
options.put("ShowNotification/NewPosts", currentSone.getOptions().getBooleanOption("ShowNotification/NewPosts").get());
package net.pterodactylus.sone.web.ajax;
import java.io.StringWriter;
+import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
import com.google.common.base.Optional;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.io.Closer;
-import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.template.TemplateException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
/**
* This AJAX handler retrieves information and rendered representation of a
* {@link Post}.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String postId = request.getHttpRequest().getParam("post");
Optional<Post> post = webInterface.getCore().getPost(postId);
if (!post.isPresent()) {
* The currently logged in Sone (to store in the template)
* @return The JSON representation of the post
*/
- private JsonObject createJsonPost(FreenetRequest request, Post post, Sone currentSone) {
- JsonObject jsonPost = new JsonObject();
+ private JsonNode createJsonPost(FreenetRequest request, Post post, Sone currentSone) {
+ ObjectNode jsonPost = new ObjectNode(instance);
jsonPost.put("id", post.getId());
jsonPost.put("sone", post.getSone().getId());
jsonPost.put("recipient", post.getRecipientId().orNull());
package net.pterodactylus.sone.web.ajax;
import java.io.StringWriter;
+import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
import com.google.common.base.Optional;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.io.Closer;
-import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.template.TemplateException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
/**
* This AJAX page returns the details of a reply.
*
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String replyId = request.getHttpRequest().getParam("reply");
Optional<PostReply> reply = webInterface.getCore().getPostReply(replyId);
if (!reply.isPresent()) {
* The currently logged in Sone (to store in the template)
* @return The JSON representation of the reply
*/
- private JsonObject createJsonReply(FreenetRequest request, PostReply reply, Sone currentSone) {
- JsonObject jsonReply = new JsonObject();
+ private JsonNode createJsonReply(FreenetRequest request, PostReply reply, Sone currentSone) {
+ ObjectNode jsonReply = new ObjectNode(instance);
jsonReply.put("id", reply.getId());
jsonReply.put("postId", reply.getPostId());
jsonReply.put("soneId", reply.getSone().getId());
package net.pterodactylus.sone.web.ajax;
+import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
+
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import net.pterodactylus.sone.template.SoneAccessor;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonArray;
-import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.notify.Notification;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
final Sone currentSone = getCurrentSone(request.getToadletContext(), false);
/* load Sones. always return the status of the current Sone. */
Set<Sone> sones = new HashSet<Sone>(Collections.singleton(getCurrentSone(request.getToadletContext(), false)));
sones.add(webInterface.getCore().getSone(soneId).orNull());
}
}
- JsonArray jsonSones = new JsonArray();
+ ArrayNode jsonSones = new ArrayNode(instance);
for (Sone sone : sones) {
if (sone == null) {
continue;
}
- JsonObject jsonSone = createJsonSone(sone);
- jsonSones.add(jsonSone);
+ jsonSones.add(createJsonSone(sone));
}
/* load notifications. */
List<Notification> notifications = ListNotificationFilters.filterNotifications(webInterface.getNotifications().getNotifications(), currentSone);
});
}
- JsonArray jsonPosts = new JsonArray();
+ ArrayNode jsonPosts = new ArrayNode(instance);
for (Post post : newPosts) {
- JsonObject jsonPost = new JsonObject();
+ ObjectNode jsonPost = new ObjectNode(instance);
jsonPost.put("id", post.getId());
jsonPost.put("sone", post.getSone().getId());
jsonPost.put("recipient", post.getRecipientId().orNull());
}
/* remove replies to unknown posts. */
newReplies = Collections2.filter(newReplies, PostReply.HAS_POST_FILTER);
- JsonArray jsonReplies = new JsonArray();
+ ArrayNode jsonReplies = new ArrayNode(instance);
for (PostReply reply : newReplies) {
- JsonObject jsonReply = new JsonObject();
+ ObjectNode jsonReply = new ObjectNode(instance);
jsonReply.put("id", reply.getId());
jsonReply.put("sone", reply.getSone().getId());
jsonReply.put("post", reply.getPostId());
* The Sone to convert to a JSON object
* @return The JSON representation of the given Sone
*/
- private JsonObject createJsonSone(Sone sone) {
- JsonObject jsonSone = new JsonObject();
+ private JsonNode createJsonSone(Sone sone) {
+ ObjectNode jsonSone = new ObjectNode(instance);
jsonSone.put("id", sone.getId());
jsonSone.put("name", SoneAccessor.getNiceName(sone));
jsonSone.put("local", sone.getInsertUri() != null);
* The current Sone (may be {@code null})
* @return The current options
*/
- private static JsonObject createJsonOptions(Sone currentSone) {
- JsonObject options = new JsonObject();
+ private static JsonNode createJsonOptions(Sone currentSone) {
+ ObjectNode options = new ObjectNode(instance);
if (currentSone != null) {
options.put("ShowNotification/NewSones", currentSone.getOptions().getBooleanOption("ShowNotification/NewSones").get());
options.put("ShowNotification/NewPosts", currentSone.getOptions().getBooleanOption("ShowNotification/NewPosts").get());
package net.pterodactylus.sone.web.ajax;
+import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
+
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Optional;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String allIds = request.getHttpRequest().getParam("posts");
- JsonObject postTimes = new JsonObject();
+ ObjectNode postTimes = new ObjectNode(instance);
if (allIds.length() > 0) {
String[] ids = allIds.split(",");
for (String id : ids) {
if (!post.isPresent()) {
continue;
}
- JsonObject postTime = new JsonObject();
+ ObjectNode postTime = new ObjectNode(instance);
Time time = getTime(post.get().getTime());
postTime.put("timeText", time.getText());
postTime.put("refreshTime", TimeUnit.MILLISECONDS.toSeconds(time.getRefresh()));
postTimes.put(id, postTime);
}
}
- JsonObject replyTimes = new JsonObject();
+ ObjectNode replyTimes = new ObjectNode(instance);
allIds = request.getHttpRequest().getParam("replies");
if (allIds.length() > 0) {
String[] ids = allIds.split(",");
if (!reply.isPresent()) {
continue;
}
- JsonObject replyTime = new JsonObject();
+ ObjectNode replyTime = new ObjectNode(instance);
Time time = getTime(reply.get().getTime());
replyTime.put("timeText", time.getText());
replyTime.put("refreshTime", TimeUnit.MILLISECONDS.toSeconds(time.getRefresh()));
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* Returns the translation for a given key as JSON object.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String key = request.getHttpRequest().getParam("key");
String translation = webInterface.getL10n().getString(key);
return createSuccessJsonObject().put("value", translation);
--- /dev/null
+/*
+ * © 2013 xplosion interactive
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * {@link JsonReturnObject} that signals an error has occured.
+ *
+ * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
+ */
+public class JsonErrorReturnObject extends JsonReturnObject {
+
+ /** The error that has occured. */
+ @JsonProperty
+ private final String error;
+
+ /**
+ * Creates a new error JSON return object.
+ *
+ * @param error
+ * The error that occured
+ */
+ public JsonErrorReturnObject(String error) {
+ super(false);
+ this.error = error;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the error that occured.
+ *
+ * @return The error that occured
+ */
+ @VisibleForTesting
+ public String getError() {
+ return error;
+ }
+
+}
import net.pterodactylus.sone.web.page.FreenetPage;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.io.Closer;
-import net.pterodactylus.util.json.JsonObject;
-import net.pterodactylus.util.json.JsonUtils;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.web.Page;
import net.pterodactylus.util.web.Response;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
import freenet.clients.http.SessionManager.Session;
import freenet.clients.http.ToadletContext;
/** The logger. */
private static final Logger logger = Logging.getLogger(JsonPage.class);
+ /** The JSON serializer. */
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
/** The path of the page. */
private final String path;
* The request to handle
* @return The created JSON object
*/
- protected abstract JsonObject createJsonObject(FreenetRequest request);
+ protected abstract JsonReturnObject createJsonObject(FreenetRequest request);
/**
* Returns whether this command needs the form password for authentication
*
* @return A reply signaling success
*/
- protected static JsonObject createSuccessJsonObject() {
- return new JsonObject().put("success", true);
+ protected static JsonReturnObject createSuccessJsonObject() {
+ return new JsonReturnObject(true);
}
/**
* The error that has occured
* @return The JSON object, signalling failure and the error code
*/
- protected static JsonObject createErrorJsonObject(String error) {
- return new JsonObject().put("success", false).put("error", error);
+ protected static JsonReturnObject createErrorJsonObject(String error) {
+ return new JsonErrorReturnObject(error);
}
//
@Override
public Response handleRequest(FreenetRequest request, Response response) throws IOException {
if (webInterface.getCore().getPreferences().isRequireFullAccess() && !request.getToadletContext().isAllowedFullAccess()) {
- return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
+ return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(objectMapper.writeValueAsString(new JsonErrorReturnObject("auth-required")));
}
if (needsFormPassword()) {
String formPassword = request.getHttpRequest().getParam("formPassword");
if (!webInterface.getFormPassword().equals(formPassword)) {
- return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
+ return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(objectMapper.writeValueAsString(new JsonErrorReturnObject("auth-required")));
}
}
if (requiresLogin()) {
if (getCurrentSone(request.getToadletContext(), false) == null) {
- return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
+ return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(objectMapper.writeValueAsString(new JsonErrorReturnObject("auth-required")));
}
}
try {
- JsonObject jsonObject = createJsonObject(request);
- return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject));
+ JsonReturnObject jsonObject = createJsonObject(request);
+ return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(objectMapper.writeValueAsString(jsonObject));
} catch (Exception e1) {
logger.log(Level.WARNING, "Error executing JSON page!", e1);
return response.setStatusCode(500).setStatusText(e1.getMessage()).setContentType("text/plain").write(dumpStackTrace(e1));
--- /dev/null
+/*
+ * © 2013 xplosion interactive
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.node.IntNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+
+/**
+ * JSON return object for AJAX requests.
+ *
+ * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
+ */
+public class JsonReturnObject {
+
+ /** Whether the request was successful. */
+ @JsonProperty
+ private final boolean success;
+
+ /** The returned values. */
+ private final Map<String, JsonNode> content = Maps.newHashMap();
+
+ /**
+ * Creates a new JSON return object.
+ *
+ * @param success
+ * {@code true} if the request was successful, {@code false} otherwise
+ */
+ public JsonReturnObject(boolean success) {
+ this.success = success;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns whether the request was successful.
+ *
+ * @return {@code true} if the request was successful, {@code false} otherwise
+ */
+ @VisibleForTesting
+ public boolean isSuccess() {
+ return success;
+ }
+
+ /**
+ * Returns the value stored under the given key.
+ *
+ * @param key
+ * The key of the value to retrieve
+ * @return The value of the key, or {@code null} if there is no value for the
+ * given key
+ */
+ @VisibleForTesting
+ public JsonNode get(String key) {
+ return content.get(key);
+ }
+
+ /**
+ * Returns the content of this object for serialization.
+ *
+ * @return The content of this object
+ */
+ @JsonAnyGetter
+ public Map<String, JsonNode> getContent() {
+ return content;
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Stores the given value under the given key.
+ *
+ * @param key
+ * The key under which to store the value
+ * @param value
+ * The value to store
+ * @return This JSON return object
+ */
+ public JsonReturnObject put(String key, boolean value) {
+ return put(key, BooleanNode.valueOf(value));
+ }
+
+ /**
+ * Stores the given value under the given key.
+ *
+ * @param key
+ * The key under which to store the value
+ * @param value
+ * The value to store
+ * @return This JSON return object
+ */
+ public JsonReturnObject put(String key, int value) {
+ return put(key, new IntNode(value));
+ }
+
+ /**
+ * Stores the given value under the given key.
+ *
+ * @param key
+ * The key under which to store the value
+ * @param value
+ * The value to store
+ * @return This JSON return object
+ */
+ public JsonReturnObject put(String key, String value) {
+ return put(key, new TextNode(value));
+ }
+
+ /**
+ * Stores the given value under the given key.
+ *
+ * @param key
+ * The key under which to store the value
+ * @param value
+ * The value to store
+ * @return This JSON return object
+ */
+ public JsonReturnObject put(String key, JsonNode value) {
+ content.put(key, value);
+ return this;
+ }
+
+}
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user like a {@link Post}.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String type = request.getHttpRequest().getParam("type", null);
String id = request.getHttpRequest().getParam(type, null);
if ((id == null) || (id.length() == 0)) {
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* Lets the user {@link Core#lockSone(Sone) lock} a {@link Sone}.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
Sone sone = webInterface.getCore().getLocalSone(soneId, false);
if (sone == null) {
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
import com.google.common.base.Optional;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String type = request.getHttpRequest().getParam("type");
if (!type.equals("sone") && !type.equals("post") && !type.equals("reply")) {
return createErrorJsonObject("invalid-type");
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user move a profile field up or down.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext());
Profile profile = currentSone.getProfile();
String fieldId = request.getHttpRequest().getParam("field");
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user trust a Sone.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
if (currentSone == null) {
return createErrorJsonObject("auth-required");
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user unbookmark a post.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String id = request.getHttpRequest().getParam("post", null);
if ((id == null) || (id.length() == 0)) {
return createErrorJsonObject("invalid-post-id");
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets a Sone unfollow another Sone.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
if (!webInterface.getCore().getSone(soneId).isPresent()) {
return createErrorJsonObject("invalid-sone-id");
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user unlike a {@link Post}.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String type = request.getHttpRequest().getParam("type", null);
String id = request.getHttpRequest().getParam(type, null);
if ((id == null) || (id.length() == 0)) {
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* Lets the user {@link Core#unlockSone(Sone) unlock} a {@link Sone}.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
Sone sone = webInterface.getCore().getLocalSone(soneId, false);
if (sone == null) {
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.json.JsonObject;
/**
* AJAX page that lets the user untrust a Sone.
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(FreenetRequest request) {
+ protected JsonReturnObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
if (currentSone == null) {
return createErrorJsonObject("auth-required");
package net.pterodactylus.sone.web.page;
import java.io.IOException;
+import java.io.OutputStream;
import java.net.URI;
import net.pterodactylus.util.web.Header;
import net.pterodactylus.util.web.Method;
import net.pterodactylus.util.web.Page;
import net.pterodactylus.util.web.Response;
+
import freenet.client.HighLevelSimpleClient;
import freenet.clients.http.LinkEnabledCallback;
import freenet.clients.http.LinkFilterExceptedToadlet;
*/
private void handleRequest(FreenetRequest pageRequest) throws IOException, ToadletContextClosedException {
Bucket pageBucket = null;
+ OutputStream pageBucketOutputStream = null;
+ Response pageResponse;
try {
pageBucket = pageRequest.getToadletContext().getBucketFactory().makeBucket(-1);
- Response pageResponse = new Response(pageBucket.getOutputStream());
- pageResponse = page.handleRequest(pageRequest, pageResponse);
- MultiValueTable<String, String> headers = new MultiValueTable<String, String>();
- if (pageResponse.getHeaders() != null) {
- for (Header header : pageResponse.getHeaders()) {
- for (String value : header) {
- headers.put(header.getName(), value);
- }
+ pageBucketOutputStream = pageBucket.getOutputStream();
+ pageResponse = page.handleRequest(pageRequest, new Response(pageBucketOutputStream));
+ } catch (IOException ioe1) {
+ Closer.close(pageBucket);
+ throw ioe1;
+ } finally {
+ Closer.close(pageBucketOutputStream);
+ }
+ MultiValueTable<String, String> headers = new MultiValueTable<String, String>();
+ if (pageResponse.getHeaders() != null) {
+ for (Header header : pageResponse.getHeaders()) {
+ for (String value : header) {
+ headers.put(header.getName(), value);
}
}
+ }
+ try {
writeReply(pageRequest.getToadletContext(), pageResponse.getStatusCode(), pageResponse.getContentType(), pageResponse.getStatusText(), headers, pageBucket);
- } catch (Throwable t1) {
- writeInternalError(t1, pageRequest.getToadletContext());
} finally {
Closer.close(pageBucket);
}
Notification.ImageInsertFailed.Text=Diese Bilder konnten nicht nach Freenet hoch geladen werden:
Notification.Mention.ShortText=Sie wurden erwähnt.
Notification.Mention.Text=Sie wurden in diesen Nachrichten erwähnt:
-Notification.SoneInsert.Duration={0,number} {0,choice,0#Sekunden|1#Sekunde|1<Sekunden}
+Notification.SoneIsInserting.Text=Ihre Sone sone://{0} wird jetzt hoch geladen.
+Notification.SoneIsInserted.Text=Ihre Sone sone://{0} wurde in {1,number} {1,choice,0#Sekunden|1#Sekunde|1<Sekunden} hoch geladen.
+Notification.SoneInsertAborted.Text=Ihre Sone sone://{0} konnte nicht hoch geladen werden.
Notification.ImageInsertFailed.Text=The following images could not be inserted:
Notification.Mention.ShortText=You have been mentioned.
Notification.Mention.Text=You have been mentioned in the following posts:
-Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
+Notification.SoneIsInserting.Text=Your Sone sone://{0} is now being inserted.
+Notification.SoneIsInserted.Text=Your Sone sone://{0} has been inserted in {1,number} {1,choice,0#seconds|1#second|1<seconds}.
+Notification.SoneInsertAborted.Text=Your Sone sone://{0} could not be inserted.
Notification.ImageInsertFailed.Text=Les images suivantes ne peuvent être insérées:
Notification.Mention.ShortText=Vous avez été mentionné.
Notification.Mention.Text=Vous avez été mentionné dans les messages suivants:
-Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
-# 120-121
+Notification.SoneIsInserting.Text=Your Sone sone://{0} is now being inserted.
+Notification.SoneIsInserted.Text=Your Sone sone://{0} has been inserted in {1,number} {1,choice,0#seconds|1#second|1<seconds}.
+Notification.SoneInsertAborted.Text=Your Sone sone://{0} could not be inserted.
+# 120-121, 454-456
Page.Options.Option.ImagesPerPage.Description=ページ送りのボタンが表示されるまでに表示する画像の数。
Page.Options.Option.CharactersPerPost.Description=投稿を切って全文を見るリンクが表示されるまでの文字数。(-1で無効になります。)文字数は以下の設定により判定されます。
Page.Options.Option.PostCutOffLength.Description=投稿が長い場合に表示される文字数。(上記の設定も参照してください。)
-Page.Options.Option.RequireFullAccess.Description=完全なアクセスが設定されていないホストに対してSoneへのアクセスを拒否するか否かの設定。
+Page.Options.Option.RequireFullAccess.Description=完全なアクセスが設定されていないホストに対してSoneへのアクセスを拒否する
Page.Options.Section.TrustOptions.Title=信用設定
Page.Options.Option.PositiveTrust.Description=返信のリンクの下に表示されるチェックメークのリンクをクリックした際にSoneに設定されるポジティブな信用値。
Page.Options.Option.NegativeTrust.Description=返信のリンクの下に表示される赤い☓のリンクをクリックした際にSoneに設定されるネガティブな信用値。
Page.Options.Option.FcpInterfaceActive.Description=FCPインターフェースを有効にし、Soneプラグインに他のプラグインやリモートクライアントからアクセスできるようにする。
Page.Options.Option.FcpFullAccessRequired.Description=許可されたホストのみFCP接続を許可する。({link}ノード設定の「FCP」内の設定{/link}も確認してください。)
Page.Options.Option.FcpFullAccessRequired.Value.No=いいえ
-Page.Options.Option.FcpFullAccessRequired.Value.Writing=書き込みのアクセス
+Page.Options.Option.FcpFullAccessRequired.Value.Writing=書き込みのアクセスの場合
Page.Options.Option.FcpFullAccessRequired.Value.Always=常に
Page.Options.Section.Cleaning.Title=クリーンアップ
Page.Options.Option.ClearOnNextRestart.Description=次回のプラグインの再起動時に設定を初期化する。注意:{strong}これを行うとあなたの全てのSoneが破棄されます{/strong}。実行の前に必要なものは全てバックアップされていることを確認してください。また、次項の設定も同時に有効にする必要があります。
Page.KnownSones.Filter.NotFollowed=フォローしているSoneを隠す
Page.KnownSones.Filter.New=新しいSoneのみ表示
Page.KnownSones.Filter.NotNew=新しいSoneを隠す
-Page.KnownSones.Filter.Own=Show only local Sones
-Page.KnownSones.Filter.NotOwn=Show only remote Sones
+Page.KnownSones.Filter.Own=ローカルのSoneのみ表示
+Page.KnownSones.Filter.NotOwn=リモートのSoneのみ表示
Page.KnownSones.Button.Apply=実行
Page.KnownSones.Button.FollowAllSones=このページ内の全てのSoneをフォロー
Page.KnownSones.Button.UnfollowAllSones=このページ内の全てのフォローを解除
Page.CreatePost.Title=投稿文を作成 - Sone
Page.CreatePost.Page.Title=投稿文を作成
Page.CreatePost.Label.Text=投稿文のテキスト:
-Page.CreatePost.Button.Post=投稿する!
-Page.CreatePost.Error.EmptyText=投稿文が空っぽです。何か書いてください!
+Page.CreatePost.Button.Post=投稿する
+Page.CreatePost.Error.EmptyText=投稿文が空っぽです。何か書いてください。
Page.CreateReply.Title=返信を作成 - Sone
Page.CreateReply.Page.Title=返信を作成
-Page.CreateReply.Error.EmptyText=返信が空っぽです。何か書いてください!
+Page.CreateReply.Error.EmptyText=返信が空っぽです。何か書いてください。
Page.CreateReply.Label.Text=返信テキスト:
-Page.CreateReply.Button.Post=返信する!
+Page.CreateReply.Button.Post=返信する
Page.ViewSone.Title=Soneを表示 - Sone
Page.ViewSone.Page.TitleWithoutSone=未知のSoneを表示する
View.Time.AFewSecondsAgo=およそ数秒前
View.Time.HalfAMinuteAgo=およそ30秒前
View.Time.AMinuteAgo=およそ1分前
-View.Time.XMinutesAgo=${min}分前o
+View.Time.XMinutesAgo=${min}分前
View.Time.HalfAnHourAgo=30分前
View.Time.AnHourAgo=およそ1時間前
View.Time.XHoursAgo=${hour}時間前
Notification.ImageInsertFailed.Text=次の画像のインサートに失敗しました:
Notification.Mention.ShortText=誰かにメンションされました。
Notification.Mention.Text=次の投稿でメンションされています:
-Notification.SoneInsert.Duration={0,number} {0,choice,0#秒|1#秒|1<秒}
-# 120-121
+Notification.SoneIsInserting.Text=あなたのSone sone://{0}は現在インサート中です。
+Notification.SoneIsInserted.Text=あなたのSone sone://{0}は{1,number}{1,choice,0#秒|1#秒|1<秒}でインサートされました。
+Notification.SoneInsertAborted.Text=あなたのSone sone://{0}のインサートに失敗しました。
Notification.ImageInsertFailed.Text=De følgende bildene kunne ikke bli innsatt:
Notification.Mention.ShortText=Du har blitt nevnt:
Notification.Mention.Text=Du har blitt nevnt i følgende innlegg:
-Notification.SoneInsert.Duration={0,number} {0,choice,0#sekund|1#sekund|1<sekunder}
-# 120-121
+Notification.SoneIsInserting.Text=Your Sone sone://{0} is now being inserted.
+Notification.SoneIsInserted.Text=Your Sone sone://{0} has been inserted in {1,number} {1,choice,0#seconds|1#second|1<seconds}.
+Notification.SoneInsertAborted.Text=Your Sone sone://{0} could not be inserted.
+# 120-121, 454-456
Page.KnownSones.Filter.NotFollowed=Ukryj śledzone Sone
Page.KnownSones.Filter.New=Pokazuj tylko nowe Sone
Page.KnownSones.Filter.NotNew=Ukryj nowe Sone
-Page.KnownSones.Filter.Own=Show only local Sones
-Page.KnownSones.Filter.NotOwn=Show only remote Sones
+Page.KnownSones.Filter.Own=Pokazuj tylko moje Sone
+Page.KnownSones.Filter.NotOwn=Pokazuj tylko zdalne Sone
Page.KnownSones.Button.Apply=Zastosuj
Page.KnownSones.Button.FollowAllSones=Śledź wszystkie Sone na tej stronie
Page.KnownSones.Button.UnfollowAllSones=Przestań śledzić wszystkie Sone na tej stronie
Notification.ImageInsertFailed.Text=Nie można załadowac następujących obrazów:
Notification.Mention.ShortText=Zostałeś oznaczony.
Notification.Mention.Text=Zostałeś oznaczony w następujących postach:
-Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
-# 120-121
+Notification.SoneIsInserting.Text=Twoje Sone sone://{0} jest w tej chili wysyłane.
+Notification.SoneIsInserted.Text=Twoje sone://{0} zostało wysłane w {1,number} {1,choice,0#seconds|1#second|1<seconds}.
+Notification.SoneInsertAborted.Text=Twoje Sone sone://{0} nie mogło zostać wysłane.
Notification.ImageInsertFailed.Text=Следующие изображения не могут быть выгружены:
Notification.Mention.ShortText=Вас упомянули.
Notification.Mention.Text=Вас упомянули в следующих сообщениях:
-Notification.SoneInsert.Duration={0,number} {0,choice,0#секунд|1#секунда|2#секунды|4<секунд}
-# 120-121
+Notification.SoneIsInserting.Text=Your Sone sone://{0} is now being inserted.
+Notification.SoneIsInserted.Text=Your Sone sone://{0} has been inserted in {1,number} {1,choice,0#seconds|1#second|1<seconds}.
+Notification.SoneInsertAborted.Text=Your Sone sone://{0} could not be inserted.
+# 120-121, 454-456
function ajaxGet(url, data, successCallback, errorCallback) {
(function(url, data, successCallback, errorCallback) {
- $.ajax({"type": "GET", "url": url, "data": data, "dataType": "json", "success": function(data, textStatus, xmlHttpRequest) {
+ $.ajax({"cache": false, "type": "GET", "url": url, "data": data, "dataType": "json", "success": function(data, textStatus, xmlHttpRequest) {
ajaxSuccess();
if (typeof successCallback != "undefined") {
successCallback(data, textStatus);
function registerInputTextareaSwap(inputElement, defaultText, inputFieldName, optional, dontUseTextarea) {
$(inputElement).each(function() {
- textarea = $(dontUseTextarea ? "<input type=\"text\" name=\"" + inputFieldName + "\">" : "<textarea name=\"" + inputFieldName + "\"></textarea>").blur(function() {
+ var textarea = $(dontUseTextarea ? "<input type=\"text\" name=\"" + inputFieldName + "\">" : "<textarea name=\"" + inputFieldName + "\"></textarea>").blur(function() {
if ($(this).val() == "") {
$(this).hide();
- inputField = $(this).data("inputField");
+ var inputField = $(this).data("inputField");
inputField.show().removeAttr("disabled").addClass("default");
inputField.val(defaultText);
}
return;
}
(function(postId, author, insertAfterThisElement) {
- separator = $("<span> · </span>").addClass("separator");
+ var separator = $("<span> · </span>").addClass("separator");
getTranslation("WebInterface.Button.Comment", function(text) {
- commentElement = $("<div><span>" + text + "</span></div>").addClass("show-reply-form").click(function() {
- replyElement = $("#sone .post#post-" + postId + " .create-reply");
+ var commentElement = $("<div><span>" + text + "</span></div>").addClass("show-reply-form").click(function() {
+ var replyElement = sone.find(".post#post-" + postId + " .create-reply");
replyElement.removeClass("hidden");
replyElement.removeClass("light");
(function(replyElement) {
- replyElement.find("input.reply-input").blur(function() {
+ replyElement.find(":input.reply-input").blur(function() {
if ($(this).hasClass("default")) {
replyElement.addClass("light");
}
replyElement.removeClass("light");
});
})(replyElement);
- textArea = replyElement.find("input.reply-input").focus().data("textarea");
+ var textArea = replyElement.find(":input.reply-input").focus().data("textarea");
if (author != getCurrentSoneId()) {
textArea.val(textArea.val() + "@sone://" + author + " ");
}
* The date and time of the last update (formatted for display)
*/
function updateSoneStatus(soneId, name, status, modified, locked, lastUpdated, lastUpdatedText) {
- $("#sone .sone." + filterSoneId(soneId)).
- toggleClass("unknown", status == "unknown").
+ var updateSone = sone.find(".sone." + filterSoneId(soneId));
+ updateSone.toggleClass("unknown", status == "unknown").
toggleClass("idle", status == "idle").
toggleClass("inserting", status == "inserting").
toggleClass("downloading", status == "downloading").
toggleClass("modified", modified);
- $("#sone .sone." + filterSoneId(soneId) + " .lock").toggleClass("hidden", locked);
- $("#sone .sone." + filterSoneId(soneId) + " .unlock").toggleClass("hidden", !locked);
+ updateSone.find(".lock").toggleClass("hidden", locked);
+ updateSone.find(".unlock").toggleClass("hidden", !locked);
if (lastUpdated != null) {
- $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").attr("title", lastUpdated).text(lastUpdatedText);
+ updateSone.find(".last-update span.time").attr("title", lastUpdated).text(lastUpdatedText);
} else {
getTranslation("View.Sone.Text.UnknownDate", function(unknown) {
- $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(unknown);
+ updateSone.find(".last-update span.time").text(unknown);
});
}
- $("#sone .sone." + filterSoneId(soneId) + " .profile-link a").text(name);
+ updateSone.find(".profile-link a").text(name);
}
/**
*/
function enhanceDeleteButton(button, text, deleteCallback) {
(function(button) {
- newButton = $("<button></button>").addClass("confirm").hide().text(text).click(function() {
+ var newButton = $("<button></button>").addClass("confirm").hide().text(text).click(function() {
$(this).fadeOut("slow");
deleteCallback();
return false;
return;
}
if (data.success) {
- $("#sone .post#post-" + postId).slideUp();
+ sone.find(".post#post-" + postId).slideUp();
} else if (data.error == "invalid-post-id") {
/* pretend the post is already gone. */
getPost(postId).slideUp();
*/
function enhanceDeleteReplyButton(button, replyId, text) {
enhanceDeleteButton(button, text, function() {
- ajaxGet("deleteReply.ajax", { "reply": replyId, "formPassword": $("#sone #formPassword").text() }, function(data, textStatus) {
+ ajaxGet("deleteReply.ajax", { "reply": replyId, "formPassword": sone.find("#formPassword").text() }, function(data, textStatus) {
if (data == null) {
return;
}
if (data.success) {
- $("#sone .reply#reply-" + replyId).slideUp();
+ sone.find(".reply#reply-" + replyId).slideUp();
} else if (data.error == "invalid-reply-id") {
/* pretend the reply is already gone. */
getReply(replyId).slideUp();
}
function getFormPassword() {
- return $("#sone #formPassword").text();
+ return sone.find("#formPassword").text();
}
/**
* @returns All Sone elements with the given ID
*/
function getSone(soneId) {
- return $("#sone .sone").filter(function(index) {
+ return sone.find(".sone").filter(function(index) {
return $(".id", this).text() == soneId;
});
}
* @returns The element of the post
*/
function getPost(postId) {
- return $("#sone .post#post-" + postId);
+ return sone.find(".post#post-" + postId);
}
function getPostElement(element) {
* @returns The element of the reply
*/
function getReply(replyId) {
- return $("#sone .reply#reply-" + replyId);
+ return sone.find(".reply#reply-" + replyId);
}
function getReplyElement(element) {
* @returns The notification element
*/
function getNotification(notificationId) {
- return $("#sone #notification-area .notification#" + notificationId);
+ return sone.find("#notification-area .notification#" + notificationId);
}
/**
if ((data == null) || !data.success) {
return;
}
- $("#sone .post#post-" + postId + " > .inner-part > .status-line .like").addClass("hidden");
- $("#sone .post#post-" + postId + " > .inner-part > .status-line .unlike").removeClass("hidden");
+ sone.find(".post#post-" + postId + " > .inner-part > .status-line .like").addClass("hidden");
+ sone.find(".post#post-" + postId + " > .inner-part > .status-line .unlike").removeClass("hidden");
updatePostLikes(postId);
}, function(xmlHttpRequest, textStatus, error) {
/* ignore error. */
if ((data == null) || !data.success) {
return;
}
- $("#sone .post#post-" + postId + " > .inner-part > .status-line .unlike").addClass("hidden");
- $("#sone .post#post-" + postId + " > .inner-part > .status-line .like").removeClass("hidden");
+ sone.find(".post#post-" + postId + " > .inner-part > .status-line .unlike").addClass("hidden");
+ sone.find(".post#post-" + postId + " > .inner-part > .status-line .like").removeClass("hidden");
updatePostLikes(postId);
}, function(xmlHttpRequest, textStatus, error) {
/* ignore error. */
function updatePostLikes(postId) {
ajaxGet("getLikes.ajax", { "type": "post", "post": postId }, function(data, textStatus) {
if ((data != null) && data.success) {
- $("#sone .post#post-" + postId + " > .inner-part > .status-line .likes").toggleClass("hidden", data.likes == 0);
- $("#sone .post#post-" + postId + " > .inner-part > .status-line .likes span.like-count").text(data.likes);
- $("#sone .post#post-" + postId + " > .inner-part > .status-line .likes > span").attr("title", generateSoneList(data.sones));
+ sone.find(".post#post-" + postId + " > .inner-part > .status-line .likes").toggleClass("hidden", data.likes == 0);
+ sone.find(".post#post-" + postId + " > .inner-part > .status-line .likes span.like-count").text(data.likes);
+ sone.find(".post#post-" + postId + " > .inner-part > .status-line .likes > span").attr("title", generateSoneList(data.sones));
}
}, function(xmlHttpRequest, textStatus, error) {
/* ignore error. */
if ((data == null) || !data.success) {
return;
}
- $("#sone .reply#reply-" + replyId + " .status-line .like").addClass("hidden");
- $("#sone .reply#reply-" + replyId + " .status-line .unlike").removeClass("hidden");
+ sone.find(".reply#reply-" + replyId + " .status-line .like").addClass("hidden");
+ sone.find(".reply#reply-" + replyId + " .status-line .unlike").removeClass("hidden");
updateReplyLikes(replyId);
}, function(xmlHttpRequest, textStatus, error) {
/* ignore error. */
if ((data == null) || !data.success) {
return;
}
- $("#sone .reply#reply-" + replyId + " .status-line .unlike").addClass("hidden");
- $("#sone .reply#reply-" + replyId + " .status-line .like").removeClass("hidden");
+ sone.find(".reply#reply-" + replyId + " .status-line .unlike").addClass("hidden");
+ sone.find(".reply#reply-" + replyId + " .status-line .like").removeClass("hidden");
updateReplyLikes(replyId);
}, function(xmlHttpRequest, textStatus, error) {
/* ignore error. */
* The trust value for the Sone
*/
function updateTrustControls(soneId, trustValue) {
- $("#sone .post").each(function() {
+ sone.find(".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() {
+ sone.find(".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);
function updateReplyLikes(replyId) {
ajaxGet("getLikes.ajax", { "type": "reply", "reply": replyId }, function(data, textStatus) {
if ((data != null) && data.success) {
- $("#sone .reply#reply-" + replyId + " .status-line .likes").toggleClass("hidden", data.likes == 0);
- $("#sone .reply#reply-" + replyId + " .status-line .likes span.like-count").text(data.likes);
- $("#sone .reply#reply-" + replyId + " .status-line .likes > span").attr("title", generateSoneList(data.sones));
+ sone.find(".reply#reply-" + replyId + " .status-line .likes").toggleClass("hidden", data.likes == 0);
+ sone.find(".reply#reply-" + replyId + " .status-line .likes span.like-count").text(data.likes);
+ sone.find(".reply#reply-" + replyId + " .status-line .likes > span").attr("title", generateSoneList(data.sones));
}
}, function(xmlHttpRequest, textStatus, error) {
/* ignore error. */
return false;
});
$(postElement).find(".create-reply button:submit").click(function() {
- button = $(this);
+ var button = $(this);
button.attr("disabled", "disabled");
- sender = $(this.form).find(":input[name=sender]").val();
- inputField = $(this.form).find(":input[name=text]:enabled").get(0);
- postId = getPostId(this);
- text = $(inputField).val();
+ var sender = $(this.form).find(":input[name=sender]").val();
+ var inputField = $(this.form).find(":input[name=text]:enabled").get(0);
+ var postId = getPostId(this);
+ var text = $(inputField).val();
(function(sender, postId, text, inputField) {
postReply(sender, postId, text, function(success, error, replyId, soneId) {
if (success) {
$(inputField).val("");
loadNewReply(replyId, soneId, postId);
- $("#sone .post#post-" + postId + " .create-reply").addClass("hidden");
- $("#sone .post#post-" + postId + " .create-reply .sender").hide();
- $("#sone .post#post-" + postId + " .create-reply .select-sender").show();
- $("#sone .post#post-" + postId + " .create-reply :input[name=sender]").val(getCurrentSoneId());
+ sone.find(".post#post-" + postId + " .create-reply").addClass("hidden");
+ sone.find(".post#post-" + postId + " .create-reply .sender").hide();
+ sone.find(".post#post-" + postId + " .create-reply .select-sender").show();
+ sone.find(".post#post-" + postId + " .create-reply :input[name=sender]").val(getCurrentSoneId());
} else {
alert(error);
}
/* replace all “delete” buttons with javascript. */
(function(postElement) {
getTranslation("WebInterface.Confirmation.DeletePostButton", function(deletePostText) {
- postId = getPostId(postElement);
+ var postId = getPostId(postElement);
enhanceDeletePostButton($(postElement).find(".delete-post button"), postId, deletePostText);
});
})(postElement);
/* convert “show source” link into javascript function. */
$(postElement).find(".show-source").each(function() {
$("a", this).click(function() {
- post = getPostElement(this);
- rawPostText = $(".post-text.raw-text", post);
+ var post = getPostElement(this);
+ var rawPostText = $(".post-text.raw-text", post);
rawPostText.toggleClass("hidden");
if (rawPostText.hasClass("hidden")) {
$(".post-text.short-text", post).removeClass("hidden");
/* ajaxify author/post links */
$(".post-status-line .permalink a", postElement).click(function() {
if (!$(".create-reply", postElement).hasClass("hidden")) {
- textArea = $("input.reply-input", postElement).focus().data("textarea");
+ var textArea = $(":input.reply-input", postElement).focus().data("textarea");
$(textArea).replaceSelection($(this).attr("href"));
}
return false;
addCommentLink(getPostId(postElement), getPostAuthor(postElement), postElement, $(postElement).find(".post-status-line .permalink-author"));
/* process all replies. */
- replyIds = [];
+ var replyIds = [];
$(postElement).find(".reply").each(function() {
replyIds.push(getReplyId(this));
ajaxifyReply(this);
/* process reply input fields. */
getTranslation("WebInterface.DefaultText.Reply", function(text) {
- $(postElement).find("input.reply-input").each(function() {
+ $(postElement).find(":input.reply-input").each(function() {
registerInputTextareaSwap(this, text, "text", false, false);
});
});
}).fadeIn();
}, 1000);
}).mouseleave(function() {
- if (currentSoneMenuId = getPostId(this)) {
+ if (currentSoneMenuId == getPostId(this)) {
clearTimeout(currentSoneMenuTimeoutHandler);
}
});
ajaxGet("followSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
$(followElement).addClass("hidden");
$(followElement).parent().find(".unfollow").removeClass("hidden");
- $("#sone .sone-menu").each(function() {
+ sone.find(".sone-menu").each(function() {
if (getMenuSone(this) == soneId) {
$(".follow", this).toggleClass("hidden", true);
$(".unfollow", this).toggleClass("hidden", false);
ajaxGet("unfollowSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
$(unfollowElement).addClass("hidden");
$(unfollowElement).parent().find(".follow").removeClass("hidden");
- $("#sone .sone-menu").each(function() {
+ sone.find(".sone-menu").each(function() {
if (getMenuSone(this) == soneId) {
$(".follow", this).toggleClass("hidden", false);
$(".unfollow", this).toggleClass("hidden", true);
/* ajaxify author links */
$(".reply-status-line .permalink a", replyElement).click(function() {
if (!$(".create-reply", getPostElement(replyElement)).hasClass("hidden")) {
- textArea = $("input.reply-input", getPostElement(replyElement)).focus().data("textarea");
+ var textArea = $(":input.reply-input", getPostElement(replyElement)).focus().data("textarea");
$(textArea).replaceSelection($(this).attr("href"));
}
return false;
/* convert “show source” link into javascript function. */
$(replyElement).find(".show-reply-source").each(function() {
$("a", this).click(function() {
- reply = getReplyElement(this);
- rawReplyText = $(".reply-text.raw-text", reply);
+ var reply = getReplyElement(this);
+ var rawReplyText = $(".reply-text.raw-text", reply);
rawReplyText.toggleClass("hidden");
if (rawReplyText.hasClass("hidden")) {
$(".reply-text.short-text", reply).removeClass("hidden");
}).fadeIn();
}, 1000);
}).mouseleave(function() {
- if (currentSoneMenuId = getPostId(this) + "-" + getReplyId(this)) {
+ if (currentSoneMenuId == getPostId(this) + "-" + getReplyId(this)) {
clearTimeout(currentSoneMenuTimeoutHandler);
}
});
ajaxGet("followSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
$(followElement).addClass("hidden");
$(followElement).parent().find(".unfollow").removeClass("hidden");
- $("#sone .sone-menu").each(function() {
+ sone.find(".sone-menu").each(function() {
if (getMenuSone(this) == soneId) {
$(".follow", this).toggleClass("hidden", true);
$(".unfollow", this).toggleClass("hidden", false);
ajaxGet("unfollowSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
$(unfollowElement).addClass("hidden");
$(unfollowElement).parent().find(".follow").removeClass("hidden");
- $("#sone .sone-menu").each(function() {
+ sone.find(".sone-menu").each(function() {
if (getMenuSone(this) == soneId) {
$(".follow", this).toggleClass("hidden", false);
$(".unfollow", this).toggleClass("hidden", true);
notification.find(".text").addClass("hidden");
}
notification.find("form.mark-as-read button").click(function() {
- allIds = $(":input[name=id]", this.form).val().split(" ");
+ var allIds = $(":input[name=id]", this.form).val().split(" ");
for (var index = 0; index < allIds.length; index += 16) {
- ids = allIds.slice(index, index + 16).join(" ");
+ var ids = allIds.slice(index, index + 16).join(" ");
ajaxGet("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": $(":input[name=type]", this.form).val(), "id": ids});
}
});
notification.find("a[class^='link-']").each(function() {
- linkElement = $(this);
+ var linkElement = $(this);
if (linkElement.is("[href^='viewPost']")) {
- id = linkElement.attr("class").substr(5);
+ var id = linkElement.attr("class").substr(5);
if (hasPost(id)) {
linkElement.attr("href", "#post-" + id).addClass("in-page-link");
}
* determine whether the notifications changed and need to be reloaded.
*/
function getNotificationHash() {
- return $("#sone #notification-area #notification-hash").text();
+ return sone.find("#notification-area #notification-hash").text();
}
/**
* The new notification hash
*/
function setNotificationHash(notificationHash) {
- $("#sone #notification-area #notification-hash").text(notificationHash);
+ sone.find("#notification-area #notification-hash").text(notificationHash);
}
/**
* @returns All extracted IDs
*/
function getElementIds(notification, selector) {
- elementIds = [];
+ var elementIds = [];
$(selector, notification).each(function() {
elementIds.push($(this).text());
});
if (getNotificationId(oldNotification) != "new-sone-notification") {
return;
}
- oldIds = getElementIds(oldNotification, ".new-sone-id");
- newIds = getElementIds(newNotification, ".new-sone-id");
+ var oldIds = getElementIds(oldNotification, ".new-sone-id");
+ var newIds = getElementIds(newNotification, ".new-sone-id");
$.each(oldIds, function(index, value) {
if ($.inArray(value, newIds) == -1) {
markSoneAsKnown(getSone(value), true);
if (getNotificationId(oldNotification) != "new-post-notification") {
return;
}
- oldIds = getElementIds(oldNotification, ".post-id");
- newIds = getElementIds(newNotification, ".post-id");
+ var oldIds = getElementIds(oldNotification, ".post-id");
+ var newIds = getElementIds(newNotification, ".post-id");
$.each(oldIds, function(index, value) {
if ($.inArray(value, newIds) == -1) {
markPostAsKnown(getPost(value), true);
if (getNotificationId(oldNotification) != "new-reply-notification") {
return;
}
- oldIds = getElementIds(oldNotification, ".reply-id");
- newIds = getElementIds(newNotification, ".reply-id");
+ var oldIds = getElementIds(oldNotification, ".reply-id");
+ var newIds = getElementIds(newNotification, ".reply-id");
$.each(oldIds, function(index, value) {
if ($.inArray(value, newIds) == -1) {
markReplyAsKnown(getReply(value), true);
ajaxGet("getNotifications.ajax", {}, function(data, textStatus) {
if (data && data.success) {
/* search for removed notifications. */
- $("#sone #notification-area .notification").each(function() {
- notificationId = $(this).attr("id");
- foundNotification = false;
+ sone.find("#notification-area .notification").each(function() {
+ var notificationId = $(this).attr("id");
+ var foundNotification = false;
$.each(data.notifications, function(index, value) {
if (value.id == notificationId) {
foundNotification = true;
if (!foundNotification) {
if (notificationId == "new-sone-notification" && (data.options["ShowNotification/NewSones"] == true)) {
$(".new-sone-id", this).each(function(index, element) {
- soneId = $(this).text();
+ var soneId = $(this).text();
markSoneAsKnown(getSone(soneId), true);
});
} else if (notificationId == "new-post-notification" && (data.options["ShowNotification/NewPosts"] == true)) {
$(".post-id", this).each(function(index, element) {
- postId = $(this).text();
+ var postId = $(this).text();
markPostAsKnown(getPost(postId), true);
});
} else if (notificationId == "new-reply-notification" && (data.options["ShowNotification/NewReplies"] == true)) {
$(".reply-id", this).each(function(index, element) {
- replyId = $(this).text();
+ var replyId = $(this).text();
markReplyAsKnown(getReply(replyId), true);
});
}
$(this).slideUp("normal", function() {
$(this).remove();
/* remove activity when no notifications are visible. */
- if ($("#sone #notification-area .notification").length == 0) {
+ if (sone.find("#notification-area .notification").length == 0) {
resetActivity();
}
});
});
/* process notifications. */
$.each(data.notifications, function(index, value) {
- oldNotification = getNotification(value.id);
- notification = ajaxifyNotification(createNotification(value.id, value.lastUpdatedTime, value.text, value.dismissable)).hide();
+ var oldNotification = getNotification(value.id);
+ var notification = ajaxifyNotification(createNotification(value.id, value.lastUpdatedTime, 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");
+ var opened = oldNotification.is(":visible") && oldNotification.find(".short-text").hasClass("hidden");
notification.find(".short-text").toggleClass("hidden", opened);
notification.find(".text").toggleClass("hidden", !opened);
}
checkForRemovedReplies(oldNotification, notification);
oldNotification.replaceWith(notification.show());
} else {
- $("#sone #notification-area").append(notification);
+ sone.find("#notification-area").append(notification);
if (value.id.substring(0, 5) != "local") {
notification.slideDown();
setActivity();
* @returns The page ID
*/
function getPageId() {
- return $("#sone .page-id").text();
+ return sone.find(".page-id").text();
}
/**
* @returns The current page of the pagination
*/
function getPage(paginationSelector) {
- pagination = $(paginationSelector);
+ var pagination = $(paginationSelector);
if (pagination.length > 0) {
return $(".current-page", paginationSelector).text();
}
* @returns The ID of the currently shown Sone
*/
function getShownSoneId() {
- return $("#sone .sone-id").first().text();
+ return sone.find(".sone-id").first().text();
}
/**
* @returns The ID of the currently shown Sones
*/
function getShownSoneIds() {
- var soneIds = new Array();
- $("#sone #known-sones .sone .id").each(function() {
+ var soneIds = [];
+ sone.find("#known-sones .sone .id").each(function() {
soneIds.push($(this).text());
});
return soneIds.join(",");
* @returns The ID of the currently shown post
*/
function getShownPostId() {
- return $("#sone .post-id").text();
+ return sone.find(".post-id").text();
}
/**
* exists on the page, <code>false</code> otherwise
*/
function hasReply(replyId) {
- return $("#sone .reply#reply-" + replyId).length > 0;
+ return sone.find(".reply#reply-" + replyId).length > 0;
}
function loadNewPost(postId, soneId, recipientId, time) {
}
}
}
- if (getPostTime($("#sone .post").last()) > time) {
+ if (getPostTime(sone.find(".post").last()) > time) {
return;
}
ajaxGet("getPost.ajax", { "post" : postId }, function(data, textStatus) {
return;
}
var firstOlderPost = null;
- $("#sone .post").each(function() {
+ sone.find(".post").each(function() {
if (getPostTime(this) < data.post.time) {
firstOlderPost = $(this);
return false;
}
});
- newPost = $(data.post.html).addClass("hidden");
+ var newPost = $(data.post.html).addClass("hidden");
if ($(".post-author-local", newPost).text() == "true") {
newPost.removeClass("new");
}
if (hasReply(data.reply.id)) {
return;
}
- $("#sone .post#post-" + data.reply.postId).each(function() {
+ sone.find(".post#post-" + data.reply.postId).each(function() {
var firstNewerReply = null;
$(this).find(".replies .reply").each(function() {
if (getReplyTime(this) > data.reply.time) {
return false;
}
});
- newReply = $(data.reply.html).addClass("hidden");
+ var newReply = $(data.reply.html).addClass("hidden");
if ($(".reply-author-local", newReply).text() == "true") {
newReply.removeClass("new");
(function(newReply) {
function markPostAsKnown(postElements, skipRequest) {
$(postElements).each(function() {
- postElement = this;
+ var postElement = this;
if ($(postElement).hasClass("new") || ((typeof skipRequest != "undefined"))) {
(function(postElement) {
$(postElement).removeClass("new");
function markReplyAsKnown(replyElements, skipRequest) {
$(replyElements).each(function() {
- replyElement = this;
+ var replyElement = this;
if ($(replyElement).hasClass("new") || ((typeof skipRequest != "undefined"))) {
(function(replyElement) {
$(replyElement).removeClass("new");
}
function resetActivity() {
- title = document.title;
+ var title = document.title;
if (title.indexOf('(') == 0) {
setTitle(title.substr(title.indexOf(' ') + 1));
}
function setActivity() {
if (!focus) {
- title = document.title;
+ var title = document.title;
if (title.indexOf('(') != 0) {
setTitle("(!) " + title);
}
* user
*/
function createNotification(id, lastUpdatedTime, text, dismissable) {
- notification = $("<div></div>").addClass("notification").attr("id", id).attr("lastUpdatedTime", lastUpdatedTime);
+ var notification = $("<div></div>").addClass("notification").attr("id", id).attr("lastUpdatedTime", lastUpdatedTime);
if (dismissable) {
- dismissForm = $("#sone #notification-area #notification-dismiss-template").clone().removeClass("hidden").removeAttr("id");
+ var dismissForm = sone.find("#notification-area #notification-dismiss-template").clone().removeClass("hidden").removeAttr("id");
dismissForm.find("input[name=notification]").val(id);
notification.append(dismissForm);
}
* The ID of the notification
*/
function showNotificationDetails(notificationId) {
- $("#sone .notification#" + notificationId + " .text").removeClass("hidden");
- $("#sone .notification#" + notificationId + " .short-text").addClass("hidden");
+ sone.find(".notification#" + notificationId + " .text").removeClass("hidden");
+ sone.find(".notification#" + notificationId + " .short-text").addClass("hidden");
}
/**
function deleteProfileField(fieldId) {
ajaxGet("deleteProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId}, function(data, textStatus) {
if (data && data.success) {
- $("#sone .profile-field#" + data.field.id).slideUp();
+ sone.find(".profile-field#" + data.field.id).slideUp();
}
});
}
*/
function showOfflineMarker(visible) {
/* jQuery documentation says toggle() works the other way around?! */
- $("#sone #offline-marker").toggle(visible);
+ sone.find("#offline-marker").toggle(visible);
if (visible) {
- $("#sone #main").addClass("offline");
+ sone.find("#main").addClass("offline");
} else {
- $("#sone #main").removeClass("offline");
+ sone.find("#main").removeClass("offline");
}
}
// EVERYTHING BELOW HERE IS EXECUTED AFTER LOADING THE PAGE
//
+var sone = $("#sone");
var focus = true;
var online = true;
-var initiallyLoggedIn = $("#sone #loggedIn").text() == "true";
+var initiallyLoggedIn = sone.find("#loggedIn").text() == "true";
var notLoggedIn = !initiallyLoggedIn;
/** ID of the next-to-show Sone context menu. */
$(document).ready(function() {
/* rip out the status update textarea. */
- $("#sone .rip-out").each(function() {
+ sone.find(".rip-out").each(function() {
var oldElement = $(this);
- newElement = $("<input type='text'/>");
+ var newElement = $("<input type='text'/>");
newElement.attr("class", oldElement.attr("class")).attr("name", oldElement.attr("name"));
oldElement.before(newElement).remove();
});
/* 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();
+ sone.find("#update-status .select-sender").css("display", "inline");
+ sone.find("#update-status .sender").hide();
+ sone.find("#update-status .select-sender button").click(function() {
+ sone.find("#update-status .sender").show();
+ sone.find("#update-status .select-sender").hide();
return false;
});
- $("#sone #update-status").submit(function() {
- button = $("button:submit", this);
+ sone.find("#update-status").submit(function() {
+ var button = $("button:submit", this);
button.attr("disabled", "disabled");
if ($(this).find(":input.default:enabled").length > 0) {
return false;
}
- sender = $(this).find(":input[name=sender]").val();
- text = $(this).find(":input[name=text]:enabled").val();
+ var sender = $(this).find(":input[name=sender]").val();
+ var text = $(this).find(":input[name=text]:enabled").val();
ajaxGet("createPost.ajax", { "formPassword": getFormPassword(), "sender": sender, "text": text }, function(data, textStatus) {
button.removeAttr("disabled");
});
/* 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();
+ sone.find("#post-message .select-sender").css("display", "inline");
+ sone.find("#post-message .sender").hide();
+ sone.find("#post-message .select-sender button").click(function() {
+ sone.find("#post-message .sender").show();
+ sone.find("#post-message .select-sender").hide();
return false;
});
- $("#sone #post-message").submit(function() {
- sender = $(this).find(":input[name=sender]").val();
- text = $(this).find(":input[name=text]:enabled").val();
+ sone.find("#post-message").submit(function() {
+ var sender = $(this).find(":input[name=sender]").val();
+ var text = $(this).find(":input[name=text]:enabled").val();
ajaxGet("createPost.ajax", { "formPassword": getFormPassword(), "recipient": getShownSoneId(), "sender": sender, "text": text });
$(this).find(":input[name=sender]").val(getCurrentSoneId());
$(this).find(":input[name=text]:enabled").val("").blur();
/* Ajaxifies all posts. */
/* calling getTranslation here will cache the necessary values. */
- getTranslation("WebInterface.Confirmation.DeletePostButton", function(text) {
- getTranslation("WebInterface.Confirmation.DeleteReplyButton", function(text) {
- getTranslation("WebInterface.DefaultText.Reply", function(text) {
- getTranslation("WebInterface.Button.Comment", function(text) {
- $("#sone .post").each(function() {
+ getTranslation("WebInterface.Confirmation.DeletePostButton", function() {
+ getTranslation("WebInterface.Confirmation.DeleteReplyButton", function() {
+ getTranslation("WebInterface.DefaultText.Reply", function() {
+ getTranslation("WebInterface.Button.Comment", function () {
+ sone.find(".post").each(function() {
ajaxifyPost(this);
});
});
});
/* update post times. */
- postIds = [];
- $("#sone .post").each(function() {
+ var postIds = [];
+ sone.find(".post").each(function() {
postIds.push(getPostId(this));
});
updatePostTimes(postIds.join(","));
/* hides all replies but the latest two. */
if (!isViewPostPage()) {
getTranslation("WebInterface.ClickToShow.Replies", function(text) {
- $("#sone .post .replies").each(function() {
- allReplies = $(this).find(".reply");
+ sone.find(".post .replies").each(function() {
+ var allReplies = $(this).find(".reply");
if (allReplies.length > 2) {
- newHidden = false;
- for (replyIndex = 0; replyIndex < (allReplies.length - 2); ++replyIndex) {
+ var newHidden = false;
+ for (var replyIndex = 0; replyIndex < (allReplies.length - 2); ++replyIndex) {
$(allReplies[replyIndex]).addClass("hidden");
newHidden |= $(allReplies[replyIndex]).hasClass("new");
}
- clickToShowElement = $("<div></div>").addClass("click-to-show");
+ var clickToShowElement = $("<div></div>").addClass("click-to-show");
if (newHidden) {
clickToShowElement.addClass("new");
}
});
}
- $("#sone .sone").each(function() {
+ sone.find(".sone").each(function() {
ajaxifySone($(this));
});
/* process all existing notifications, ajaxify dismiss buttons. */
- $("#sone #notification-area .notification").each(function() {
+ sone.find("#notification-area .notification").each(function() {
ajaxifyNotification($(this));
});
<h1><%= Page.DeleteAlbum.Page.Title|l10n|html></h1>
- <p><%= Page.DeleteAlbum.Text.AlbumWillBeGone|l10n|replace needle=="{title}" replacementKey==album.title|html></p>
+ <p><%= Page.DeleteAlbum.Text.AlbumWillBeGone|l10n|replace needle=="{title}" replacement=album.title|html></p>
<form method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<div class="backlink"><a href="imageBrowser.html?sone=<%sone.id|html>"><%sone.niceName|html></a></div>
</div>
- <%include include/browseAlbums.html albums=sone.albums>
+ <%include include/browseAlbums.html albums=sone.rootAlbum.albums>
<%if sone.local>
<div class="show-create-album hidden toggle-link"><a class="small-link">» <%= View.CreateAlbum.Title|l10n|html></a></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="sender"><%= Page.Index.Label.Sender|l10n|html></label>
- <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><label for="text"><%= Page.Index.Label.Text|l10n|html></label>
- <textarea class="rip-out status-input" name="text"></textarea>
- <button type="submit"><%= Page.Index.Button.Post|l10n|html></button>
-</form>
+<%ifnull !currentSone>
+ <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="sender"><%= Page.Index.Label.Sender|l10n|html></label>
+ <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><label for="text"><%= Page.Index.Label.Text|l10n|html></label>
+ <textarea class="rip-out status-input" name="text"></textarea>
+ <button type="submit"><%= Page.Index.Button.Post|l10n|html></button>
+ </form>
+<%/if>
<%/first>
<album>
<id><%album.id|xml></id>
- <%ifnull !album.parent>
+ <%if !album.parent.root>
<parent><%album.parent.id|xml></parent>
<%/if>
<title><%album.title|xml></title>
<%if soneStatus|match value=="inserting">
- Your Sone <a href="viewSone.html?sone=<%insertSone.id|html>"><%insertSone.niceName|html></a> is now being inserted.
+ <%= Notification.SoneIsInserting.Text|l10n 0=insertSone.id|parse>
<%elseif soneStatus|match value=="inserted">
- Your Sone <a href="viewSone.html?sone=<%insertSone.id|html>"><%insertSone.niceName|html></a> has been inserted in <%= Notification.SoneInsert.Duration|l10n 0=insertDuration>.
+ <%= Notification.SoneIsInserted.Text|l10n 0=insertSone.id 1=insertDuration|parse>
<%elseif soneStatus|match value=="insert-aborted">
- Inserting your Sone <a href="viewSone.html?sone=<%insertSone.id|html>"><%insertSone.niceName|html></a> has failed.
-<%/if>
\ No newline at end of file
+ <%= Notification.SoneInsertAborted.Text|l10n 0=insertSone.id|parse>
+<%/if>
--- /dev/null
+/*
+ * © 2013 xplosion interactive
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+
+import freenet.clients.http.HTTPRequestImpl;
+import freenet.support.api.HTTPRequest;
+import org.junit.Test;
+
+/**
+ * Tests for {@link BookmarkAjaxPage}.
+ *
+ * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
+ */
+public class BookmarkAjaxPageTest {
+
+ @Test
+ public void testBookmarkingExistingPost() throws URISyntaxException {
+ /* create mocks. */
+ Core core = mock(Core.class);
+ WebInterface webInterface = mock(WebInterface.class);
+ when(webInterface.getCore()).thenReturn(core);
+ HTTPRequest httpRequest = new HTTPRequestImpl(new URI("/ajax/bookmark.ajax?post=abc"), "GET");
+ FreenetRequest request = mock(FreenetRequest.class);
+ when(request.getHttpRequest()).thenReturn(httpRequest);
+
+ /* create JSON page. */
+ BookmarkAjaxPage bookmarkAjaxPage = new BookmarkAjaxPage(webInterface);
+ JsonReturnObject jsonReturnObject = bookmarkAjaxPage.createJsonObject(request);
+
+ /* verify response. */
+ assertThat(jsonReturnObject, notNullValue());
+ assertThat(jsonReturnObject.isSuccess(), is(true));
+
+ /* verify behaviour. */
+ verify(core, times(1)).bookmarkPost(anyString());
+ verify(core).bookmarkPost("abc");
+ }
+
+ @Test
+ public void testBookmarkingMissingPost() throws URISyntaxException {
+ /* create mocks. */
+ Core core = mock(Core.class);
+ WebInterface webInterface = mock(WebInterface.class);
+ when(webInterface.getCore()).thenReturn(core);
+ HTTPRequest httpRequest = new HTTPRequestImpl(new URI("/ajax/bookmark.ajax"), "GET");
+ FreenetRequest request = mock(FreenetRequest.class);
+ when(request.getHttpRequest()).thenReturn(httpRequest);
+
+ /* create JSON page. */
+ BookmarkAjaxPage bookmarkAjaxPage = new BookmarkAjaxPage(webInterface);
+ JsonReturnObject jsonReturnObject = bookmarkAjaxPage.createJsonObject(request);
+
+ /* verify response. */
+ assertThat(jsonReturnObject, notNullValue());
+ assertThat(jsonReturnObject.isSuccess(), is(false));
+ assertThat(((JsonErrorReturnObject) jsonReturnObject).getError(), is("invalid-post-id"));
+
+ /* verify behaviour. */
+ verify(core, never()).bookmarkPost(anyString());
+ }
+
+}