From: David ‘Bombe’ Roden Date: Mon, 26 Sep 2011 20:17:49 +0000 (+0200) Subject: Merge branch 'release-0.7' X-Git-Tag: 0.7^0 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=38cb6c5ec82298ee351d0eb15ddd8331db273af2;hp=f399a0d5d29599c318a0f14f109a26a6667c1230 Merge branch 'release-0.7' --- diff --git a/pom.xml b/pom.xml index 037d3be..f494135 100644 --- a/pom.xml +++ b/pom.xml @@ -2,12 +2,12 @@ 4.0.0 net.pterodactylus sone - 0.6.7 + 0.7 net.pterodactylus utils - 0.10.0 + 0.11 junit diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index c8d7588..8aac5d7 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -34,12 +34,15 @@ 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; 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; @@ -67,7 +70,7 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -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}. @@ -116,6 +119,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** 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); @@ -182,6 +188,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** Trusted identities, sorted by own identities. */ private Map> trustedIdentities = Collections.synchronizedMap(new HashMap>()); + /** All known albums. */ + private Map albums = new HashMap(); + + /** All known images. */ + private Map images = new HashMap(); + + /** All temporary images. */ + private Map temporaryImages = new HashMap(); + /** Ticker for threads that mark own elements as known. */ private Ticker localElementTicker = new Ticker(); @@ -204,6 +219,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis this.freenetInterface = freenetInterface; this.identityManager = identityManager; this.soneDownloader = new SoneDownloader(this, freenetInterface); + this.imageInserter = new ImageInserter(this, freenetInterface); this.updateChecker = new UpdateChecker(freenetInterface); } @@ -800,6 +816,89 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis 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 // @@ -1124,6 +1223,22 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } } } + 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()); @@ -1143,11 +1258,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis 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()); } @@ -1323,6 +1442,65 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis friends.add(friendId); } + /* load albums. */ + List topLevelAlbums = new ArrayList(); + 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)); @@ -1336,6 +1514,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis sone.setLikePostIds(likedPostIds); sone.setLikeReplyIds(likedReplyIds); sone.setFriends(friends); + sone.setAlbums(topLevelAlbums); soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint); } synchronized (newSones) { @@ -1625,6 +1804,150 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } /** + * 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. @@ -1773,6 +2096,40 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null); + /* save albums. first, collect in a flat structure, top-level first. */ + List 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()); @@ -2125,12 +2482,13 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } // - // SONEINSERTLISTENER METHODS + // INTERFACE ImageInsertListener // /** * {@inheritDoc} */ + @Override public void insertStarted(Sone sone) { coreListenerManager.fireSoneInserting(sone); } @@ -2151,6 +2509,49 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis 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. diff --git a/src/main/java/net/pterodactylus/sone/core/CoreListener.java b/src/main/java/net/pterodactylus/sone/core/CoreListener.java index d5120ac..1658745 100644 --- a/src/main/java/net/pterodactylus/sone/core/CoreListener.java +++ b/src/main/java/net/pterodactylus/sone/core/CoreListener.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.core; 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; @@ -164,4 +165,38 @@ public interface CoreListener extends EventListener { */ 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); + } diff --git a/src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java b/src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java index 875a2b6..5748ffc 100644 --- a/src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java +++ b/src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java @@ -17,6 +17,7 @@ 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; @@ -246,4 +247,58 @@ public class CoreListenerManager extends AbstractListenerManagerDavid ‘Bombe’ Roden + */ + public class InsertToken implements ClientPutCallback { + + /** The image being inserted. */ + private final Image image; + + /** The list of registered image insert listeners. */ + private final List imageInsertListeners = Collections.synchronizedList(new ArrayList()); + + /** 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); + } + } + + } + } diff --git a/src/main/java/net/pterodactylus/sone/core/ImageInsertListener.java b/src/main/java/net/pterodactylus/sone/core/ImageInsertListener.java new file mode 100644 index 0000000..a0e8a56 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/ImageInsertListener.java @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + +} diff --git a/src/main/java/net/pterodactylus/sone/core/ImageInserter.java b/src/main/java/net/pterodactylus/sone/core/ImageInserter.java new file mode 100644 index 0000000..b4421ce --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/ImageInserter.java @@ -0,0 +1,104 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 insertTokens = Collections.synchronizedMap(new HashMap()); + + /** + * 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); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java index 0812f66..e8d452b 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java @@ -17,16 +17,19 @@ 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; @@ -198,8 +201,8 @@ public class SoneDownloader extends AbstractService { } } 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(); @@ -431,6 +434,64 @@ public class SoneDownloader extends AbstractService { } } + /* parse albums. */ + SimpleXML albumsXml = soneXml.getNode("albums"); + List topLevelAlbums = new ArrayList(); + 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) { @@ -439,6 +500,7 @@ public class SoneDownloader extends AbstractService { sone.setReplies(replies); sone.setLikePostIds(likedPostIds); sone.setLikeReplyIds(likedReplyIds); + sone.setAlbums(topLevelAlbums); } return sone; diff --git a/src/main/java/net/pterodactylus/sone/core/SoneException.java b/src/main/java/net/pterodactylus/sone/core/SoneException.java index c786661..271627e 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneException.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneException.java @@ -37,6 +37,9 @@ public class SoneException extends Exception { /** An invalid URI was specified. */ INVALID_URI, + /** An insert failed. */ + INSERT_FAILED, + } /** The type of the exception. */ diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index f654d09..44f8bbc 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -302,6 +302,7 @@ public class SoneInserter extends AbstractService { soneProperties.put("replies", new ListBuilder(new ArrayList(sone.getReplies())).sort(new ReverseComparator(Reply.TIME_COMPARATOR)).get()); soneProperties.put("likedPostIds", new HashSet(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet(sone.getLikedReplyIds())); + soneProperties.put("albums", Sone.flattenAlbums(sone.getAlbums())); } // 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 index 0000000..b15bc00 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/Album.java @@ -0,0 +1,464 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 albums = new ArrayList(); + + /** The image IDs in order. */ + private final List imageIds = new ArrayList(); + + /** The images in this album. */ + private final Map images = new HashMap(); + + /** 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 getAlbums() { + return new ArrayList(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 + * null 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 + * null 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 getImages() { + return Mappers.mappedList(imageIds, new Mapper() { + + @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 + * null 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 + * null 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); + } + +} 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 index 0000000..04eb349 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/Image.java @@ -0,0 +1,326 @@ +/* + * 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 . + */ + +package net.pterodactylus.sone.data; + +import java.util.UUID; + +import net.pterodactylus.util.validation.Validation; + +/** + * Container for image metadata. + * + * @author David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 3477346..e124387 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -34,6 +34,7 @@ import net.pterodactylus.sone.freenet.wot.OwnIdentity; 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; /** @@ -145,6 +146,9 @@ public class Sone implements Fingerprintable, Comparable { /** The IDs of all liked replies. */ private final Set likedReplyIds = Collections.synchronizedSet(new HashSet()); + /** The albums of this Sone. */ + private final List albums = Collections.synchronizedList(new ArrayList()); + /** Sone-specific options. */ private final Options options = new Options(); @@ -282,7 +286,7 @@ public class Sone implements Fingerprintable, Comparable { */ 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; @@ -632,6 +636,91 @@ public class Sone implements Fingerprintable, Comparable { } /** + * Returns the albums of this Sone. + * + * @return The albums of this Sone + */ + public List 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 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 + * null 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 + * null 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 @@ -682,10 +771,43 @@ public class Sone implements Fingerprintable, Comparable { } 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 flattenAlbums(Collection albums) { + List flatAlbums = new ArrayList(); + flatAlbums.addAll(albums); + int lastAlbumIndex = 0; + while (lastAlbumIndex < flatAlbums.size()) { + int previousAlbumCount = flatAlbums.size(); + for (Album album : new ArrayList(flatAlbums.subList(lastAlbumIndex, flatAlbums.size()))) { + flatAlbums.addAll(album.getAlbums()); + } + lastAlbumIndex = previousAlbumCount; + } + return flatAlbums; + } + + // // INTERFACE Comparable // diff --git a/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java b/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java new file mode 100644 index 0000000..ddac505 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java @@ -0,0 +1,113 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java index 59da039..de30a5d 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java @@ -70,6 +70,13 @@ public class WebOfTrustConnector implements ConnectorListener { // /** + * 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 diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index d642dd8..f21c1c2 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -83,7 +83,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr } /** 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); @@ -103,6 +103,9 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /** The l10n helper. */ private PluginL10n l10n; + /** The web of trust connector. */ + private WebOfTrustConnector webOfTrustConnector; + /** The identity manager. */ private IdentityManager identityManager; @@ -181,7 +184,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /* 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"); @@ -236,6 +239,9 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /* 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 { 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 index 0000000..2c0a00e --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +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> backlinks = new ArrayList>(); + 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 createLink(String target, String name) { + Map link = new HashMap(); + link.put("target", target); + link.put("name", name); + return link; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java b/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java index a25d619..b2a4962 100644 --- a/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java +++ b/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java @@ -19,9 +19,10 @@ package net.pterodactylus.sone.template; 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 @@ -50,7 +51,7 @@ public class GetPagePlugin implements Plugin { pageKey = "page"; } - Request request = (Request) templateContext.get(requestKey); + FreenetRequest request = (FreenetRequest) templateContext.get(requestKey); String pageString = request.getHttpRequest().getParam(parameter); int page = 0; try { diff --git a/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java new file mode 100644 index 0000000..9e758cf --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java @@ -0,0 +1,108 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class ImageLinkFilter implements Filter { + + /** The template to render for the <img> tag. */ + private static final Template linkTemplate = TemplateParser.parse(new StringReader(" 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 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(); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java index 332f504..3494c21 100644 --- a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java @@ -35,7 +35,7 @@ import net.pterodactylus.sone.text.PostPart; 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; @@ -108,7 +108,7 @@ public class ParserFilter implements Filter { 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 { diff --git a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java index fb31719..a0d80af 100644 --- a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java @@ -26,15 +26,15 @@ import java.util.HashMap; 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 David ‘Bombe’ Roden */ @@ -45,7 +45,7 @@ public class RequestChangeFilter implements Filter { */ @Override public Object format(TemplateContext templateContext, Object data, Map parameters) { - Request request = (Request) data; + FreenetRequest request = (FreenetRequest) data; String name = parameters.get("name"); String nameKey = parameters.get("nameKey"); if (nameKey != null) { diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index 4195516..051c02a 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -123,6 +123,11 @@ public class SoneTextParser implements Parser { 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@"); @@ -175,120 +180,111 @@ public class SoneTextParser implements Parser { 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; } diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java index 4a89016..35b190b 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java @@ -18,7 +18,7 @@ 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 @@ -30,7 +30,7 @@ import net.pterodactylus.sone.web.page.Page.Request; public class SoneTextParserContext implements ParserContext { /** The request being processed. */ - private final Request request; + private final FreenetRequest request; /** The posting Sone. */ private final Sone postingSone; @@ -43,7 +43,7 @@ public class SoneTextParserContext implements ParserContext { * @param postingSone * The posting Sone */ - public SoneTextParserContext(Request request, Sone postingSone) { + public SoneTextParserContext(FreenetRequest request, Sone postingSone) { this.request = request; this.postingSone = postingSone; } @@ -53,7 +53,7 @@ public class SoneTextParserContext implements ParserContext { * * @return The request being processed */ - public Request getRequest() { + public FreenetRequest getRequest() { return request; } diff --git a/src/main/java/net/pterodactylus/sone/web/AboutPage.java b/src/main/java/net/pterodactylus/sone/web/AboutPage.java index 4c14457..a9698e4 100644 --- a/src/main/java/net/pterodactylus/sone/web/AboutPage.java +++ b/src/main/java/net/pterodactylus/sone/web/AboutPage.java @@ -17,6 +17,7 @@ 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; @@ -54,7 +55,7 @@ public class AboutPage extends SoneTemplatePage { * {@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); } diff --git a/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java b/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java index 12a0934..0ebf9d5 100644 --- a/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java +++ b/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java @@ -17,9 +17,10 @@ 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. @@ -46,7 +47,7 @@ public class BookmarkPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java b/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java index d6f63aa..ad1717b 100644 --- a/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java +++ b/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java @@ -23,6 +23,7 @@ import java.util.List; 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; @@ -57,7 +58,7 @@ public class BookmarksPage extends SoneTemplatePage { * {@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 allPosts = webInterface.getCore().getBookmarkedPosts(); Set loadedPosts = Filters.filteredSet(allPosts, new Filter() { 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 index 0000000..a695952 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java @@ -0,0 +1,73 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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()); + } + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java index 147e4ae..22f6efa 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java @@ -20,9 +20,10 @@ package net.pterodactylus.sone.web; 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}. @@ -51,7 +52,7 @@ public class CreatePostPage extends SoneTemplatePage { * {@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) { diff --git a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java index 2b12352..f28be35 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java @@ -20,9 +20,10 @@ package net.pterodactylus.sone.web; 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. @@ -51,7 +52,7 @@ public class CreateReplyPage extends SoneTemplatePage { * {@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(); diff --git a/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java b/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java index 3f940a3..9878358 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java @@ -28,10 +28,11 @@ import java.util.logging.Logger; 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; /** @@ -94,7 +95,7 @@ public class CreateSonePage extends SoneTemplatePage { * {@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 ownIdentitiesWithoutSone = getOwnIdentitiesWithoutSone(webInterface.getCore()); templateContext.set("identitiesWithoutSone", ownIdentitiesWithoutSone); diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java new file mode 100644 index 0000000..d03e065 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java @@ -0,0 +1,78 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java b/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java new file mode 100644 index 0000000..66098ff --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java @@ -0,0 +1,73 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java index 7a36d02..c8fd20f 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java @@ -18,9 +18,10 @@ 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. @@ -49,7 +50,7 @@ public class DeletePostPage extends SoneTemplatePage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java index 82f2ace..030279b 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java @@ -20,9 +20,10 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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. @@ -51,7 +52,7 @@ public class DeleteProfileFieldPage extends SoneTemplatePage { * {@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(); diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java index 782589f..226e0d3 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java @@ -18,9 +18,10 @@ 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. @@ -49,7 +50,7 @@ public class DeleteReplyPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java b/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java index 21979b8..bbf533f 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java @@ -18,9 +18,10 @@ 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 @@ -51,7 +52,7 @@ public class DeleteSonePage extends SoneTemplatePage { * {@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")) { diff --git a/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java b/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java index 15c5675..9d79195 100644 --- a/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java @@ -17,6 +17,7 @@ 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; @@ -48,7 +49,7 @@ public class DismissNotificationPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/DistrustPage.java b/src/main/java/net/pterodactylus/sone/web/DistrustPage.java index 37a792c..473bf1c 100644 --- a/src/main/java/net/pterodactylus/sone/web/DistrustPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DistrustPage.java @@ -19,9 +19,10 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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 @@ -52,7 +53,7 @@ public class DistrustPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java new file mode 100644 index 0000000..fd4bf7c --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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()); + } + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/EditImagePage.java b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java new file mode 100644 index 0000000..7e9eb7e --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java @@ -0,0 +1,83 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java b/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java index 219bdc5..a06592a 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java @@ -20,9 +20,10 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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. @@ -51,7 +52,7 @@ public class EditProfileFieldPage extends SoneTemplatePage { * {@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(); diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java index 43dd15b..59c7e3e 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java @@ -22,10 +22,11 @@ import java.util.List; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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; /** @@ -55,7 +56,7 @@ public class EditProfilePage extends SoneTemplatePage { * {@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); @@ -153,7 +154,7 @@ public class EditProfilePage extends SoneTemplatePage { * @return The parsed ID, or {@code null} if there was no part matching the * given string */ - private String getFieldId(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()); diff --git a/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java b/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java index 81225e9..143e0ba 100644 --- a/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java @@ -18,9 +18,10 @@ 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. @@ -47,7 +48,7 @@ public class FollowSonePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/GetImagePage.java b/src/main/java/net/pterodactylus/sone/web/GetImagePage.java new file mode 100644 index 0000000..29556c9 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/GetImagePage.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class GetImagePage implements Page { + + /** 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()); + } + +} 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 index 0000000..ed31283 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java @@ -0,0 +1,79 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/IndexPage.java b/src/main/java/net/pterodactylus/sone/web/IndexPage.java index 35b88a9..356b4ab 100644 --- a/src/main/java/net/pterodactylus/sone/web/IndexPage.java +++ b/src/main/java/net/pterodactylus/sone/web/IndexPage.java @@ -24,6 +24,7 @@ import java.util.List; 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; @@ -57,7 +58,7 @@ public class IndexPage extends SoneTemplatePage { * {@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 allPosts = new ArrayList(); diff --git a/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java b/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java index ad63791..e5acad5 100644 --- a/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java +++ b/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java @@ -22,6 +22,7 @@ import java.util.Collections; 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; @@ -57,7 +58,7 @@ public class KnownSonesPage extends SoneTemplatePage { * {@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"); @@ -107,4 +108,5 @@ public class KnownSonesPage extends SoneTemplatePage { templateContext.set("pagination", sonePagination); templateContext.set("knownSones", sonePagination.getItems()); } + } diff --git a/src/main/java/net/pterodactylus/sone/web/LikePage.java b/src/main/java/net/pterodactylus/sone/web/LikePage.java index 56f55ef..c5174c6 100644 --- a/src/main/java/net/pterodactylus/sone/web/LikePage.java +++ b/src/main/java/net/pterodactylus/sone/web/LikePage.java @@ -19,9 +19,10 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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}. @@ -50,7 +51,7 @@ public class LikePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/LockSonePage.java b/src/main/java/net/pterodactylus/sone/web/LockSonePage.java index d09de66..a56d263 100644 --- a/src/main/java/net/pterodactylus/sone/web/LockSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/LockSonePage.java @@ -18,6 +18,7 @@ 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; @@ -49,7 +50,7 @@ public class LockSonePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/LoginPage.java b/src/main/java/net/pterodactylus/sone/web/LoginPage.java index 8e612ea..6f43b6f 100644 --- a/src/main/java/net/pterodactylus/sone/web/LoginPage.java +++ b/src/main/java/net/pterodactylus/sone/web/LoginPage.java @@ -24,10 +24,11 @@ import java.util.logging.Logger; 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; /** @@ -61,7 +62,7 @@ public class LoginPage extends SoneTemplatePage { * {@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 localSones = new ArrayList(webInterface.getCore().getLocalSones()); @@ -87,7 +88,7 @@ public class LoginPage extends SoneTemplatePage { * {@inheritDoc} */ @Override - protected String getRedirectTarget(Request request) { + protected String getRedirectTarget(FreenetRequest request) { if (getCurrentSone(request.getToadletContext(), false) != null) { return "index.html"; } diff --git a/src/main/java/net/pterodactylus/sone/web/LogoutPage.java b/src/main/java/net/pterodactylus/sone/web/LogoutPage.java index 7cd0587..a7f769f 100644 --- a/src/main/java/net/pterodactylus/sone/web/LogoutPage.java +++ b/src/main/java/net/pterodactylus/sone/web/LogoutPage.java @@ -17,6 +17,7 @@ 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; @@ -46,7 +47,7 @@ public class LogoutPage extends SoneTemplatePage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java b/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java index 9f91c84..987fa1f 100644 --- a/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java +++ b/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java @@ -22,6 +22,7 @@ import java.util.StringTokenizer; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; @@ -53,7 +54,7 @@ public class MarkAsKnownPage extends SoneTemplatePage { * {@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")) { diff --git a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java index fda6afd..75e73a0 100644 --- a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java +++ b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java @@ -23,10 +23,11 @@ import java.util.List; 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. @@ -55,7 +56,7 @@ public class OptionsPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/RescuePage.java b/src/main/java/net/pterodactylus/sone/web/RescuePage.java index 40b03f1..f653615 100644 --- a/src/main/java/net/pterodactylus/sone/web/RescuePage.java +++ b/src/main/java/net/pterodactylus/sone/web/RescuePage.java @@ -19,10 +19,11 @@ package net.pterodactylus.sone.web; 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. @@ -52,7 +53,7 @@ public class RescuePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/SearchPage.java b/src/main/java/net/pterodactylus/sone/web/SearchPage.java index fab7a57..91e2a08 100644 --- a/src/main/java/net/pterodactylus/sone/web/SearchPage.java +++ b/src/main/java/net/pterodactylus/sone/web/SearchPage.java @@ -32,6 +32,7 @@ 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.web.page.FreenetRequest; import net.pterodactylus.util.cache.Cache; import net.pterodactylus.util.cache.CacheException; import net.pterodactylus.util.cache.CacheItem; @@ -96,7 +97,7 @@ public class SearchPage extends SoneTemplatePage { * {@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) { diff --git a/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java b/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java index b7e36f4..095db9e 100644 --- a/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java +++ b/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java @@ -27,8 +27,8 @@ import java.util.Map; 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; @@ -202,7 +202,7 @@ public class SoneTemplatePage extends FreenetTemplatePage { * {@inheritDoc} */ @Override - protected String getPageTitle(Request request) { + protected String getPageTitle(FreenetRequest request) { if (pageTitleKey != null) { return webInterface.getL10n().getString(pageTitleKey); } @@ -213,7 +213,7 @@ public class SoneTemplatePage extends FreenetTemplatePage { * {@inheritDoc} */ @Override - protected List> getAdditionalLinkNodes(Request request) { + protected List> getAdditionalLinkNodes(FreenetRequest request) { return new ListBuilder>().add(new MapBuilder().put("rel", "search").put("type", "application/opensearchdescription+xml").put("title", "Sone").put("href", "http://" + request.getHttpRequest().getHeader("host") + "/Sone/OpenSearch.xml").get()).get(); } @@ -247,7 +247,7 @@ public class SoneTemplatePage extends FreenetTemplatePage { * {@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()); @@ -266,7 +266,7 @@ public class SoneTemplatePage extends FreenetTemplatePage { * {@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(); diff --git a/src/main/java/net/pterodactylus/sone/web/TrustPage.java b/src/main/java/net/pterodactylus/sone/web/TrustPage.java index b0dadef..f2cf92e 100644 --- a/src/main/java/net/pterodactylus/sone/web/TrustPage.java +++ b/src/main/java/net/pterodactylus/sone/web/TrustPage.java @@ -19,9 +19,10 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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 @@ -52,7 +53,7 @@ public class TrustPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java b/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java index 60165b2..def3bf4 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java @@ -20,9 +20,10 @@ package net.pterodactylus.sone.web; import java.util.Set; import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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. @@ -49,7 +50,7 @@ public class UnbookmarkPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java b/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java index 33b88a1..d8e53ce 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java @@ -18,9 +18,10 @@ 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. @@ -47,7 +48,7 @@ public class UnfollowSonePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/UnlikePage.java b/src/main/java/net/pterodactylus/sone/web/UnlikePage.java index 24ff3ca..f47c4f2 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnlikePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnlikePage.java @@ -19,9 +19,10 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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}. @@ -50,7 +51,7 @@ public class UnlikePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java b/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java index ccb5959..f527558 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java @@ -18,6 +18,7 @@ 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; @@ -48,7 +49,7 @@ public class UnlockSonePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/UntrustPage.java b/src/main/java/net/pterodactylus/sone/web/UntrustPage.java index c5e7dec..d711525 100644 --- a/src/main/java/net/pterodactylus/sone/web/UntrustPage.java +++ b/src/main/java/net/pterodactylus/sone/web/UntrustPage.java @@ -19,9 +19,10 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.Page.Request.Method; +import net.pterodactylus.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 @@ -52,7 +53,7 @@ public class UntrustPage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java new file mode 100644 index 0000000..e0aa1e1 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java @@ -0,0 +1,161 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 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"; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java b/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java index b62e19c..4cc9864 100644 --- a/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web; 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; @@ -49,7 +50,7 @@ public class ViewPostPage extends SoneTemplatePage { * {@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 = ""; @@ -65,7 +66,7 @@ public class ViewPostPage extends SoneTemplatePage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java b/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java index 889c74c..5d463c0 100644 --- a/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java @@ -29,6 +29,7 @@ import net.pterodactylus.sone.data.Post; 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; @@ -61,7 +62,7 @@ public class ViewSonePage extends SoneTemplatePage { * {@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)) { @@ -75,7 +76,7 @@ public class ViewSonePage extends SoneTemplatePage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 08202d4..283e156 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -37,6 +37,8 @@ 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.Image; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; @@ -45,10 +47,12 @@ 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.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; @@ -71,6 +75,8 @@ import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage; import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage; import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage; import net.pterodactylus.sone.web.ajax.DistrustAjaxPage; +import net.pterodactylus.sone.web.ajax.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; @@ -90,11 +96,9 @@ import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage; import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage; import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage; import net.pterodactylus.sone.web.ajax.UntrustAjaxPage; +import net.pterodactylus.sone.web.page.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; @@ -113,6 +117,7 @@ import net.pterodactylus.util.template.DateFilter; 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; @@ -125,10 +130,13 @@ import net.pterodactylus.util.template.TemplateParser; 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; @@ -191,6 +199,15 @@ public class WebInterface implements CoreListener { /** The “new version” notification. */ private final TemplateNotification newVersionNotification; + /** The “inserting images” notification. */ + private final ListNotification insertingImagesNotification; + + /** The “inserted images” notification. */ + private final ListNotification insertedImagesNotification; + + /** The “image insert failed” notification. */ + private final ListNotification imageInsertFailedNotification; + /** * Creates a new web interface. * @@ -209,6 +226,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(Trust.class, new TrustAccessor()); templateContextFactory.addAccessor(HTTPRequest.class, new HttpRequestAccessor()); @@ -227,9 +245,11 @@ public class WebInterface implements CoreListener { 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); @@ -259,6 +279,15 @@ public class WebInterface implements CoreListener { 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("inserting-images-notification", "images", insertingImagesTemplate); + + Template insertedImagesTemplate = TemplateParser.parse(createReader("/templates/notify/inserted-images-notification.html")); + insertedImagesNotification = new ListNotification("inserted-images-notification", "images", insertedImagesTemplate); + + Template imageInsertFailedTemplate = TemplateParser.parse(createReader("/templates/notify/image-insert-failed-notification.html")); + imageInsertFailedNotification = new ListNotification("image-insert-failed-notification", "images", imageInsertFailedTemplate); } // @@ -559,6 +588,10 @@ 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 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")); @@ -569,7 +602,7 @@ public class WebInterface implements CoreListener { 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("", "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")); @@ -588,6 +621,13 @@ 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 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))); @@ -605,10 +645,11 @@ public class WebInterface implements CoreListener { 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("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 GetImagePage(this))); pageToadlets.add(pageToadletFactory.createPageToadlet(new GetTranslationPage(this))); pageToadlets.add(pageToadletFactory.createPageToadlet(new GetStatusAjaxPage(this))); pageToadlets.add(pageToadletFactory.createPageToadlet(new GetNotificationAjaxPage(this))); @@ -625,6 +666,8 @@ public class WebInterface implements CoreListener { 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))); @@ -771,7 +814,7 @@ public class WebInterface implements CoreListener { } 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); } @@ -911,6 +954,43 @@ public class WebInterface implements CoreListener { } /** + * {@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. diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java index 760bd34..0cf7b01 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java @@ -18,6 +18,7 @@ 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; /** @@ -45,7 +46,7 @@ public class BookmarkAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java index 0fc1236..c6f4455 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.data.Post; 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; /** @@ -44,7 +45,7 @@ public class CreatePostAjaxPage extends JsonPage { * {@inheritDoc} */ @Override - protected JsonObject createJsonObject(Request request) { + protected JsonObject createJsonObject(FreenetRequest request) { Sone sone = getCurrentSone(request.getToadletContext()); if (sone == null) { return createErrorJsonObject("auth-required"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java index ce78f58..9f5c882 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java @@ -22,6 +22,7 @@ import net.pterodactylus.sone.data.Reply; 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; /** @@ -49,7 +50,7 @@ public class CreateReplyAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java index 8d3b414..37ab040 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web.ajax; 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; /** @@ -46,7 +47,7 @@ public class DeletePostAjaxPage extends JsonPage { * {@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)) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java index 4625c13..205fb85 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -44,7 +45,7 @@ public class DeleteProfileFieldAjaxPage extends JsonPage { * {@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(); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java index e12b2cf..7bea4cf 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web.ajax; 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; /** @@ -46,7 +47,7 @@ public class DeleteReplyAjaxPage extends JsonPage { * {@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) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java index 08e3ee5..232cab6 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java @@ -18,6 +18,7 @@ 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; @@ -42,7 +43,7 @@ public class DismissNotificationAjaxPage extends JsonPage { * {@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) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java index ca770e4..4ba3dbb 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -45,7 +46,7 @@ public class DistrustAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java new file mode 100644 index 0000000..53f0466 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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()); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java new file mode 100644 index 0000000..17d171b --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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()); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java index 0036545..5351281 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -48,7 +49,7 @@ public class EditProfileFieldAjaxPage extends JsonPage { * {@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(); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java index d82ab1d..5c3e5f4 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web.ajax; 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; /** @@ -42,7 +43,7 @@ public class FollowSoneAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java index a8b991d..539be3d 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java @@ -27,6 +27,7 @@ import net.pterodactylus.sone.data.Reply; 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; @@ -55,7 +56,7 @@ public class GetLikesAjaxPage extends JsonPage { * {@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)) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationAjaxPage.java index 8bfe472..dfac8e4 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationAjaxPage.java @@ -27,6 +27,7 @@ import net.pterodactylus.sone.main.SonePlugin; 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; @@ -75,12 +76,16 @@ public class GetNotificationAjaxPage extends JsonPage { */ @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) notification, currentSone, false); } else if ("new-reply-notification".equals(notificationId)) { @@ -110,7 +115,7 @@ public class GetNotificationAjaxPage extends JsonPage { * 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(); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java index f56c5b7..8be816d 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java @@ -22,6 +22,7 @@ import java.io.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; @@ -56,7 +57,7 @@ public class GetPostAjaxPage extends JsonPage { * {@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) { @@ -89,7 +90,7 @@ public class GetPostAjaxPage extends JsonPage { * 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()); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java index 6cc7d47..2eef58a 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java @@ -22,6 +22,7 @@ import java.io.StringWriter; 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; @@ -59,7 +60,7 @@ public class GetReplyAjaxPage extends JsonPage { * {@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)) { @@ -91,7 +92,7 @@ public class GetReplyAjaxPage extends JsonPage { * 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()); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java index f704905..eaa6506 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java @@ -31,6 +31,7 @@ import net.pterodactylus.sone.data.Sone; 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; @@ -62,7 +63,7 @@ public class GetStatusAjaxPage extends JsonPage { * {@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")); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java index f03e6e4..1711a5b 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java @@ -24,6 +24,7 @@ import java.util.Date; 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; @@ -51,7 +52,7 @@ public class GetTimesAjaxPage extends JsonPage { * {@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) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java index 725b13a..e0909ca 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java @@ -18,6 +18,7 @@ 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; /** @@ -45,7 +46,7 @@ public class GetTranslationPage extends JsonPage { * {@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); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java index b027ab8..1e5e8ed 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java @@ -17,11 +17,15 @@ 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; @@ -31,7 +35,7 @@ import freenet.clients.http.ToadletContext; * * @author David ‘Bombe’ Roden */ -public abstract class JsonPage implements Page { +public abstract class JsonPage implements Page { /** The path of the page. */ private final String path; @@ -124,7 +128,7 @@ public abstract class JsonPage implements Page { * 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 @@ -187,23 +191,31 @@ public abstract class JsonPage implements Page { * {@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)); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java index bf87ead..e4d168f 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java @@ -20,6 +20,7 @@ package net.pterodactylus.sone.web.ajax; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -43,7 +44,7 @@ public class LikeAjaxPage extends JsonPage { * {@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)) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java index bca9a35..1d80905 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java @@ -20,6 +20,7 @@ package net.pterodactylus.sone.web.ajax; 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; /** @@ -43,7 +44,7 @@ public class LockSoneAjaxPage extends JsonPage { * {@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) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java index b641ea0..42ee8b2 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java @@ -22,6 +22,7 @@ import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -46,7 +47,7 @@ public class MarkAsKnownAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java index 092c1f7..b932a82 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -50,7 +51,7 @@ public class MoveProfileFieldAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java index d6e2750..b27bedf 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -45,7 +46,7 @@ public class TrustAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java index f07dcb0..6a9f8fe 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java @@ -18,6 +18,7 @@ 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; /** @@ -45,7 +46,7 @@ public class UnbookmarkAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java index 7f719b7..26f1a11 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web.ajax; 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; /** @@ -42,7 +43,7 @@ public class UnfollowSoneAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java index 84b0593..1841806 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java @@ -20,6 +20,7 @@ package net.pterodactylus.sone.web.ajax; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -43,7 +44,7 @@ public class UnlikeAjaxPage extends JsonPage { * {@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)) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java index 3160db0..d7430c1 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java @@ -20,6 +20,7 @@ package net.pterodactylus.sone.web.ajax; 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; /** @@ -43,7 +44,7 @@ public class UnlockSoneAjaxPage extends JsonPage { * {@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) { diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java index ad613ff..86222e0 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.sone.web.WebInterface; +import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; /** @@ -45,7 +46,7 @@ public class UntrustAjaxPage extends JsonPage { * {@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"); diff --git a/src/main/java/net/pterodactylus/sone/web/page/FreenetRequest.java b/src/main/java/net/pterodactylus/sone/web/page/FreenetRequest.java new file mode 100644 index 0000000..526f3ed --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/page/FreenetRequest.java @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java b/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java index 5831a1b..5c027e5 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java +++ b/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.web.page; +import java.io.IOException; import java.io.StringWriter; import java.util.Collection; import java.util.Collections; @@ -26,11 +27,14 @@ import java.util.Map.Entry; 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; @@ -43,7 +47,7 @@ import freenet.support.HTMLNode; * * @author David ‘Bombe’ Roden */ -public class FreenetTemplatePage implements Page, LinkEnabledCallback { +public class FreenetTemplatePage implements Page, LinkEnabledCallback { /** The logger. */ private static final Logger logger = Logging.getLogger(FreenetTemplatePage.class); @@ -95,7 +99,7 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { * The request to serve * @return The title of the page */ - protected String getPageTitle(Request request) { + protected String getPageTitle(FreenetRequest request) { return null; } @@ -103,14 +107,22 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { * {@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) { @@ -153,7 +165,7 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { 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()); } /** @@ -186,16 +198,15 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { * @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 @@ -203,7 +214,7 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { * @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. */ } @@ -215,7 +226,7 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { * 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; } @@ -226,7 +237,7 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { * The request for which to return the link nodes * @return All link nodes that should be added to the HTML head */ - protected List> getAdditionalLinkNodes(Request request) { + protected List> getAdditionalLinkNodes(FreenetRequest request) { return Collections.emptyList(); } @@ -256,7 +267,7 @@ public class FreenetTemplatePage implements Page, LinkEnabledCallback { /** * 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 David ‘Bombe’ Roden diff --git a/src/main/java/net/pterodactylus/sone/web/page/Page.java b/src/main/java/net/pterodactylus/sone/web/page/Page.java deleted file mode 100644 index 297081e..0000000 --- a/src/main/java/net/pterodactylus/sone/web/page/Page.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * 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 . - */ - -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 David ‘Bombe’ Roden - */ -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 David ‘Bombe’ Roden - */ - public class Request { - - /** - * Enumeration for all possible HTTP request methods. - * - * @author David ‘Bombe’ - * Roden - */ - 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 David ‘Bombe’ Roden - */ - 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 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(), 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 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 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 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 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 createHeader(String name, String value) { - Map headers = new HashMap(); - headers.put(name, value); - return headers; - } - - } - - /** - * {@link Response} implementation that performs an HTTP redirect. - * - * @author David ‘Bombe’ Roden - */ - 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)); - } - - } - -} diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java index f594e58..2d1f35a 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java +++ b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java @@ -19,9 +19,11 @@ package net.pterodactylus.sone.web.page; 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; @@ -30,7 +32,6 @@ import freenet.clients.http.ToadletContextClosedException; import freenet.support.MultiValueTable; import freenet.support.api.Bucket; import freenet.support.api.HTTPRequest; -import freenet.support.io.BucketTools; import freenet.support.io.Closer; /** @@ -44,7 +45,7 @@ public class PageToadlet extends Toadlet implements LinkEnabledCallback { private final String menuName; /** The page that handles processing. */ - private final Page page; + private final Page page; /** The path prefix for the page. */ private final String pathPrefix; @@ -62,7 +63,7 @@ public class PageToadlet extends Toadlet implements LinkEnabledCallback { * 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 page, String pathPrefix) { super(highLevelSimpleClient); this.menuName = menuName; this.page = page; @@ -101,7 +102,7 @@ public class PageToadlet extends Toadlet implements LinkEnabledCallback { * 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)); } /** @@ -119,7 +120,7 @@ public class PageToadlet extends Toadlet implements LinkEnabledCallback { * 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)); } /** @@ -140,32 +141,25 @@ public class PageToadlet extends Toadlet implements LinkEnabledCallback { * @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 headers = new MultiValueTable(); if (pageResponse.getHeaders() != null) { - for (Entry 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); } } diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java b/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java index bd0adb9..0abf3a5 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java +++ b/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java @@ -17,12 +17,13 @@ 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 David ‘Bombe’ Roden */ public class PageToadletFactory { @@ -35,7 +36,7 @@ public class PageToadletFactory { /** * Creates a new {@link PageToadlet} factory. - * + * * @param highLevelSimpleClient * The client to use when creating the toadlets * @param pathPrefix @@ -49,26 +50,26 @@ public class PageToadletFactory { /** * 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 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 page, String menuName) { return new PageToadlet(highLevelSimpleClient, menuName, page, pathPrefix); } diff --git a/src/main/java/net/pterodactylus/sone/web/page/RedirectPage.java b/src/main/java/net/pterodactylus/sone/web/page/RedirectPage.java deleted file mode 100644 index 2ce34f9..0000000 --- a/src/main/java/net/pterodactylus/sone/web/page/RedirectPage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 . - */ - -package net.pterodactylus.sone.web.page; - -/** - * Page implementation that redirects the user to another URL. - * - * @author David ‘Bombe’ Roden - */ -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); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/web/page/StaticPage.java b/src/main/java/net/pterodactylus/sone/web/page/StaticPage.java deleted file mode 100644 index 3d24fdd..0000000 --- a/src/main/java/net/pterodactylus/sone/web/page/StaticPage.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 . - */ - -package net.pterodactylus.sone.web.page; - -import java.io.InputStream; - -/** - * {@link Page} implementation that delivers static files from the class path. - * - * @author David ‘Bombe’ Roden - */ -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); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/web/page/TemplatePage.java b/src/main/java/net/pterodactylus/sone/web/page/TemplatePage.java deleted file mode 100644 index 06fc9fc..0000000 --- a/src/main/java/net/pterodactylus/sone/web/page/TemplatePage.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 . - */ - -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 David ‘Bombe’ Roden - */ -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); - } - -} diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties index 739c615..75f9d41 100644 --- a/src/main/resources/i18n/sone.en.properties +++ b/src/main/resources/i18n/sone.en.properties @@ -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 @@ -147,6 +149,8 @@ Page.ViewSone.PostList.Title=Posts by {sone} Page.ViewSone.PostList.Text.NoPostYet=This Sone has not yet posted anything. Page.ViewSone.Profile.Title=Profile Page.ViewSone.Profile.Label.Name=Name +Page.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 @@ -178,6 +182,54 @@ Page.FollowSone.Title=Follow Sone - Sone 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 @@ -250,6 +302,8 @@ View.Sone.Status.Idle=This Sone is idle, i.e. not being inserted or downloaded. 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 @@ -272,6 +326,15 @@ 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: +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 @@ -300,12 +363,20 @@ WebInterface.DefaultText.BirthMonth=Month WebInterface.DefaultText.BirthYear=Year WebInterface.DefaultText.FieldName=Field name WebInterface.DefaultText.Option.InsertionDelay=Time to wait after a Sone is modified before insert (in seconds) +WebInterface.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… @@ -332,6 +403,9 @@ Notification.SoneRescued.Text=The following Sones have been rescued: Notification.SoneRescued.Text.RememberToUnlock=Please remember to control the posts and replies you have given and don’t forget to unlock your Sones! Notification.LockedSones.Text=The following Sones have been locked for more than 5 minutes. Please check if you really want to keep these Sones locked: Notification.NewVersion.Text=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 + +

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

+ + <%if nameMissing> +

<%= Page.CreateAlbum.Error.NameMissing|l10n|html>

+ <%/if> + + <%include include/createAlbum.html> + +<%include include/tail.html> diff --git a/src/main/resources/templates/deleteAlbum.html b/src/main/resources/templates/deleteAlbum.html new file mode 100644 index 0000000..cb27c19 --- /dev/null +++ b/src/main/resources/templates/deleteAlbum.html @@ -0,0 +1,14 @@ +<%include include/head.html> + +

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

+ +

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

+ +
+ + + + +
+ +<%include include/tail.html> diff --git a/src/main/resources/templates/deleteImage.html b/src/main/resources/templates/deleteImage.html new file mode 100644 index 0000000..68b403e --- /dev/null +++ b/src/main/resources/templates/deleteImage.html @@ -0,0 +1,14 @@ +<%include include/head.html> + +

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

+ +

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

+ +
+ + + + +
+ +<%include include/tail.html> diff --git a/src/main/resources/templates/imageBrowser.html b/src/main/resources/templates/imageBrowser.html new file mode 100644 index 0000000..d97fc25 --- /dev/null +++ b/src/main/resources/templates/imageBrowser.html @@ -0,0 +1,570 @@ +<%include include/head.html> + + + + + + <%if albumRequested> + + <%ifnull album> + +

<%= Page.ImageBrowser.Album.Error.NotFound.Text|l10n|html>

+ + <%elseifnull album.title> + +

<%= Page.ImageBrowser.Album.Error.NotFound.Text|l10n|html>

+ + <%else> + + <%if album.sone.local> + + <%/if> + +

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

+ + + +

<% album.description|html>

+ + <%if album.sone.local> + + +
+

<%= Page.ImageBrowser.Album.Edit.Title|l10n|html>

+ +
+ + + + <%if ! album.images.empty> +
+ + +
+ <%/if> +
+ + +
+
+ + +
+ +
+
+ <%/if> + + <%include include/browseAlbums.html albums=album.albums> + + <%if album.sone.local> + + +
+ <%include include/createAlbum.html> +
+ <%/if> + + <%foreach album.images image> + <%first>

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

<%/first> + <%if loop.count|mod divisor=3>
<%/if> +
+ + +
+
<% image.title|html>
+
<% image.description|html>
+
+ <%if album.sone.local> +
+ + + + + +
+ <%/if> +
+ <%= false|store key=endRow> + <%if loop.count|mod divisor=3 offset=1><%= true|store key=endRow><%/if> + <%last><%= true|store key=endRow><%/last> + <%if endRow>
<%/if> + <%/foreach> + + <%if album.sone.local> + + +
+ <%include include/uploadImage.html> +
+ + <%if album.empty> + + +
+
+ + +
+
+ <%/if> + + <%/if> + + <%/if> + + <%elseif imageRequested> + +

<%image.title|html>

+ + + + <%ifnull image> + + <%else> + + <%if image.sone.local> + + <%/if> + + + +

<%image.description|parse sone=image.sone>

+ + <%if image.sone.local> + + + +
+

<%= Page.ImageBrowser.Image.Edit.Title|l10n|html>

+ +
+ + + + +
+ + +
+
+ + +
+
+ +
+
+
+ + + +
+

<%= Page.ImageBrowser.Image.Delete.Title|l10n|html>

+ +
+ + +
+
+ + <%/if> + + <%/if> + + <%elseif soneRequested> + + <%if sone.local> + + <%/if> + + <%ifnull sone> + +

<%= Page.ImageBrowser.Sone.Error.NotFound.Text|l10n|html>

+ + <%else> + +

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

+ + <%include include/browseAlbums.html albums=sone.albums> + + <%if sone.local> + + +
+ <%include include/createAlbum.html> +
+ <%/if> + + <%/if> + + <%/if> + +<%include include/tail.html> diff --git a/src/main/resources/templates/include/browseAlbums.html b/src/main/resources/templates/include/browseAlbums.html new file mode 100644 index 0000000..b77bcd1 --- /dev/null +++ b/src/main/resources/templates/include/browseAlbums.html @@ -0,0 +1,45 @@ +<%foreach albums album> + <%first>

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

<%/first> + <%if loop.count|mod divisor=3>
<%/if> +
+ + +
+
<% album.title|html>
+
<% album.description|html>
+
+ <%if album.sone.local> +
+ + + + + +
+ <%/if> +
+ <%= false|store key=endRow> + <%if loop.count|mod divisor=3 offset=1><%= true|store key=endRow><%/if> + <%last><%= true|store key=endRow><%/last> + <%if endRow>
<%/if> +<%/foreach> diff --git a/src/main/resources/templates/include/createAlbum.html b/src/main/resources/templates/include/createAlbum.html new file mode 100644 index 0000000..4887199 --- /dev/null +++ b/src/main/resources/templates/include/createAlbum.html @@ -0,0 +1,11 @@ +

<%= View.CreateAlbum.Title|l10n|html>

+ +
+ + + + + + + +
diff --git a/src/main/resources/templates/include/soneMenu.html b/src/main/resources/templates/include/soneMenu.html index 49237ba..a1dd944 100644 --- a/src/main/resources/templates/include/soneMenu.html +++ b/src/main/resources/templates/include/soneMenu.html @@ -1,16 +1,21 @@
- - Avatar Image + + Avatar Image
<%sone.niceName|html> - (<% =View.Post.WebOfTrustLink|l10n|html>) + (<%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size>)
-
<%= View.Sone.Stats.Posts|l10n 0=sone.posts.size>, <%= View.Sone.Stats.Replies|l10n 0=sone.replies.size>
+ + <%foreach sone.albums album> + <%first> + + <%/first> + <%/foreach> <%if !sone.local>
- <%= View.Sone.Button.FollowSone|l10n|html> - <%= View.Sone.Button.UnfollowSone|l10n|html> + +
<%/if>
diff --git a/src/main/resources/templates/include/uploadImage.html b/src/main/resources/templates/include/uploadImage.html new file mode 100644 index 0000000..fcee2f2 --- /dev/null +++ b/src/main/resources/templates/include/uploadImage.html @@ -0,0 +1,12 @@ +

<%= View.UploadImage.Title|l10n|html>

+ +
+ + + + + + + + +
diff --git a/src/main/resources/templates/include/viewPost.html b/src/main/resources/templates/include/viewPost.html index ec1c299..89a8ba5 100644 --- a/src/main/resources/templates/include/viewPost.html +++ b/src/main/resources/templates/include/viewPost.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/main/resources/templates/include/viewReply.html b/src/main/resources/templates/include/viewReply.html index abf3463..ee7299e 100644 --- a/src/main/resources/templates/include/viewReply.html +++ b/src/main/resources/templates/include/viewReply.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/main/resources/templates/insert/include/album.xml b/src/main/resources/templates/insert/include/album.xml new file mode 100644 index 0000000..cd845da --- /dev/null +++ b/src/main/resources/templates/insert/include/album.xml @@ -0,0 +1,23 @@ + + <% album.id|xml> + <% album.name|xml> + <% album.description|xml> + + <%foreach album.albums album> + <%include insert/include/album.xml> + <%/foreach> + + + <%foreach album.images image> + + <% image.id|xml> + <% image.creationTime|xml> + <% image.key|xml> + <% image.width|xml> + <% image.height|xml> + <% image.title|xml> + <% image.description|xml> + + <%/foreach> + + diff --git a/src/main/resources/templates/insert/sone.xml b/src/main/resources/templates/insert/sone.xml index e25cac9..2e4f298 100644 --- a/src/main/resources/templates/insert/sone.xml +++ b/src/main/resources/templates/insert/sone.xml @@ -60,4 +60,39 @@ <%/foreach> + <%foreach currentSone.albums album> + <%first> + + <%/first> + + <%album.id|xml> + <%ifnull !album.parent> + <%album.parent.id|xml> + <%/if> + <%album.title|xml> + <%album.description|xml> + <%album.albumImage.id|xml> + <%foreach album.images image> + <%first> + + <%/first> + + <%image.id|xml> + <%image.creationTime|xml> + <%image.key|xml> + <%image.title|xml> + <%image.description|xml> + <%image.width|xml> + <%image.height|xml> + + <%last> + + <%/last> + <%/foreach> + + <%last> + + <%/last> + <%/foreach> + diff --git a/src/main/resources/templates/invalid.html b/src/main/resources/templates/invalid.html index d838134..ea97d00 100644 --- a/src/main/resources/templates/invalid.html +++ b/src/main/resources/templates/invalid.html @@ -2,6 +2,14 @@

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

-

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

+ <%foreach messages message> + <%if message|substring start=0 length=1|match value='!'> +

<% message|substring start=1|parse>

+ <%else> +

<% message|parse>

+ <%/if> + <%foreachelse> +

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

+ <%/foreach> <%include include/tail.html> diff --git a/src/main/resources/templates/notify/image-insert-failed-notification.html b/src/main/resources/templates/notify/image-insert-failed-notification.html new file mode 100644 index 0000000..c4b3c67 --- /dev/null +++ b/src/main/resources/templates/notify/image-insert-failed-notification.html @@ -0,0 +1,6 @@ +
+ <%= Notification.ImageInsertFailed.Text|l10n|html> + <%foreach images image> + <%image.title|html><%notlast>,<%/notlast><%last>.<%/last> + <%/foreach> +
diff --git a/src/main/resources/templates/notify/inserted-images-notification.html b/src/main/resources/templates/notify/inserted-images-notification.html new file mode 100644 index 0000000..f388d59 --- /dev/null +++ b/src/main/resources/templates/notify/inserted-images-notification.html @@ -0,0 +1,6 @@ +
+ <%= Notification.InsertedImages.Text|l10n|html> + <%foreach images image> + <%image.title|html><%notlast>,<%/notlast><%last>.<%/last> + <%/foreach> +
diff --git a/src/main/resources/templates/notify/inserting-images-notification.html b/src/main/resources/templates/notify/inserting-images-notification.html new file mode 100644 index 0000000..efe930d --- /dev/null +++ b/src/main/resources/templates/notify/inserting-images-notification.html @@ -0,0 +1,6 @@ +
+ <%= Notification.InsertingImages.Text|l10n|html> + <%foreach images image> + <%image.title|html><%notlast>,<%/notlast><%last>.<%/last> + <%/foreach> +
diff --git a/src/main/resources/templates/viewSone.html b/src/main/resources/templates/viewSone.html index 55cc2a8..1d87e46 100644 --- a/src/main/resources/templates/viewSone.html +++ b/src/main/resources/templates/viewSone.html @@ -32,6 +32,20 @@
+ <%foreach sone.albums album> + <%first> +
+
<%= Page.ViewSone.Profile.Label.Albums|l10n|html>
+
+ <% =Page.ViewSone.Profile.Albums.Text.All|l10n|html>, + <%/first> + <%album.title|html><%notlast>, <%/notlast> + <%last> +
+
+ <%/last> + <%/foreach> + <%foreach sone.profile.fields field>
<% field.name|html>
diff --git a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java index 0343f05..d98d4ec 100644 --- a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java +++ b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java @@ -21,6 +21,8 @@ import java.io.IOException; 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}. @@ -85,6 +87,23 @@ public class SoneTextParserTest extends TestCase { 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 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 // @@ -122,9 +141,38 @@ public class SoneTextParserTest extends TestCase { } 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 David ‘Bombe’ Roden + */ + 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; + } + }; + } + + } + }