<dependency>
<groupId>net.pterodactylus</groupId>
<artifactId>utils</artifactId>
- <version>0.9.4-SNAPSHOT</version>
- <version>0.10.0</version>
++ <version>0.10.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
import net.pterodactylus.sone.core.Options.DefaultOption;
import net.pterodactylus.sone.core.Options.Option;
import net.pterodactylus.sone.core.Options.OptionWatcher;
+import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Client;
+import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Profile;
--import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.TemporaryImage;
++import net.pterodactylus.sone.data.Profile.Field;
+ import net.pterodactylus.sone.fcp.FcpInterface;
+ import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
import net.pterodactylus.sone.freenet.wot.Identity;
import net.pterodactylus.sone.freenet.wot.IdentityListener;
import net.pterodactylus.sone.freenet.wot.IdentityManager;
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
- public class Core implements IdentityListener, UpdateListener, ImageInsertListener {
-public class Core extends AbstractService implements IdentityListener, UpdateListener, SoneProvider, PostProvider, SoneInsertListener {
++public class Core extends AbstractService implements IdentityListener, UpdateListener, SoneProvider, PostProvider, SoneInsertListener, ImageInsertListener {
/**
* Enumeration for the possible states of a {@link Sone}.
/** The Sone downloader. */
private final SoneDownloader soneDownloader;
+ /** The image inserter. */
+ private final ImageInserter imageInserter;
+
+ /** Sone downloader thread-pool. */
+ private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10);
+
/** The update checker. */
private final UpdateChecker updateChecker;
/** Trusted identities, sorted by own identities. */
private Map<OwnIdentity, Set<Identity>> trustedIdentities = Collections.synchronizedMap(new HashMap<OwnIdentity, Set<Identity>>());
+ /** All known albums. */
+ private Map<String, Album> albums = new HashMap<String, Album>();
+
+ /** All known images. */
+ private Map<String, Image> images = new HashMap<String, Image>();
+
+ /** All temporary images. */
+ private Map<String, TemporaryImage> temporaryImages = new HashMap<String, TemporaryImage>();
+
+ /** Ticker for threads that mark own elements as known. */
+ private Ticker localElementTicker = new Ticker();
+
+ /** The time the configuration was last touched. */
+ private volatile long lastConfigurationUpdate;
+
/**
* Creates a new core.
*
friends.add(friendId);
}
+ /* load albums. */
+ List<Album> topLevelAlbums = new ArrayList<Album>();
+ int albumCounter = 0;
+ while (true) {
+ String albumPrefix = sonePrefix + "/Albums/" + albumCounter++;
+ String albumId = configuration.getStringValue(albumPrefix + "/ID").getValue(null);
+ if (albumId == null) {
+ break;
+ }
+ String albumTitle = configuration.getStringValue(albumPrefix + "/Title").getValue(null);
+ String albumDescription = configuration.getStringValue(albumPrefix + "/Description").getValue(null);
+ String albumParentId = configuration.getStringValue(albumPrefix + "/Parent").getValue(null);
+ 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);
+ 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().addBooleanOption("AutoFollow", new DefaultOption<Boolean>(false));
sone.getOptions().getBooleanOption("AutoFollow").set(configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").getValue(null));
+ sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").set(configuration.getBooleanValue(sonePrefix + "/Options/EnableSoneInsertNotifications").getValue(null));
/* if we’re still here, Sone was loaded successfully. */
synchronized (sone) {
}
/**
+ * 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.
+ */
+ public void touchConfiguration() {
+ lastConfigurationUpdate = System.currentTimeMillis();
+ }
+
+ //
+ // SERVICE METHODS
+ //
+
+ /**
* Starts the core.
*/
- public void start() {
+ @Override
+ public void serviceStart() {
loadConfiguration();
updateChecker.addUpdateListener(this);
updateChecker.start();
updateChecker.stop();
updateChecker.removeUpdateListener(this);
soneDownloader.stop();
- saveConfiguration();
- stopped = true;
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Saves the given Sone. This will persist all local settings for the given
+ * Sone, such as the friends list and similar, private options.
+ *
+ * @param sone
+ * The Sone to save
+ */
+ private synchronized void saveSone(Sone sone) {
+ if (!isLocalSone(sone)) {
+ logger.log(Level.FINE, "Tried to save non-local Sone: %s", sone);
+ return;
+ }
+ if (!(sone.getIdentity() instanceof OwnIdentity)) {
+ logger.log(Level.WARNING, "Local Sone without OwnIdentity found, refusing to save: %s", sone);
+ return;
+ }
+
+ logger.log(Level.INFO, "Saving Sone: %s", sone);
+ try {
+ ((OwnIdentity) sone.getIdentity()).setProperty("Sone.LatestEdition", String.valueOf(sone.getLatestEdition()));
+
+ /* save Sone into configuration. */
+ String sonePrefix = "Sone/" + sone.getId();
+ configuration.getLongValue(sonePrefix + "/Time").setValue(sone.getTime());
+ configuration.getStringValue(sonePrefix + "/LastInsertFingerprint").setValue(soneInserters.get(sone).getLastInsertFingerprint());
+
+ /* save profile. */
+ Profile profile = sone.getProfile();
+ configuration.getStringValue(sonePrefix + "/Profile/FirstName").setValue(profile.getFirstName());
+ configuration.getStringValue(sonePrefix + "/Profile/MiddleName").setValue(profile.getMiddleName());
+ configuration.getStringValue(sonePrefix + "/Profile/LastName").setValue(profile.getLastName());
+ configuration.getIntValue(sonePrefix + "/Profile/BirthDay").setValue(profile.getBirthDay());
+ configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").setValue(profile.getBirthMonth());
+ configuration.getIntValue(sonePrefix + "/Profile/BirthYear").setValue(profile.getBirthYear());
+
+ /* save profile fields. */
+ int fieldCounter = 0;
+ for (Field profileField : profile.getFields()) {
+ String fieldPrefix = sonePrefix + "/Profile/Fields/" + fieldCounter++;
+ configuration.getStringValue(fieldPrefix + "/Name").setValue(profileField.getName());
+ configuration.getStringValue(fieldPrefix + "/Value").setValue(profileField.getValue());
+ }
+ configuration.getStringValue(sonePrefix + "/Profile/Fields/" + fieldCounter + "/Name").setValue(null);
+
+ /* save posts. */
+ int postCounter = 0;
+ for (Post post : sone.getPosts()) {
+ String postPrefix = sonePrefix + "/Posts/" + postCounter++;
+ configuration.getStringValue(postPrefix + "/ID").setValue(post.getId());
+ configuration.getStringValue(postPrefix + "/Recipient").setValue((post.getRecipient() != null) ? post.getRecipient().getId() : null);
+ configuration.getLongValue(postPrefix + "/Time").setValue(post.getTime());
+ configuration.getStringValue(postPrefix + "/Text").setValue(post.getText());
+ }
+ configuration.getStringValue(sonePrefix + "/Posts/" + postCounter + "/ID").setValue(null);
+
+ /* save replies. */
+ int replyCounter = 0;
+ for (Reply reply : sone.getReplies()) {
+ String replyPrefix = sonePrefix + "/Replies/" + replyCounter++;
+ configuration.getStringValue(replyPrefix + "/ID").setValue(reply.getId());
+ configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPost().getId());
+ configuration.getLongValue(replyPrefix + "/Time").setValue(reply.getTime());
+ configuration.getStringValue(replyPrefix + "/Text").setValue(reply.getText());
+ }
+ configuration.getStringValue(sonePrefix + "/Replies/" + replyCounter + "/ID").setValue(null);
+
+ /* save post likes. */
+ int postLikeCounter = 0;
+ for (String postId : sone.getLikedPostIds()) {
+ configuration.getStringValue(sonePrefix + "/Likes/Post/" + postLikeCounter++ + "/ID").setValue(postId);
+ }
+ configuration.getStringValue(sonePrefix + "/Likes/Post/" + postLikeCounter + "/ID").setValue(null);
+
+ /* save reply likes. */
+ int replyLikeCounter = 0;
+ for (String replyId : sone.getLikedReplyIds()) {
+ configuration.getStringValue(sonePrefix + "/Likes/Reply/" + replyLikeCounter++ + "/ID").setValue(replyId);
+ }
+ configuration.getStringValue(sonePrefix + "/Likes/Reply/" + replyLikeCounter + "/ID").setValue(null);
+
+ /* save friends. */
+ int friendCounter = 0;
+ for (String friendId : sone.getFriends()) {
+ configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter++ + "/ID").setValue(friendId);
+ }
+ configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null);
+
++ /* save albums. first, collect in a flat structure, top-level first. */
++ List<Album> albums = Sone.flattenAlbums(sone.getAlbums());
++
++ int albumCounter = 0;
++ for (Album album : albums) {
++ String albumPrefix = sonePrefix + "/Albums/" + albumCounter++;
++ configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId());
++ configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle());
++ configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription());
++ configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent() == null ? null : album.getParent().getId());
++ }
++ configuration.getStringValue(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());
+
+ configuration.save();
+ logger.log(Level.INFO, "Sone %s saved.", sone);
+ } catch (ConfigurationException ce1) {
+ logger.log(Level.WARNING, "Could not save Sone: " + sone, ce1);
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not set WoT property for Sone: " + sone, wote1);
+ }
}
/**
/**
* {@inheritDoc}
*/
+ @Override
+ public void insertStarted(Sone sone) {
+ coreListenerManager.fireSoneInserting(sone);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void insertFinished(Sone sone, long insertDuration) {
+ coreListenerManager.fireSoneInserted(sone, insertDuration);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void insertAborted(Sone sone, Throwable cause) {
+ coreListenerManager.fireSoneInsertAborted(sone, cause);
+ }
+
++ //
++ // SONEINSERTLISTENER METHODS
++ //
++
++ /**
++ * {@inheritDoc}
++ */
++ @Override
+ public void imageInsertStarted(Image image) {
+ logger.log(Level.WARNING, "Image insert started for " + image);
+ coreListenerManager.fireImageInsertStarted(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertAborted(Image image) {
+ logger.log(Level.WARNING, "Image insert aborted for " + image);
+ coreListenerManager.fireImageInsertAborted(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertFinished(Image image, FreenetURI key) {
+ logger.log(Level.WARNING, "Image insert finished for " + image + ": " + key);
+ image.setKey(key.toString());
+ deleteTemporaryImage(image.getId());
+ saveSone(image.getSone());
+ coreListenerManager.fireImageInsertFinished(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void imageInsertFailed(Image image, Throwable cause) {
+ logger.log(Level.WARNING, "Image insert failed for " + image, cause);
+ coreListenerManager.fireImageInsertFailed(image, cause);
+ }
+
/**
* Convenience interface for external classes that want to access the core’s
* configuration.
import java.util.logging.Level;
import java.util.logging.Logger;
- import net.pterodactylus.sone.core.Core.Preferences;
import net.pterodactylus.sone.core.Core.SoneStatus;
+import net.pterodactylus.sone.data.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;
soneProperties.put("requestUri", sone.getRequestUri());
soneProperties.put("insertUri", sone.getInsertUri());
soneProperties.put("profile", sone.getProfile());
- soneProperties.put("posts", new ArrayList<Post>(sone.getPosts()));
- soneProperties.put("replies", new HashSet<Reply>(sone.getReplies()));
+ soneProperties.put("posts", new ListBuilder<Post>(new ArrayList<Post>(sone.getPosts())).sort(Post.TIME_COMPARATOR).get());
+ soneProperties.put("replies", new ListBuilder<Reply>(new ArrayList<Reply>(sone.getReplies())).sort(new ReverseComparator<Reply>(Reply.TIME_COMPARATOR)).get());
soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
+ soneProperties.put("albums", Sone.flattenAlbums(sone.getAlbums()));
}
//
--- /dev/null
- import net.pterodactylus.sone.web.page.Page.Request.Method;
+/*
+ * Sone - CreateAlbumPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Sone;
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
++import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
++import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user create a new album.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreateAlbumPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “create album” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public CreateAlbumPage(Template template, WebInterface webInterface) {
+ super("createAlbum.html", template, "Page.CreateAlbum.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
- webInterface.getCore().saveSone(currentSone);
++ 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());
+ }
+ }
+
+}
--- /dev/null
- import net.pterodactylus.sone.web.page.Page.Request.Method;
+/*
+ * Sone - DeleteAlbumPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
++import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
++import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user delete an {@link Album}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeleteAlbumPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “delete album” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DeleteAlbumPage(Template template, WebInterface webInterface) {
+ super("deleteAlbum.html", template, "Page.DeleteAlbum.Title", webInterface, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
++ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ if (album == null) {
+ throw new RedirectException("invalid.html");
+ }
+ if (!webInterface.getCore().isLocalSone(album.getSone())) {
+ throw new RedirectException("noPermission.html");
+ }
+ if (request.getHttpRequest().isPartSet("abortDelete")) {
+ throw new RedirectException("imageBrowser.html?album=" + album.getId());
+ }
+ Album parentAlbum = album.getParent();
+ webInterface.getCore().deleteAlbum(album);
+ if (parentAlbum == null) {
+ throw new RedirectException("imageBrowser.html?sone=" + album.getSone().getId());
+ }
+ throw new RedirectException("imageBrowser.html?album=" + parentAlbum.getId());
+ }
+ String albumId = request.getHttpRequest().getParam("album");
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ if (album == null) {
+ throw new RedirectException("invalid.html");
+ }
+ templateContext.set("album", album);
+ }
+
+}
--- /dev/null
- import net.pterodactylus.sone.web.page.Page.Request.Method;
+/*
+ * Sone - DeleteImagePage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Image;
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
++import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
++import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user delete an {@link Image}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeleteImagePage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “delete image” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public DeleteImagePage(Template template, WebInterface webInterface) {
+ super("deleteImage.html", template, "Page.DeleteImage.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
++ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ String imageId = (request.getMethod() == Method.POST) ? request.getHttpRequest().getPartAsStringFailsafe("image", 36) : request.getHttpRequest().getParam("image");
+ Image image = webInterface.getCore().getImage(imageId, false);
+ if (image == null) {
+ throw new RedirectException("invalid.html");
+ }
+ if (!webInterface.getCore().isLocalSone(image.getSone())) {
+ throw new RedirectException("noPermission.html");
+ }
+ if (request.getMethod() == Method.POST) {
+ if (request.getHttpRequest().isPartSet("abortDelete")) {
+ throw new RedirectException("imageBrowser.html?image=" + image.getId());
+ }
+ webInterface.getCore().deleteImage(image);
+ throw new RedirectException("imageBrowser.html?album=" + image.getAlbum().getId());
+ }
+ templateContext.set("image", image);
+ }
+
+}
--- /dev/null
- import net.pterodactylus.sone.web.page.Page.Request.Method;
+/*
+ * Sone - EditAlbumPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
++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;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditAlbumPage extends SoneTemplatePage {
+
+ /**
+ * TODO
+ *
+ * @param template
+ * @param webInterface
+ */
+ public EditAlbumPage(Template template, WebInterface webInterface) {
+ super("editAlbum.html", template, "Page.EditAlbum.Title", webInterface, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
- webInterface.getCore().saveSone(album.getSone());
++ 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 title = request.getHttpRequest().getPartAsStringFailsafe("title", 100).trim();
+ if (title.length() == 0) {
+ templateContext.set("titleMissing", true);
+ return;
+ }
+ String description = request.getHttpRequest().getPartAsStringFailsafe("description", 1000).trim();
+ album.setTitle(title).setDescription(description);
++ webInterface.getCore().touchConfiguration();
+ throw new RedirectException("imageBrowser.html?album=" + album.getId());
+ }
+ }
+
+}
--- /dev/null
- import net.pterodactylus.sone.web.page.Page.Request.Method;
+/*
+ * FreenetSone - WebInterface.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Image;
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
++import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
++import net.pterodactylus.util.web.Method;
+
+/**
+ * Page that lets the user edit title and description of an {@link Image}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class EditImagePage extends SoneTemplatePage {
+
+ /**
+ * Creates a new “edit image” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public EditImagePage(Template template, WebInterface webInterface) {
+ super("editImage.html", template, "Page.EditImage.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
- webInterface.getCore().saveSone(image.getSone());
++ 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);
+ 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");
+ }
+ 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("imageBrowser.html?image=" + image.getId());
+ }
+ }
+
+}
--- /dev/null
- import net.pterodactylus.sone.web.page.Page;
+/*
+ * Sone - GetImagePage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
++import java.io.IOException;
++
+import net.pterodactylus.sone.data.TemporaryImage;
- public class GetImagePage implements Page {
++import net.pterodactylus.sone.web.page.FreenetRequest;
++import net.pterodactylus.util.web.Page;
++import net.pterodactylus.util.web.Response;
+
+/**
+ * Page that delivers a {@link TemporaryImage} to the browser.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
- public Response handleRequest(Request request) {
++public class GetImagePage implements Page<FreenetRequest> {
+
+ /** The Sone web interface. */
+ private final WebInterface webInterface;
+
+ /**
+ * Creates a new “get image” page.
+ *
+ * @param webInterface
+ * The Sone web interface
+ */
+ public GetImagePage(WebInterface webInterface) {
+ this.webInterface = webInterface;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPath() {
+ return "getImage.html";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
- return new Response(404, "Not found.", "text/plain; charset=utf-8", "");
++ 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 new Response(200, "OK", temporaryImage.getMimeType(), temporaryImage.getImageData()).setHeader("Content-Disposition", "attachment; filename=" + temporaryImage.getId() + "." + temporaryImage.getMimeType().substring(temporaryImage.getMimeType().lastIndexOf("/") + 1));
++ return response.setStatusCode(404).setStatusText("Not found.").setContentType("text/html; charset=utf-8");
+ }
++ String contentType= temporaryImage.getMimeType();
++ return response.setStatusCode(200).setStatusText("OK").setContentType(contentType).addHeader("Content-Disposition", "attachment; filename=" + temporaryImage.getId() + "." + contentType.substring(contentType.lastIndexOf('/') + 1)).write(temporaryImage.getImageData());
+ }
+
+}
--- /dev/null
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
+/*
+ * Sone - ImageBrowserPage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.data.Sone;
++import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * The image browser page is the entry page for the image management.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ImageBrowserPage extends SoneTemplatePage {
+
+ /**
+ * Creates a new image browser page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public ImageBrowserPage(Template template, WebInterface webInterface) {
+ super("imageBrowser.html", template, "Page.ImageBrowser.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
++ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ String albumId = request.getHttpRequest().getParam("album", null);
+ if (albumId != null) {
+ Album album = webInterface.getCore().getAlbum(albumId, false);
+ templateContext.set("albumRequested", true);
+ templateContext.set("album", album);
+ return;
+ }
+ String imageId = request.getHttpRequest().getParam("image", null);
+ if (imageId != null) {
+ Image image = webInterface.getCore().getImage(imageId, false);
+ templateContext.set("imageRequested", true);
+ templateContext.set("image", image);
+ return;
+ }
+ Sone sone = getCurrentSone(request.getToadletContext(), false);
+ String soneId = request.getHttpRequest().getParam("sone", null);
+ if (soneId != null) {
+ sone = webInterface.getCore().getSone(soneId, false);
+ }
+ templateContext.set("soneRequested", true);
+ templateContext.set("sone", sone);
+ }
+
+}
--- /dev/null
- import net.pterodactylus.sone.web.page.Page.Request.Method;
+/*
+ * Sone - UploadImagePage.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import java.awt.Image;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.TemporaryImage;
- protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
++import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.io.Closer;
+import net.pterodactylus.util.io.StreamCopier;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
++import net.pterodactylus.util.web.Method;
+import freenet.support.api.Bucket;
+import freenet.support.api.HTTPUploadedFile;
+
+/**
+ * Page implementation that lets the user upload an image.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class UploadImagePage extends SoneTemplatePage {
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(UploadImagePage.class);
+
+ /**
+ * Creates a new “upload image” page.
+ *
+ * @param template
+ * The template to render
+ * @param webInterface
+ * The Sone web interface
+ */
+ public UploadImagePage(Template template, WebInterface webInterface) {
+ super("uploadImage.html", template, "Page.UploadImage.Title", webInterface, true);
+ }
+
+ //
+ // SONETEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
++ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ Sone currentSone = getCurrentSone(request.getToadletContext());
+ String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
+ Album parent = webInterface.getCore().getAlbum(parentId, false);
+ if (parent == null) {
+ /* TODO - signal error */
+ return;
+ }
+ if (!currentSone.equals(parent.getSone())) {
+ /* TODO - signal error. */
+ return;
+ }
+ String name = request.getHttpRequest().getPartAsStringFailsafe("title", 200);
+ String description = request.getHttpRequest().getPartAsStringFailsafe("description", 4000);
+ HTTPUploadedFile uploadedFile = request.getHttpRequest().getUploadedFile("image");
+ Bucket fileBucket = uploadedFile.getData();
+ InputStream imageInputStream = null;
+ ByteArrayOutputStream imageDataOutputStream = null;
+ net.pterodactylus.sone.data.Image image = null;
+ try {
+ imageInputStream = fileBucket.getInputStream();
+ /* TODO - check length */
+ imageDataOutputStream = new ByteArrayOutputStream((int) fileBucket.size());
+ StreamCopier.copy(imageInputStream, imageDataOutputStream);
+ } catch (IOException ioe1) {
+ logger.log(Level.WARNING, "Could not read uploaded image!", ioe1);
+ return;
+ } finally {
+ fileBucket.free();
+ Closer.close(imageInputStream);
+ Closer.close(imageDataOutputStream);
+ }
+ byte[] imageData = imageDataOutputStream.toByteArray();
+ ByteArrayInputStream imageDataInputStream = null;
+ Image uploadedImage = null;
+ try {
+ imageDataInputStream = new ByteArrayInputStream(imageData);
+ uploadedImage = ImageIO.read(imageDataInputStream);
+ if (uploadedImage == null) {
+ templateContext.set("messages", webInterface.getL10n().getString("Page.UploadImage.Error.InvalidImage"));
+ return;
+ }
+ String mimeType = getMimeType(imageData);
+ TemporaryImage temporaryImage = webInterface.getCore().createTemporaryImage(mimeType, imageData);
+ image = webInterface.getCore().createImage(currentSone, parent, temporaryImage);
+ image.setTitle(name).setDescription(description).setWidth(uploadedImage.getWidth(null)).setHeight(uploadedImage.getHeight(null));
+ } catch (IOException ioe1) {
+ logger.log(Level.WARNING, "Could not read uploaded image!", ioe1);
+ return;
+ } finally {
+ Closer.close(imageDataInputStream);
+ Closer.flush(uploadedImage);
+ }
+ throw new RedirectException("imageBrowser.html?album=" + parent.getId());
+ }
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Tries to detect the MIME type of the encoded image.
+ *
+ * @param imageData
+ * The encoded image
+ * @return The MIME type of the image, or “application/octet-stream” if the
+ * image type could not be detected
+ */
+ private String getMimeType(byte[] imageData) {
+ ByteArrayInputStream imageDataInputStream = new ByteArrayInputStream(imageData);
+ try {
+ ImageInputStream imageInputStream = ImageIO.createImageInputStream(imageDataInputStream);
+ Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
+ if (imageReaders.hasNext()) {
+ return imageReaders.next().getOriginatingProvider().getMIMETypes()[0];
+ }
+ } catch (IOException ioe1) {
+ logger.log(Level.FINE, "Could not detect MIME type for image.", ioe1);
+ }
+ return "application/octet-stream";
+ }
+
+}
import net.pterodactylus.sone.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.NotificationManagerAccessor;
import net.pterodactylus.sone.template.ParserFilter;
import net.pterodactylus.sone.template.PostAccessor;
import net.pterodactylus.sone.template.ReplyAccessor;
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;
templateContextFactory.addAccessor(Sone.class, new SoneAccessor(getCore()));
templateContextFactory.addAccessor(Post.class, new PostAccessor(getCore()));
templateContextFactory.addAccessor(Reply.class, new ReplyAccessor(getCore()));
+ templateContextFactory.addAccessor(Album.class, new AlbumAccessor());
templateContextFactory.addAccessor(Identity.class, new IdentityAccessor(getCore()));
- templateContextFactory.addAccessor(NotificationManager.class, new NotificationManagerAccessor());
templateContextFactory.addAccessor(Trust.class, new TrustAccessor());
templateContextFactory.addAccessor(HTTPRequest.class, new HttpRequestAccessor());
templateContextFactory.addFilter("date", new DateFilter());
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.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER);
templateContextFactory.addProvider(new ClassPathTemplateProvider());
+ templateContextFactory.addTemplateObject("webInterface", this);
templateContextFactory.addTemplateObject("formPassword", formPassword);
/* create notifications. */
Template deletePostTemplate = TemplateParser.parse(createReader("/templates/deletePost.html"));
Template deleteReplyTemplate = TemplateParser.parse(createReader("/templates/deleteReply.html"));
Template deleteSoneTemplate = TemplateParser.parse(createReader("/templates/deleteSone.html"));
+ Template imageBrowserTemplate = TemplateParser.parse(createReader("/templates/imageBrowser.html"));
+ Template createAlbumTemplate = TemplateParser.parse(createReader("/templates/createAlbum.html"));
+ Template deleteAlbumTemplate = TemplateParser.parse(createReader("/templates/deleteAlbum.html"));
+ Template deleteImageTemplate = TemplateParser.parse(createReader("/templates/deleteImage.html"));
Template noPermissionTemplate = TemplateParser.parse(createReader("/templates/noPermission.html"));
Template optionsTemplate = TemplateParser.parse(createReader("/templates/options.html"));
+ Template rescueTemplate = TemplateParser.parse(createReader("/templates/rescue.html"));
Template aboutTemplate = TemplateParser.parse(createReader("/templates/about.html"));
Template invalidTemplate = TemplateParser.parse(createReader("/templates/invalid.html"));
Template postTemplate = TemplateParser.parse(createReader("/templates/include/viewPost.html"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("noPermission.html", noPermissionTemplate, "Page.NoPermission.Title", this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DismissNotificationPage(emptyTemplate, this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("invalid.html", invalidTemplate, "Page.Invalid.Title", this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("css/", "/static/css/", "text/css")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("javascript/", "/static/javascript/", "text/javascript")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage("images/", "/static/images/", "image/png")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new TemplatePage("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("css/", "/static/css/", "text/css")));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("javascript/", "/static/javascript/", "text/javascript")));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("images/", "/static/images/", "image/png")));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new GetImagePage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetTranslationPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new GetStatusAjaxPage(this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new GetNotificationAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new DismissNotificationAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreatePostAjaxPage(this)));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateReplyAjaxPage(this)));
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.Replies.Title=Replies to Posts
+Page.ViewSone.Profile.Label.Albums=Albums
+ Page.ViewSone.Profile.Name.WoTLink=web of trust profile
+ Page.ViewSone.Replies.Title=Posts {sone} has replied to
Page.ViewPost.Title=View Post - Sone
Page.ViewPost.Page.Title=View Post by {sone}
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
Notification.SoneRescued.Text.RememberToUnlock=Please remember to control the posts and replies you have given and don’t forget to unlock your Sones!
Notification.LockedSones.Text=The following Sones have been locked for more than 5 minutes. Please check if you really want to keep these Sones locked:
Notification.NewVersion.Text=Version {version} of the Sone plugin was found. Download it from USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}!
+Notification.InsertingImages.Text=The following images are being inserted:
+Notification.InsertedImages.Text=The following images have been inserted:
+Notification.ImageInsertFailed.Text=The following images could not be inserted:
+ Notification.Mention.ShortText=You have been mentioned.
+ Notification.Mention.Text=You have been mentioned in the following posts:
+ Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
border: none;
}
+#sone .parsed {
+ white-space: pre-wrap;
+}
+
+ #sone #main.offline {
+ opacity: 0.5;
+ }
+
+ #sone #offline-marker {
+ display: none;
+ position: fixed;
+ top: 2em;
+ right: 2em;
+ width: 128px;
+ height: 128px;
+ background-image: url("../images/sone-offline.png");
+ }
+
#sone #notification-area {
margin-top: 1em;
}
<div class="profile-field">
<div class="name"><%= Page.ViewSone.Profile.Label.Name|l10n|html></div>
- <div class="value"><a href="/WebOfTrust/ShowIdentity?id=<% sone.id|html>"><% sone.niceName|html></a></div>
+ <div class="value"><% sone.niceName|html> (<a href="/WebOfTrust/ShowIdentity?id=<% sone.id|html>"><%= Page.ViewSone.Profile.Name.WoTLink|l10n|html></a>)</div>
</div>
+ <%foreach sone.albums album>
+ <%first>
+ <div class="profile-field">
+ <div class="name"><%= Page.ViewSone.Profile.Label.Albums|l10n|html></div>
+ <div class="value">
+ <%/first>
+ <a href="imageBrowser.html?album=<%album.id|html>"><%album.title|html></a><%notlast>, <%/notlast>
+ <%last>
+ </div>
+ </div>
+ <%/last>
+ <%/foreach>
+
<%foreach sone.profile.fields field>
<div class="profile-field">
<div class="name"><% field.name|html></div>