<modelVersion>4.0.0</modelVersion>
<groupId>net.pterodactylus</groupId>
<artifactId>sone</artifactId>
- <version>0.6.7</version>
+ <version>0.7</version>
<dependencies>
<dependency>
<groupId>net.pterodactylus</groupId>
<artifactId>utils</artifactId>
- <version>0.10.0</version>
+ <version>0.11</version>
</dependency>
<dependency>
<groupId>junit</groupId>
import net.pterodactylus.sone.core.Options.DefaultOption;
import net.pterodactylus.sone.core.Options.Option;
import net.pterodactylus.sone.core.Options.OptionWatcher;
+import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Client;
+import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Profile;
-import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.TemporaryImage;
+import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.fcp.FcpInterface;
import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
import net.pterodactylus.sone.freenet.wot.Identity;
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Core extends AbstractService implements IdentityListener, UpdateListener, SoneProvider, PostProvider, SoneInsertListener {
+public class Core extends AbstractService implements IdentityListener, UpdateListener, SoneProvider, PostProvider, SoneInsertListener, ImageInsertListener {
/**
* Enumeration for the possible states of a {@link Sone}.
/** The Sone downloader. */
private final SoneDownloader soneDownloader;
+ /** The image inserter. */
+ private final ImageInserter imageInserter;
+
/** Sone downloader thread-pool. */
private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10);
/** Trusted identities, sorted by own identities. */
private Map<OwnIdentity, Set<Identity>> trustedIdentities = Collections.synchronizedMap(new HashMap<OwnIdentity, Set<Identity>>());
+ /** All known albums. */
+ private Map<String, Album> albums = new HashMap<String, Album>();
+
+ /** All known images. */
+ private Map<String, Image> images = new HashMap<String, Image>();
+
+ /** All temporary images. */
+ private Map<String, TemporaryImage> temporaryImages = new HashMap<String, TemporaryImage>();
+
/** Ticker for threads that mark own elements as known. */
private Ticker localElementTicker = new Ticker();
this.freenetInterface = freenetInterface;
this.identityManager = identityManager;
this.soneDownloader = new SoneDownloader(this, freenetInterface);
+ this.imageInserter = new ImageInserter(this, freenetInterface);
this.updateChecker = new UpdateChecker(freenetInterface);
}
return posts;
}
+ /**
+ * Returns the album with the given ID, creating a new album if no album
+ * with the given ID can be found.
+ *
+ * @param albumId
+ * The ID of the album
+ * @return The album with the given ID
+ */
+ public Album getAlbum(String albumId) {
+ return getAlbum(albumId, true);
+ }
+
+ /**
+ * Returns the album with the given ID, optionally creating a new album if
+ * an album with the given ID can not be found.
+ *
+ * @param albumId
+ * The ID of the album
+ * @param create
+ * {@code true} to create a new album if none exists for the
+ * given ID
+ * @return The album with the given ID, or {@code null} if no album with the
+ * given ID exists and {@code create} is {@code false}
+ */
+ public Album getAlbum(String albumId, boolean create) {
+ synchronized (albums) {
+ Album album = albums.get(albumId);
+ if (create && (album == null)) {
+ album = new Album(albumId);
+ albums.put(albumId, album);
+ }
+ return album;
+ }
+ }
+
+ /**
+ * Returns the image with the given ID, creating it if necessary.
+ *
+ * @param imageId
+ * The ID of the image
+ * @return The image with the given ID
+ */
+ public Image getImage(String imageId) {
+ return getImage(imageId, true);
+ }
+
+ /**
+ * Returns the image with the given ID, optionally creating it if it does
+ * not exist.
+ *
+ * @param imageId
+ * The ID of the image
+ * @param create
+ * {@code true} to create an image if none exists with the given
+ * ID
+ * @return The image with the given ID, or {@code null} if none exists and
+ * none was created
+ */
+ public Image getImage(String imageId, boolean create) {
+ synchronized (images) {
+ Image image = images.get(imageId);
+ if (create && (image == null)) {
+ image = new Image(imageId);
+ images.put(imageId, image);
+ }
+ return image;
+ }
+ }
+
+ /**
+ * Returns the temporary image with the given ID.
+ *
+ * @param imageId
+ * The ID of the temporary image
+ * @return The temporary image, or {@code null} if there is no temporary
+ * image with the given ID
+ */
+ public TemporaryImage getTemporaryImage(String imageId) {
+ synchronized (temporaryImages) {
+ return temporaryImages.get(imageId);
+ }
+ }
+
//
// ACTIONS
//
}
}
}
+ synchronized (albums) {
+ synchronized (images) {
+ for (Album album : storedSone.getAlbums()) {
+ albums.remove(album.getId());
+ for (Image image : album.getImages()) {
+ images.remove(image.getId());
+ }
+ }
+ for (Album album : sone.getAlbums()) {
+ albums.put(album.getId(), album);
+ for (Image image : album.getImages()) {
+ images.put(image.getId(), image);
+ }
+ }
+ }
+ }
synchronized (storedSone) {
if (!soneRescueMode || (sone.getTime() > storedSone.getTime())) {
storedSone.setTime(sone.getTime());
for (String likedReplyId : sone.getLikedReplyIds()) {
storedSone.addLikedReplyId(likedReplyId);
}
+ for (Album album : sone.getAlbums()) {
+ storedSone.addAlbum(album);
+ }
} else {
storedSone.setPosts(sone.getPosts());
storedSone.setReplies(sone.getReplies());
storedSone.setLikePostIds(sone.getLikedPostIds());
storedSone.setLikeReplyIds(sone.getLikedReplyIds());
+ storedSone.setAlbums(sone.getAlbums());
}
storedSone.setLatestEdition(sone.getLatestEdition());
}
friends.add(friendId);
}
+ /* load albums. */
+ List<Album> topLevelAlbums = new ArrayList<Album>();
+ int albumCounter = 0;
+ while (true) {
+ String albumPrefix = sonePrefix + "/Albums/" + albumCounter++;
+ String albumId = configuration.getStringValue(albumPrefix + "/ID").getValue(null);
+ if (albumId == null) {
+ break;
+ }
+ String albumTitle = configuration.getStringValue(albumPrefix + "/Title").getValue(null);
+ String albumDescription = configuration.getStringValue(albumPrefix + "/Description").getValue(null);
+ String albumParentId = configuration.getStringValue(albumPrefix + "/Parent").getValue(null);
+ String albumImageId = configuration.getStringValue(albumPrefix + "/AlbumImage").getValue(null);
+ if ((albumTitle == null) || (albumDescription == null)) {
+ logger.log(Level.WARNING, "Invalid album found, aborting load!");
+ return;
+ }
+ Album album = getAlbum(albumId).setSone(sone).setTitle(albumTitle).setDescription(albumDescription).setAlbumImage(albumImageId);
+ if (albumParentId != null) {
+ Album parentAlbum = getAlbum(albumParentId, false);
+ if (parentAlbum == null) {
+ logger.log(Level.WARNING, "Invalid parent album ID: " + albumParentId);
+ return;
+ }
+ parentAlbum.addAlbum(album);
+ } else {
+ topLevelAlbums.add(album);
+ }
+ }
+
+ /* load images. */
+ int imageCounter = 0;
+ while (true) {
+ String imagePrefix = sonePrefix + "/Images/" + imageCounter++;
+ String imageId = configuration.getStringValue(imagePrefix + "/ID").getValue(null);
+ if (imageId == null) {
+ break;
+ }
+ String albumId = configuration.getStringValue(imagePrefix + "/Album").getValue(null);
+ String key = configuration.getStringValue(imagePrefix + "/Key").getValue(null);
+ String title = configuration.getStringValue(imagePrefix + "/Title").getValue(null);
+ String description = configuration.getStringValue(imagePrefix + "/Description").getValue(null);
+ Long creationTime = configuration.getLongValue(imagePrefix + "/CreationTime").getValue(null);
+ Integer width = configuration.getIntValue(imagePrefix + "/Width").getValue(null);
+ Integer height = configuration.getIntValue(imagePrefix + "/Height").getValue(null);
+ if ((albumId == null) || (key == null) || (title == null) || (description == null) || (creationTime == null) || (width == null) || (height == null)) {
+ logger.log(Level.WARNING, "Invalid image found, aborting load!");
+ return;
+ }
+ Album album = getAlbum(albumId, false);
+ if (album == null) {
+ logger.log(Level.WARNING, "Invalid album image encountered, aborting load!");
+ return;
+ }
+ Image image = getImage(imageId).setSone(sone).setCreationTime(creationTime).setKey(key);
+ image.setTitle(title).setDescription(description).setWidth(width).setHeight(height);
+ album.addImage(image);
+ }
+
/* load options. */
sone.getOptions().getBooleanOption("AutoFollow").set(configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").getValue(null));
sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").set(configuration.getBooleanValue(sonePrefix + "/Options/EnableSoneInsertNotifications").getValue(null));
sone.setLikePostIds(likedPostIds);
sone.setLikeReplyIds(likedReplyIds);
sone.setFriends(friends);
+ sone.setAlbums(topLevelAlbums);
soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint);
}
synchronized (newSones) {
}
/**
+ * Creates a new top-level album for the given Sone.
+ *
+ * @param sone
+ * The Sone to create the album for
+ * @return The new album
+ */
+ public Album createAlbum(Sone sone) {
+ return createAlbum(sone, null);
+ }
+
+ /**
+ * Creates a new album for the given Sone.
+ *
+ * @param sone
+ * The Sone to create the album for
+ * @param parent
+ * The parent of the album (may be {@code null} to create a
+ * top-level album)
+ * @return The new album
+ */
+ public Album createAlbum(Sone sone, Album parent) {
+ Album album = new Album();
+ synchronized (albums) {
+ albums.put(album.getId(), album);
+ }
+ album.setSone(sone);
+ if (parent != null) {
+ parent.addAlbum(album);
+ } else {
+ sone.addAlbum(album);
+ }
+ return album;
+ }
+
+ /**
+ * Deletes the given album. The owner of the album has to be a local Sone,
+ * and the album has to be {@link Album#isEmpty() empty} to be deleted.
+ *
+ * @param album
+ * The album to remove
+ */
+ public void deleteAlbum(Album album) {
+ Validation.begin().isNotNull("Album", album).check().is("Local Sone", isLocalSone(album.getSone())).check();
+ if (!album.isEmpty()) {
+ return;
+ }
+ if (album.getParent() == null) {
+ album.getSone().removeAlbum(album);
+ } else {
+ album.getParent().removeAlbum(album);
+ }
+ synchronized (albums) {
+ albums.remove(album.getId());
+ }
+ saveSone(album.getSone());
+ }
+
+ /**
+ * Creates a new image.
+ *
+ * @param sone
+ * The Sone creating the image
+ * @param album
+ * The album the image will be inserted into
+ * @param temporaryImage
+ * The temporary image to create the image from
+ * @return The newly created image
+ */
+ public Image createImage(Sone sone, Album album, TemporaryImage temporaryImage) {
+ Validation.begin().isNotNull("Sone", sone).isNotNull("Album", album).isNotNull("Temporary Image", temporaryImage).check().is("Local Sone", isLocalSone(sone)).check().isEqual("Owner and Album Owner", sone, album.getSone()).check();
+ Image image = new Image(temporaryImage.getId()).setSone(sone).setCreationTime(System.currentTimeMillis());
+ album.addImage(image);
+ synchronized (images) {
+ images.put(image.getId(), image);
+ }
+ imageInserter.insertImage(temporaryImage, image);
+ return image;
+ }
+
+ /**
+ * Deletes the given image. This method will also delete a matching
+ * temporary image.
+ *
+ * @see #deleteTemporaryImage(TemporaryImage)
+ * @param image
+ * The image to delete
+ */
+ public void deleteImage(Image image) {
+ Validation.begin().isNotNull("Image", image).check().is("Local Sone", isLocalSone(image.getSone())).check();
+ deleteTemporaryImage(image.getId());
+ image.getAlbum().removeImage(image);
+ synchronized (images) {
+ images.remove(image.getId());
+ }
+ saveSone(image.getSone());
+ }
+
+ /**
+ * Creates a new temporary image.
+ *
+ * @param mimeType
+ * The MIME type of the temporary image
+ * @param imageData
+ * The encoded data of the image
+ * @return The temporary image
+ */
+ public TemporaryImage createTemporaryImage(String mimeType, byte[] imageData) {
+ TemporaryImage temporaryImage = new TemporaryImage();
+ temporaryImage.setMimeType(mimeType).setImageData(imageData);
+ synchronized (temporaryImages) {
+ temporaryImages.put(temporaryImage.getId(), temporaryImage);
+ }
+ return temporaryImage;
+ }
+
+ /**
+ * Deletes the given temporary image.
+ *
+ * @param temporaryImage
+ * The temporary image to delete
+ */
+ public void deleteTemporaryImage(TemporaryImage temporaryImage) {
+ Validation.begin().isNotNull("Temporary Image", temporaryImage).check();
+ deleteTemporaryImage(temporaryImage.getId());
+ }
+
+ /**
+ * Deletes the temporary image with the given ID.
+ *
+ * @param imageId
+ * The ID of the temporary image to delete
+ */
+ public void deleteTemporaryImage(String imageId) {
+ Validation.begin().isNotNull("Temporary Image ID", imageId).check();
+ synchronized (temporaryImages) {
+ temporaryImages.remove(imageId);
+ }
+ Image image = getImage(imageId, false);
+ if (image != null) {
+ imageInserter.cancelImageInsert(image);
+ }
+ }
+
+ /**
* Notifies the core that the configuration, either of the core or of a
* single local Sone, has changed, and that the configuration should be
* saved.
}
configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null);
+ /* save albums. first, collect in a flat structure, top-level first. */
+ List<Album> albums = Sone.flattenAlbums(sone.getAlbums());
+
+ int albumCounter = 0;
+ for (Album album : albums) {
+ String albumPrefix = sonePrefix + "/Albums/" + albumCounter++;
+ 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 + "/AlbumImage").setValue(album.getAlbumImage() == null ? null : album.getAlbumImage().getId());
+ }
+ configuration.getStringValue(sonePrefix + "/Albums/" + albumCounter + "/ID").setValue(null);
+
+ /* save images. */
+ int imageCounter = 0;
+ for (Album album : albums) {
+ for (Image image : album.getImages()) {
+ if (!image.isInserted()) {
+ continue;
+ }
+ String imagePrefix = sonePrefix + "/Images/" + imageCounter++;
+ configuration.getStringValue(imagePrefix + "/ID").setValue(image.getId());
+ configuration.getStringValue(imagePrefix + "/Album").setValue(album.getId());
+ configuration.getStringValue(imagePrefix + "/Key").setValue(image.getKey());
+ configuration.getStringValue(imagePrefix + "/Title").setValue(image.getTitle());
+ configuration.getStringValue(imagePrefix + "/Description").setValue(image.getDescription());
+ configuration.getLongValue(imagePrefix + "/CreationTime").setValue(image.getCreationTime());
+ configuration.getIntValue(imagePrefix + "/Width").setValue(image.getWidth());
+ configuration.getIntValue(imagePrefix + "/Height").setValue(image.getHeight());
+ }
+ }
+ configuration.getStringValue(sonePrefix + "/Images/" + imageCounter + "/ID").setValue(null);
+
/* save options. */
configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").setValue(sone.getOptions().getBooleanOption("AutoFollow").getReal());
configuration.getBooleanValue(sonePrefix + "/Options/EnableSoneInsertNotifications").setValue(sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").getReal());
}
//
- // SONEINSERTLISTENER METHODS
+ // INTERFACE ImageInsertListener
//
/**
* {@inheritDoc}
*/
+ @Override
public void insertStarted(Sone sone) {
coreListenerManager.fireSoneInserting(sone);
}
coreListenerManager.fireSoneInsertAborted(sone, cause);
}
+ //
+ // SONEINSERTLISTENER METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertStarted(Image image) {
+ logger.log(Level.WARNING, "Image insert started for " + image);
+ coreListenerManager.fireImageInsertStarted(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertAborted(Image image) {
+ logger.log(Level.WARNING, "Image insert aborted for " + image);
+ coreListenerManager.fireImageInsertAborted(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertFinished(Image image, FreenetURI key) {
+ logger.log(Level.WARNING, "Image insert finished for " + image + ": " + key);
+ image.setKey(key.toString());
+ deleteTemporaryImage(image.getId());
+ saveSone(image.getSone());
+ coreListenerManager.fireImageInsertFinished(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertFailed(Image image, Throwable cause) {
+ logger.log(Level.WARNING, "Image insert failed for " + image, cause);
+ coreListenerManager.fireImageInsertFailed(image, cause);
+ }
+
/**
* Convenience interface for external classes that want to access the core’s
* configuration.
import java.util.EventListener;
+import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
*/
public void updateFound(Version version, long releaseTime, long latestEdition);
+ /**
+ * Notifies a listener that an image has started being inserted.
+ *
+ * @param image
+ * The image that is now inserted
+ */
+ public void imageInsertStarted(Image image);
+
+ /**
+ * Notifies a listener that an image insert was aborted by the user.
+ *
+ * @param image
+ * The image that is not inserted anymore
+ */
+ public void imageInsertAborted(Image image);
+
+ /**
+ * Notifies a listener that an image was successfully inserted.
+ *
+ * @param image
+ * The image that was inserted
+ */
+ public void imageInsertFinished(Image image);
+
+ /**
+ * Notifies a listener that an image failed to be inserted.
+ *
+ * @param image
+ * The image that could not be inserted
+ * @param cause
+ * The reason for the failed insert
+ */
+ public void imageInsertFailed(Image image, Throwable cause);
+
}
package net.pterodactylus.sone.core;
+import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
}
}
+ /**
+ * Notifies all listeners that an image has started being inserted.
+ *
+ * @see CoreListener#imageInsertStarted(Image)
+ * @param image
+ * The image that is now inserted
+ */
+ void fireImageInsertStarted(Image image) {
+ for (CoreListener coreListener : getListeners()) {
+ coreListener.imageInsertStarted(image);
+ }
+ }
+
+ /**
+ * Notifies all listeners that an image insert was aborted by the user.
+ *
+ * @see CoreListener#imageInsertAborted(Image)
+ * @param image
+ * The image that is not inserted anymore
+ */
+ void fireImageInsertAborted(Image image) {
+ for (CoreListener coreListener : getListeners()) {
+ coreListener.imageInsertAborted(image);
+ }
+ }
+
+ /**
+ * Notifies all listeners that an image was successfully inserted.
+ *
+ * @see CoreListener#imageInsertFinished(Image)
+ * @param image
+ * The image that was inserted
+ */
+ void fireImageInsertFinished(Image image) {
+ for (CoreListener coreListener : getListeners()) {
+ coreListener.imageInsertFinished(image);
+ }
+ }
+
+ /**
+ * Notifies all listeners that an image failed to be inserted.
+ *
+ * @see CoreListener#imageInsertFailed(Image, Throwable)
+ * @param image
+ * The image that could not be inserted
+ * @param cause
+ * The cause of the failure
+ */
+ void fireImageInsertFailed(Image image, Throwable cause) {
+ for (CoreListener coreListener : getListeners()) {
+ coreListener.imageInsertFailed(image, cause);
+ }
+ }
+
}
package net.pterodactylus.sone.core;
import java.net.MalformedURLException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
+import net.pterodactylus.sone.core.SoneException.Type;
+import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.TemporaryImage;
import net.pterodactylus.util.collection.Pair;
import net.pterodactylus.util.logging.Logging;
import com.db4o.ObjectContainer;
+import freenet.client.ClientMetadata;
import freenet.client.FetchException;
import freenet.client.FetchResult;
import freenet.client.HighLevelSimpleClient;
import freenet.client.HighLevelSimpleClientImpl;
+import freenet.client.InsertBlock;
+import freenet.client.InsertContext;
import freenet.client.InsertException;
+import freenet.client.async.BaseClientPutter;
import freenet.client.async.ClientContext;
+import freenet.client.async.ClientPutCallback;
+import freenet.client.async.ClientPutter;
import freenet.client.async.USKCallback;
import freenet.keys.FreenetURI;
+import freenet.keys.InsertableClientSSK;
import freenet.keys.USK;
import freenet.node.Node;
import freenet.node.RequestStarter;
+import freenet.support.api.Bucket;
+import freenet.support.io.ArrayBucket;
/**
* Contains all necessary functionality for interacting with the Freenet node.
}
/**
+ * Inserts the image data of the given {@link TemporaryImage} and returns
+ * the given insert token that can be used to add listeners or cancel the
+ * insert.
+ *
+ * @param temporaryImage
+ * The temporary image data
+ * @param image
+ * The image
+ * @param insertToken
+ * The insert token
+ * @throws SoneException
+ * if the insert could not be started
+ */
+ public void insertImage(TemporaryImage temporaryImage, Image image, InsertToken insertToken) throws SoneException {
+ String filenameHint = image.getId() + "." + temporaryImage.getMimeType().substring(temporaryImage.getMimeType().lastIndexOf("/") + 1);
+ InsertableClientSSK key = InsertableClientSSK.createRandom(node.random, "");
+ FreenetURI targetUri = key.getInsertURI().setDocName(filenameHint);
+ InsertContext insertContext = client.getInsertContext(true);
+ Bucket bucket = new ArrayBucket(temporaryImage.getImageData());
+ ClientMetadata metadata = new ClientMetadata(temporaryImage.getMimeType());
+ InsertBlock insertBlock = new InsertBlock(bucket, metadata, targetUri);
+ try {
+ ClientPutter clientPutter = client.insert(insertBlock, false, null, false, insertContext, insertToken, RequestStarter.INTERACTIVE_PRIORITY_CLASS);
+ insertToken.setClientPutter(clientPutter);
+ } catch (InsertException ie1) {
+ throw new SoneException(Type.INSERT_FAILED, "Could not start image insert.", ie1);
+ }
+ }
+
+ /**
* Inserts a directory into Freenet.
*
* @param insertUri
}
+ /**
+ * Insert token that can be used to add {@link ImageInsertListener}s and
+ * cancel a running insert.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public class InsertToken implements ClientPutCallback {
+
+ /** The image being inserted. */
+ private final Image image;
+
+ /** The list of registered image insert listeners. */
+ private final List<ImageInsertListener> imageInsertListeners = Collections.synchronizedList(new ArrayList<ImageInsertListener>());
+
+ /** The client putter. */
+ private ClientPutter clientPutter;
+
+ /** The final URI. */
+ private volatile FreenetURI resultingUri;
+
+ /**
+ * Creates a new insert token for the given image.
+ *
+ * @param image
+ * The image being inserted
+ */
+ public InsertToken(Image image) {
+ this.image = image;
+ }
+
+ //
+ // LISTENER MANAGEMENT
+ //
+
+ /**
+ * Adds the given listener to the list of registered listener.
+ *
+ * @param imageInsertListener
+ * The listener to add
+ */
+ public void addImageInsertListener(ImageInsertListener imageInsertListener) {
+ imageInsertListeners.add(imageInsertListener);
+ }
+
+ /**
+ * Removes the given listener from the list of registered listener.
+ *
+ * @param imageInsertListener
+ * The listener to remove
+ */
+ public void removeImageInsertListener(ImageInsertListener imageInsertListener) {
+ imageInsertListeners.remove(imageInsertListener);
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Sets the client putter that is inserting the image. This will also
+ * signal all registered listeners that the image has started.
+ *
+ * @see ImageInsertListener#imageInsertStarted(Image)
+ * @param clientPutter
+ * The client putter
+ */
+ public void setClientPutter(ClientPutter clientPutter) {
+ this.clientPutter = clientPutter;
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ imageInsertListener.imageInsertStarted(image);
+ }
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Cancels the running insert.
+ *
+ * @see ImageInsertListener#imageInsertAborted(Image)
+ */
+ @SuppressWarnings("synthetic-access")
+ public void cancel() {
+ clientPutter.cancel(null, node.clientCore.clientContext);
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ imageInsertListener.imageInsertAborted(image);
+ }
+ }
+
+ //
+ // INTERFACE ClientPutCallback
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onMajorProgress(ObjectContainer objectContainer) {
+ /* ignore, we don’t care. */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFailure(InsertException insertException, BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ if ((insertException != null) && ("Cancelled by user".equals(insertException.getMessage()))) {
+ imageInsertListener.imageInsertAborted(image);
+ } else {
+ imageInsertListener.imageInsertFailed(image, insertException);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFetchable(BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ /* ignore, we don’t care. */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onGeneratedURI(FreenetURI generatedUri, BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ resultingUri = generatedUri;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSuccess(BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ imageInsertListener.imageInsertFinished(image, resultingUri);
+ }
+ }
+
+ }
+
}
--- /dev/null
+/*
+ * Sone - ImageInsertListener.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import java.util.EventListener;
+
+import net.pterodactylus.sone.core.FreenetInterface.InsertToken;
+import net.pterodactylus.sone.data.Image;
+import freenet.keys.FreenetURI;
+
+/**
+ * Listener interface for objects that want to be notified about the status of
+ * an image insert.
+ *
+ * @see ImageInserter#insertImage(net.pterodactylus.sone.data.TemporaryImage,
+ * Image)
+ * @see FreenetInterface#insertImage(net.pterodactylus.sone.data.TemporaryImage,
+ * Image, net.pterodactylus.sone.core.FreenetInterface.InsertToken)
+ * @see InsertToken
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface ImageInsertListener extends EventListener {
+
+ /**
+ * Notifies a listener that the insert of the given image started.
+ *
+ * @param image
+ * The image that is being inserted
+ */
+ public void imageInsertStarted(Image image);
+
+ /**
+ * Notifies a listener that the insert of the given image was aborted by the
+ * user.
+ *
+ * @param image
+ * The image that is no longer being inserted
+ */
+ public void imageInsertAborted(Image image);
+
+ /**
+ * Notifies a listener that the given image was inserted successfully.
+ *
+ * @param image
+ * The image that was inserted
+ * @param key
+ * The final key of the image
+ */
+ public void imageInsertFinished(Image image, FreenetURI key);
+
+ /**
+ * Notifies a listener that the given image could not be inserted.
+ *
+ * @param image
+ * The image that could not be inserted
+ * @param cause
+ * The cause of the insertion failure
+ */
+ public void imageInsertFailed(Image image, Throwable cause);
+
+}
--- /dev/null
+/*
+ * Sone - ImageInserter.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.sone.core.FreenetInterface.InsertToken;
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.data.TemporaryImage;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.validation.Validation;
+
+/**
+ * The image inserter is responsible for inserting images using
+ * {@link FreenetInterface#insertImage(TemporaryImage, Image, InsertToken)} and
+ * also tracks running inserts, giving the possibility to abort a running
+ * insert.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ImageInserter {
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(ImageInserter.class);
+
+ /** The core. */
+ private final Core core;
+
+ /** The freenet interface. */
+ private final FreenetInterface freenetInterface;
+
+ /** The tokens of running inserts. */
+ private final Map<String, InsertToken> insertTokens = Collections.synchronizedMap(new HashMap<String, InsertToken>());
+
+ /**
+ * Creates a new image inserter.
+ *
+ * @param core
+ * The Sone core
+ * @param freenetInterface
+ * The freenet interface
+ */
+ public ImageInserter(Core core, FreenetInterface freenetInterface) {
+ this.core = core;
+ this.freenetInterface = freenetInterface;
+ }
+
+ /**
+ * Inserts the given image. The {@link #core} will automatically added as
+ * {@link ImageInsertListener} to the created {@link InsertToken}.
+ *
+ * @param temporaryImage
+ * The temporary image data
+ * @param image
+ * The image
+ */
+ public void insertImage(TemporaryImage temporaryImage, Image image) {
+ Validation.begin().isNotNull("Temporary Image", temporaryImage).isNotNull("Image", image).check().isEqual("Image IDs", image.getId(), temporaryImage.getId()).check();
+ try {
+ InsertToken insertToken = freenetInterface.new InsertToken(image);
+ insertTokens.put(image.getId(), insertToken);
+ insertToken.addImageInsertListener(core);
+ freenetInterface.insertImage(temporaryImage, image, insertToken);
+ } catch (SoneException se1) {
+ logger.log(Level.WARNING, "Could not insert image!", se1);
+ }
+ }
+
+ /**
+ * Cancels a running image insert. If no insert is running for the given
+ * image, nothing happens.
+ *
+ * @param image
+ * The image being inserted
+ */
+ public void cancelImageInsert(Image image) {
+ InsertToken insertToken = insertTokens.remove(image.getId());
+ if (insertToken == null) {
+ return;
+ }
+ insertToken.cancel();
+ insertToken.removeImageInsertListener(core);
+ }
+
+}
package net.pterodactylus.sone.core;
-import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.pterodactylus.sone.core.Core.SoneStatus;
+import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Client;
+import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Reply;
}
}
return parsedSone;
- } catch (IOException ioe1) {
- logger.log(Level.WARNING, "Could not parse Sone from " + requestUri + "!", ioe1);
+ } catch (Exception e1) {
+ logger.log(Level.WARNING, "Could not parse Sone from " + requestUri + "!", e1);
} finally {
Closer.close(soneInputStream);
soneBucket.free();
}
}
+ /* parse albums. */
+ SimpleXML albumsXml = soneXml.getNode("albums");
+ List<Album> topLevelAlbums = new ArrayList<Album>();
+ if (albumsXml != null) {
+ for (SimpleXML albumXml : albumsXml.getNodes("album")) {
+ String id = albumXml.getValue("id", null);
+ String parentId = albumXml.getValue("parent", null);
+ String title = albumXml.getValue("title", null);
+ String description = albumXml.getValue("description", null);
+ String albumImageId = albumXml.getValue("album-image", null);
+ if ((id == null) || (title == null) || (description == null)) {
+ logger.log(Level.WARNING, "Downloaded Sone %s contains invalid album!", new Object[] { sone });
+ return null;
+ }
+ Album parent = null;
+ if (parentId != null) {
+ parent = core.getAlbum(parentId, false);
+ if (parent == null) {
+ logger.log(Level.WARNING, "Downloaded Sone %s has album with invalid parent!", new Object[] { sone });
+ return null;
+ }
+ }
+ Album album = core.getAlbum(id).setSone(sone).setTitle(title).setDescription(description).setAlbumImage(albumImageId);
+ if (parent != null) {
+ parent.addAlbum(album);
+ } else {
+ topLevelAlbums.add(album);
+ }
+ SimpleXML imagesXml = albumXml.getNode("images");
+ if (imagesXml != null) {
+ for (SimpleXML imageXml : imagesXml.getNodes("image")) {
+ String imageId = imageXml.getValue("id", null);
+ String imageCreationTimeString = imageXml.getValue("creation-time", null);
+ String imageKey = imageXml.getValue("key", null);
+ String imageTitle = imageXml.getValue("title", null);
+ String imageDescription = imageXml.getValue("description", "");
+ String imageWidthString = imageXml.getValue("width", null);
+ String imageHeightString = imageXml.getValue("height", null);
+ if ((imageId == null) || (imageCreationTimeString == null) || (imageKey == null) || (imageTitle == null) || (imageWidthString == null) || (imageHeightString == null)) {
+ logger.log(Level.WARNING, "Downloaded Sone %s contains invalid images!", new Object[] { sone });
+ return null;
+ }
+ long creationTime = Numbers.safeParseLong(imageCreationTimeString, 0L);
+ int imageWidth = Numbers.safeParseInteger(imageWidthString, 0);
+ int imageHeight = Numbers.safeParseInteger(imageHeightString, 0);
+ if ((imageWidth < 1) || (imageHeight < 1)) {
+ logger.log(Level.WARNING, "Downloaded Sone %s contains image %s with invalid dimensions (%s, %s)!", new Object[] { sone, imageId, imageWidthString, imageHeightString });
+ return null;
+ }
+ Image image = core.getImage(imageId).setSone(sone).setKey(imageKey).setCreationTime(creationTime);
+ image.setTitle(imageTitle).setDescription(imageDescription);
+ image.setWidth(imageWidth).setHeight(imageHeight);
+ album.addImage(image);
+ }
+ }
+ }
+ }
+
/* okay, apparently everything was parsed correctly. Now import. */
/* atomic setter operation on the Sone. */
synchronized (sone) {
sone.setReplies(replies);
sone.setLikePostIds(likedPostIds);
sone.setLikeReplyIds(likedReplyIds);
+ sone.setAlbums(topLevelAlbums);
}
return sone;
/** An invalid URI was specified. */
INVALID_URI,
+ /** An insert failed. */
+ INSERT_FAILED,
+
}
/** The type of the exception. */
soneProperties.put("replies", new ListBuilder<Reply>(new ArrayList<Reply>(sone.getReplies())).sort(new ReverseComparator<Reply>(Reply.TIME_COMPARATOR)).get());
soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
+ soneProperties.put("albums", Sone.flattenAlbums(sone.getAlbums()));
}
//
--- /dev/null
+/*
+ * Sone - Album.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import net.pterodactylus.util.collection.Mapper;
+import net.pterodactylus.util.collection.Mappers;
+import net.pterodactylus.util.object.Default;
+import net.pterodactylus.util.validation.Validation;
+
+/**
+ * Container for images that can also contain nested {@link Album}s.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Album implements Fingerprintable {
+
+ /** The ID of this album. */
+ private final String id;
+
+ /** The Sone this album belongs to. */
+ private Sone sone;
+
+ /** Nested albums. */
+ private final List<Album> albums = new ArrayList<Album>();
+
+ /** The image IDs in order. */
+ private final List<String> imageIds = new ArrayList<String>();
+
+ /** The images in this album. */
+ private final Map<String, Image> images = new HashMap<String, Image>();
+
+ /** The parent album. */
+ private Album parent;
+
+ /** The title of this album. */
+ private String title;
+
+ /** The description of this album. */
+ private String description;
+
+ /** The ID of the album picture. */
+ private String albumImage;
+
+ /**
+ * Creates a new album with a random ID.
+ */
+ public Album() {
+ this(UUID.randomUUID().toString());
+ }
+
+ /**
+ * Creates a new album with the given ID.
+ *
+ * @param id
+ * The ID of the album
+ */
+ public Album(String id) {
+ Validation.begin().isNotNull("Album ID", id).check();
+ this.id = id;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the ID of this album.
+ *
+ * @return The ID of this album
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the Sone this album belongs to.
+ *
+ * @return The Sone this album belongs to
+ */
+ public Sone getSone() {
+ return sone;
+ }
+
+ /**
+ * Sets the owner of the album. The owner can only be set as long as the
+ * current owner is {@code null}.
+ *
+ * @param sone
+ * The album owner
+ * @return This album
+ */
+ public Album setSone(Sone sone) {
+ Validation.begin().isNotNull("New Album Owner", sone).isEither("Old Album Owner", this.sone, null, sone).check();
+ this.sone = sone;
+ return this;
+ }
+
+ /**
+ * Returns the nested albums.
+ *
+ * @return The nested albums
+ */
+ public List<Album> getAlbums() {
+ return new ArrayList<Album>(albums);
+ }
+
+ /**
+ * Adds an album to this album.
+ *
+ * @param album
+ * The album to add
+ */
+ public void addAlbum(Album album) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEither("Old Album Parent", this.parent, null, album.parent).check();
+ album.setParent(this);
+ if (!albums.contains(album)) {
+ albums.add(album);
+ }
+ }
+
+ /**
+ * Removes an album from this album.
+ *
+ * @param album
+ * The album to remove
+ */
+ public void removeAlbum(Album album) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEqual("Album Parent", album.parent, this).check();
+ albums.remove(album);
+ album.removeParent();
+ }
+
+ /**
+ * 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) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEqual("Album Parent", album.parent, this).check();
+ 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) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEqual("Album Parent", album.parent, this).check();
+ 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);
+ }
+
+ /**
+ * Returns the images in this album.
+ *
+ * @return The images in this album
+ */
+ public List<Image> getImages() {
+ return Mappers.mappedList(imageIds, new Mapper<String, Image>() {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public Image map(String imageId) {
+ return images.get(imageId);
+ }
+
+ });
+ }
+
+ /**
+ * Adds the given image to this album.
+ *
+ * @param image
+ * The image to add
+ */
+ public void addImage(Image image) {
+ Validation.begin().isNotNull("Image", image).check().isNotNull("Image Owner", image.getSone()).check().isEqual("Image Owner", image.getSone(), sone).check();
+ if (image.getAlbum() != null) {
+ image.getAlbum().removeImage(image);
+ }
+ image.setAlbum(this);
+ if (imageIds.isEmpty() && (albumImage == null)) {
+ albumImage = image.getId();
+ }
+ if (!imageIds.contains(image.getId())) {
+ imageIds.add(image.getId());
+ images.put(image.getId(), image);
+ }
+ }
+
+ /**
+ * Removes the given image from this album.
+ *
+ * @param image
+ * The image to remove
+ */
+ public void removeImage(Image image) {
+ Validation.begin().isNotNull("Image", image).check().isEqual("Image Owner", image.getSone(), sone).check();
+ imageIds.remove(image.getId());
+ images.remove(image.getId());
+ if (image.getId().equals(albumImage)) {
+ if (images.isEmpty()) {
+ albumImage = null;
+ } else {
+ albumImage = images.values().iterator().next().getId();
+ }
+ }
+ }
+
+ /**
+ * Moves the given image up in this album’s images. If the image is already
+ * the first image, nothing happens.
+ *
+ * @param image
+ * The image to move up
+ * @return The image that the given image swapped the place with, or
+ * <code>null</code> if the image did not change its place
+ */
+ public Image moveImageUp(Image image) {
+ Validation.begin().isNotNull("Image", image).check().isEqual("Image Album", image.getAlbum(), this).isEqual("Album Owner", image.getAlbum().getSone(), sone).check();
+ int oldIndex = imageIds.indexOf(image.getId());
+ if (oldIndex <= 0) {
+ return null;
+ }
+ imageIds.remove(image.getId());
+ imageIds.add(oldIndex - 1, image.getId());
+ return images.get(imageIds.get(oldIndex));
+ }
+
+ /**
+ * Moves the given image down in this album’s images. If the image is
+ * already the last image, nothing happens.
+ *
+ * @param image
+ * The image to move down
+ * @return The image that the given image swapped the place with, or
+ * <code>null</code> if the image did not change its place
+ */
+ public Image moveImageDown(Image image) {
+ Validation.begin().isNotNull("Image", image).check().isEqual("Image Album", image.getAlbum(), this).isEqual("Album Owner", image.getAlbum().getSone(), sone).check();
+ int oldIndex = imageIds.indexOf(image.getId());
+ if ((oldIndex == -1) || (oldIndex >= (imageIds.size() - 1))) {
+ return null;
+ }
+ imageIds.remove(image.getId());
+ imageIds.add(oldIndex + 1, image.getId());
+ return images.get(imageIds.get(oldIndex));
+ }
+
+ /**
+ * Returns the album image of this album, or {@code null} if no album image
+ * has been set.
+ *
+ * @return The image to show when this album is listed
+ */
+ public Image getAlbumImage() {
+ if (albumImage == null) {
+ return null;
+ }
+ return Default.forNull(images.get(albumImage), images.values().iterator().next());
+ }
+
+ /**
+ * Sets the ID of the album image.
+ *
+ * @param id
+ * The ID of the album image
+ * @return This album
+ */
+ public Album setAlbumImage(String id) {
+ this.albumImage = id;
+ return this;
+ }
+
+ /**
+ * Returns whether this album contains any other albums or images.
+ *
+ * @return {@code true} if this album is empty, {@code false} otherwise
+ */
+ public boolean isEmpty() {
+ return albums.isEmpty() && images.isEmpty();
+ }
+
+ /**
+ * Returns the parent album of this album.
+ *
+ * @return The parent album of this album, or {@code null} if this album
+ * does not have a parent
+ */
+ public Album getParent() {
+ return parent;
+ }
+
+ /**
+ * Sets the parent album of this album.
+ *
+ * @param parent
+ * The new parent album of this album
+ * @return This album
+ */
+ protected Album setParent(Album parent) {
+ Validation.begin().isNotNull("Album Parent", parent).check();
+ this.parent = parent;
+ return this;
+ }
+
+ /**
+ * Removes the parent album of this album.
+ *
+ * @return This album
+ */
+ protected Album removeParent() {
+ this.parent = null;
+ return this;
+ }
+
+ /**
+ * Returns the title of this album.
+ *
+ * @return The title of this album
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Sets the title of this album.
+ *
+ * @param title
+ * The title of this album
+ * @return This album
+ */
+ public Album setTitle(String title) {
+ Validation.begin().isNotNull("Album Title", title).check();
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * Returns the description of this album.
+ *
+ * @return The description of this album
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the description of this album.
+ *
+ * @param description
+ * The description of this album
+ * @return This album
+ */
+ public Album setDescription(String description) {
+ Validation.begin().isNotNull("Album Description", description).check();
+ this.description = description;
+ return this;
+ }
+
+ //
+ // FINGERPRINTABLE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getFingerprint() {
+ StringBuilder fingerprint = new StringBuilder();
+ fingerprint.append("Album(");
+ fingerprint.append("ID(").append(id).append(')');
+ fingerprint.append("Title(").append(title).append(')');
+ fingerprint.append("Description(").append(description).append(')');
+ if (albumImage != null) {
+ fingerprint.append("AlbumImage(").append(albumImage).append(')');
+ }
+
+ /* add nested albums. */
+ fingerprint.append("Albums(");
+ for (Album album : albums) {
+ fingerprint.append(album.getFingerprint());
+ }
+ fingerprint.append(')');
+
+ /* add images. */
+ fingerprint.append("Images(");
+ for (Image image : getImages()) {
+ if (image.isInserted()) {
+ fingerprint.append(image.getFingerprint());
+ }
+ }
+ fingerprint.append(')');
+
+ fingerprint.append(')');
+ return fingerprint.toString();
+ }
+
+ //
+ // OBJECT METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Album)) {
+ return false;
+ }
+ Album album = (Album) object;
+ return id.equals(album.id);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - Image.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data;
+
+import java.util.UUID;
+
+import net.pterodactylus.util.validation.Validation;
+
+/**
+ * Container for image metadata.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Image implements Fingerprintable {
+
+ /** The ID of the image. */
+ private final String id;
+
+ /** The Sone the image belongs to. */
+ private Sone sone;
+
+ /** The album this image belongs to. */
+ private Album album;
+
+ /** The request key of the image. */
+ private String key;
+
+ /** The creation time of the image. */
+ private long creationTime;
+
+ /** The width of the image. */
+ private int width;
+
+ /** The height of the image. */
+ private int height;
+
+ /** The title of the image. */
+ private String title;
+
+ /** The description of the image. */
+ private String description;
+
+ /**
+ * Creates a new image with a random ID.
+ */
+ public Image() {
+ this(UUID.randomUUID().toString());
+ setCreationTime(System.currentTimeMillis());
+ }
+
+ /**
+ * Creates a new image.
+ *
+ * @param id
+ * The ID of the image
+ */
+ public Image(String id) {
+ Validation.begin().isNotNull("Image ID", id).check();
+ this.id = id;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the ID of this image.
+ *
+ * @return The ID of this image
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the Sone this image belongs to.
+ *
+ * @return The Sone this image belongs to
+ */
+ public Sone getSone() {
+ return sone;
+ }
+
+ /**
+ * Sets the owner of this image. The owner can only be set if no owner has
+ * yet been set.
+ *
+ * @param sone
+ * The new owner of this image
+ * @return This image
+ */
+ public Image setSone(Sone sone) {
+ Validation.begin().isNotNull("New Image Owner", sone).isEither("Old Image Owner", this.sone, null, sone).check();
+ this.sone = sone;
+ return this;
+ }
+
+ /**
+ * Returns the album this image belongs to.
+ *
+ * @return The album this image belongs to
+ */
+ public Album getAlbum() {
+ return album;
+ }
+
+ /**
+ * Sets the album this image belongs to. The album of an image can only be
+ * set once, and it is usually called by {@link Album#addImage(Image)}.
+ *
+ * @param album
+ * The album this image belongs to
+ * @return This image
+ */
+ public Image setAlbum(Album album) {
+ Validation.begin().isNotNull("New Album", album).check().isEqual("Album Owner and Image Owner", album.getSone(), getSone()).check();
+ this.album = album;
+ return this;
+ }
+
+ /**
+ * Returns the request key of this image.
+ *
+ * @return The request key of this image
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Sets the request key of this image. The request key can only be set as
+ * long as no request key has yet been set.
+ *
+ * @param key
+ * The new request key of this image
+ * @return This image
+ */
+ public Image setKey(String key) {
+ Validation.begin().isNotNull("New Image Key", key).isEither("Old Image Key", this.key, null, key).check();
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * Returns whether the image has already been inserted. An image is
+ * considered as having been inserted it its {@link #getKey() key} is not
+ * {@code null}.
+ *
+ * @return {@code true} if there is a key for this image, {@code false}
+ * otherwise
+ */
+ public boolean isInserted() {
+ return key != null;
+ }
+
+ /**
+ * Returns the creation time of this image.
+ *
+ * @return The creation time of this image (in milliseconds since 1970, Jan
+ * 1, UTC)
+ */
+ public long getCreationTime() {
+ return creationTime;
+ }
+
+ /**
+ * Sets the new creation time of this image. The creation time can only be
+ * set as long as no creation time has been set yet.
+ *
+ * @param creationTime
+ * The new creation time of this image
+ * @return This image
+ */
+ public Image setCreationTime(long creationTime) {
+ Validation.begin().isGreater("New Image Creation Time", creationTime, 0).isEither("Old Image Creation Time", this.creationTime, 0L, creationTime).check();
+ this.creationTime = creationTime;
+ return this;
+ }
+
+ /**
+ * Returns the width of this image.
+ *
+ * @return The width of this image (in pixels)
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Sets the width of this image. The width can only be set as long as no
+ * width has been set yet.
+ *
+ * @param width
+ * The new width of this image
+ * @return This image
+ */
+ public Image setWidth(int width) {
+ Validation.begin().isGreater("New Image Width", width, 0).isEither("Old Image Width", this.width, 0, width).check();
+ this.width = width;
+ return this;
+ }
+
+ /**
+ * Returns the height of this image.
+ *
+ * @return The height of this image (in pixels)
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Sets the new height of this image. The height can only be set as long as
+ * no height has yet been set.
+ *
+ * @param height
+ * The new height of this image
+ * @return This image
+ */
+ public Image setHeight(int height) {
+ Validation.begin().isGreater("New Image Height", height, 0).isEither("Old Image Height", this.height, 0, height).check();
+ this.height = height;
+ return this;
+ }
+
+ /**
+ * Returns the title of this image.
+ *
+ * @return The title of this image
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Sets the title of this image.
+ *
+ * @param title
+ * The title of this image
+ * @return This image
+ */
+ public Image setTitle(String title) {
+ Validation.begin().isNotNull("Image Title", title).check();
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * Returns the description of this image.
+ *
+ * @return The description of this image
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the description of this image.
+ *
+ * @param description
+ * The description of this image
+ * @return This image
+ */
+ public Image setDescription(String description) {
+ Validation.begin().isNotNull("Image Description", description).check();
+ this.description = description;
+ return this;
+ }
+
+ //
+ // FINGERPRINTABLE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getFingerprint() {
+ StringBuilder fingerprint = new StringBuilder();
+ fingerprint.append("Image(");
+ fingerprint.append("ID(").append(id).append(')');
+ fingerprint.append("Title(").append(title).append(')');
+ fingerprint.append("Description(").append(description).append(')');
+ fingerprint.append(')');
+ return fingerprint.toString();
+ }
+
+ //
+ // OBJECT METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Image)) {
+ return false;
+ }
+ return ((Image) object).id.equals(id);
+ }
+
+}
import net.pterodactylus.sone.template.SoneAccessor;
import net.pterodactylus.util.filter.Filter;
import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.validation.Validation;
import freenet.keys.FreenetURI;
/**
/** The IDs of all liked replies. */
private final Set<String> likedReplyIds = Collections.synchronizedSet(new HashSet<String>());
+ /** The albums of this Sone. */
+ private final List<Album> albums = Collections.synchronizedList(new ArrayList<Album>());
+
/** Sone-specific options. */
private final Options options = new Options();
*/
public void setLatestEdition(long latestEdition) {
if (!(latestEdition > this.latestEdition)) {
- logger.log(Level.INFO, "New latest edition %d is not greater than current latest edition %d!", new Object[] { latestEdition, this.latestEdition });
+ logger.log(Level.FINE, "New latest edition %d is not greater than current latest edition %d!", new Object[] { latestEdition, this.latestEdition });
return;
}
this.latestEdition = latestEdition;
}
/**
+ * Returns the albums of this Sone.
+ *
+ * @return The albums of this Sone
+ */
+ public List<Album> getAlbums() {
+ return Collections.unmodifiableList(albums);
+ }
+
+ /**
+ * Adds an album to this Sone.
+ *
+ * @param album
+ * The album to add
+ */
+ public synchronized void addAlbum(Album album) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check();
+ albums.add(album);
+ }
+
+ /**
+ * Sets the albums of this Sone.
+ *
+ * @param albums
+ * The albums of this Sone
+ */
+ public synchronized void setAlbums(Collection<? extends Album> albums) {
+ Validation.begin().isNotNull("Albums", albums).check();
+ this.albums.clear();
+ for (Album album : albums) {
+ addAlbum(album);
+ }
+ }
+
+ /**
+ * Removes an album from this Sone.
+ *
+ * @param album
+ * The album to remove
+ */
+ public synchronized void removeAlbum(Album album) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check();
+ 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) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).isNull("Album Parent", album.getParent()).check();
+ 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) {
+ Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).isNull("Album Parent", album.getParent()).check();
+ 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);
+ }
+
+ /**
* Returns Sone-specific options.
*
* @return The options of this Sone
}
fingerprint.append(')');
+ fingerprint.append("Albums(");
+ for (Album album : albums) {
+ fingerprint.append(album.getFingerprint());
+ }
+ fingerprint.append(')');
+
return fingerprint.toString();
}
//
+ // STATIC METHODS
+ //
+
+ /**
+ * Flattens the given top-level albums so that the resulting list contains
+ * parent albums before child albums and the resulting list can be parsed in
+ * a single pass.
+ *
+ * @param albums
+ * The albums to flatten
+ * @return The flattened albums
+ */
+ public static List<Album> flattenAlbums(Collection<? extends Album> albums) {
+ List<Album> flatAlbums = new ArrayList<Album>();
+ flatAlbums.addAll(albums);
+ int lastAlbumIndex = 0;
+ while (lastAlbumIndex < flatAlbums.size()) {
+ int previousAlbumCount = flatAlbums.size();
+ for (Album album : new ArrayList<Album>(flatAlbums.subList(lastAlbumIndex, flatAlbums.size()))) {
+ flatAlbums.addAll(album.getAlbums());
+ }
+ lastAlbumIndex = previousAlbumCount;
+ }
+ return flatAlbums;
+ }
+
+ //
// INTERFACE Comparable<Sone>
//
--- /dev/null
+/*
+ * Sone - TemporaryImage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data;
+
+import java.util.UUID;
+
+import net.pterodactylus.util.validation.Validation;
+
+/**
+ * A temporary image stores an uploaded image in memory until it has been
+ * inserted into Freenet and is subsequently loaded from there.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class TemporaryImage {
+
+ /** The ID of the temporary image. */
+ private final String id;
+
+ /** The MIME type of the image. */
+ private String mimeType;
+
+ /** The encoded image data. */
+ private byte[] imageData;
+
+ /**
+ * Creates a new temporary image with a random ID.
+ */
+ public TemporaryImage() {
+ this(UUID.randomUUID().toString());
+ }
+
+ /**
+ * Creates a new temporary image.
+ *
+ * @param id
+ * The ID of the temporary image
+ */
+ public TemporaryImage(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the ID of the temporary image.
+ *
+ * @return The ID of the temporary image
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the MIME type of the image.
+ *
+ * @return The MIME type of the image
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * Sets the MIME type of the image. The MIME type can only be set once and
+ * it must not be {@code null}.
+ *
+ * @param mimeType
+ * The MIME type of the image
+ * @return This temporary image
+ */
+ public TemporaryImage setMimeType(String mimeType) {
+ Validation.begin().isNotNull("MIME Type", mimeType).isNull("Previous MIME Type", this.mimeType).check();
+ this.mimeType = mimeType;
+ return this;
+ }
+
+ /**
+ * Returns the encoded image data.
+ *
+ * @return The encoded image data
+ */
+ public byte[] getImageData() {
+ return imageData;
+ }
+
+ /**
+ * Sets the encoded image data. The encoded image data can only be set once
+ * and it must not be {@code null}.
+ *
+ * @param imageData
+ * The encoded image data
+ * @return This temporary image
+ */
+ public TemporaryImage setImageData(byte[] imageData) {
+ Validation.begin().isNotNull("Image Data", imageData).isNull("Previous Image Data", this.imageData).check();
+ this.imageData = imageData;
+ return this;
+ }
+
+}
//
/**
+ * Stops the web of trust connector.
+ */
+ public void stop() {
+ pluginConnector.removeConnectorListener(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, this);
+ }
+
+ /**
* Loads all own identities from the Web of Trust plugin.
*
* @return All own identity
}
/** The version. */
- public static final Version VERSION = new Version(0, 6, 7);
+ public static final Version VERSION = new Version(0, 7);
/** The logger. */
private static final Logger logger = Logging.getLogger(SonePlugin.class);
/** The l10n helper. */
private PluginL10n l10n;
+ /** The web of trust connector. */
+ private WebOfTrustConnector webOfTrustConnector;
+
/** The identity manager. */
private IdentityManager identityManager;
/* create web of trust connector. */
PluginConnector pluginConnector = new PluginConnector(pluginRespirator);
- WebOfTrustConnector webOfTrustConnector = new WebOfTrustConnector(pluginConnector);
+ webOfTrustConnector = new WebOfTrustConnector(pluginConnector);
identityManager = new IdentityManager(webOfTrustConnector);
identityManager.setContext("Sone");
/* stop the identity manager. */
identityManager.stop();
+
+ /* stop the web of trust connector. */
+ webOfTrustConnector.stop();
} catch (Throwable t1) {
logger.log(Level.SEVERE, "Error while shutting down!", t1);
} finally {
--- /dev/null
+/*
+ * Sone - AlbumAccessor.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template;
+
+import java.util.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;
+import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * {@link Accessor} implementation for {@link Album}s. A property named
+ * “backlinks” is added, it returns links to all parents and the owner Sone of
+ * an album.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class AlbumAccessor extends ReflectionAccessor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ 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>>();
+ Album currentAlbum = album;
+ while (currentAlbum != null) {
+ backlinks.add(0, createLink("imageBrowser.html?album=" + currentAlbum.getId(), currentAlbum.getTitle()));
+ currentAlbum = currentAlbum.getParent();
+ }
+ backlinks.add(0, createLink("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.”
+ *
+ * @param target
+ * The target to link to
+ * @param name
+ * The name of the link
+ * @return The created map containing the mappings
+ */
+ private 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;
+ }
+
+}
import java.util.Map;
-import net.pterodactylus.sone.web.page.Page.Request;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Plugin;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Request;
/**
* Extracts a page number from a {@link Request}’s parameters and stores it in
pageKey = "page";
}
- Request request = (Request) templateContext.get(requestKey);
+ FreenetRequest request = (FreenetRequest) templateContext.get(requestKey);
String pageString = request.getHttpRequest().getParam(parameter);
int page = 0;
try {
--- /dev/null
+/*
+ * Sone - ImageLinkFilter.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.Map;
+
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.object.Default;
+import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateContextFactory;
+import net.pterodactylus.util.template.TemplateParser;
+
+/**
+ * Template filter that turns an {@link Image} into an HTML <img> tag,
+ * using some parameters to influence parameters of the image.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ImageLinkFilter implements Filter {
+
+ /** The template to render for the <img> tag. */
+ private static final Template linkTemplate = TemplateParser.parse(new StringReader("<img<%ifnull !class> class=\"<%class|css>\"<%/if> src=\"<%src|html>\" alt=\"<%alt|html>\" title=\"<%title|html>\" width=\"<%width|html>\" height=\"<%height|html>\" style=\"position: relative;<%ifnull ! top>top: <% top|html>;<%/if><%ifnull ! left>left: <% left|html>;<%/if>\"/>"));
+
+ /** The template context factory. */
+ private final TemplateContextFactory templateContextFactory;
+
+ /**
+ * Creates a new image link filter.
+ *
+ * @param templateContextFactory
+ * The template context factory
+ */
+ public ImageLinkFilter(TemplateContextFactory templateContextFactory) {
+ this.templateContextFactory = templateContextFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+ Image image = (Image) data;
+ String imageClass = parameters.get("class");
+ int maxWidth = Numbers.safeParseInteger(parameters.get("max-width"), Integer.MAX_VALUE);
+ int maxHeight = Numbers.safeParseInteger(parameters.get("max-height"), Integer.MAX_VALUE);
+ String mode = String.valueOf(parameters.get("mode"));
+ String title = parameters.get("title");
+ if ((title != null) && title.startsWith("=")) {
+ title = String.valueOf(templateContext.get(title.substring(1)));
+ }
+
+ TemplateContext linkTemplateContext = templateContextFactory.createTemplateContext();
+ linkTemplateContext.set("class", imageClass);
+ if (image.isInserted()) {
+ linkTemplateContext.set("src", "/" + image.getKey());
+ } else {
+ linkTemplateContext.set("src", "getImage.html?image=" + image.getId());
+ }
+ int imageWidth = image.getWidth();
+ int imageHeight = image.getHeight();
+ if ("enlarge".equals(mode)) {
+ double scale = Math.max(maxWidth / (double) imageWidth, maxHeight / (double) imageHeight);
+ linkTemplateContext.set("width", (int) (imageWidth * scale + 0.5));
+ linkTemplateContext.set("height", (int) (imageHeight * scale + 0.5));
+ if (scale >= 1) {
+ linkTemplateContext.set("left", String.format("%dpx", (int) ((imageWidth * scale) - maxWidth) / 2));
+ linkTemplateContext.set("top", String.format("%dpx", (int) ((imageHeight * scale) - maxHeight) / 2));
+ } else {
+ linkTemplateContext.set("left", String.format("%dpx", (int) (maxWidth - (imageWidth * scale)) / 2));
+ linkTemplateContext.set("top", String.format("%dpx", (int) (maxHeight - (imageHeight * scale)) / 2));
+ }
+ } else {
+ double scale = 1;
+ if ((imageWidth > maxWidth) || (imageHeight > maxHeight)) {
+ scale = Math.min(maxWidth / (double) imageWidth, maxHeight / (double) imageHeight);
+ }
+ linkTemplateContext.set("width", (int) (imageWidth * scale + 0.5));
+ linkTemplateContext.set("height", (int) (imageHeight * scale + 0.5));
+ }
+ linkTemplateContext.set("alt", Default.forNull(title, image.getDescription()));
+ linkTemplateContext.set("title", Default.forNull(title, image.getTitle()));
+
+ StringWriter stringWriter = new StringWriter();
+ linkTemplate.render(linkTemplateContext, stringWriter);
+ return stringWriter.toString();
+ }
+
+}
import net.pterodactylus.sone.text.SonePart;
import net.pterodactylus.sone.text.SoneTextParser;
import net.pterodactylus.sone.text.SoneTextParserContext;
-import net.pterodactylus.sone.web.page.Page.Request;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Filter;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
if (sone == null) {
sone = core.getSone(soneKey, false);
}
- Request request = (Request) templateContext.get("request");
+ FreenetRequest request = (FreenetRequest) templateContext.get("request");
SoneTextParserContext context = new SoneTextParserContext(request, sone);
StringWriter parsedTextWriter = new StringWriter();
try {
import java.util.Map;
import java.util.Map.Entry;
-import net.pterodactylus.sone.web.page.Page.Request;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Filter;
import net.pterodactylus.util.template.TemplateContext;
/**
- * This filter expects a {@link Request} as input and outputs a {@link URI} that
- * is modified by the parameters. The name of the parameter is handed in as
- * “name”, the value may either be stored in “value”, or in a template variable
- * whose key is stored in “key”.
+ * This filter expects a {@link FreenetRequest} as input and outputs a
+ * {@link URI} that is modified by the parameters. The name of the parameter is
+ * handed in as “name”, the value may either be stored in “value”, or in a
+ * template variable whose key is stored in “key”.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
*/
@Override
public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
- Request request = (Request) data;
+ FreenetRequest request = (FreenetRequest) data;
String name = parameters.get("name");
String nameKey = parameters.get("nameKey");
if (nameKey != null) {
continue;
}
emptyLines = 0;
+ /*
+ * lineComplete tracks whether the block you are parsing is the
+ * first block of the line. this is important because sometimes you
+ * have to add an additional line break.
+ */
boolean lineComplete = true;
while (line.length() > 0) {
int nextKsk = line.indexOf("KSK@");
next = nextPost;
linkType = LinkType.POST;
}
+
+ /* cut off “freenet:” from before keys. */
+ if (((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (next >= 8) && (line.substring(next - 8, next).equals("freenet:"))) {
+ next -= 8;
+ line = line.substring(0, next) + line.substring(next + 8);
+ }
+
+ /* if there is text before the next item, write it out. */
+ if (lineComplete && !lastLineEmpty) {
+ parts.add(new PlainTextPart("\n"));
+ }
+ if (next > 0) {
+ parts.add(new PlainTextPart(line.substring(0, next)));
+ line = line.substring(next);
+ next = 0;
+ }
+ lineComplete = false;
+
if (linkType == LinkType.SONE) {
- if (next > 0) {
- parts.add(new PlainTextPart(line.substring(0, next)));
- }
- if (line.length() >= (next + 7 + 43)) {
- String soneId = line.substring(next + 7, next + 50);
+ if (line.length() >= (7 + 43)) {
+ String soneId = line.substring(7, 50);
Sone sone = soneProvider.getSone(soneId, false);
if ((sone != null) && (sone.getName() != null)) {
parts.add(new SonePart(sone));
} else {
- parts.add(new PlainTextPart(line.substring(next, next + 50)));
+ parts.add(new PlainTextPart(line.substring(0, 50)));
}
- line = line.substring(next + 50);
+ line = line.substring(50);
} else {
- parts.add(new PlainTextPart(line.substring(next)));
+ parts.add(new PlainTextPart(line));
line = "";
}
continue;
}
if (linkType == LinkType.POST) {
- if (next > 0) {
- parts.add(new PlainTextPart(line.substring(0, next)));
- }
- if (line.length() >= (next + 7 + 36)) {
- String postId = line.substring(next + 7, next + 43);
+ if (line.length() >= (7 + 36)) {
+ String postId = line.substring(7, 43);
Post post = postProvider.getPost(postId, false);
if ((post != null) && (post.getSone() != null)) {
parts.add(new PostPart(post));
} else {
- parts.add(new PlainTextPart(line.substring(next, next + 43)));
+ parts.add(new PlainTextPart(line.substring(0, 43)));
}
- line = line.substring(next + 43);
+ line = line.substring(43);
} else {
- parts.add(new PlainTextPart(line.substring(next)));
+ parts.add(new PlainTextPart(line));
line = "";
}
continue;
}
- if ((next >= 8) && (line.substring(next - 8, next).equals("freenet:"))) {
- next -= 8;
- line = line.substring(0, next) + line.substring(next + 8);
- }
Matcher matcher = whitespacePattern.matcher(line);
- int nextSpace = matcher.find(next) ? matcher.start() : line.length();
- if (nextSpace > (next + 4)) {
- if (!lastLineEmpty && lineComplete) {
- parts.add(new PlainTextPart("\n" + line.substring(0, next)));
- } else {
- if (next > 0) {
- parts.add(new PlainTextPart(line.substring(0, next)));
- }
- }
- String link = line.substring(next, nextSpace);
- String name = link;
- logger.log(Level.FINER, "Found link: %s", link);
- logger.log(Level.FINEST, "Next: %d, CHK: %d, SSK: %d, USK: %d", new Object[] { next, nextChk, nextSsk, nextUsk });
+ int nextSpace = matcher.find(0) ? matcher.start() : line.length();
+ String link = line.substring(0, nextSpace);
+ String name = link;
+ logger.log(Level.FINER, "Found link: %s", link);
+ logger.log(Level.FINEST, "CHK: %d, SSK: %d, USK: %d", new Object[] { nextChk, nextSsk, nextUsk });
- if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
- FreenetURI uri;
- if (name.indexOf('?') > -1) {
- name = name.substring(0, name.indexOf('?'));
- }
- if (name.endsWith("/")) {
- name = name.substring(0, name.length() - 1);
- }
- try {
- uri = new FreenetURI(name);
- name = uri.lastMetaString();
- if (name == null) {
- name = uri.getDocName();
- }
- if (name == null) {
- name = link.substring(0, Math.min(9, link.length()));
- }
- boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
- parts.add(new FreenetLinkPart(link, name, fromPostingSone));
- } catch (MalformedURLException mue1) {
- /* not a valid link, insert as plain text. */
- parts.add(new PlainTextPart(link));
- } catch (NullPointerException npe1) {
- /* FreenetURI sometimes throws these, too. */
- parts.add(new PlainTextPart(link));
- } catch (ArrayIndexOutOfBoundsException aioobe1) {
- /* oh, and these, too. */
- parts.add(new PlainTextPart(link));
- }
- } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
- name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
- int firstSlash = name.indexOf('/');
- int lastSlash = name.lastIndexOf('/');
- if ((lastSlash - firstSlash) > 3) {
- name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash);
- }
- if (name.endsWith("/")) {
- name = name.substring(0, name.length() - 1);
- }
- if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
- name = name.substring(4);
+ if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
+ FreenetURI uri;
+ if (name.indexOf('?') > -1) {
+ name = name.substring(0, name.indexOf('?'));
+ }
+ if (name.endsWith("/")) {
+ name = name.substring(0, name.length() - 1);
+ }
+ try {
+ uri = new FreenetURI(name);
+ name = uri.lastMetaString();
+ if (name == null) {
+ name = uri.getDocName();
}
- if (name.indexOf('?') > -1) {
- name = name.substring(0, name.indexOf('?'));
+ if (name == null) {
+ name = link.substring(0, Math.min(9, link.length()));
}
- parts.add(new LinkPart(link, name));
+ boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
+ parts.add(new FreenetLinkPart(link, name, fromPostingSone));
+ } catch (MalformedURLException mue1) {
+ /* not a valid link, insert as plain text. */
+ parts.add(new PlainTextPart(link));
+ } catch (NullPointerException npe1) {
+ /* FreenetURI sometimes throws these, too. */
+ parts.add(new PlainTextPart(link));
+ } catch (ArrayIndexOutOfBoundsException aioobe1) {
+ /* oh, and these, too. */
+ parts.add(new PlainTextPart(link));
}
- line = line.substring(nextSpace);
- } else {
- if (!lastLineEmpty && lineComplete) {
- parts.add(new PlainTextPart("\n" + line.substring(0, next + 4)));
- } else {
- parts.add(new PlainTextPart(line.substring(0, next + 4)));
+ } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
+ name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
+ int firstSlash = name.indexOf('/');
+ int lastSlash = name.lastIndexOf('/');
+ if ((lastSlash - firstSlash) > 3) {
+ name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash);
+ }
+ if (name.endsWith("/")) {
+ name = name.substring(0, name.length() - 1);
}
- line = line.substring(next + 4);
+ if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
+ name = name.substring(4);
+ }
+ if (name.indexOf('?') > -1) {
+ name = name.substring(0, name.indexOf('?'));
+ }
+ parts.add(new LinkPart(link, name));
}
- lineComplete = false;
+ line = line.substring(nextSpace);
}
lastLineEmpty = false;
}
package net.pterodactylus.sone.text;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request;
+import net.pterodactylus.sone.web.page.FreenetRequest;
/**
* {@link ParserContext} implementation for the {@link SoneTextParser}. It
public class SoneTextParserContext implements ParserContext {
/** The request being processed. */
- private final Request request;
+ private final FreenetRequest request;
/** The posting Sone. */
private final Sone postingSone;
* @param postingSone
* The posting Sone
*/
- public SoneTextParserContext(Request request, Sone postingSone) {
+ public SoneTextParserContext(FreenetRequest request, Sone postingSone) {
this.request = request;
this.postingSone = postingSone;
}
*
* @return The request being processed
*/
- public Request getRequest() {
+ public FreenetRequest getRequest() {
return request;
}
package net.pterodactylus.sone.web;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.version.Version;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
templateContext.set("version", version);
}
package net.pterodactylus.sone.web;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user bookmark a post.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String id = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
import java.util.Set;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.collection.Pagination;
import net.pterodactylus.util.filter.Filter;
import net.pterodactylus.util.filter.Filters;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
Set<Post> allPosts = webInterface.getCore().getBookmarkedPosts();
Set<Post> loadedPosts = Filters.filteredSet(allPosts, new Filter<Post>() {
--- /dev/null
+/*
+ * Sone - CreateAlbumPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user create a new album.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreateAlbumPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “create album” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public CreateAlbumPage(Template template, WebInterface webInterface) {
+ super("createAlbum.html", template, "Page.CreateAlbum.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String name = request.getHttpRequest().getPartAsStringFailsafe("name", 64).trim();
+ if (name.length() == 0) {
+ templateContext.set("nameMissing", true);
+ return;
+ }
+ String description = request.getHttpRequest().getPartAsStringFailsafe("description", 256).trim();
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
+ Album parent = webInterface.getCore().getAlbum(parentId, false);
+ Album album = webInterface.getCore().createAlbum(currentSone, parent);
+ album.setTitle(name).setDescription(description);
+ webInterface.getCore().touchConfiguration();
+ throw new RedirectException("imageBrowser.html?album=" + album.getId());
+ }
+ }
+
+}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.text.TextFilter;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* This page lets the user create a new {@link Post}.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
if (request.getMethod() == Method.POST) {
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.text.TextFilter;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* This page lets the user post a reply to a post.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
import freenet.clients.http.ToadletContext;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
List<OwnIdentity> ownIdentitiesWithoutSone = getOwnIdentitiesWithoutSone(webInterface.getCore());
templateContext.set("identitiesWithoutSone", ownIdentitiesWithoutSone);
--- /dev/null
+/*
+ * Sone - DeleteAlbumPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user delete an {@link Album}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeleteAlbumPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “delete album” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DeleteAlbumPage(Template template, WebInterface webInterface) {
+ super("deleteAlbum.html", template, "Page.DeleteAlbum.Title", webInterface, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ if (album == null) {
+ throw new RedirectException("invalid.html");
+ }
+ if (!webInterface.getCore().isLocalSone(album.getSone())) {
+ throw new RedirectException("noPermission.html");
+ }
+ if (request.getHttpRequest().isPartSet("abortDelete")) {
+ throw new RedirectException("imageBrowser.html?album=" + album.getId());
+ }
+ Album parentAlbum = album.getParent();
+ webInterface.getCore().deleteAlbum(album);
+ if (parentAlbum == null) {
+ throw new RedirectException("imageBrowser.html?sone=" + album.getSone().getId());
+ }
+ throw new RedirectException("imageBrowser.html?album=" + parentAlbum.getId());
+ }
+ String albumId = request.getHttpRequest().getParam("album");
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ if (album == null) {
+ throw new RedirectException("invalid.html");
+ }
+ templateContext.set("album", album);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - DeleteImagePage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user delete an {@link Image}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeleteImagePage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “delete image” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DeleteImagePage(Template template, WebInterface webInterface) {
+ super("deleteImage.html", template, "Page.DeleteImage.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ String imageId = (request.getMethod() == Method.POST) ? request.getHttpRequest().getPartAsStringFailsafe("image", 36) : request.getHttpRequest().getParam("image");
+ Image image = webInterface.getCore().getImage(imageId, false);
+ if (image == null) {
+ throw new RedirectException("invalid.html");
+ }
+ if (!webInterface.getCore().isLocalSone(image.getSone())) {
+ throw new RedirectException("noPermission.html");
+ }
+ if (request.getMethod() == Method.POST) {
+ if (request.getHttpRequest().isPartSet("abortDelete")) {
+ throw new RedirectException("imageBrowser.html?image=" + image.getId());
+ }
+ webInterface.getCore().deleteImage(image);
+ throw new RedirectException("imageBrowser.html?album=" + image.getAlbum().getId());
+ }
+ templateContext.set("image", image);
+ }
+
+}
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Lets the user delete a post they made.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.GET) {
String postId = request.getHttpRequest().getParam("post");
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user confirm the deletion of a profile field.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
Sone currentSone = getCurrentSone(request.getToadletContext());
Profile profile = currentSone.getProfile();
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Reply;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* This page lets the user delete a reply.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", 36);
Reply reply = webInterface.getCore().getReply(replyId);
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Lets the user delete a Sone. Of course the Sone is not really deleted from
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
if (request.getHttpRequest().isPartSet("deleteSone")) {
package net.pterodactylus.sone.web;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.notify.Notification;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String notificationId = request.getHttpRequest().getPartAsStringFailsafe("notification", 36);
Notification notification = webInterface.getNotifications().getNotification(notificationId);
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user distrust another Sone. This will assign a
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
--- /dev/null
+/*
+ * Sone - EditAlbumPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user edit the name and description of an album.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditAlbumPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “edit album” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public EditAlbumPage(Template template, WebInterface webInterface) {
+ super("editAlbum.html", template, "Page.EditAlbum.Title", webInterface, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ if (album == null) {
+ throw new RedirectException("invalid.html");
+ }
+ if (!webInterface.getCore().isLocalSone(album.getSone())) {
+ throw new RedirectException("noPermission.html");
+ }
+ String albumImageId = request.getHttpRequest().getPartAsStringFailsafe("album-image", 36);
+ if (webInterface.getCore().getImage(albumImageId, false) == null) {
+ albumImageId = null;
+ }
+ album.setAlbumImage(albumImageId);
+ String title = request.getHttpRequest().getPartAsStringFailsafe("title", 100).trim();
+ if (title.length() == 0) {
+ templateContext.set("titleMissing", true);
+ return;
+ }
+ String description = request.getHttpRequest().getPartAsStringFailsafe("description", 1000).trim();
+ album.setTitle(title).setDescription(description);
+ webInterface.getCore().touchConfiguration();
+ throw new RedirectException("imageBrowser.html?album=" + album.getId());
+ }
+ }
+
+}
--- /dev/null
+/*
+ * FreenetSone - WebInterface.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user edit title and description of an {@link Image}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditImagePage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “edit image” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public EditImagePage(Template template, WebInterface webInterface) {
+ super("editImage.html", template, "Page.EditImage.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String imageId = request.getHttpRequest().getPartAsStringFailsafe("image", 36);
+ String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
+ Image image = webInterface.getCore().getImage(imageId, false);
+ if (image == null) {
+ throw new RedirectException("invalid.html");
+ }
+ if (!webInterface.getCore().isLocalSone(image.getSone())) {
+ throw new RedirectException("noPermission.html");
+ }
+ if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveLeft", 4))) {
+ image.getAlbum().moveImageUp(image);
+ } else if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveRight", 4))) {
+ image.getAlbum().moveImageDown(image);
+ } else {
+ String title = request.getHttpRequest().getPartAsStringFailsafe("title", 100).trim();
+ String description = request.getHttpRequest().getPartAsStringFailsafe("description", 1024).trim();
+ if (title.length() == 0) {
+ templateContext.set("titleMissing", true);
+ }
+ image.setTitle(title);
+ image.setDescription(description);
+ }
+ webInterface.getCore().touchConfiguration();
+ throw new RedirectException(returnPage);
+ }
+ }
+
+}
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user edit the name of a profile field.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
Sone currentSone = getCurrentSone(request.getToadletContext());
Profile profile = currentSone.getProfile();
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.number.Numbers;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
import freenet.clients.http.ToadletContext;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
ToadletContext toadletContenxt = request.getToadletContext();
Sone currentSone = getCurrentSone(toadletContenxt);
* @return The parsed ID, or {@code null} if there was no part matching the
* given string
*/
- private String getFieldId(Request request, String partNameStart) {
+ private String getFieldId(FreenetRequest request, String partNameStart) {
for (String partName : request.getHttpRequest().getParts()) {
if (partName.startsWith(partNameStart)) {
return partName.substring(partNameStart.length());
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* This page lets the user follow another Sone.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
--- /dev/null
+/*
+ * Sone - GetImagePage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import java.io.IOException;
+
+import net.pterodactylus.sone.data.TemporaryImage;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Response;
+
+/**
+ * Page that delivers a {@link TemporaryImage} to the browser.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class GetImagePage implements Page<FreenetRequest> {
+
+ /** The Sone web interface. */
+ private final WebInterface webInterface;
+
+ /**
+ * Creates a new “get image” page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public GetImagePage(WebInterface webInterface) {
+ this.webInterface = webInterface;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPath() {
+ return "getImage.html";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isPrefixPage() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Response handleRequest(FreenetRequest request, Response response) throws IOException {
+ String imageId = request.getHttpRequest().getParam("image");
+ TemporaryImage temporaryImage = webInterface.getCore().getTemporaryImage(imageId);
+ if (temporaryImage == null) {
+ return response.setStatusCode(404).setStatusText("Not found.").setContentType("text/html; charset=utf-8");
+ }
+ String contentType= temporaryImage.getMimeType();
+ return response.setStatusCode(200).setStatusText("OK").setContentType(contentType).addHeader("Content-Disposition", "attachment; filename=" + temporaryImage.getId() + "." + contentType.substring(contentType.lastIndexOf('/') + 1)).write(temporaryImage.getImageData());
+ }
+
+}
--- /dev/null
+/*
+ * Sone - ImageBrowserPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * The image browser page is the entry page for the image management.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ImageBrowserPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new image browser page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public ImageBrowserPage(Template template, WebInterface webInterface) {
+ super("imageBrowser.html", template, "Page.ImageBrowser.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ String albumId = request.getHttpRequest().getParam("album", null);
+ if (albumId != null) {
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ templateContext.set("albumRequested", true);
+ templateContext.set("album", album);
+ return;
+ }
+ String imageId = request.getHttpRequest().getParam("image", null);
+ if (imageId != null) {
+ Image image = webInterface.getCore().getImage(imageId, false);
+ templateContext.set("imageRequested", true);
+ templateContext.set("image", image);
+ return;
+ }
+ Sone sone = getCurrentSone(request.getToadletContext(), false);
+ String soneId = request.getHttpRequest().getParam("sone", null);
+ if (soneId != null) {
+ sone = webInterface.getCore().getSone(soneId, false);
+ }
+ templateContext.set("soneRequested", true);
+ templateContext.set("sone", sone);
+ }
+
+}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.notify.ListNotificationFilters;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.collection.Pagination;
import net.pterodactylus.util.filter.Filter;
import net.pterodactylus.util.filter.Filters;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
final Sone currentSone = getCurrentSone(request.getToadletContext());
List<Post> allPosts = new ArrayList<Post>();
import java.util.List;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.collection.Pagination;
import net.pterodactylus.util.collection.ReverseComparator;
import net.pterodactylus.util.filter.Filter;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String sortField = request.getHttpRequest().getParam("sort");
String sortOrder = request.getHttpRequest().getParam("order");
templateContext.set("pagination", sonePagination);
templateContext.set("knownSones", sonePagination.getItems());
}
+
}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user like a {@link Post}.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16);
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
Sone sone = webInterface.getCore().getLocalSone(soneId, false);
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
import freenet.clients.http.ToadletContext;
/**
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
/* get all own identities. */
List<Sone> localSones = new ArrayList<Sone>(webInterface.getCore().getLocalSones());
* {@inheritDoc}
*/
@Override
- protected String getRedirectTarget(Request request) {
+ protected String getRedirectTarget(FreenetRequest request) {
if (getCurrentSone(request.getToadletContext(), false) != null) {
return "index.html";
}
package net.pterodactylus.sone.web;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
import freenet.clients.http.ToadletContext;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
setCurrentSone(request.getToadletContext(), null);
super.processTemplate(request, templateContext);
throw new RedirectException("index.html");
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String type = request.getHttpRequest().getPartAsStringFailsafe("type", 5);
if (!type.equals("sone") && !type.equals("post") && !type.equals("reply")) {
import net.pterodactylus.sone.core.Core.Preferences;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.number.Numbers;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* This page lets the user edit the options of the Sone plugin.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
Preferences preferences = webInterface.getCore().getPreferences();
Sone currentSone = webInterface.getCurrentSone(request.getToadletContext(), false);
import net.pterodactylus.sone.core.SoneRescuer;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.number.Numbers;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user control the rescue mode for a Sone.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
SoneRescuer soneRescuer = webInterface.getCore().getSoneRescuer(currentSone);
import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.cache.Cache;
import net.pterodactylus.util.cache.CacheException;
import net.pterodactylus.util.cache.CacheItem;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String query = request.getHttpRequest().getParam("query").trim();
if (query.length() == 0) {
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.main.SonePlugin;
import net.pterodactylus.sone.notify.ListNotificationFilters;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.sone.web.page.FreenetTemplatePage;
-import net.pterodactylus.sone.web.page.Page;
import net.pterodactylus.util.collection.ListBuilder;
import net.pterodactylus.util.collection.MapBuilder;
import net.pterodactylus.util.template.Template;
* {@inheritDoc}
*/
@Override
- protected String getPageTitle(Request request) {
+ protected String getPageTitle(FreenetRequest request) {
if (pageTitleKey != null) {
return webInterface.getL10n().getString(pageTitleKey);
}
* {@inheritDoc}
*/
@Override
- protected List<Map<String, String>> getAdditionalLinkNodes(Request request) {
+ protected List<Map<String, String>> getAdditionalLinkNodes(FreenetRequest request) {
return new ListBuilder<Map<String, String>>().add(new MapBuilder<String, String>().put("rel", "search").put("type", "application/opensearchdescription+xml").put("title", "Sone").put("href", "http://" + request.getHttpRequest().getHeader("host") + "/Sone/OpenSearch.xml").get()).get();
}
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
templateContext.set("core", webInterface.getCore());
* {@inheritDoc}
*/
@Override
- protected String getRedirectTarget(Page.Request request) {
+ protected String getRedirectTarget(FreenetRequest request) {
if (requiresLogin() && (getCurrentSone(request.getToadletContext(), false) == null)) {
HTTPRequest httpRequest = request.getHttpRequest();
String originalUrl = httpRequest.getPath();
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user trust another Sone. This will assign a configurable
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
import java.util.Set;
import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user unbookmark a post.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String id = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* This page lets the user unfollow another Sone.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user unlike a {@link Post}.
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16);
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
Sone sone = webInterface.getCore().getLocalSone(soneId, false);
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
/**
* Page that lets the user untrust another Sone. This will remove all trust
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
--- /dev/null
+/*
+ * Sone - UploadImagePage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import java.awt.Image;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.TemporaryImage;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.io.Closer;
+import net.pterodactylus.util.io.StreamCopier;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+import freenet.support.api.Bucket;
+import freenet.support.api.HTTPUploadedFile;
+
+/**
+ * Page implementation that lets the user upload an image.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UploadImagePage extends SoneTemplatePage {
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(UploadImagePage.class);
+
+ /**
+ * Creates a new “upload image” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public UploadImagePage(Template template, WebInterface webInterface) {
+ super("uploadImage.html", template, "Page.UploadImage.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
+ Album parent = webInterface.getCore().getAlbum(parentId, false);
+ if (parent == null) {
+ /* TODO - signal error */
+ return;
+ }
+ if (!currentSone.equals(parent.getSone())) {
+ /* TODO - signal error. */
+ return;
+ }
+ String name = request.getHttpRequest().getPartAsStringFailsafe("title", 200);
+ String description = request.getHttpRequest().getPartAsStringFailsafe("description", 4000);
+ HTTPUploadedFile uploadedFile = request.getHttpRequest().getUploadedFile("image");
+ Bucket fileBucket = uploadedFile.getData();
+ InputStream imageInputStream = null;
+ ByteArrayOutputStream imageDataOutputStream = null;
+ net.pterodactylus.sone.data.Image image = null;
+ try {
+ imageInputStream = fileBucket.getInputStream();
+ /* TODO - check length */
+ imageDataOutputStream = new ByteArrayOutputStream((int) fileBucket.size());
+ StreamCopier.copy(imageInputStream, imageDataOutputStream);
+ } catch (IOException ioe1) {
+ logger.log(Level.WARNING, "Could not read uploaded image!", ioe1);
+ return;
+ } finally {
+ fileBucket.free();
+ Closer.close(imageInputStream);
+ Closer.close(imageDataOutputStream);
+ }
+ byte[] imageData = imageDataOutputStream.toByteArray();
+ ByteArrayInputStream imageDataInputStream = null;
+ Image uploadedImage = null;
+ try {
+ imageDataInputStream = new ByteArrayInputStream(imageData);
+ uploadedImage = ImageIO.read(imageDataInputStream);
+ if (uploadedImage == null) {
+ templateContext.set("messages", webInterface.getL10n().getString("Page.UploadImage.Error.InvalidImage"));
+ return;
+ }
+ String mimeType = getMimeType(imageData);
+ TemporaryImage temporaryImage = webInterface.getCore().createTemporaryImage(mimeType, imageData);
+ image = webInterface.getCore().createImage(currentSone, parent, temporaryImage);
+ image.setTitle(name).setDescription(description).setWidth(uploadedImage.getWidth(null)).setHeight(uploadedImage.getHeight(null));
+ } catch (IOException ioe1) {
+ logger.log(Level.WARNING, "Could not read uploaded image!", ioe1);
+ return;
+ } finally {
+ Closer.close(imageDataInputStream);
+ Closer.flush(uploadedImage);
+ }
+ throw new RedirectException("imageBrowser.html?album=" + parent.getId());
+ }
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Tries to detect the MIME type of the encoded image.
+ *
+ * @param imageData
+ * The encoded image
+ * @return The MIME type of the image, or “application/octet-stream” if the
+ * image type could not be detected
+ */
+ private String getMimeType(byte[] imageData) {
+ ByteArrayInputStream imageDataInputStream = new ByteArrayInputStream(imageData);
+ try {
+ ImageInputStream imageInputStream = ImageIO.createImageInputStream(imageDataInputStream);
+ Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
+ if (imageReaders.hasNext()) {
+ return imageReaders.next().getOriginatingProvider().getMIMETypes()[0];
+ }
+ } catch (IOException ioe1) {
+ logger.log(Level.FINE, "Could not detect MIME type for image.", ioe1);
+ }
+ return "application/octet-stream";
+ }
+
+}
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.template.SoneAccessor;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
* {@inheritDoc}
*/
@Override
- protected String getPageTitle(Request request) {
+ protected String getPageTitle(FreenetRequest request) {
String postId = request.getHttpRequest().getParam("post");
Post post = webInterface.getCore().getPost(postId, false);
String title = "";
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String postId = request.getHttpRequest().getParam("post");
boolean raw = request.getHttpRequest().getParam("raw").equals("true");
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.template.SoneAccessor;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.collection.Pagination;
import net.pterodactylus.util.number.Numbers;
import net.pterodactylus.util.template.Template;
* {@inheritDoc}
*/
@Override
- protected String getPageTitle(Request request) {
+ protected String getPageTitle(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
Sone sone = webInterface.getCore().getSone(soneId, false);
if ((sone != null) && (sone.getTime() > 0)) {
* {@inheritDoc}
*/
@Override
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String soneId = request.getHttpRequest().getParam("sone");
Sone sone = webInterface.getCore().getSone(soneId, false);
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.core.CoreListener;
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.wot.Trust;
import net.pterodactylus.sone.main.SonePlugin;
import net.pterodactylus.sone.notify.ListNotification;
+import net.pterodactylus.sone.template.AlbumAccessor;
import net.pterodactylus.sone.template.CollectionAccessor;
import net.pterodactylus.sone.template.CssClassNameFilter;
import net.pterodactylus.sone.template.HttpRequestAccessor;
import net.pterodactylus.sone.template.IdentityAccessor;
+import net.pterodactylus.sone.template.ImageLinkFilter;
import net.pterodactylus.sone.template.JavascriptFilter;
import net.pterodactylus.sone.template.ParserFilter;
import net.pterodactylus.sone.template.PostAccessor;
import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage;
import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage;
import net.pterodactylus.sone.web.ajax.DistrustAjaxPage;
+import net.pterodactylus.sone.web.ajax.EditAlbumAjaxPage;
+import net.pterodactylus.sone.web.ajax.EditImageAjaxPage;
import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage;
import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
import net.pterodactylus.sone.web.ajax.UntrustAjaxPage;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.sone.web.page.PageToadlet;
import net.pterodactylus.sone.web.page.PageToadletFactory;
-import net.pterodactylus.sone.web.page.RedirectPage;
-import net.pterodactylus.sone.web.page.StaticPage;
-import net.pterodactylus.sone.web.page.TemplatePage;
import net.pterodactylus.util.cache.Cache;
import net.pterodactylus.util.cache.CacheException;
import net.pterodactylus.util.cache.CacheItem;
import net.pterodactylus.util.template.FormatFilter;
import net.pterodactylus.util.template.HtmlFilter;
import net.pterodactylus.util.template.MatchFilter;
+import net.pterodactylus.util.template.ModFilter;
import net.pterodactylus.util.template.Provider;
import net.pterodactylus.util.template.ReflectionAccessor;
import net.pterodactylus.util.template.ReplaceFilter;
import net.pterodactylus.util.template.XmlFilter;
import net.pterodactylus.util.thread.Ticker;
import net.pterodactylus.util.version.Version;
+import net.pterodactylus.util.web.RedirectPage;
+import net.pterodactylus.util.web.StaticPage;
+import net.pterodactylus.util.web.TemplatePage;
import freenet.clients.http.SessionManager;
-import freenet.clients.http.SessionManager.Session;
import freenet.clients.http.ToadletContainer;
import freenet.clients.http.ToadletContext;
+import freenet.clients.http.SessionManager.Session;
import freenet.l10n.BaseL10n;
import freenet.support.api.HTTPRequest;
/** The “new version” notification. */
private final TemplateNotification newVersionNotification;
+ /** The “inserting images” notification. */
+ private final ListNotification<Image> insertingImagesNotification;
+
+ /** The “inserted images” notification. */
+ private final ListNotification<Image> insertedImagesNotification;
+
+ /** The “image insert failed” notification. */
+ private final ListNotification<Image> imageInsertFailedNotification;
+
/**
* Creates a new web interface.
*
templateContextFactory.addAccessor(Sone.class, new SoneAccessor(getCore()));
templateContextFactory.addAccessor(Post.class, new PostAccessor(getCore()));
templateContextFactory.addAccessor(Reply.class, new ReplyAccessor(getCore()));
+ templateContextFactory.addAccessor(Album.class, new AlbumAccessor());
templateContextFactory.addAccessor(Identity.class, new IdentityAccessor(getCore()));
templateContextFactory.addAccessor(Trust.class, new TrustAccessor());
templateContextFactory.addAccessor(HTTPRequest.class, new HttpRequestAccessor());
templateContextFactory.addFilter("unknown", new UnknownDateFilter(getL10n(), "View.Sone.Text.UnknownDate"));
templateContextFactory.addFilter("format", new FormatFilter());
templateContextFactory.addFilter("sort", new CollectionSortFilter());
+ templateContextFactory.addFilter("image-link", new ImageLinkFilter(templateContextFactory));
templateContextFactory.addFilter("replyGroup", new ReplyGroupFilter());
templateContextFactory.addFilter("in", new ContainsFilter());
templateContextFactory.addFilter("unique", new UniqueElementFilter());
+ templateContextFactory.addFilter("mod", new ModFilter());
templateContextFactory.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER);
templateContextFactory.addProvider(new ClassPathTemplateProvider());
templateContextFactory.addTemplateObject("webInterface", this);
Template newVersionTemplate = TemplateParser.parse(createReader("/templates/notify/newVersionNotification.html"));
newVersionNotification = new TemplateNotification("new-version-notification", newVersionTemplate);
+
+ Template insertingImagesTemplate = TemplateParser.parse(createReader("/templates/notify/inserting-images-notification.html"));
+ insertingImagesNotification = new ListNotification<Image>("inserting-images-notification", "images", insertingImagesTemplate);
+
+ Template insertedImagesTemplate = TemplateParser.parse(createReader("/templates/notify/inserted-images-notification.html"));
+ insertedImagesNotification = new ListNotification<Image>("inserted-images-notification", "images", insertedImagesTemplate);
+
+ Template imageInsertFailedTemplate = TemplateParser.parse(createReader("/templates/notify/image-insert-failed-notification.html"));
+ imageInsertFailedNotification = new ListNotification<Image>("image-insert-failed-notification", "images", imageInsertFailedTemplate);
}
//
Template deletePostTemplate = TemplateParser.parse(createReader("/templates/deletePost.html"));
Template deleteReplyTemplate = TemplateParser.parse(createReader("/templates/deleteReply.html"));
Template deleteSoneTemplate = TemplateParser.parse(createReader("/templates/deleteSone.html"));
+ Template imageBrowserTemplate = TemplateParser.parse(createReader("/templates/imageBrowser.html"));
+ Template createAlbumTemplate = TemplateParser.parse(createReader("/templates/createAlbum.html"));
+ Template deleteAlbumTemplate = TemplateParser.parse(createReader("/templates/deleteAlbum.html"));
+ Template deleteImageTemplate = TemplateParser.parse(createReader("/templates/deleteImage.html"));
Template noPermissionTemplate = TemplateParser.parse(createReader("/templates/noPermission.html"));
Template optionsTemplate = TemplateParser.parse(createReader("/templates/options.html"));
Template rescueTemplate = TemplateParser.parse(createReader("/templates/rescue.html"));
Template openSearchTemplate = TemplateParser.parse(createReader("/templates/xml/OpenSearch.xml"));
PageToadletFactory pageToadletFactory = new PageToadletFactory(sonePlugin.pluginRespirator().getHLSimpleClient(), "/Sone/");
- pageToadlets.add(pageToadletFactory.createPageToadlet(new RedirectPage("", "index.html")));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new RedirectPage<FreenetRequest>("", "index.html")));
pageToadlets.add(pageToadletFactory.createPageToadlet(new IndexPage(indexTemplate, this), "Index"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateSonePage(createSoneTemplate, this), "CreateSone"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new KnownSonesPage(knownSonesTemplate, this), "KnownSones"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlockSonePage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new FollowSonePage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnfollowSonePage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new ImageBrowserPage(imageBrowserTemplate, this), "ImageBrowser"));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateAlbumPage(createAlbumTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new EditAlbumPage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteAlbumPage(deleteAlbumTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new UploadImagePage(invalidTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new EditImagePage(emptyTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteImagePage(deleteImageTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustPage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustPage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustPage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("noPermission.html", noPermissionTemplate, "Page.NoPermission.Title", this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DismissNotificationPage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("invalid.html", invalidTemplate, "Page.Invalid.Title", this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("css/", "/static/css/", "text/css")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("javascript/", "/static/javascript/", "text/javascript")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("images/", "/static/images/", "image/png")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new TemplatePage("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("css/", "/static/css/", "text/css")));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("javascript/", "/static/javascript/", "text/javascript")));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("images/", "/static/images/", "image/png")));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new GetImagePage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetTranslationPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetStatusAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetNotificationAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlockSoneAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new FollowSoneAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UnfollowSoneAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new EditAlbumAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new EditImageAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustAjaxPage(this)));
}
if (!hasFirstStartNotification()) {
notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification);
- if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal) {
+ if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal && (reply.getPost().getSone() != null) && (reply.getTime() <= System.currentTimeMillis())) {
mentionNotification.add(reply.getPost());
notificationManager.addNotification(mentionNotification);
}
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertStarted(Image image) {
+ insertingImagesNotification.add(image);
+ notificationManager.addNotification(insertingImagesNotification);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertAborted(Image image) {
+ insertingImagesNotification.remove(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertFinished(Image image) {
+ insertingImagesNotification.remove(image);
+ insertedImagesNotification.add(image);
+ notificationManager.addNotification(insertedImagesNotification);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertFailed(Image image, Throwable cause) {
+ insertingImagesNotification.remove(image);
+ imageInsertFailedNotification.add(image);
+ notificationManager.addNotification(imageInsertFailedNotification);
+ }
+
+ /**
* Template provider implementation that uses
* {@link WebInterface#createReader(String)} to load templates for
* inclusion.
package net.pterodactylus.sone.web.ajax;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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.text.TextFilter;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
Sone sone = getCurrentSone(request.getToadletContext());
if (sone == null) {
return createErrorJsonObject("auth-required");
import net.pterodactylus.sone.data.Sone;
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String postId = request.getHttpRequest().getParam("post");
Post post = webInterface.getCore().getPost(postId, false);
if ((post == null) || (post.getSone() == null)) {
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String fieldId = request.getHttpRequest().getParam("field");
Sone currentSone = getCurrentSone(request.getToadletContext());
Profile profile = currentSone.getProfile();
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String replyId = request.getHttpRequest().getParam("reply");
Reply reply = webInterface.getCore().getReply(replyId);
if (reply == null) {
package net.pterodactylus.sone.web.ajax;
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(Request request) {
+ protected JsonObject 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.freenet.wot.Trust;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
if (currentSone == null) {
return createErrorJsonObject("auth-required");
--- /dev/null
+/*
+ * Sone - EditAlbumAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.data.Album;
+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.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditAlbumAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new edit album AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public EditAlbumAjaxPage(WebInterface webInterface) {
+ super("editAlbum.ajax", webInterface);
+ }
+
+ //
+ // JSONPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(FreenetRequest request) {
+ String albumId = request.getHttpRequest().getParam("album");
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ if (album == null) {
+ return createErrorJsonObject("invalid-album-id");
+ }
+ if (!webInterface.getCore().isLocalSone(album.getSone())) {
+ return createErrorJsonObject("not-authorized");
+ }
+ if ("true".equals(request.getHttpRequest().getParam("moveLeft"))) {
+ Album swappedAlbum = (album.getParent() != null) ? album.getParent().moveAlbumUp(album) : album.getSone().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);
+ webInterface.getCore().touchConfiguration();
+ return createSuccessJsonObject().put("sourceAlbumId", album.getId()).put("destinationAlbumId", swappedAlbum.getId());
+ }
+ String title = request.getHttpRequest().getParam("title").trim();
+ String description = request.getHttpRequest().getParam("description").trim();
+ album.setTitle(title).setDescription(description);
+ webInterface.getCore().touchConfiguration();
+ return createSuccessJsonObject().put("albumId", album.getId()).put("title", album.getTitle()).put("description", album.getDescription());
+ }
+
+}
--- /dev/null
+/*
+ * Sone - EditImageAjaxPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.ajax;
+
+import net.pterodactylus.sone.data.Image;
+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 image modifications.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditImageAjaxPage extends JsonPage {
+
+ /**
+ * Creates a new edit image AJAX page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public EditImageAjaxPage(WebInterface webInterface) {
+ super("editImage.ajax", webInterface);
+ }
+
+ //
+ // JSONPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JsonObject createJsonObject(FreenetRequest request) {
+ String imageId = request.getHttpRequest().getParam("image");
+ Image image = webInterface.getCore().getImage(imageId, false);
+ if (image == null) {
+ return createErrorJsonObject("invalid-image-id");
+ }
+ if (!webInterface.getCore().isLocalSone(image.getSone())) {
+ return createErrorJsonObject("not-authorized");
+ }
+ if ("true".equals(request.getHttpRequest().getParam("moveLeft"))) {
+ Image swappedImage = image.getAlbum().moveImageUp(image);
+ webInterface.getCore().touchConfiguration();
+ return createSuccessJsonObject().put("sourceImageId", image.getId()).put("destinationImageId", swappedImage.getId());
+ }
+ if ("true".equals(request.getHttpRequest().getParam("moveRight"))) {
+ Image swappedImage = image.getAlbum().moveImageDown(image);
+ webInterface.getCore().touchConfiguration();
+ return createSuccessJsonObject().put("sourceImageId", image.getId()).put("destinationImageId", swappedImage.getId());
+ }
+ String title = request.getHttpRequest().getParam("title").trim();
+ String description = request.getHttpRequest().getParam("description").trim();
+ image.setTitle(title).setDescription(description);
+ webInterface.getCore().touchConfiguration();
+ return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription());
+ }
+
+}
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
if (!webInterface.getCore().hasSone(soneId)) {
return createErrorJsonObject("invalid-sone-id");
import net.pterodactylus.sone.data.Sone;
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;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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.notify.ListNotification;
import net.pterodactylus.sone.notify.ListNotificationFilters;
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;
import net.pterodactylus.util.notify.TemplateNotification;
*/
@Override
@SuppressWarnings("unchecked")
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String[] notificationIds = request.getHttpRequest().getParam("notifications").split(",");
JsonObject jsonNotifications = new JsonObject();
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
for (String notificationId : notificationIds) {
Notification notification = webInterface.getNotifications().getNotification(notificationId);
+ if (notification == null) {
+ // TODO - show error
+ continue;
+ }
if ("new-post-notification".equals(notificationId)) {
notification = ListNotificationFilters.filterNewPostNotification((ListNotification<Post>) notification, currentSone, false);
} else if ("new-reply-notification".equals(notificationId)) {
* The notification to create a JSON object
* @return The JSON object
*/
- private JsonObject createJsonNotification(Request request, Notification notification) {
+ private JsonObject createJsonNotification(FreenetRequest request, Notification notification) {
JsonObject jsonNotification = new JsonObject();
jsonNotification.put("id", notification.getId());
StringWriter notificationWriter = new StringWriter();
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
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;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String postId = request.getHttpRequest().getParam("post");
Post post = webInterface.getCore().getPost(postId, false);
if (post == null) {
* The currently logged in Sone (to store in the template)
* @return The JSON representation of the post
*/
- private JsonObject createJsonPost(Request request, Post post, Sone currentSone) {
+ private JsonObject createJsonPost(FreenetRequest request, Post post, Sone currentSone) {
JsonObject jsonPost = new JsonObject();
jsonPost.put("id", post.getId());
jsonPost.put("sone", post.getSone().getId());
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
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;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String replyId = request.getHttpRequest().getParam("reply");
Reply reply = webInterface.getCore().getReply(replyId);
if ((reply == null) || (reply.getSone() == null)) {
* The currently logged in Sone (to store in the template)
* @return The JSON representation of the reply
*/
- private JsonObject createJsonReply(Request request, Reply reply, Sone currentSone) {
+ private JsonObject createJsonReply(FreenetRequest request, Reply reply, Sone currentSone) {
JsonObject jsonReply = new JsonObject();
jsonReply.put("id", reply.getId());
jsonReply.put("postId", reply.getPost().getId());
import net.pterodactylus.sone.notify.ListNotificationFilters;
import net.pterodactylus.sone.template.SoneAccessor;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.filter.Filter;
import net.pterodactylus.util.filter.Filters;
import net.pterodactylus.util.json.JsonArray;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
final Sone currentSone = getCurrentSone(request.getToadletContext(), false);
/* load Sones. */
boolean loadAllSones = Boolean.parseBoolean(request.getHttpRequest().getParam("loadAllSones", "false"));
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.number.Digits;
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String allIds = request.getHttpRequest().getParam("posts");
JsonObject postTimes = new JsonObject();
if (allIds.length() > 0) {
package net.pterodactylus.sone.web.ajax;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String key = request.getHttpRequest().getParam("key");
String translation = webInterface.getL10n().getString(key);
return createSuccessJsonObject().put("value", translation);
package net.pterodactylus.sone.web.ajax;
+import java.io.IOException;
+
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
-import net.pterodactylus.sone.web.page.Page;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
import net.pterodactylus.util.json.JsonUtils;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Response;
import freenet.clients.http.SessionManager.Session;
import freenet.clients.http.ToadletContext;
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public abstract class JsonPage implements Page {
+public abstract class JsonPage implements Page<FreenetRequest> {
/** The path of the page. */
private final String path;
* The request to handle
* @return The created JSON object
*/
- protected abstract JsonObject createJsonObject(Request request);
+ protected abstract JsonObject createJsonObject(FreenetRequest request);
/**
* Returns whether this command needs the form password for authentication
* {@inheritDoc}
*/
@Override
- public Response handleRequest(Request request) {
+ public boolean isPrefixPage() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Response handleRequest(FreenetRequest request, Response response) throws IOException {
if (webInterface.getCore().getPreferences().isRequireFullAccess() && !request.getToadletContext().isAllowedFullAccess()) {
- return new Response(403, "Forbidden", "application/json", JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
+ return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
}
if (needsFormPassword()) {
String formPassword = request.getHttpRequest().getParam("formPassword");
if (!webInterface.getFormPassword().equals(formPassword)) {
- return new Response(403, "Forbidden", "application/json", JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
+ return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
}
}
if (requiresLogin()) {
if (getCurrentSone(request.getToadletContext(), false) == null) {
- return new Response(403, "Forbidden", "application/json", JsonUtils.format(createErrorJsonObject("auth-required")));
+ return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
}
}
JsonObject jsonObject = createJsonObject(request);
- return new Response(200, "OK", "application/json", JsonUtils.format(jsonObject));
+ return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject));
}
}
import net.pterodactylus.sone.data.Post;
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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.core.Core;
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
Sone sone = webInterface.getCore().getLocalSone(soneId, false);
if (sone == null) {
import net.pterodactylus.sone.data.Reply;
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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.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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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.freenet.wot.Trust;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
if (currentSone == null) {
return createErrorJsonObject("auth-required");
package net.pterodactylus.sone.web.ajax;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
String soneId = request.getHttpRequest().getParam("sone");
if (!webInterface.getCore().hasSone(soneId)) {
return createErrorJsonObject("invalid-sone-id");
import net.pterodactylus.sone.data.Post;
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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.core.Core;
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;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject 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.freenet.wot.Trust;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.json.JsonObject;
/**
* {@inheritDoc}
*/
@Override
- protected JsonObject createJsonObject(Request request) {
+ protected JsonObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
if (currentSone == null) {
return createErrorJsonObject("auth-required");
--- /dev/null
+/*
+ * Sone - FreenetRequest.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.page;
+
+import java.net.URI;
+
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.util.web.Request;
+import freenet.clients.http.ToadletContext;
+import freenet.support.api.HTTPRequest;
+
+/**
+ * Encapsulates all Freenet-specific properties of a request.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FreenetRequest extends Request {
+
+ /** The underlying HTTP request from Freenet. */
+ private final HTTPRequest httpRequest;
+
+ /** The toadlet context. */
+ private final ToadletContext toadletContext;
+
+ /**
+ * Creates a new freenet request.
+ *
+ * @param uri
+ * The URI that is being accessed
+ * @param method
+ * The method used to access this page
+ * @param httpRequest
+ * The underlying HTTP request from Freenet
+ * @param toadletContext
+ * The toadlet context
+ */
+ public FreenetRequest(URI uri, Method method, HTTPRequest httpRequest, ToadletContext toadletContext) {
+ super(uri, method);
+ this.httpRequest = httpRequest;
+ this.toadletContext = toadletContext;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the underlying HTTP request from Freenet.
+ *
+ * @return The underlying HTTP request from Freenet
+ */
+ public HTTPRequest getHttpRequest() {
+ return httpRequest;
+ }
+
+ /**
+ * Returns the toadlet context.
+ *
+ * @return The toadlet context
+ */
+ public ToadletContext getToadletContext() {
+ return toadletContext;
+ }
+
+}
package net.pterodactylus.sone.web.page;
+import java.io.IOException;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.template.TemplateContextFactory;
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.RedirectResponse;
+import net.pterodactylus.util.web.Response;
import freenet.clients.http.LinkEnabledCallback;
import freenet.clients.http.PageMaker;
import freenet.clients.http.PageNode;
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class FreenetTemplatePage implements Page, LinkEnabledCallback {
+public class FreenetTemplatePage implements Page<FreenetRequest>, LinkEnabledCallback {
/** The logger. */
private static final Logger logger = Logging.getLogger(FreenetTemplatePage.class);
* The request to serve
* @return The title of the page
*/
- protected String getPageTitle(Request request) {
+ protected String getPageTitle(FreenetRequest request) {
return null;
}
* {@inheritDoc}
*/
@Override
- public Response handleRequest(Request request) {
+ public boolean isPrefixPage() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Response handleRequest(FreenetRequest request, Response response) throws IOException {
String redirectTarget = getRedirectTarget(request);
if (redirectTarget != null) {
return new RedirectResponse(redirectTarget);
}
if (isFullAccessOnly() && !request.getToadletContext().isAllowedFullAccess()) {
- return new Response(401, "Not authorized", "text/html", "Not authorized");
+ return response.setStatusCode(401).setStatusText("Not authorized").setContentType("text/html");
}
ToadletContext toadletContext = request.getToadletContext();
if (request.getMethod() == Method.POST) {
postProcess(request, templateContext);
- return new Response(200, "OK", "text/html", pageNode.outer.generate());
+ return response.setStatusCode(200).setStatusText("OK").setContentType("text/html").write(pageNode.outer.generate());
}
/**
* @throws RedirectException
* if the processing page wants to redirect after processing
*/
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
/* do nothing. */
}
/**
* This method will be called after
- * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, TemplateContext)}
- * has processed the template and the template was rendered. This method
- * will not be called if
- * {@link #processTemplate(net.pterodactylus.sone.web.page.Page.Request, TemplateContext)}
+ * {@link #processTemplate(FreenetRequest, TemplateContext)} has processed
+ * the template and the template was rendered. This method will not be
+ * called if {@link #processTemplate(FreenetRequest, TemplateContext)}
* throws a {@link RedirectException}!
*
* @param request
* @param templateContext
* The template context that supplied the rendered data
*/
- protected void postProcess(Request request, TemplateContext templateContext) {
+ protected void postProcess(FreenetRequest request, TemplateContext templateContext) {
/* do nothing. */
}
* The request that is processed
* @return The URL to redirect to, or {@code null} to not redirect
*/
- protected String getRedirectTarget(Page.Request request) {
+ protected String getRedirectTarget(FreenetRequest request) {
return null;
}
* The request for which to return the link nodes
* @return All link nodes that should be added to the HTML head
*/
- protected List<Map<String, String>> getAdditionalLinkNodes(Request request) {
+ protected List<Map<String, String>> getAdditionalLinkNodes(FreenetRequest request) {
return Collections.emptyList();
}
/**
* Exception that can be thrown to signal that a subclassed {@link Page}
* wants to redirect the user during the
- * {@link FreenetTemplatePage#processTemplate(net.pterodactylus.sone.web.page.Page.Request, TemplateContext)}
+ * {@link FreenetTemplatePage#processTemplate(FreenetRequest, TemplateContext)}
* method call.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+++ /dev/null
-/*
- * Sone - Page.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
-
-import freenet.clients.http.ToadletContext;
-import freenet.support.api.HTTPRequest;
-
-/**
- * A page is responsible for handling HTTP requests and creating appropriate
- * responses.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface Page {
-
- /**
- * Returns the path of this toadlet.
- *
- * @return The path of this toadlet
- */
- public String getPath();
-
- /**
- * Handles a request.
- *
- * @param request
- * The request to handle
- * @return The response
- */
- public Response handleRequest(Request request);
-
- /**
- * Container for request data.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- public class Request {
-
- /**
- * Enumeration for all possible HTTP request methods.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’
- * Roden</a>
- */
- public enum Method {
-
- /** GET. */
- GET,
-
- /** POST. */
- POST,
-
- /** PUT. */
- PUT,
-
- /** DELETE. */
- DELETE,
-
- /** HEAD. */
- HEAD,
-
- /** OPTIONS. */
- OPTIONS,
-
- /** TRACE. */
- TRACE,
-
- }
-
- /** The URI that was accessed. */
- private final URI uri;
-
- /** The HTTP method that was used. */
- private final Method method;
-
- /** The HTTP request. */
- private final HTTPRequest httpRequest;
-
- /** The toadlet context. */
- private final ToadletContext toadletContext;
-
- /**
- * Creates a new request that holds the given data.
- *
- * @param uri
- * The URI of the request
- * @param method
- * The HTTP method of the request
- * @param httpRequest
- * The HTTP request
- * @param toadletContext
- * The toadlet context of the request
- */
- public Request(URI uri, Method method, HTTPRequest httpRequest, ToadletContext toadletContext) {
- this.uri = uri;
- this.method = method;
- this.httpRequest = httpRequest;
- this.toadletContext = toadletContext;
- }
-
- /**
- * Returns the URI that was accessed.
- *
- * @return The accessed URI
- */
- public URI getUri() {
- return uri;
- }
-
- /**
- * Returns the HTTP method that was used to access the page.
- *
- * @return The HTTP method
- */
- public Method getMethod() {
- return method;
- }
-
- /**
- * Returns the HTTP request.
- *
- * @return The HTTP request
- */
- public HTTPRequest getHttpRequest() {
- return httpRequest;
- }
-
- /**
- * Returns the toadlet context.
- *
- * @return The toadlet context
- */
- public ToadletContext getToadletContext() {
- return toadletContext;
- }
-
- }
-
- /**
- * Container for the HTTP response of a {@link Page}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- public class Response {
-
- /** The HTTP status code of the response. */
- private final int statusCode;
-
- /** The HTTP status text of the response. */
- private final String statusText;
-
- /** The content type of the response. */
- private final String contentType;
-
- /** The headers of the response. */
- private final Map<String, String> headers;
-
- /** The content of the response body. */
- private final InputStream content;
-
- /**
- * Creates a new response.
- *
- * @param statusCode
- * The HTTP status code of the response
- * @param statusText
- * The HTTP status text of the response
- * @param contentType
- * The content type of the response
- * @param text
- * The text in the response body
- */
- public Response(int statusCode, String statusText, String contentType, String text) {
- this(statusCode, statusText, contentType, getBytes(text));
- }
-
- /**
- * Creates a new response.
- *
- * @param statusCode
- * The HTTP status code of the response
- * @param statusText
- * The HTTP status text of the response
- * @param contentType
- * The content type of the response
- * @param content
- * The content of the reponse body
- */
- public Response(int statusCode, String statusText, String contentType, byte[] content) {
- this(statusCode, statusText, contentType, new HashMap<String, String>(), content);
- }
-
- /**
- * Creates a new response.
- *
- * @param statusCode
- * The HTTP status code of the response
- * @param statusText
- * The HTTP status text of the response
- * @param contentType
- * The content type of the response
- * @param headers
- * The headers of the response
- */
- public Response(int statusCode, String statusText, String contentType, Map<String, String> headers) {
- this(statusCode, statusText, contentType, headers, (InputStream) null);
- }
-
- /**
- * Creates a new response.
- *
- * @param statusCode
- * The HTTP status code of the response
- * @param statusText
- * The HTTP status text of the response
- * @param contentType
- * The content type of the response
- * @param headers
- * The headers of the response
- * @param content
- * The content of the reponse body
- */
- public Response(int statusCode, String statusText, String contentType, Map<String, String> headers, byte[] content) {
- this(statusCode, statusText, contentType, headers, new ByteArrayInputStream(content));
- }
-
- /**
- * Creates a new response.
- *
- * @param statusCode
- * The HTTP status code of the response
- * @param statusText
- * The HTTP status text of the response
- * @param contentType
- * The content type of the response
- * @param headers
- * The headers of the response
- * @param content
- * The content of the reponse body
- */
- public Response(int statusCode, String statusText, String contentType, Map<String, String> headers, InputStream content) {
- this.statusCode = statusCode;
- this.statusText = statusText;
- this.contentType = contentType;
- this.headers = headers;
- this.content = content;
- }
-
- /**
- * Returns the HTTP status code of the response.
- *
- * @return The HTTP status code
- */
- public int getStatusCode() {
- return statusCode;
- }
-
- /**
- * Returns the HTTP status text.
- *
- * @return The HTTP status text
- */
- public String getStatusText() {
- return statusText;
- }
-
- /**
- * Returns the content type of the response.
- *
- * @return The content type of the reponse
- */
- public String getContentType() {
- return contentType;
- }
-
- /**
- * Returns HTTP headers of the response. May be {@code null} if no
- * headers are returned.
- *
- * @return The response headers, or {@code null} if there are no
- * response headers
- */
- public Map<String, String> getHeaders() {
- return headers;
- }
-
- /**
- * Sets the HTTP header with the given name to the given value. Multiple
- * headers with the same name are not implemented so that latest call to
- * {@link #setHeader(String, String)} determines what is sent to the
- * browser.
- *
- * @param name
- * The name of the header
- * @param value
- * The value of the header
- */
- public void setHeader(String name, String value) {
- headers.put(name, value);
- }
-
- /**
- * Returns the content of the response body. May be {@code null} if the
- * response does not have a body.
- *
- * @return The content of the response body
- */
- public InputStream getContent() {
- return content;
- }
-
- //
- // PRIVATE METHODS
- //
-
- /**
- * Returns the UTF-8 representation of the given text.
- *
- * @param text
- * The text to encode
- * @return The encoded text
- */
- private static byte[] getBytes(String text) {
- try {
- return text.getBytes("UTF-8");
- } catch (UnsupportedEncodingException uee1) {
- /* every JVM needs to support UTF-8. */
- }
- return null;
- }
-
- /**
- * Creates a header map containing a single header.
- *
- * @param name
- * The name of the header
- * @param value
- * The value of the header
- * @return The map containing the single header
- */
- protected static Map<String, String> createHeader(String name, String value) {
- Map<String, String> headers = new HashMap<String, String>();
- headers.put(name, value);
- return headers;
- }
-
- }
-
- /**
- * {@link Response} implementation that performs an HTTP redirect.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- public class RedirectResponse extends Response {
-
- /**
- * Creates a new redirect response to the new location.
- *
- * @param newLocation
- * The new location
- */
- public RedirectResponse(String newLocation) {
- this(newLocation, true);
- }
-
- /**
- * Creates a new redirect response to the new location.
- *
- * @param newLocation
- * The new location
- * @param permanent
- * Whether the redirect should be marked as permanent
- */
- public RedirectResponse(String newLocation, boolean permanent) {
- super(permanent ? 302 : 307, "Redirected", null, createHeader("Location", newLocation));
- }
-
- }
-
-}
import java.io.IOException;
import java.net.URI;
-import java.util.Map.Entry;
-import net.pterodactylus.sone.web.page.Page.Request.Method;
+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.Toadlet;
import freenet.support.MultiValueTable;
import freenet.support.api.Bucket;
import freenet.support.api.HTTPRequest;
-import freenet.support.io.BucketTools;
import freenet.support.io.Closer;
/**
private final String menuName;
/** The page that handles processing. */
- private final Page page;
+ private final Page<FreenetRequest> page;
/** The path prefix for the page. */
private final String pathPrefix;
* Prefix that is prepended to all {@link Page#getPath()} return
* values
*/
- protected PageToadlet(HighLevelSimpleClient highLevelSimpleClient, String menuName, Page page, String pathPrefix) {
+ protected PageToadlet(HighLevelSimpleClient highLevelSimpleClient, String menuName, Page<FreenetRequest> page, String pathPrefix) {
super(highLevelSimpleClient);
this.menuName = menuName;
this.page = page;
* if the toadlet context is closed
*/
public void handleMethodGET(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
- handleRequest(new Page.Request(uri, Method.GET, httpRequest, toadletContext));
+ handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext));
}
/**
* if the toadlet context is closed
*/
public void handleMethodPOST(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
- handleRequest(new Page.Request(uri, Method.POST, httpRequest, toadletContext));
+ handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext));
}
/**
* @throws ToadletContextClosedException
* if the toadlet context is closed
*/
- private void handleRequest(Page.Request pageRequest) throws IOException, ToadletContextClosedException {
- Bucket data = null;
+ private void handleRequest(FreenetRequest pageRequest) throws IOException, ToadletContextClosedException {
+ Bucket pageBucket = null;
try {
- Page.Response pageResponse = page.handleRequest(pageRequest);
+ 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 (Entry<String, String> headerEntry : pageResponse.getHeaders().entrySet()) {
- headers.put(headerEntry.getKey(), headerEntry.getValue());
+ for (Header header : pageResponse.getHeaders()) {
+ for (String value : header) {
+ headers.put(header.getName(), value);
+ }
}
}
- data = pageRequest.getToadletContext().getBucketFactory().makeBucket(-1);
- if (pageResponse.getContent() != null) {
- try {
- BucketTools.copyFrom(data, pageResponse.getContent(), -1);
- } finally {
- Closer.close(pageResponse.getContent());
- }
- } else {
- /* get an OutputStream and close it immediately. */
- Closer.close(data.getOutputStream());
- }
- writeReply(pageRequest.getToadletContext(), pageResponse.getStatusCode(), pageResponse.getContentType(), pageResponse.getStatusText(), headers, data);
+ writeReply(pageRequest.getToadletContext(), pageResponse.getStatusCode(), pageResponse.getContentType(), pageResponse.getStatusText(), headers, pageBucket);
} catch (Throwable t1) {
writeInternalError(t1, pageRequest.getToadletContext());
} finally {
- Closer.close(data);
+ Closer.close(pageBucket);
}
}
package net.pterodactylus.sone.web.page;
+import net.pterodactylus.util.web.Page;
import freenet.client.HighLevelSimpleClient;
/**
* Factory that creates {@link PageToadlet}s using a given
* {@link HighLevelSimpleClient}.
- *
+ *
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PageToadletFactory {
/**
* Creates a new {@link PageToadlet} factory.
- *
+ *
* @param highLevelSimpleClient
* The client to use when creating the toadlets
* @param pathPrefix
/**
* Creates a {@link PageToadlet} that wraps the given page and does not
* appear in the node’s menu.
- *
+ *
* @param page
* The page to wrap
* @return The toadlet wrapped around the page
*/
- public PageToadlet createPageToadlet(Page page) {
+ public PageToadlet createPageToadlet(Page<FreenetRequest> page) {
return createPageToadlet(page, null);
}
/**
* Creates a {@link PageToadlet} that wraps the given page and appears in
* the node’s menu under the given name.
- *
+ *
* @param page
* The page to wrap
* @param menuName
* The name of the menu item
* @return The toadlet wrapped around the page
*/
- public PageToadlet createPageToadlet(Page page, String menuName) {
+ public PageToadlet createPageToadlet(Page<FreenetRequest> page, String menuName) {
return new PageToadlet(highLevelSimpleClient, menuName, page, pathPrefix);
}
+++ /dev/null
-/*
- * Sone - RedirectPage.java - Copyright © 2011 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-/**
- * Page implementation that redirects the user to another URL.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class RedirectPage implements Page {
-
- /** The original path. */
- private String originalPath;
-
- /** The path to redirect the browser to. */
- private String newPath;
-
- /**
- * Creates a new redirect page.
- *
- * @param originalPath
- * The original path
- * @param newPath
- * The path to redirect the browser to
- */
- public RedirectPage(String originalPath, String newPath) {
- this.originalPath = originalPath;
- this.newPath = newPath;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getPath() {
- return originalPath;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Response handleRequest(Request request) {
- return new RedirectResponse(newPath);
- }
-
-}
+++ /dev/null
-/*
- * Sone - StaticPage.java - Copyright © 2010 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-import java.io.InputStream;
-
-/**
- * {@link Page} implementation that delivers static files from the class path.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class StaticPage implements Page {
-
- /** The prefix for {@link #getPath()}. */
- private final String pathPrefix;
-
- /** The path used as prefix when loading resources. */
- private final String resourcePathPrefix;
-
- /** The MIME type for the files this path contains. */
- private final String mimeType;
-
- /**
- * Creates a new CSS page.
- *
- * @param pathPrefix
- * The prefix for {@link #getPath()}
- * @param resourcePathPrefix
- * The path prefix when loading resources
- * @param mimeType
- * The MIME type of the files this path contains
- */
- public StaticPage(String pathPrefix, String resourcePathPrefix, String mimeType) {
- this.pathPrefix = pathPrefix;
- this.resourcePathPrefix = resourcePathPrefix;
- this.mimeType = mimeType;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getPath() {
- return pathPrefix;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Response handleRequest(Request request) {
- String path = request.getUri().getPath();
- int lastSlash = path.lastIndexOf('/');
- String filename = path.substring(lastSlash + 1);
- InputStream fileInputStream = getClass().getResourceAsStream(resourcePathPrefix + filename);
- if (fileInputStream == null) {
- return new Response(404, "Not found.", null, "");
- }
- return new Response(200, "OK", mimeType, null, fileInputStream);
- }
-
-}
+++ /dev/null
-/*
- * Sone - StaticTemplatePage.java - Copyright © 2011 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.pterodactylus.util.io.Closer;
-import net.pterodactylus.util.logging.Logging;
-import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
-import net.pterodactylus.util.template.TemplateContextFactory;
-
-/**
- * A template page is a single page that is created from a {@link Template} but
- * does not necessarily return HTML.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class TemplatePage implements Page {
-
- /** The logger. */
- private static final Logger logger = Logging.getLogger(TemplatePage.class);
-
- /** The path of this page. */
- private final String path;
-
- /** The content type of this page. */
- private final String contentType;
-
- /** The template context factory. */
- private final TemplateContextFactory templateContextFactory;
-
- /** The template to render. */
- private final Template template;
-
- /**
- * Creates a new template page.
- *
- * @param path
- * The path of the page
- * @param contentType
- * The content type of the page
- * @param templateContextFactory
- * The template context factory
- * @param template
- * The template to render
- */
- public TemplatePage(String path, String contentType, TemplateContextFactory templateContextFactory, Template template) {
- this.path = path;
- this.contentType = contentType;
- this.templateContextFactory = templateContextFactory;
- this.template = template;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getPath() {
- return path;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Response handleRequest(Request request) {
- ByteArrayOutputStream responseOutputStream = new ByteArrayOutputStream();
- OutputStreamWriter responseWriter = null;
- try {
- responseWriter = new OutputStreamWriter(responseOutputStream, "UTF-8");
- TemplateContext templateContext = templateContextFactory.createTemplateContext();
- templateContext.set("request", request);
- template.render(templateContext, responseWriter);
- } catch (IOException ioe1) {
- logger.log(Level.WARNING, "Could not render template for path “" + path + "”!", ioe1);
- } finally {
- Closer.close(responseWriter);
- Closer.close(responseOutputStream);
- }
- ByteArrayInputStream responseInputStream = new ByteArrayInputStream(responseOutputStream.toByteArray());
- /* no need to close a ByteArrayInputStream. */
- return new Response(200, "OK", contentType, null, responseInputStream);
- }
-
-}
Navigation.Menu.Item.Bookmarks.Tooltip=Show bookmarked posts
Navigation.Menu.Item.EditProfile.Name=Edit Profile
Navigation.Menu.Item.EditProfile.Tooltip=Edit the Profile of your Sone
+Navigation.Menu.Item.ImageBrowser.Name=Images
+Navigation.Menu.Item.ImageBrowser.Tooltip=Manages your Images
Navigation.Menu.Item.DeleteSone.Name=Delete Sone
Navigation.Menu.Item.DeleteSone.Tooltip=Deletes the current Sone
Navigation.Menu.Item.Logout.Name=Logout
Page.ViewSone.PostList.Text.NoPostYet=This Sone has not yet posted anything.
Page.ViewSone.Profile.Title=Profile
Page.ViewSone.Profile.Label.Name=Name
+Page.ViewSone.Profile.Label.Albums=Albums
+Page.ViewSone.Profile.Albums.Text.All=All albums
Page.ViewSone.Profile.Name.WoTLink=web of trust profile
Page.ViewSone.Replies.Title=Posts {sone} has replied to
Page.UnfollowSone.Title=Unfollow Sone - Sone
+Page.ImageBrowser.Title=Image Browser - Sone
+Page.ImageBrowser.Album.Title=Album “{album}”
+Page.ImageBrowser.Album.Error.NotFound.Text=The requested album could not be found. It is possible that it has not yet been downloaded, or that it has been deleted.
+Page.ImageBrowser.Sone.Title=Albums of {sone}
+Page.ImageBrowser.Sone.Error.NotFound.Text=The requested Sone could not be found. It is possible that it has not yet been downloaded.
+Page.ImageBrowser.Header.Albums=Albums
+Page.ImageBrowser.Header.Images=Images
+Page.ImageBrowser.CreateAlbum.Button.CreateAlbum=Create Album
+Page.ImageBrowser.Album.Edit.Title=Edit Album
+Page.ImageBrowser.Album.Delete.Title=Delete Album
+Page.ImageBrowser.Album.Label.AlbumImage=Album Image:
+Page.ImageBrowser.Album.Label.Title=Title:
+Page.ImageBrowser.Album.Label.Description=Description:
+Page.ImageBrowser.Album.AlbumImage.Choose=Choose Album Image…
+Page.ImageBrowser.Album.Button.Save=Save Album
+Page.ImageBrowser.Album.Button.Delete=Delete Album
+Page.ImageBrowser.Image.Edit.Title=Edit Image
+Page.ImageBrowser.Image.Title.Label=Title:
+Page.ImageBrowser.Image.Description.Label=Description:
+Page.ImageBrowser.Image.Button.MoveLeft=◀
+Page.ImageBrowser.Image.Button.Save=Save Image
+Page.ImageBrowser.Image.Button.MoveRight=►
+Page.ImageBrowser.Image.Delete.Title=Delete Image
+Page.ImageBrowser.Image.Button.Delete=Delete Image
+
+Page.CreateAlbum.Title=Create Album - Sone
+Page.CreateAlbum.Page.Title=Create Album
+Page.CreateAlbum.Error.NameMissing=You seem to have forgotten to enter a name for your new album.
+
+Page.UploadImage.Title=Upload Image - Sone
+Page.UploadImage.Error.InvalidImage=The image you were trying to upload could not be recognized. Please upload only JPEG (*.jpg or *.jpeg), or PNG (*.png) images.
+
+Page.EditImage.Title=Edit Image
+
+Page.DeleteImage.Title=Delete Image
+Page.DeleteImage.Page.Title=Delete Image
+Page.DeleteImage.Text.ImageWillBeGone=This will remove the image “{image}” from your album “{album}”. If it has already been inserted into Freenet it can not be removed from there forcefully. Do you want to do delete the image?
+Page.DeleteImage.Button.Yes=Yes, delete image.
+Page.DeleteImage.Button.No=No, don’t delete image.
+
+Page.EditAlbum.Title=Edit Album
+
+Page.DeleteAlbum.Title=Delete Album
+Page.DeleteAlbum.Page.Title=Delete Album
+Page.DeleteAlbum.Text.AlbumWillBeGone=This will remove your album “{title}”. Do you really want to do that?
+Page.DeleteAlbum.Button.Yes=Yes, delete album.
+Page.DeleteAlbum.Button.No=No, don’t delete album.
+
Page.Trust.Title=Trust Sone - Sone
Page.Distrust.Title=Distrust Sone - Sone
View.Sone.Status.Downloading=This Sone is currently being downloaded.
View.Sone.Status.Inserting=This Sone is currently being inserted.
+View.SoneMenu.Link.AllAlbums=all albums
+
View.Post.UnknownAuthor=(unknown)
View.Post.WebOfTrustLink=web of trust profile
View.Post.Permalink=link post
View.Trust.Tooltip.Distrust=Assign negative trust to this person
View.Trust.Tooltip.Untrust=Remove your trust assignment for this person
+View.CreateAlbum.Title=Create Album
+View.CreateAlbum.Label.Name=Name:
+View.CreateAlbum.Label.Description=Description:
+
+View.UploadImage.Title=Upload Image
+View.UploadImage.Label.Title=Title:
+View.UploadImage.Label.Description=Description:
+View.UploadImage.Button.UploadImage=Upload Image
+
View.Time.InTheFuture=in the future
View.Time.AFewSecondsAgo=a few seconds ago
View.Time.HalfAMinuteAgo=about half a minute ago
WebInterface.DefaultText.BirthYear=Year
WebInterface.DefaultText.FieldName=Field name
WebInterface.DefaultText.Option.InsertionDelay=Time to wait after a Sone is modified before insert (in seconds)
+WebInterface.DefaultText.Search=What are you looking for?
+WebInterface.DefaultText.CreateAlbum.Name=Album title
+WebInterface.DefaultText.CreateAlbum.Description=Album description
+WebInterface.DefaultText.EditAlbum.Title=Album title
+WebInterface.DefaultText.EditAlbum.Description=Album description
+WebInterface.DefaultText.UploadImage.Title=Image title
+WebInterface.DefaultText.UploadImage.Description=Image description
+WebInterface.DefaultText.EditImage.Title=Image title
+WebInterface.DefaultText.EditImage.Description=Image description
WebInterface.DefaultText.Option.PostsPerPage=Number of posts to show on a page
WebInterface.DefaultText.Option.CharactersPerPost=Number of characters per post after which to cut the post off
WebInterface.DefaultText.Option.PositiveTrust=The positive trust to assign
WebInterface.DefaultText.Option.NegativeTrust=The negative trust to assign
WebInterface.DefaultText.Option.TrustComment=The comment to set in the web of trust
-WebInterface.DefaultText.Search=What are you looking for?
WebInterface.Confirmation.DeletePostButton=Yes, delete!
WebInterface.Confirmation.DeleteReplyButton=Yes, delete!
WebInterface.SelectBox.Choose=Choose…
Notification.SoneRescued.Text.RememberToUnlock=Please remember to control the posts and replies you have given and don’t forget to unlock your Sones!
Notification.LockedSones.Text=The following Sones have been locked for more than 5 minutes. Please check if you really want to keep these Sones locked:
Notification.NewVersion.Text=Version {version} of the Sone plugin was found. Download it from USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}!
+Notification.InsertingImages.Text=The following images are being inserted:
+Notification.InsertedImages.Text=The following images have been inserted:
+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}
display: none;
}
+#sone .toggle-link {
+ margin-top: 1em;
+}
+
#sone #formPassword {
display: none;
}
border: none;
}
+#sone .small-link {
+ font-size: 85%;
+}
+
+#sone .parsed {
+ white-space: pre-wrap;
+}
+
#sone #main.offline {
opacity: 0.5;
}
position: relative;
}
+#sone .backlinks {
+ font-size: 80%;
+ margin-bottom: 1em;
+}
+
+#sone .backlinks .backlink {
+ display: inline;
+}
+
+#sone .album {
+}
+
+#sone .image-row, #sone .album-row {
+ display: table-row;
+}
+
+#sone .image-container, #sone .album-container {
+ width: 250px;
+ height: 250px;
+ overflow: hidden;
+ padding: -1px;
+ border: solid 1px #000;
+}
+
+#sone .image, #sone .album {
+ display: table-cell;
+ vertical-align: top;
+ text-align: center;
+ padding: 0.5ex;
+}
+
+#sone .single-image img {
+ border: solid 1px #000;
+ background-color: #fff;
+}
+
+#sone .image .edit-image input, #sone .album .edit-album input {
+ width: 95%;
+}
+
+#sone .image .edit-image textarea, #sone .album .edit-album textarea {
+ width: 95%;
+}
+
+#sone .image .image-title, #sone .album .album-title {
+ font-weight: bold;
+}
+
+#sone .image .image-description, #sone .album .album-description {
+ text-align: left;
+ width: 195px;
+ word-wrap: break-word;
+ max-height: 5em;
+ overflow: auto;
+}
+
+#sone .backlinks .separator {
+ display: inline;
+}
+
#sone #search {
text-align: right;
}
clear: both;
}
+#sone h1.backlink {
+ margin-bottom: 0px;
+}
+
#sone h2 {
font-family: inherit;
font-size: 150%;
font-weight: bold;
}
-#sone input.default {
+#sone input.default, #sone textarea.default {
color: #888;
}
* @return The Sone ID
*/
function getMenuSone(element) {
- return $(element).closest(".sone-menu").find(".sone-id").text();
+ return $(element).closest(".sone-menu").find(".sone-menu-id").text();
}
/**
/* show Sone menu when hovering over the avatar. */
$(postElement).find(".post-avatar").mouseover(function() {
- $(".sone-menu:visible").fadeOut();
- $(".sone-post-menu", postElement).mouseleave(function() {
- $(this).fadeOut();
- }).fadeIn();
- return false;
+ if (typeof currentSoneMenuTimeoutHandler != undefined) {
+ clearTimeout(currentSoneMenuTimeoutHandler);
+ }
+ currentSoneMenuId = getPostId(this);
+ currentSoneMenuTimeoutHandler = setTimeout(function() {
+ $(".sone-menu:visible").fadeOut();
+ $(".sone-post-menu", postElement).mouseleave(function() {
+ $(this).fadeOut();
+ }).fadeIn();
+ }, 1000);
+ }).mouseleave(function() {
+ if (currentSoneMenuId = getPostId(this)) {
+ clearTimeout(currentSoneMenuTimeoutHandler);
+ }
});
(function(postElement) {
- var soneId = $(".sone-id", postElement).text();
+ var soneId = $(".sone-menu-id", postElement).text();
$(".sone-post-menu .follow", postElement).click(function() {
var followElement = this;
ajaxGet("followSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
/* show Sone menu when hovering over the avatar. */
$(replyElement).find(".reply-avatar").mouseover(function() {
- $(".sone-menu:visible").fadeOut();
- $(".sone-reply-menu", replyElement).mouseleave(function() {
- $(this).fadeOut();
- }).fadeIn();
- return false;
+ if (typeof currentSoneMenuTimeoutHandler != undefined) {
+ clearTimeout(currentSoneMenuTimeoutHandler);
+ }
+ currentSoneMenuId = getPostId(this) + "-" + getReplyId(this);
+ currentSoneMenuTimeoutHandler = setTimeout(function() {
+ $(".sone-menu:visible").fadeOut();
+ $(".sone-reply-menu", replyElement).mouseleave(function() {
+ $(this).fadeOut();
+ }).fadeIn();
+ }, 1000);
+ }).mouseleave(function() {
+ if (currentSoneMenuId = getPostId(this) + "-" + getReplyId(this)) {
+ clearTimeout(currentSoneMenuTimeoutHandler);
+ }
});
(function(replyElement) {
- var soneId = $(".sone-id", replyElement).text();
+ var soneId = $(".sone-menu-id", replyElement).text();
$(".sone-menu .follow", replyElement).click(function() {
var followElement = this;
ajaxGet("followSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
var initiallyLoggedIn = $("#sone #loggedIn").text() == "true";
var notLoggedIn = !initiallyLoggedIn;
+/** ID of the next-to-show Sone context menu. */
+var currentSoneMenuId;
+
+/** Timeout handler for the next-to-show Sone context menu. */
+var currentSoneMenuTimeoutHandler;
+
$(document).ready(function() {
/* this initializes the status update input field. */
--- /dev/null
+<%include include/head.html>
+
+ <h1><%= Page.CreateAlbum.Page.Title|l10n|html></h1>
+
+ <%if nameMissing>
+ <p><%= Page.CreateAlbum.Error.NameMissing|l10n|html></p>
+ <%/if>
+
+ <%include include/createAlbum.html>
+
+<%include include/tail.html>
--- /dev/null
+<%include include/head.html>
+
+ <h1><%= Page.DeleteAlbum.Page.Title|l10n|html></h1>
+
+ <p><%= Page.DeleteAlbum.Text.AlbumWillBeGone|l10n|replace needle="{title}" replacementKey=album.title|html></p>
+
+ <form method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="album" value="<%album.id|html>" />
+ <button type="submit" name="confirmDelete" value="1"><%= Page.DeleteAlbum.Button.Yes|l10n|html></button>
+ <button type="submit" name="abortDelete" value="1"><%= Page.DeleteAlbum.Button.No|l10n|html></button>
+ </form>
+
+<%include include/tail.html>
--- /dev/null
+<%include include/head.html>
+
+ <h1><%= Page.DeleteImage.Page.Title|l10n|html></h1>
+
+ <p><%= Page.DeleteImage.Text.ImageWillBeGone|l10n|replace needle="{image}" replacementKey=image.title|replace needle="{album}" replacementKey=image.album.title|html></p>
+
+ <form method="post">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="image" value="<%image.id|html>" />
+ <button type="submit" name="confirmDelete" value="1"><%= Page.DeleteImage.Button.Yes|l10n|html></button>
+ <button type="submit" name="abortDelete" value="1"><%= Page.DeleteImage.Button.No|l10n|html></button>
+ </form>
+
+<%include include/tail.html>
--- /dev/null
+<%include include/head.html>
+
+ <div class="page-id hidden">image-browser</div>
+
+ <script language="javascript">
+
+ /* hide all those forms. */
+ function hideAndShowBlock(blockElement, clickToShowElement, clickToHideElement) {
+ $(blockElement).hide();
+ $(clickToShowElement).removeClass("hidden");
+ $(clickToShowElement).click(function() {
+ $(blockElement).slideDown();
+ $(clickToShowElement).addClass("hidden");
+ $(clickToHideElement).removeClass("hidden");
+ });
+ $(clickToHideElement).click(function() {
+ $(blockElement).slideUp();
+ $(clickToHideElement).addClass("hidden");
+ $(clickToShowElement).removeClass("hidden");
+ });
+ }
+
+ /* ID of the image currently being edited. */
+ var editingImageId = null;
+
+ /**
+ * Shows the form for editing an image.
+ *
+ * @param imageId The ID of the image to edit.
+ */
+ function editImage(imageId) {
+ if (editingImageId != imageId) {
+ cancelImageEditing();
+ } else {
+ return;
+ }
+ editingImageId = imageId;
+ $(".show-data", getImage(imageId)).hide();
+ $(".edit-data", getImage(imageId)).show();
+ $(document).bind("click.sone", function(event) {
+ if ($(event.target).closest("#image-" + imageId).size() == 0) {
+ cancelImageEditing();
+ }
+ });
+ }
+
+ /**
+ * Cancels all image editing.
+ */
+ function cancelImageEditing() {
+ $(".image .show-data").show();
+ $(".image .edit-data").hide();
+ $("form.edit-image").each(function() {
+ this.reset();
+ });
+ $(document).unbind("click.sone");
+ editingImageId = null;
+ }
+
+ /**
+ * Returns the image element with the given ID.
+ *
+ * @param imageId The ID of the image
+ * @return The image element
+ */
+ function getImage(imageId) {
+ return $("#sone .image .image-id:contains('" + imageId + "')").closest(".image");
+ }
+
+ /**
+ * Swaps two images.
+ *
+ * @param sourceId The ID of the source image
+ * @param destinationId The ID of the destionation image
+ */
+ function swapImage(sourceId, destinationId) {
+ sourceElement = getImage(sourceId);
+ destinationElement = getImage(destinationId);
+ sourceParent = sourceElement.closest(".image-row");
+ sourcePrevSibling = sourceElement.prev();
+ sourceElement.detach();
+ destinationElement.before(sourceElement);
+ if (sourcePrevSibling.get(0) != destinationElement.get(0)) {
+ destinationElement.detach();
+ (sourcePrevSibling.size() > 0) ? sourcePrevSibling.after(destinationElement) : sourceParent.prepend(destinationElement);
+ }
+ if ($("button[name='moveLeft']", sourceElement).hasClass("hidden") != $("button[name='moveLeft']", destinationElement).hasClass("hidden")) {
+ $("button[name='moveLeft']", sourceElement).toggleClass("hidden");
+ $("button[name='moveLeft']", destinationElement).toggleClass("hidden");
+ }
+ if ($("button[name='moveRight']", sourceElement).hasClass("hidden") != $("button[name='moveRight']", destinationElement).hasClass("hidden")) {
+ $("button[name='moveRight']", sourceElement).toggleClass("hidden");
+ $("button[name='moveRight']", destinationElement).toggleClass("hidden");
+ }
+ }
+
+ /**
+ * Prepare all images for inline editing.
+ */
+ function prepareImages() {
+ $(".image").each(function() {
+ imageId = $(this).closest(".image").find(".image-id").text();
+ (function(element, imageId) {
+ $(".show-data", element).click(function() {
+ editImage(imageId);
+ });
+ $("button[name='moveLeft'], button[name='moveRight']", element).click(function() {
+ ajaxGet("editImage.ajax", { "formPassword": getFormPassword(), "image": imageId, "moveLeft": this.name == "moveLeft", "moveRight": this.name == "moveRight" }, function(data) {
+ if (data && data.success) {
+ swapImage(data.sourceImageId, data.destinationImageId);
+ }
+ });
+ return false;
+ });
+ $("button[name='submit']", element).click(function() {
+ title = $(":input[name='title']:enabled", this.form).val();
+ description = $(":input[name='description']:enabled", this.form).val();
+ ajaxGet("editImage.ajax", { "formPassword": getFormPassword(), "image": imageId, "title": title, "description": description }, function(data) {
+ if (data && data.success) {
+ getImage(data.imageId).find(".image-title").text(data.title);
+ getImage(data.imageId).find(".image-description").text(data.description);
+ getImage(data.imageId).find(":input[name='title']").attr("defaultValue", title);
+ getImage(data.imageId).find(":input[name='description']").attr("defaultValue", description);
+ cancelImageEditing();
+ }
+ });
+ return false;
+ });
+ })(this, imageId);
+ });
+ }
+
+ /* ID of the album currently being edited. */
+ var editingAlbumId = null;
+
+ /**
+ * Shows the form for editing an album.
+ *
+ * @param albumId The ID of the album to edit.
+ */
+ function editAlbum(albumId) {
+ if (editingAlbumId != albumId) {
+ if (editingAlbumId != null) {
+ cancelAlbumEditing();
+ }
+ } else {
+ console.log("already editing " + albumId);
+ return;
+ }
+ editingAlbumId = albumId;
+ $(".show-data", getAlbum(albumId)).hide();
+ $(".edit-data", getAlbum(albumId)).show();
+ console.log(getAlbum(albumId));
+ $(document).bind("click.sone", function(event) {
+ if ($(event.target).closest("#album-" + albumId).size() == 0) {
+ cancelAlbumEditing();
+ }
+ });
+ }
+
+ /**
+ * Cancels all album editing.
+ */
+ function cancelAlbumEditing() {
+ console.log("cancel-album-edit");
+ $(".album .show-data").show();
+ $(".album .edit-data").hide();
+ $("form.edit-album").each(function() {
+ this.reset();
+ });
+ $(document).unbind("click.sone");
+ editingAlbumId = null;
+ }
+
+ /**
+ * Returns the album element with the given ID.
+ *
+ * @param albumId The ID of the album
+ * @return The album element
+ */
+ function getAlbum(albumId) {
+ return $("#sone .album .album-id:contains('" + albumId + "')").closest(".album");
+ }
+
+ /**
+ * Swaps two albums.
+ *
+ * @param sourceId The ID of the source album
+ * @param destinationId The ID of the destionation album
+ */
+ function swapAlbum(sourceId, destinationId) {
+ sourceElement = getAlbum(sourceId);
+ destinationElement = getAlbum(destinationId);
+ sourceParent = sourceElement.closest(".album-row");
+ sourcePrevSibling = sourceElement.prev();
+ sourceElement.detach();
+ destinationElement.before(sourceElement);
+ if (sourcePrevSibling.get(0) != destinationElement.get(0)) {
+ destinationElement.detach();
+ (sourcePrevSibling.size() > 0) ? sourcePrevSibling.after(destinationElement) : sourceParent.prepend(destinationElement);
+ }
+ if ($("button[name='moveLeft']", sourceElement).hasClass("hidden") != $("button[name='moveLeft']", destinationElement).hasClass("hidden")) {
+ $("button[name='moveLeft']", sourceElement).toggleClass("hidden");
+ $("button[name='moveLeft']", destinationElement).toggleClass("hidden");
+ }
+ if ($("button[name='moveRight']", sourceElement).hasClass("hidden") != $("button[name='moveRight']", destinationElement).hasClass("hidden")) {
+ $("button[name='moveRight']", sourceElement).toggleClass("hidden");
+ $("button[name='moveRight']", destinationElement).toggleClass("hidden");
+ }
+ }
+
+ /**
+ * Prepare all albums for inline editing.
+ */
+ function prepareAlbums() {
+ $(".album").each(function() {
+ albumId = $(this).closest(".album").find(".album-id").text();
+ (function(element, albumId) {
+ $(".show-data", element).click(function() {
+ console.log("show-data");
+ editAlbum(albumId);
+ });
+ $("button[name='moveLeft'], button[name='moveRight']", element).click(function() {
+ ajaxGet("editAlbum.ajax", { "formPassword": getFormPassword(), "album": albumId, "moveLeft": this.name == "moveLeft", "moveRight": this.name == "moveRight" }, function(data) {
+ if (data && data.success) {
+ swapAlbum(data.sourceAlbumId, data.destinationAlbumId);
+ }
+ });
+ return false;
+ });
+ $("button[name='submit']", element).click(function() {
+ title = $(":input[name='title']:enabled", this.form).val();
+ description = $(":input[name='description']:enabled", this.form).val();
+ ajaxGet("editAlbum.ajax", { "formPassword": getFormPassword(), "album": albumId, "title": title, "description": description }, function(data) {
+ if (data && data.success) {
+ getAlbum(data.albumId).find(".album-title").text(data.title);
+ getAlbum(data.albumId).find(".album-description").text(data.description);
+ getAlbum(data.albumId).find(":input[name='title']").attr("defaultValue", title);
+ getAlbum(data.albumId).find(":input[name='description']").attr("defaultValue", description);
+ cancelAlbumEditing();
+ }
+ });
+ return false;
+ });
+ })(this, albumId);
+ });
+ }
+
+ </script>
+
+ <%if albumRequested>
+
+ <%ifnull album>
+
+ <p><%= Page.ImageBrowser.Album.Error.NotFound.Text|l10n|html></p>
+
+ <%elseifnull album.title>
+
+ <p><%= Page.ImageBrowser.Album.Error.NotFound.Text|l10n|html></p>
+
+ <%else>
+
+ <%if album.sone.local>
+ <script language="javascript">
+
+ $(function() {
+ getTranslation("WebInterface.DefaultText.UploadImage.Title", function(text) {
+ $("#upload-image :input[name='title']").each(function() {
+ registerInputTextareaSwap(this, text, "title", false, true);
+ });
+ });
+ getTranslation("WebInterface.DefaultText.UploadImage.Description", function(text) {
+ $("#upload-image :input[name='description']").each(function() {
+ registerInputTextareaSwap(this, text, "description", true, false);
+ });
+ });
+ $("#upload-image label").hide();
+ getTranslation("WebInterface.DefaultText.CreateAlbum.Name", function(text) {
+ $("#create-album input[name='name']").each(function() {
+ registerInputTextareaSwap(this, text, "name", false, true);
+ });
+ });
+ getTranslation("WebInterface.DefaultText.CreateAlbum.Description", function(text) {
+ $("#create-album input[name='description']").each(function() {
+ registerInputTextareaSwap(this, text, "description", true, true);
+ });
+ });
+ $("#create-album label").hide();
+ getTranslation("WebInterface.DefaultText.EditAlbum.Title", function(text) {
+ $("#edit-album input[name='title']").each(function() {
+ registerInputTextareaSwap(this, text, "title", false, true);
+ });
+ });
+ getTranslation("WebInterface.DefaultText.EditAlbum.Description", function(text) {
+ $("#edit-album :input[name='description']").each(function() {
+ registerInputTextareaSwap(this, text, "description", true, false);
+ });
+ });
+ $("#edit-album label").hide();
+
+ hideAndShowBlock("div.edit-album", ".show-edit-album", ".hide-edit-album");
+ hideAndShowBlock("div.create-album", ".show-create-album", ".hide-create-album");
+ hideAndShowBlock("div.upload-image", ".show-upload-image", ".hide-upload-image");
+ hideAndShowBlock("div.delete-album", ".show-delete-album", ".hide-delete-album");
+
+ prepareAlbums();
+ prepareImages();
+ });
+ </script>
+ <%/if>
+
+ <h1 class="backlink"><%= Page.ImageBrowser.Album.Title|l10n|replace needle='{album}' replacementKey=album.title|html></h1>
+
+ <div class="backlinks">
+ <%foreach album.backlinks backlink backlinks>
+ <div class="backlink">
+ <a href="<% backlink.target|html>"><% backlink.name|html></a>
+ </div>
+ <%if ! backlinks.last>
+ <div class="separator">></div>
+ <%/if>
+ <%/foreach>
+ </div>
+
+ <p id="description"><% album.description|html></p>
+
+ <%if album.sone.local>
+ <div class="show-edit-album hidden toggle-link"><a class="small-link">» <%= Page.ImageBrowser.Album.Edit.Title|l10n|html></a></div>
+ <div class="hide-edit-album hidden toggle-link"><a class="small-link">« <%= Page.ImageBrowser.Album.Edit.Title|l10n|html></a></div>
+ <div class="edit-album">
+ <h2><%= Page.ImageBrowser.Album.Edit.Title|l10n|html></h2>
+
+ <form id="edit-album" action="editAlbum.html" method="post">
+ <input type="hidden" name="formPassword" value="<%formPassword|html>" />
+ <input type="hidden" name="album" value="<%album.id|html>" />
+
+ <%if ! album.images.empty>
+ <div>
+ <label for="album-image"><%= Page.ImageBrowser.Album.Label.AlbumImage|l10n|html></label>
+ <select name="album-image">
+ <option disabled="disabled"><%= Page.ImageBrowser.Album.AlbumImage.Choose|l10n|html></option>
+ <%foreach album.images image>
+ <option value="<% image.id|html>"<%if album.albumImage.id|match key=image.id> selected="selected"<%/if>><% image.title|html></option>
+ <%/foreach>
+ </select>
+ </div>
+ <%/if>
+ <div>
+ <label for="title"><%= Page.ImageBrowser.Album.Label.Title|l10n|html></label>
+ <input type="text" name="title" value="<%album.title|html>" />
+ </div>
+ <div>
+ <label for="description"><%= Page.ImageBrowser.Album.Label.Description|l10n|html></label>
+ <textarea name="description"><%album.description|html></textarea>
+ </div>
+ <button type="submit"><%= Page.ImageBrowser.Album.Button.Save|l10n|html></button>
+ </form>
+ </div>
+ <%/if>
+
+ <%include include/browseAlbums.html albums=album.albums>
+
+ <%if album.sone.local>
+ <div class="show-create-album hidden toggle-link"><a class="small-link">» <%= View.CreateAlbum.Title|l10n|html></a></div>
+ <div class="hide-create-album hidden toggle-link"><a class="small-link">« <%= View.CreateAlbum.Title|l10n|html></a></div>
+ <div class="create-album">
+ <%include include/createAlbum.html>
+ </div>
+ <%/if>
+
+ <%foreach album.images image>
+ <%first><h2><%= Page.ImageBrowser.Header.Images|l10n|html></h2><%/first>
+ <%if loop.count|mod divisor=3><div class="image-row"><%/if>
+ <div id="image-<% image.id|html>" class="image">
+ <div class="image-id hidden"><% image.id|html></div>
+ <div class="image-container">
+ <a href="imageBrowser.html?image=<%image.id|html>"><% image|image-link max-width=250 max-height=250 mode=enlarge title==image.title></a>
+ </div>
+ <div class="show-data">
+ <div class="image-title"><% image.title|html></div>
+ <div class="image-description"><% image.description|html></div>
+ </div>
+ <%if album.sone.local>
+ <form class="edit-image" action="editImage.html" method="post">
+ <input type="hidden" name="formPassword" value="<%formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<%request.uri|html>" />
+ <input type="hidden" name="image" value="<%image.id|html>" />
+
+ <div class="edit-data hidden">
+ <div>
+ <input type="text" name="title" value="<%image.title|html>" />
+ </div>
+ <div>
+ <textarea name="description"><%image.description|html></textarea>
+ </div>
+ <div>
+ <button <%first>class="hidden" <%/first>type="submit" name="moveLeft" value="true"><%= Page.ImageBrowser.Image.Button.MoveLeft|l10n|html></button>
+ <button type="submit" name="submit"><%= Page.ImageBrowser.Image.Button.Save|l10n|html></button>
+ <button <%last>class="hidden" <%/last>type="submit" name="moveRight" value="true"><%= Page.ImageBrowser.Image.Button.MoveRight|l10n|html></button>
+ </div>
+ </div>
+ </form>
+ <%/if>
+ </div>
+ <%= false|store key=endRow>
+ <%if loop.count|mod divisor=3 offset=1><%= true|store key=endRow><%/if>
+ <%last><%= true|store key=endRow><%/last>
+ <%if endRow></div><%/if>
+ <%/foreach>
+
+ <%if album.sone.local>
+ <div class="show-upload-image hidden toggle-link"><a class="small-link">» <%= View.UploadImage.Title|l10n|html></a></div>
+ <div class="hide-upload-image hidden toggle-link"><a class="small-link">« <%= View.UploadImage.Title|l10n|html></a></div>
+ <div class="upload-image">
+ <%include include/uploadImage.html>
+ </div>
+
+ <%if album.empty>
+ <div class="show-delete-album hidden toggle-link"><a class="small-link">» <%= Page.ImageBrowser.Album.Delete.Title|l10n|html></a></div>
+ <div class="hide-delete-album hidden toggle-link"><a class="small-link">« <%= Page.ImageBrowser.Album.Delete.Title|l10n|html></a></div>
+ <div class="delete-album">
+ <form id="delete-album" action="deleteAlbum.html" method="get">
+ <input type="hidden" name="album" value="<%album.id|html>" />
+ <button type="submit"><%= Page.ImageBrowser.Album.Button.Delete|l10n|html></button>
+ </form>
+ </div>
+ <%/if>
+
+ <%/if>
+
+ <%/if>
+
+ <%elseif imageRequested>
+
+ <h1 class="backlink"><%image.title|html></h1>
+
+ <div class="backlinks">
+ <%foreach image.album.backlinks backlink backlinks>
+ <div class="backlink">
+ <a href="<% backlink.target|html>"><% backlink.name|html></a>
+ </div>
+ <%if ! backlinks.last>
+ <div class="separator">></div>
+ <%/if>
+ <%/foreach>
+ </div>
+
+ <%ifnull image>
+
+ <%else>
+
+ <%if image.sone.local>
+ <script language="javascript">
+ $(function() {
+ getTranslation("WebInterface.DefaultText.EditImage.Title", function(text) {
+ $("#edit-image input[name='title']").each(function() {
+ registerInputTextareaSwap(this, text, "title", false, true);
+ });
+ });
+ getTranslation("WebInterface.DefaultText.EditImage.Description", function(text) {
+ $("#edit-image :input[name='description']").each(function() {
+ registerInputTextareaSwap(this, text, "description", true, false);
+ });
+ });
+ $("#edit-image label").hide();
+
+ hideAndShowBlock(".edit-image", ".show-edit-image", ".hide-edit-image");
+ hideAndShowBlock(".delete-image", ".show-delete-image", ".hide-delete-image");
+ });
+ </script>
+ <%/if>
+
+ <div class="single-image">
+ <%ifnull !image.key>
+ <a href="/<%image.key|html>"><% image|image-link max-width=640 max-height=480></a>
+ <%else>
+ <a href="imageBrowser.html?image=<%image.id|html>"><% image|image-link max-width=640 max-height=480></a>
+ <%/if>
+ </div>
+
+ <p class="parsed"><%image.description|parse sone=image.sone></p>
+
+ <%if image.sone.local>
+
+ <div class="show-edit-image hidden toggle-link"><a class="small-link">» <%= Page.ImageBrowser.Image.Edit.Title|l10n|html></a></div>
+ <div class="hide-edit-image hidden toggle-link"><a class="small-link">« <%= Page.ImageBrowser.Image.Edit.Title|l10n|html></a></div>
+ <div class="edit-image">
+ <h2><%= Page.ImageBrowser.Image.Edit.Title|l10n|html></h2>
+
+ <form id="edit-image" action="editImage.html" method="post">
+ <input type="hidden" name="formPassword" value="<%formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<%request.uri|html>" />
+ <input type="hidden" name="image" value="<%image.id|html>" />
+
+ <div>
+ <label for="title"><%= Page.ImageBrowser.Image.Title.Label|l10n|html></label>
+ <input type="text" name="title" value="<%image.title|html>" />
+ </div>
+ <div>
+ <label for="description"><%= Page.ImageBrowser.Image.Description.Label|l10n|html></label>
+ <textarea name="description"><%image.description|html></textarea>
+ </div>
+ <div>
+ <button type="submit"><%= Page.ImageBrowser.Image.Button.Save|l10n|html></button>
+ </div>
+ </form>
+ </div>
+
+ <div class="show-delete-image hidden toggle-link"><a class="small-link">» <%= Page.ImageBrowser.Image.Delete.Title|l10n|html></a></div>
+ <div class="hide-delete-image hidden toggle-link"><a class="small-link">« <%= Page.ImageBrowser.Image.Delete.Title|l10n|html></a></div>
+ <div class="delete-image">
+ <h2><%= Page.ImageBrowser.Image.Delete.Title|l10n|html></h2>
+
+ <form id="delete-image" action="deleteImage.html" method="get">
+ <input type="hidden" name="image" value="<%image.id|html>" />
+ <button type="submit"><%= Page.ImageBrowser.Image.Button.Delete|l10n|html></button>
+ </form>
+ </div>
+
+ <%/if>
+
+ <%/if>
+
+ <%elseif soneRequested>
+
+ <%if sone.local>
+ <script language="javascript">
+ $(function() {
+ getTranslation("WebInterface.DefaultText.CreateAlbum.Name", function(text) {
+ $("#create-album input[name='name']").each(function() {
+ registerInputTextareaSwap(this, text, "name", false, true);
+ });
+ });
+ getTranslation("WebInterface.DefaultText.CreateAlbum.Description", function(text) {
+ $("#create-album input[name='description']").each(function() {
+ registerInputTextareaSwap(this, text, "description", true, true);
+ });
+ });
+ $("#create-album label").hide();
+
+ hideAndShowBlock(".create-album", ".show-create-album", ".hide-create-album");
+
+ prepareAlbums();
+ });
+ </script>
+ <%/if>
+
+ <%ifnull sone>
+
+ <p><%= Page.ImageBrowser.Sone.Error.NotFound.Text|l10n|html></p>
+
+ <%else>
+
+ <h1><%= Page.ImageBrowser.Sone.Title|l10n|replace needle='{sone}' replacementKey=sone.niceName|html></h1>
+
+ <%include include/browseAlbums.html albums=sone.albums>
+
+ <%if sone.local>
+ <div class="show-create-album hidden toggle-link"><a class="small-link">» <%= View.CreateAlbum.Title|l10n|html></a></div>
+ <div class="hide-create-album hidden toggle-link"><a class="small-link">« <%= View.CreateAlbum.Title|l10n|html></a></div>
+ <div class="create-album">
+ <%include include/createAlbum.html>
+ </div>
+ <%/if>
+
+ <%/if>
+
+ <%/if>
+
+<%include include/tail.html>
--- /dev/null
+<%foreach albums album>
+ <%first><h2><%= Page.ImageBrowser.Header.Albums|l10n|html></h2><%/first>
+ <%if loop.count|mod divisor=3><div class="album-row"><%/if>
+ <div id="album-<% album.id|html>" class="album">
+ <div class="album-id hidden"><% album.id|html></div>
+ <div class="album-container">
+ <a href="imageBrowser.html?album=<% album.id|html>" title="<% album.title|html>">
+ <%ifnull album.albumImage>
+ <img src="images/unknown-image-0.png" width="333" height="250" alt="<% album.title|html>" title="<% album.title|html>" style="position: relative; top: 0px; left: -41px;" />
+ <%else><!-- TODO -->
+ <% album.albumImage|image-link max-width=250 max-height=250 mode=enlarge title==album.title>
+ <%/if>
+ </a>
+ </div>
+ <div class="show-data">
+ <div class="album-title"><% album.title|html></div>
+ <div class="album-description"><% album.description|html></div>
+ </div>
+ <%if album.sone.local>
+ <form class="edit-album" action="editAlbum.html" method="post">
+ <input type="hidden" name="formPassword" value="<%formPassword|html>" />
+ <input type="hidden" name="returnPage" value="<%request.uri|html>" />
+ <input type="hidden" name="album" value="<%album.id|html>" />
+
+ <div class="edit-data hidden">
+ <div>
+ <input type="text" name="title" value="<%album.title|html>" />
+ </div>
+ <div>
+ <textarea name="description"><%album.description|html></textarea>
+ </div>
+ <div>
+ <button <%first>class="hidden" <%/first>type="submit" name="moveLeft" value="true"><%= Page.ImageBrowser.Image.Button.MoveLeft|l10n|html></button>
+ <button type="submit" name="submit"><%= Page.ImageBrowser.Album.Button.Save|l10n|html></button>
+ <button <%last>class="hidden" <%/last>type="submit" name="moveRight" value="true"><%= Page.ImageBrowser.Image.Button.MoveRight|l10n|html></button>
+ </div>
+ </div>
+ </form>
+ <%/if>
+ </div>
+ <%= false|store key=endRow>
+ <%if loop.count|mod divisor=3 offset=1><%= true|store key=endRow><%/if>
+ <%last><%= true|store key=endRow><%/last>
+ <%if endRow></div><%/if>
+<%/foreach>
--- /dev/null
+<h2><%= View.CreateAlbum.Title|l10n|html></h2>
+
+<form id="create-album" method="post" action="createAlbum.html">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="parent" value="<%ifnull ! album><% album.id|html><%/if>" />
+ <label for="album"><%= View.CreateAlbum.Label.Name|l10n|html></label>
+ <input type="text" name="name" value="" />
+ <label for="description"><%= View.CreateAlbum.Label.Description|l10n|html></label>
+ <input type="text" name="description" value="" />
+ <button type="submit" name="uploadImage" value="1"><%= Page.ImageBrowser.CreateAlbum.Button.CreateAlbum|l10n|html></button>
+</form>
<div class="sone-menu <% class|css|html>">
- <div class="sone-id hidden"><%sone.id|html></div>
- <img class="avatar" src="/WebOfTrust/GetIdenticon?identity=<%sone.id|html>&width=64&height=64" width="64" height="64" alt="Avatar Image" />
+ <div class="sone-menu-id hidden"><%sone.id|html></div>
+ <img class="avatar" src="/WebOfTrust/GetIdenticon?identity=<%sone.id|html>&width=64&height=64" width="64" height="64" alt="Avatar Image" />
<div class="inner-menu">
<div>
<a class="author" href="viewSone.html?sone=<%sone.id|html>"><%sone.niceName|html></a>
- <span class="author-wot-link">(<a href="/WebOfTrust/ShowIdentity?id=<%sone.id|html>"><% =View.Post.WebOfTrustLink|l10n|html></a>)</span>
+ (<%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size>)
</div>
- <div><%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size></div>
+ <div><a href="/WebOfTrust/ShowIdentity?id=<%sone.id|html>">» <% =View.Post.WebOfTrustLink|l10n|html></a></div>
+ <%foreach sone.albums album>
+ <%first>
+ <div><a href="imageBrowser.html?sone=<% sone.id|html>">» <% =View.SoneMenu.Link.AllAlbums|l10n|html></a></div>
+ <%/first>
+ <%/foreach>
<%if !sone.local>
<div>
- <a class="follow<%if sone.friend> hidden<%/if>"><%= View.Sone.Button.FollowSone|l10n|html></a>
- <a class="unfollow<%if !sone.friend> hidden<%/if>"><%= View.Sone.Button.UnfollowSone|l10n|html></a>
+ <button class="follow<%if sone.friend> hidden<%/if>"><%= View.Sone.Button.FollowSone|l10n|html></button>
+ <button class="unfollow<%if !sone.friend> hidden<%/if>"><%= View.Sone.Button.UnfollowSone|l10n|html></buton>
</div>
<%/if>
</div>
--- /dev/null
+<h2><%= View.UploadImage.Title|l10n|html></h2>
+
+<form id="upload-image" method="post" action="uploadImage.html" enctype="multipart/form-data">
+ <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+ <input type="hidden" name="parent" value="<% album.id|html>" />
+ <label for="title"><%= View.UploadImage.Label.Title|l10n|html></label>
+ <input type="text" name="title" value="" />
+ <label for="description"><%= View.UploadImage.Label.Description|l10n|html></label>
+ <textarea name="description"></textarea>
+ <input type="file" name="image" />
+ <button type="submit" name="uploadImage" value="1"><%= View.UploadImage.Button.UploadImage|l10n|html></button>
+</form>
-<div id="<% post.id|html>" class="post<%if loop.last> last<%/if><%if post.new> new<%/if>">
+<div id="<% post.id|html>" class="post<%if loop.last> last<%/if><%if !post.sone.local><%if post.new> new<%/if><%/if>">
<a name="post-<% post.id|html>"></a>
<div class="post-time hidden"><% post.time|html></div>
<div class="post-author hidden"><% post.sone.id|html></div>
-<div id="<% reply.id|html>" class="reply<%if reply.new> new<%/if>">
+<div id="<% reply.id|html>" class="reply<%if !reply.sone.local><%if reply.new> new<%/if><%/if>">
<a name="reply-<% reply.id|html>"></a>
<div class="reply-time hidden"><% reply.time|html></div>
<div class="reply-author hidden"><% reply.sone.id|html></div>
--- /dev/null
+<album>
+ <id><% album.id|xml></id>
+ <name><% album.name|xml></name>
+ <description><% album.description|xml></description>
+ <albums>
+ <%foreach album.albums album>
+ <%include insert/include/album.xml>
+ <%/foreach>
+ </albums>
+ <images>
+ <%foreach album.images image>
+ <image>
+ <id><% image.id|xml></id>
+ <creation-time><% image.creationTime|xml></creation-time>
+ <key><% image.key|xml></key>
+ <width><% image.width|xml></width>
+ <height><% image.height|xml></height>
+ <title><% image.title|xml></title>
+ <description><% image.description|xml></description>
+ </image>
+ <%/foreach>
+ </images>
+</album>
<%/foreach>
</reply-likes>
+ <%foreach currentSone.albums album>
+ <%first>
+ <albums>
+ <%/first>
+ <album>
+ <id><%album.id|xml></id>
+ <%ifnull !album.parent>
+ <parent><%album.parent.id|xml></parent>
+ <%/if>
+ <title><%album.title|xml></title>
+ <description><%album.description|xml></description>
+ <album-image><%album.albumImage.id|xml></album-image>
+ <%foreach album.images image>
+ <%first>
+ <images>
+ <%/first>
+ <image>
+ <id><%image.id|xml></id>
+ <creation-time><%image.creationTime|xml></creation-time>
+ <key><%image.key|xml></key>
+ <title><%image.title|xml></title>
+ <description><%image.description|xml></description>
+ <width><%image.width|xml></width>
+ <height><%image.height|xml></height>
+ </image>
+ <%last>
+ </images>
+ <%/last>
+ <%/foreach>
+ </album>
+ <%last>
+ </albums>
+ <%/last>
+ <%/foreach>
+
</sone>
<h1><%= Page.Invalid.Page.Title|l10n|html></h1>
- <p><%= Page.Invalid.Text|l10n|html|replace needle="{link}" replacement='<a href="index.html">'|replace needle="{/link}" replacement='</a>'></p>
+ <%foreach messages message>
+ <%if message|substring start=0 length=1|match value='!'>
+ <p class="error"><% message|substring start=1|parse></p>
+ <%else>
+ <p><% message|parse></p>
+ <%/if>
+ <%foreachelse>
+ <p><%= Page.Invalid.Text|l10n|html|replace needle="{link}" replacement='<a href="index.html">'|replace needle="{/link}" replacement='</a>'></p>
+ <%/foreach>
<%include include/tail.html>
--- /dev/null
+<div class="text">
+ <%= Notification.ImageInsertFailed.Text|l10n|html>
+ <%foreach images image>
+ <a href="imageBrowser.html?image=<%image.id|html>" title="<%image.title|html>"><%image.title|html></a><%notlast>,<%/notlast><%last>.<%/last>
+ <%/foreach>
+</div>
--- /dev/null
+<div class="text">
+ <%= Notification.InsertedImages.Text|l10n|html>
+ <%foreach images image>
+ <a href="imageBrowser.html?image=<%image.id|html>" title="<%image.title|html>"><%image.title|html></a><%notlast>,<%/notlast><%last>.<%/last>
+ <%/foreach>
+</div>
--- /dev/null
+<div class="text">
+ <%= Notification.InsertingImages.Text|l10n|html>
+ <%foreach images image>
+ <a href="imageBrowser.html?image=<%image.id|html>" title="<%image.title|html>"><%image.title|html></a><%notlast>,<%/notlast><%last>.<%/last>
+ <%/foreach>
+</div>
<div class="value"><% sone.niceName|html> (<a href="/WebOfTrust/ShowIdentity?id=<% sone.id|html>"><%= Page.ViewSone.Profile.Name.WoTLink|l10n|html></a>)</div>
</div>
+ <%foreach sone.albums album>
+ <%first>
+ <div class="profile-field">
+ <div class="name"><%= Page.ViewSone.Profile.Label.Albums|l10n|html></div>
+ <div class="value">
+ <a href="imageBrowser.html?sone=<% sone.id|html>"><% =Page.ViewSone.Profile.Albums.Text.All|l10n|html></a>,
+ <%/first>
+ <a href="imageBrowser.html?album=<%album.id|html>"><%album.title|html></a><%notlast>, <%/notlast>
+ <%last>
+ </div>
+ </div>
+ <%/last>
+ <%/foreach>
+
<%foreach sone.profile.fields field>
<div class="profile-field">
<div class="name"><% field.name|html></div>
import java.io.StringReader;
import junit.framework.TestCase;
+import net.pterodactylus.sone.core.SoneProvider;
+import net.pterodactylus.sone.data.Sone;
/**
* JUnit test case for {@link SoneTextParser}.
assertEquals("Part Text", "Link is [KSK@gpl.txt|gpl.txt|gpl.txt]\n[KSK@test.dat|test.dat|test.dat]", convertText(parts, PlainTextPart.class, FreenetLinkPart.class));
}
+ /**
+ * Test case for a bug that was discovered in 0.6.7.
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ */
+ @SuppressWarnings("synthetic-access")
+ public void testEmptyLinesAndSoneLinks() throws IOException {
+ SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
+ Iterable<Part> parts;
+
+ /* check basic links. */
+ parts = soneTextParser.parse(null, new StringReader("Some text.\n\nLink to sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU and stuff."));
+ assertNotNull("Parts", parts);
+ assertEquals("Part Text", "Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff.", convertText(parts, PlainTextPart.class, SonePart.class));
+ }
+
//
// PRIVATE METHODS
//
} else if (part instanceof LinkPart) {
LinkPart linkPart = (LinkPart) part;
text.append('[').append(linkPart.getLink()).append('|').append(linkPart.getTitle()).append('|').append(linkPart.getText()).append(']');
+ } else if (part instanceof SonePart) {
+ SonePart sonePart = (SonePart) part;
+ text.append("[Sone|").append(sonePart.getSone().getId()).append(']');
}
}
return text.toString();
}
+ /**
+ * Mock Sone provider.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ private static class TestSoneProvider implements SoneProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Sone getSone(final String soneId, boolean create) {
+ return new Sone(soneId) {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return soneId;
+ }
+ };
+ }
+
+ }
+
}