Merge branch 'next' into image-management
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 22 Mar 2011 18:46:01 +0000 (19:46 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 22 Mar 2011 18:46:01 +0000 (19:46 +0100)
Conflicts:
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/data/Sone.java
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/resources/i18n/sone.en.properties

15 files changed:
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/data/Album.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/data/Image.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/data/Sone.java
src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/resources/i18n/sone.en.properties
src/main/resources/static/javascript/sone.js
src/main/resources/templates/createAlbum.html [new file with mode: 0644]
src/main/resources/templates/imageBrowser.html [new file with mode: 0644]
src/main/resources/templates/include/createAlbum.html [new file with mode: 0644]
src/main/resources/templates/insert/include/album.xml [new file with mode: 0644]
src/main/resources/templates/insert/sone.xml

index 7dc398e..d61211d 100644 (file)
@@ -31,7 +31,9 @@ import java.util.logging.Logger;
 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;
@@ -165,6 +167,12 @@ public class Core implements IdentityListener, UpdateListener {
        /** 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>();
+
        /**
         * Creates a new core.
         *
@@ -722,6 +730,76 @@ public class Core implements IdentityListener, UpdateListener {
                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;
+               }
+       }
+
        //
        // ACTIONS
        //
@@ -1605,6 +1683,40 @@ public class Core implements IdentityListener, UpdateListener {
        }
 
        /**
+        * 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);
+               }
+               sone.addAlbum(album);
+               return album;
+       }
+
+       /**
         * Starts the core.
         */
        public void start() {
diff --git a/src/main/java/net/pterodactylus/sone/data/Album.java b/src/main/java/net/pterodactylus/sone/data/Album.java
new file mode 100644 (file)
index 0000000..4f52f50
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * 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.List;
+import java.util.UUID;
+
+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 images in this album. */
+       private final List<Image> images = new ArrayList<Image>();
+
+       /** The parent album. */
+       private Album parent;
+
+       /** The name of this album. */
+       private String name;
+
+       /** The description of this album. */
+       private String description;
+
+       /**
+        * 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().isNull("Current Album Owner", this.sone).isNotNull("New Album Owner", 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).isNull("Album Parent", album.parent).check();
+               albums.add(album);
+               album.setParent(this);
+       }
+
+       /**
+        * 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();
+       }
+
+       /**
+        * Returns the images in this album.
+        *
+        * @return The images in this album
+        */
+       public List<Image> getImages() {
+               return new ArrayList<Image>(images);
+       }
+
+       /**
+        * Adds the given image to this album.
+        *
+        * @param image
+        *            The image to add
+        */
+       public void addImage(Image image) {
+               Validation.begin().isNotNull("Image", image).check().isEqual("Image Owner", image.getSone(), sone).check();
+               images.add(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();
+               images.remove(image);
+       }
+
+       /**
+        * 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() {
+               Validation.begin().isNotNull("Album Parent", parent).check();
+               this.parent = null;
+               return this;
+       }
+
+       /**
+        * Returns the name of this album.
+        *
+        * @return The name of this album
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Sets the name of this album.
+        *
+        * @param name
+        *            The name of this album
+        * @return This album
+        */
+       public Album setName(String name) {
+               Validation.begin().isNotNull("Album Name", name).check();
+               this.name = name;
+               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("Name(").append(name).append(')');
+               fingerprint.append("Description(").append(description).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 : images) {
+                       fingerprint.append(image.getFingerprint());
+               }
+               fingerprint.append(')');
+
+               fingerprint.append(')');
+               return fingerprint.toString();
+       }
+
+       //
+       // OBJECT METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public boolean equals(Object object) {
+               if (!(object instanceof Album)) {
+                       return false;
+               }
+               Album album = (Album) object;
+               return id.equals(album.id);
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/data/Image.java b/src/main/java/net/pterodactylus/sone/data/Image.java
new file mode 100644 (file)
index 0000000..587c15e
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * 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 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());
+       }
+
+       /**
+        * 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().isNull("Current Image Owner", this.sone).isNotNull("New Image Owner", sone);
+               this.sone = sone;
+               return this;
+       }
+
+       /**
+        * Returns the key of this image.
+        *
+        * @return The key of this image
+        */
+       public String getKey() {
+               return key;
+       }
+
+       /**
+        * Sets the key of this image. The key can only be set as long as no key has
+        * yet been set.
+        *
+        * @param key
+        *            The new key of this image
+        * @return This image
+        */
+       public Image setKey(String key) {
+               Validation.begin().isNull("Current Image Key", this.key).isNotNull("New Image Key", key).check();
+               this.key = key;
+               return this;
+       }
+
+       /**
+        * 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().isEqual("Current Image Creation Time", this.creationTime, 0).isGreater("New Image Creation Time", creationTime, 0).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().isEqual("Current Image Width", this.width, 0).isGreater("New Image Width", width, 0).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().isEqual("Current Image Height", this.height, 0).isGreater("New Image Height", height, 0);
+               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();
+       }
+
+}
index cc54697..23bbb3e 100644 (file)
@@ -30,6 +30,7 @@ import java.util.logging.Logger;
 import net.pterodactylus.sone.freenet.wot.Identity;
 import net.pterodactylus.sone.template.SoneAccessor;
 import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.validation.Validation;
 import freenet.keys.FreenetURI;
 
 /**
@@ -99,6 +100,9 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
        /** 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>());
+
        /**
         * Creates a new Sone.
         *
@@ -580,6 +584,37 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
                return this;
        }
 
+       /**
+        * 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);
+       }
+
+       /**
+        * 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);
+       }
+
        //
        // FINGERPRINTABLE METHODS
        //
@@ -622,6 +657,12 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
                }
                fingerprint.append(')');
 
+               fingerprint.append("Albums(");
+               for (Album album : albums) {
+                       fingerprint.append(album.getFingerprint());
+               }
+               fingerprint.append(')');
+
                return fingerprint.toString();
        }
 
diff --git a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java
new file mode 100644 (file)
index 0000000..5042995
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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=" + album.getId(), album.getName()));
+                               currentAlbum = currentAlbum.getParent();
+                       }
+                       backlinks.add(0, createLink("viewSone.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;
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java
new file mode 100644 (file)
index 0000000..7b51fb5
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.Page.Request.Method;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * 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(Request request, TemplateContext templateContext) throws RedirectException {
+               super.processTemplate(request, templateContext);
+               if (request.getMethod() == Method.POST) {
+                       String name = request.getHttpRequest().getPartAsStringFailsafe("name", 64).trim();
+                       if (name.length() == 0) {
+                               templateContext.set("nameMissing", true);
+                               return;
+                       }
+                       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.setName(name);
+                       throw new RedirectException("imageBrowser.html?album=" + album.getId());
+               }
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java
new file mode 100644 (file)
index 0000000..6da7cda
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.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(Request request, TemplateContext templateContext) throws RedirectException {
+               super.processTemplate(request, templateContext);
+               String albumId = request.getHttpRequest().getParam("album", null);
+               if (albumId != null) {
+                       Album album = webInterface.getCore().getAlbum(albumId, false);
+                       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);
+               }
+       }
+}
index 89cec92..e3a95d4 100644 (file)
@@ -36,6 +36,7 @@ import java.util.logging.Logger;
 
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.core.CoreListener;
+import net.pterodactylus.sone.data.Album;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
@@ -44,6 +45,7 @@ import net.pterodactylus.sone.freenet.wot.Identity;
 import net.pterodactylus.sone.freenet.wot.Trust;
 import net.pterodactylus.sone.main.SonePlugin;
 import net.pterodactylus.sone.notify.ListNotification;
+import net.pterodactylus.sone.template.AlbumAccessor;
 import net.pterodactylus.sone.template.CollectionAccessor;
 import net.pterodactylus.sone.template.CssClassNameFilter;
 import net.pterodactylus.sone.template.GetPagePlugin;
@@ -187,6 +189,7 @@ public class WebInterface implements CoreListener {
                templateContextFactory.addAccessor(Sone.class, new SoneAccessor(getCore()));
                templateContextFactory.addAccessor(Post.class, new PostAccessor(getCore()));
                templateContextFactory.addAccessor(Reply.class, new ReplyAccessor(getCore()));
+               templateContextFactory.addAccessor(Album.class, new AlbumAccessor());
                templateContextFactory.addAccessor(Identity.class, new IdentityAccessor(getCore()));
                templateContextFactory.addAccessor(NotificationManager.class, new NotificationManagerAccessor());
                templateContextFactory.addAccessor(Trust.class, new TrustAccessor());
@@ -531,6 +534,8 @@ public class WebInterface implements CoreListener {
                Template deletePostTemplate = TemplateParser.parse(createReader("/templates/deletePost.html"));
                Template deleteReplyTemplate = TemplateParser.parse(createReader("/templates/deleteReply.html"));
                Template deleteSoneTemplate = TemplateParser.parse(createReader("/templates/deleteSone.html"));
+               Template imageBrowserTemplate = TemplateParser.parse(createReader("/templates/imageBrowser.html"));
+               Template createAlbumTemplate = TemplateParser.parse(createReader("/templates/createAlbum.html"));
                Template noPermissionTemplate = TemplateParser.parse(createReader("/templates/noPermission.html"));
                Template optionsTemplate = TemplateParser.parse(createReader("/templates/options.html"));
                Template aboutTemplate = TemplateParser.parse(createReader("/templates/about.html"));
@@ -557,6 +562,8 @@ public class WebInterface implements CoreListener {
                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 TrustPage(emptyTemplate, this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustPage(emptyTemplate, this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustPage(emptyTemplate, this)));
index 9b48cad..3f64f51 100644 (file)
@@ -12,6 +12,8 @@ Navigation.Menu.Item.Bookmarks.Name=Bookmarks
 Navigation.Menu.Item.Bookmarks.Tooltip=Show bookmarked posts
 Navigation.Menu.Item.EditProfile.Name=Edit Profile
 Navigation.Menu.Item.EditProfile.Tooltip=Edit the Profile of your Sone
+Navigation.Menu.Item.ImageBrowser.Name=Images
+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
@@ -154,6 +156,15 @@ Page.FollowSone.Title=Follow Sone - Sone
 
 Page.UnfollowSone.Title=Unfollow Sone - Sone
 
+Page.ImageBrowser.Title=Image Browser - Sone
+Page.ImageBrowser.Page.Title=Image Browser
+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.CreateAlbum.Button.CreateAlbum=Create Album
+
+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.Trust.Title=Trust Sone - Sone
 
 Page.Distrust.Title=Distrust Sone - Sone
@@ -221,6 +232,9 @@ View.Trust.Tooltip.Trust=Trust this person
 View.Trust.Tooltip.Distrust=Assign negative trust to this person
 View.Trust.Tooltip.Untrust=Remove your trust assignment for this person
 
+View.CreateAlbum.Title=Create Album
+View.CreateAlbum.Label.Name=Name:
+
 WebInterface.DefaultText.StatusUpdate=What’s on your mind?
 WebInterface.DefaultText.Message=Write a Message…
 WebInterface.DefaultText.Reply=Write a Reply…
index 9ef6812..b6b1257 100644 (file)
@@ -1342,6 +1342,13 @@ $(document).ready(function() {
                });
        });
 
+       /* ajaxify album creation input field. */
+       getTranslation("WebInterface.DefaultText.Reply", function(text) {
+               $("#create-album input[type=text]").each(function() {
+                       registerInputTextareaSwap(this, text, "name", false, true);
+               });
+       });
+
        /* Ajaxifies all posts. */
        /* calling getTranslation here will cache the necessary values. */
        getTranslation("WebInterface.Confirmation.DeletePostButton", function(text) {
diff --git a/src/main/resources/templates/createAlbum.html b/src/main/resources/templates/createAlbum.html
new file mode 100644 (file)
index 0000000..7df8103
--- /dev/null
@@ -0,0 +1,11 @@
+<%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>
diff --git a/src/main/resources/templates/imageBrowser.html b/src/main/resources/templates/imageBrowser.html
new file mode 100644 (file)
index 0000000..ca2014d
--- /dev/null
@@ -0,0 +1,60 @@
+<%include include/head.html>
+
+       <div class="page-id hidden">image-browser</div>
+
+       <h1><%= Page.ImageBrowser.Page.Title|l10n|html></h1>
+
+       <%if albumRequested>
+
+               <%ifnull album>
+
+                       <p><%= Page.ImageBrowser.Album.Error.NotFound.Text|l10n|html></p>
+
+               <%elseifnull album.name>
+
+                       <p><%= Page.ImageBrowser.Album.Error.NotFound.Text|l10n|html></p>
+
+               <%else>
+
+                       <h2><% album.name|html></h2>
+
+                       <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">&gt;</div>
+                                       <%/if>
+                               <%/foreach>
+                       </div>
+
+                       <%foreach album.albums album>
+                               <div class="album">
+                                       <div class="name"><% album.name|html></div>
+                               </div>
+                       <%/foreach>
+
+                       <%include include/createAlbum.html>
+
+                       <div id="description">
+                               <% album.description|html>
+                       </div>
+
+               <%/if>
+
+       <%elseif imageRequested>
+
+       <%else>
+
+               <%foreach currentSone.albums album>
+                       <div class="album">
+                               <div class="name"><% album.name|html></div>
+                       </div>
+               <%/foreach>
+
+               <%include include/createAlbum.html>
+
+       <%/if>
+
+<%include include/tail.html>
diff --git a/src/main/resources/templates/include/createAlbum.html b/src/main/resources/templates/include/createAlbum.html
new file mode 100644 (file)
index 0000000..6e3623f
--- /dev/null
@@ -0,0 +1,9 @@
+<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="" />
+       <button type="submit" name="uploadImage" value="1"><%= Page.ImageBrowser.CreateAlbum.Button.CreateAlbum|l10n|html></button>
+</form>
diff --git a/src/main/resources/templates/insert/include/album.xml b/src/main/resources/templates/insert/include/album.xml
new file mode 100644 (file)
index 0000000..cd845da
--- /dev/null
@@ -0,0 +1,23 @@
+<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>
index e25cac9..69f647d 100644 (file)
                <%/foreach>
        </reply-likes>
 
+       <albums>
+               <%foreach currentSone.albums album>
+               <%include insert/include/album.xml>
+               <%/foreach>
+       </albums>
+
 </sone>