From: David ‘Bombe’ Roden Date: Mon, 27 Jul 2015 18:30:21 +0000 (+0200) Subject: Merge branch 'next' into feature/album-and-image-links X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=cc18e5878bc71081da5137eb777d91821c3f4cce;hp=e80ee9c75042771d91bdd7095ca225adc6c11e6c Merge branch 'next' into feature/album-and-image-links --- diff --git a/src/main/java/net/pterodactylus/sone/core/CompatibilityMode.java b/src/main/java/net/pterodactylus/sone/core/CompatibilityMode.java new file mode 100644 index 0000000..6983632 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/CompatibilityMode.java @@ -0,0 +1,17 @@ +package net.pterodactylus.sone.core; + +import net.pterodactylus.sone.data.Post; + +/** + * Sone compatibility modes. + * + * @author David ‘Bombe’ Roden + */ +public enum CompatibilityMode { + + /** + * This mode causes Sone to use a post’s {@link Post#getInternalId() internal ID} to locate posts when parsing links. + */ + oldElementIds, + +} diff --git a/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java b/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java index a29856b..02c10db 100644 --- a/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java +++ b/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java @@ -271,7 +271,7 @@ public class ConfigurationSoneParser { .setHeight(height) .update(); album.addImage(image); - images.put(image.getId(), image); + images.put(imageId, image); } } diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index da209d8..140d80c 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -27,6 +27,7 @@ import static java.util.logging.Logger.getLogger; import java.util.ArrayList; import java.util.Collection; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -72,6 +73,7 @@ import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; import net.pterodactylus.sone.data.Sone.SoneStatus; import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.database.AlbumBuilder; +import net.pterodactylus.sone.database.AlbumProvider; import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.database.DatabaseException; import net.pterodactylus.sone.database.ImageBuilder; @@ -98,6 +100,7 @@ import net.pterodactylus.util.thread.NamedThreadFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Optional; +import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; @@ -113,7 +116,7 @@ import com.google.inject.Singleton; * @author David ‘Bombe’ Roden */ @Singleton -public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider { +public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider, AlbumProvider { /** The logger. */ private static final Logger logger = getLogger(Core.class.getName()); @@ -154,6 +157,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** The trust updater. */ private final WebOfTrustUpdater webOfTrustUpdater; + private final Set compatibilityModes = EnumSet.noneOf(CompatibilityMode.class); + /** The times Sones were followed. */ private final Map soneFollowingTimes = new HashMap(); @@ -273,6 +278,18 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return updateChecker; } + public boolean isCompatibilityMode(CompatibilityMode compatibilityMode) { + return compatibilityModes.contains(compatibilityMode); + } + + public void setCompatibilityMode(CompatibilityMode compatibilityMode) { + compatibilityModes.add(compatibilityMode); + } + + public void clearCompatibilityMod(CompatibilityMode compatibilityMode) { + compatibilityModes.remove(compatibilityMode); + } + /** * Returns the Sone rescuer for the given local Sone. * @@ -420,8 +437,17 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * {@inheritDoc} */ @Override - public Optional getPost(String postId) { - return database.getPost(postId); + public Optional getPost(final String postId) { + Optional post = database.getPost(postId); + if (post.isPresent() || !isCompatibilityMode(CompatibilityMode.oldElementIds)) { + return post; + } + return FluentIterable.from(getSones()).transformAndConcat(Sone.toAllPosts).filter(new Predicate() { + @Override + public boolean apply(Post input) { + return (input != null) && input.getInternalId().equals(postId); + } + }).first(); } /** @@ -534,8 +560,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The album with the given ID, or {@code null} if no album with the * given ID exists */ - public Album getAlbum(String albumId) { - return database.getAlbum(albumId).orNull(); + public Optional getAlbum(String albumId) { + return database.getAlbum(albumId); } public ImageBuilder imageBuilder() { @@ -1476,7 +1502,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, int postCounter = 0; for (Post post : sone.getPosts()) { String postPrefix = sonePrefix + "/Posts/" + postCounter++; - configuration.getStringValue(postPrefix + "/ID").setValue(post.getId()); + configuration.getStringValue(postPrefix + "/ID").setValue(post.getInternalId()); configuration.getStringValue(postPrefix + "/Recipient").setValue(post.getRecipientId().orNull()); configuration.getLongValue(postPrefix + "/Time").setValue(post.getTime()); configuration.getStringValue(postPrefix + "/Text").setValue(post.getText()); @@ -1487,7 +1513,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, int replyCounter = 0; for (PostReply reply : sone.getReplies()) { String replyPrefix = sonePrefix + "/Replies/" + replyCounter++; - configuration.getStringValue(replyPrefix + "/ID").setValue(reply.getId()); + configuration.getStringValue(replyPrefix + "/ID").setValue(reply.getInternalId()); configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPostId()); configuration.getLongValue(replyPrefix + "/Time").setValue(reply.getTime()); configuration.getStringValue(replyPrefix + "/Text").setValue(reply.getText()); @@ -1514,7 +1540,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, int albumCounter = 0; for (Album album : albums) { String albumPrefix = sonePrefix + "/Albums/" + albumCounter++; - configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId()); + configuration.getStringValue(albumPrefix + "/ID").setValue(album.getInternalId()); configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle()); configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription()); configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId()); @@ -1530,8 +1556,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, continue; } String imagePrefix = sonePrefix + "/Images/" + imageCounter++; - configuration.getStringValue(imagePrefix + "/ID").setValue(image.getId()); - configuration.getStringValue(imagePrefix + "/Album").setValue(album.getId()); + configuration.getStringValue(imagePrefix + "/ID").setValue(image.getInternalId()); + configuration.getStringValue(imagePrefix + "/Album").setValue(album.getInternalId()); configuration.getStringValue(imagePrefix + "/Key").setValue(image.getKey()); configuration.getStringValue(imagePrefix + "/Title").setValue(image.getTitle()); configuration.getStringValue(imagePrefix + "/Description").setValue(image.getDescription()); @@ -1596,6 +1622,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(null); } + /* save compatibility modes. */ + configuration.getBooleanValue("CompatibilityModes/OldElementIds").setValue(compatibilityModes.contains(CompatibilityMode.oldElementIds)); + /* save known posts. */ database.save(); @@ -1644,6 +1673,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } ++soneCounter; } + + /* load compatibility modes. */ + if (configuration.getBooleanValue("CompatibilityModes/OldElementIds").getValue(false)) { + setCompatibilityMode(CompatibilityMode.oldElementIds); + } } /** diff --git a/src/main/java/net/pterodactylus/sone/core/SoneParser.java b/src/main/java/net/pterodactylus/sone/core/SoneParser.java index ae1e2a5..9122852 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneParser.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneParser.java @@ -29,6 +29,7 @@ import net.pterodactylus.sone.database.SoneBuilder; import net.pterodactylus.util.xml.SimpleXML; import net.pterodactylus.util.xml.XML; +import com.google.common.base.Optional; import org.w3c.dom.Document; /** @@ -266,10 +267,10 @@ public class SoneParser { logger.log(Level.WARNING, String.format("Downloaded Sone %s contains invalid album!", sone)); return null; } - Album parent = null; + Optional parent = Optional.absent(); if (parentId != null) { parent = core.getAlbum(parentId); - if (parent == null) { + if (!parent.isPresent()) { logger.log(Level.WARNING, String.format("Downloaded Sone %s has album with invalid parent!", sone)); return null; } @@ -282,8 +283,8 @@ public class SoneParser { .setTitle(title) .setDescription(description) .update(); - if (parent != null) { - parent.addAlbum(album); + if (parent.isPresent()) { + parent.get().addAlbum(album); } else { topLevelAlbums.add(album); } diff --git a/src/main/java/net/pterodactylus/sone/data/Album.java b/src/main/java/net/pterodactylus/sone/data/Album.java index c75088f..b4c6b0f 100644 --- a/src/main/java/net/pterodactylus/sone/data/Album.java +++ b/src/main/java/net/pterodactylus/sone/data/Album.java @@ -107,6 +107,7 @@ public interface Album extends Identified, Fingerprintable { * @return The ID of this album */ String getId(); + String getInternalId(); /** * Returns the Sone this album belongs to. diff --git a/src/main/java/net/pterodactylus/sone/data/IdBuilder.java b/src/main/java/net/pterodactylus/sone/data/IdBuilder.java new file mode 100644 index 0000000..f9accc1 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/IdBuilder.java @@ -0,0 +1,31 @@ +package net.pterodactylus.sone.data; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; + +import com.google.common.base.Charsets; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; + +/** + * Builds (practically) unique IDs by combining Sone and element IDs. + * + * @author David ‘Bombe’ Roden + */ +@ThreadSafe +public class IdBuilder { + + private static final HashFunction HASH_FUNCTION = Hashing.sha256(); + public static final int ID_STRING_LENGTH = HASH_FUNCTION.bits() / 4; + + private final HashFunction sha256 = HASH_FUNCTION; + + @Nonnull + public String buildId(@Nonnull String soneId, @Nonnull String id) { + return sha256.newHasher() + .putBytes(soneId.getBytes(Charsets.UTF_8)) + .putBytes(id.getBytes(Charsets.UTF_8)) + .hash().toString(); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/Image.java b/src/main/java/net/pterodactylus/sone/data/Image.java index 22ddc29..f2ef88d 100644 --- a/src/main/java/net/pterodactylus/sone/data/Image.java +++ b/src/main/java/net/pterodactylus/sone/data/Image.java @@ -30,6 +30,7 @@ public interface Image extends Identified, Fingerprintable { * @return The ID of this image */ String getId(); + String getInternalId(); /** * Returns the Sone this image belongs to. diff --git a/src/main/java/net/pterodactylus/sone/data/Post.java b/src/main/java/net/pterodactylus/sone/data/Post.java index 95abae6..d27d243 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -62,6 +62,7 @@ public interface Post extends Identified { * @return The ID of the post */ public String getId(); + String getInternalId(); /** * Returns whether this post has already been loaded. @@ -144,6 +145,11 @@ public interface Post extends Identified { } @Override + public String getInternalId() { + return id; + } + + @Override public boolean isLoaded() { return false; } diff --git a/src/main/java/net/pterodactylus/sone/data/Profile.java b/src/main/java/net/pterodactylus/sone/data/Profile.java index 4970cf9..679196b 100644 --- a/src/main/java/net/pterodactylus/sone/data/Profile.java +++ b/src/main/java/net/pterodactylus/sone/data/Profile.java @@ -243,7 +243,7 @@ public class Profile implements Fingerprintable { return this; } checkArgument(avatar.getSone().equals(sone), "avatar must belong to Sone"); - this.avatar = avatar.getId(); + this.avatar = avatar.getInternalId(); return this; } diff --git a/src/main/java/net/pterodactylus/sone/data/Reply.java b/src/main/java/net/pterodactylus/sone/data/Reply.java index e9b7a1d..69769eb 100644 --- a/src/main/java/net/pterodactylus/sone/data/Reply.java +++ b/src/main/java/net/pterodactylus/sone/data/Reply.java @@ -62,6 +62,7 @@ public interface Reply> extends Identified { * @return The ID of the reply */ public String getId(); + String getInternalId(); /** * Returns the Sone that posted this reply. diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index e64a388..7acdb7f 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -35,6 +35,7 @@ import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.template.SoneAccessor; +import com.google.common.base.Optional; import freenet.keys.FreenetURI; import com.google.common.base.Function; @@ -188,6 +189,13 @@ public interface Sone extends Identified, Fingerprintable, Comparable { } }; + Function> toAllPosts = new Function>() { + @Override + public Collection apply(@Nullable Sone sone) { + return (sone != null) ? sone.getPosts() : Collections.emptyList(); + } + }; + /** * Returns the identity of this Sone. * @@ -502,6 +510,7 @@ public interface Sone extends Identified, Fingerprintable, Comparable { * @return The root album of this Sone */ Album getRootAlbum(); + Optional getImageByInternalId(final String internalId); /** * Returns Sone-specific options. diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java index 3488577..6a45187 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.UUID; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Sone; @@ -46,6 +47,8 @@ import com.google.common.hash.Hashing; */ public class AlbumImpl implements Album { + private final IdBuilder idBuilder = new IdBuilder(); + /** The ID of this album. */ private final String id; @@ -95,6 +98,11 @@ public class AlbumImpl implements Album { @Override public String getId() { + return idBuilder.buildId(sone.getId(), id); + } + + @Override + public String getInternalId() { return id; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java index e9a0c57..85402ce 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java @@ -9,6 +9,7 @@ import java.util.Set; 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.PostReply; import net.pterodactylus.sone.data.Profile; @@ -16,6 +17,7 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.SoneOptions; import net.pterodactylus.sone.freenet.wot.Identity; +import com.google.common.base.Optional; import freenet.keys.FreenetURI; import com.google.common.base.Objects; @@ -219,6 +221,11 @@ public class IdOnlySone implements Sone { } @Override + public Optional getImageByInternalId(String internalId) { + return Optional.absent(); + } + + @Override public SoneOptions getOptions() { return null; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java index 2df98b1..b385cfd 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java @@ -25,6 +25,7 @@ import static com.google.common.base.Preconditions.checkState; import java.util.UUID; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Sone; @@ -39,6 +40,8 @@ import com.google.common.hash.Hashing; */ public class ImageImpl implements Image { + private final IdBuilder idBuilder = new IdBuilder(); + /** The ID of the image. */ private final String id; @@ -88,6 +91,11 @@ public class ImageImpl implements Image { @Override public String getId() { + return idBuilder.buildId(sone.getId(), id); + } + + @Override + public String getInternalId() { return id; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java index 9dcd7d0..4442b3b 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.data.impl; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.database.SoneProvider; @@ -31,6 +32,8 @@ import com.google.common.base.Optional; */ public class PostImpl implements Post { + private final IdBuilder idBuilder = new IdBuilder(); + /** The Sone provider. */ private final SoneProvider soneProvider; @@ -86,6 +89,11 @@ public class PostImpl implements Post { */ @Override public String getId() { + return idBuilder.buildId(soneId, id); + } + + @Override + public String getInternalId() { return id; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/ReplyImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/ReplyImpl.java index a67081f..b3681d7 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/ReplyImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/ReplyImpl.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.data.impl; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.database.SoneProvider; @@ -30,6 +31,8 @@ import net.pterodactylus.sone.database.SoneProvider; */ public abstract class ReplyImpl> implements Reply { + private final IdBuilder idBuilder = new IdBuilder(); + /** The Sone provider. */ private final SoneProvider soneProvider; @@ -75,6 +78,11 @@ public abstract class ReplyImpl> implements Reply { */ @Override public String getId() { + return idBuilder.buildId(soneId, id); + } + + @Override + public String getInternalId() { return id; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index f7ebfb1..97fe8eb 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -30,9 +30,11 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.logging.Level; import java.util.logging.Logger; +import javax.annotation.Nullable; 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.PostReply; import net.pterodactylus.sone.data.Profile; @@ -44,6 +46,9 @@ import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.sone.freenet.wot.OwnIdentity; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; import freenet.keys.FreenetURI; import com.google.common.hash.Hasher; @@ -584,6 +589,16 @@ public class SoneImpl implements Sone { return rootAlbum; } + @Override + public Optional getImageByInternalId(final String internalId) { + return FluentIterable.from(toAllImages.apply(this)).filter(new Predicate() { + @Override + public boolean apply(@Nullable Image input) { + return (input != null) && input.getInternalId().equals(internalId); + } + }).first(); + } + /** * Returns Sone-specific options. * diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java index 180cf6c..34e90f8 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.database.memory; import java.util.UUID; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.database.SoneProvider; @@ -33,6 +34,8 @@ import com.google.common.base.Optional; */ class MemoryPost implements Post { + private final IdBuilder idBuilder = new IdBuilder(); + /** The post database. */ private final MemoryDatabase postDatabase; @@ -91,6 +94,11 @@ class MemoryPost implements Post { */ @Override public String getId() { + return idBuilder.buildId(soneId, id.toString()); + } + + @Override + public String getInternalId() { return id.toString(); } diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java index a6686ca..eb98a1d 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.database.memory; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Sone; @@ -31,6 +32,8 @@ import com.google.common.base.Optional; */ class MemoryPostReply implements PostReply { + private final IdBuilder idBuilder = new IdBuilder(); + /** The database. */ private final MemoryDatabase database; @@ -89,6 +92,11 @@ class MemoryPostReply implements PostReply { */ @Override public String getId() { + return idBuilder.buildId(soneId, id); + } + + @Override + public String getInternalId() { return id; } diff --git a/src/main/java/net/pterodactylus/sone/template/BuildIdFilter.java b/src/main/java/net/pterodactylus/sone/template/BuildIdFilter.java new file mode 100644 index 0000000..321743a --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/template/BuildIdFilter.java @@ -0,0 +1,57 @@ +package net.pterodactylus.sone.template; + +import java.util.Map; + +import javax.annotation.Nullable; + +import net.pterodactylus.sone.data.IdBuilder; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.util.template.Filter; +import net.pterodactylus.util.template.TemplateContext; + +import com.google.common.base.Function; +import com.google.common.base.Optional; + +/** + * Filter that {@link IdBuilder builds IDs} from a piped-in element ID and a Sone or Sone ID given as parameter “sone.” + * + * @author David ‘Bombe’ Roden + */ +public class BuildIdFilter implements Filter { + + private final IdBuilder idBuilder = new IdBuilder(); + + @Override + public Object format(TemplateContext templateContext, Object data, Map parameters) { + Optional soneId = getSoneId(parameters); + if (!soneId.isPresent()) { + return null; + } + Optional elementId = Optional.fromNullable(data).transform(getStringValue()); + if (!elementId.isPresent()) { + return null; + } + return idBuilder.buildId(soneId.get(), elementId.get()); + } + + private Optional getSoneId(Map parameters) { + Object soneObject = parameters.get("sone"); + if (soneObject instanceof String) { + return Optional.of((String) soneObject); + } else if (soneObject instanceof Sone) { + return Optional.of(((Sone) soneObject).getId()); + } + return Optional.absent(); + } + + private Function getStringValue() { + return new Function() { + @Nullable + @Override + public String apply(Object input) { + return (input != null) ? input.toString() : null; + } + }; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java index ec35e2e..2625441 100644 --- a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java @@ -18,71 +18,33 @@ package net.pterodactylus.sone.template; import static java.lang.String.valueOf; -import static net.pterodactylus.sone.utils.NumberParsers.parseInt; import java.io.IOException; import java.io.StringReader; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; +import java.util.Collections; import java.util.Map; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.text.FreenetLinkPart; -import net.pterodactylus.sone.text.LinkPart; import net.pterodactylus.sone.text.Part; -import net.pterodactylus.sone.text.PlainTextPart; -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.FreenetRequest; 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; /** - * Filter that filters a given text through a {@link SoneTextParser}. + * Filter that filters a given text through a {@link SoneTextParser} and returns the parsed {@link Part}s. * * @author David ‘Bombe’ Roden */ public class ParserFilter implements Filter { - /** The core. */ private final Core core; - - /** The link parser. */ private final SoneTextParser soneTextParser; - /** The template context factory. */ - private final TemplateContextFactory templateContextFactory; - - /** The template for {@link PlainTextPart}s. */ - private static final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>")); - - /** The template for {@link FreenetLinkPart}s. */ - private static final Template linkTemplate = TemplateParser.parse(new StringReader("\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html>")); - - /** - * Creates a new filter that runs its input through a {@link SoneTextParser} - * . - * - * @param core - * The core - * @param templateContextFactory - * The context factory for rendering the parts - * @param soneTextParser - * The Sone text parser - */ - public ParserFilter(Core core, TemplateContextFactory templateContextFactory, SoneTextParser soneTextParser) { + public ParserFilter(Core core, SoneTextParser soneTextParser) { this.core = core; - this.templateContextFactory = templateContextFactory; this.soneTextParser = soneTextParser; } @@ -92,209 +54,18 @@ public class ParserFilter implements Filter { @Override public Object format(TemplateContext templateContext, Object data, Map parameters) { String text = valueOf(data); - int length = parseInt(valueOf(parameters.get("length")), -1); - int cutOffLength = parseInt(valueOf(parameters.get("cut-off-length")), length); Object sone = parameters.get("sone"); if (sone instanceof String) { sone = core.getSone((String) sone).orNull(); } FreenetRequest request = (FreenetRequest) templateContext.get("request"); SoneTextParserContext context = new SoneTextParserContext(request, (Sone) sone); - StringWriter parsedTextWriter = new StringWriter(); try { - Iterable parts = soneTextParser.parse(context, new StringReader(text)); - if (length > -1) { - int allPartsLength = 0; - List shortenedParts = new ArrayList(); - for (Part part : parts) { - if (part instanceof PlainTextPart) { - String longText = ((PlainTextPart) part).getText(); - if (allPartsLength < cutOffLength) { - if ((allPartsLength + longText.length()) > cutOffLength) { - shortenedParts.add(new PlainTextPart(longText.substring(0, cutOffLength - allPartsLength) + "…")); - } else { - shortenedParts.add(part); - } - } - allPartsLength += longText.length(); - } else if (part instanceof LinkPart) { - if (allPartsLength < cutOffLength) { - shortenedParts.add(part); - } - allPartsLength += ((LinkPart) part).getText().length(); - } else { - if (allPartsLength < cutOffLength) { - shortenedParts.add(part); - } - } - } - if (allPartsLength >= length) { - parts = shortenedParts; - } - } - render(parsedTextWriter, parts); + return soneTextParser.parse(context, new StringReader(text)); } catch (IOException ioe1) { - /* no exceptions in a StringReader or StringWriter, ignore. */ - } - return parsedTextWriter.toString(); - } - - // - // PRIVATE METHODS - // - - /** - * Renders the given parts. - * - * @param writer - * The writer to render the parts to - * @param parts - * The parts to render - */ - private void render(Writer writer, Iterable parts) { - for (Part part : parts) { - render(writer, part); + /* no exceptions in a StringReader, ignore. */ + return Collections.emptyList(); } } - /** - * Renders the given part. - * - * @param writer - * The writer to render the part to - * @param part - * The part to render - */ - @SuppressWarnings("unchecked") - private void render(Writer writer, Part part) { - if (part instanceof PlainTextPart) { - render(writer, (PlainTextPart) part); - } else if (part instanceof FreenetLinkPart) { - render(writer, (FreenetLinkPart) part); - } else if (part instanceof LinkPart) { - render(writer, (LinkPart) part); - } else if (part instanceof SonePart) { - render(writer, (SonePart) part); - } else if (part instanceof PostPart) { - render(writer, (PostPart) part); - } else if (part instanceof Iterable) { - render(writer, (Iterable) part); - } - } - - /** - * Renders the given plain-text part. - * - * @param writer - * The writer to render the part to - * @param plainTextPart - * The part to render - */ - private void render(Writer writer, PlainTextPart plainTextPart) { - TemplateContext templateContext = templateContextFactory.createTemplateContext(); - templateContext.set("text", plainTextPart.getText()); - plainTextTemplate.render(templateContext, writer); - } - - /** - * Renders the given freenet link part. - * - * @param writer - * The writer to render the part to - * @param freenetLinkPart - * The part to render - */ - private void render(Writer writer, FreenetLinkPart freenetLinkPart) { - renderLink(writer, "/" + freenetLinkPart.getLink(), freenetLinkPart.getText(), freenetLinkPart.getTitle(), freenetLinkPart.isTrusted() ? "freenet-trusted" : "freenet"); - } - - /** - * Renders the given link part. - * - * @param writer - * The writer to render the part to - * @param linkPart - * The part to render - */ - private void render(Writer writer, LinkPart linkPart) { - try { - renderLink(writer, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode(linkPart.getLink(), "UTF-8"), linkPart.getText(), linkPart.getTitle(), "internet"); - } catch (UnsupportedEncodingException uee1) { - /* not possible for UTF-8. */ - throw new RuntimeException("The JVM does not support UTF-8 encoding!", uee1); - } - } - - /** - * Renders the given Sone part. - * - * @param writer - * The writer to render the part to - * @param sonePart - * The part to render - */ - private void render(Writer writer, SonePart sonePart) { - if ((sonePart.getSone() != null) && (sonePart.getSone().getName() != null)) { - renderLink(writer, "viewSone.html?sone=" + sonePart.getSone().getId(), SoneAccessor.getNiceName(sonePart.getSone()), SoneAccessor.getNiceName(sonePart.getSone()), "in-sone"); - } else { - renderLink(writer, "/WebOfTrust/ShowIdentity?id=" + sonePart.getSone().getId(), sonePart.getSone().getId(), sonePart.getSone().getId(), "in-sone"); - } - } - - /** - * Renders the given post part. - * - * @param writer - * The writer to render the part to - * @param postPart - * The part to render - */ - private void render(Writer writer, PostPart postPart) { - SoneTextParser parser = new SoneTextParser(core, core); - SoneTextParserContext parserContext = new SoneTextParserContext(null, postPart.getPost().getSone()); - try { - Iterable parts = parser.parse(parserContext, new StringReader(postPart.getPost().getText())); - StringBuilder excerpt = new StringBuilder(); - for (Part part : parts) { - excerpt.append(part.getText()); - if (excerpt.length() > 20) { - int lastSpace = excerpt.lastIndexOf(" ", 20); - if (lastSpace > -1) { - excerpt.setLength(lastSpace); - } else { - excerpt.setLength(20); - } - excerpt.append("…"); - break; - } - } - renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), excerpt.toString(), SoneAccessor.getNiceName(postPart.getPost().getSone()), "in-sone"); - } catch (IOException ioe1) { - /* StringReader shouldn’t throw. */ - } - } - - /** - * Renders the given link. - * - * @param writer - * The writer to render the link to - * @param link - * The link to render - * @param text - * The text of the link - * @param title - * The title of the link - * @param cssClass - * The CSS class of the link - */ - private void renderLink(Writer writer, String link, String text, String title, String cssClass) { - TemplateContext templateContext = templateContextFactory.createTemplateContext(); - templateContext.set("cssClass", cssClass); - templateContext.set("link", link); - templateContext.set("text", text); - templateContext.set("title", title); - linkTemplate.render(templateContext, writer); - } - } diff --git a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java index 762bc14..eb280e1 100644 --- a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java @@ -65,7 +65,7 @@ public class ProfileAccessor extends ReflectionAccessor { if (avatarId == null) { return null; } - if (core.getImage(avatarId, false) == null) { + if (!profile.getSone().getImageByInternalId(avatarId).isPresent()) { /* avatar ID but no matching image? show nothing. */ return null; } diff --git a/src/main/java/net/pterodactylus/sone/template/RenderFilter.java b/src/main/java/net/pterodactylus/sone/template/RenderFilter.java new file mode 100644 index 0000000..1ab0d9e --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/template/RenderFilter.java @@ -0,0 +1,190 @@ +package net.pterodactylus.sone.template; + +import static java.lang.String.valueOf; +import static net.pterodactylus.sone.utils.NumberParsers.parseInt; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.text.AlbumPart; +import net.pterodactylus.sone.text.FreenetLinkPart; +import net.pterodactylus.sone.text.LinkPart; +import net.pterodactylus.sone.text.Part; +import net.pterodactylus.sone.text.PartContainer; +import net.pterodactylus.sone.text.PlainTextPart; +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.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; + +/** + * {@link Filter} implementation that renders an {@link Iterable} (such as a {@link PartContainer}) of {@link Part}s to HTML. + * + * @author David ‘Bombe’ Roden + */ +public class RenderFilter implements Filter { + + private static final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>")); + private static final Template linkTemplate = + TemplateParser.parse(new StringReader("\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html>")); + private final Core core; + private final TemplateContextFactory templateContextFactory; + + public RenderFilter(Core core, TemplateContextFactory templateContextFactory) { + this.core = core; + this.templateContextFactory = templateContextFactory; + } + + @Override + public Object format(TemplateContext templateContext, Object data, Map parameters) { + if (!(data instanceof Iterable)) { + return null; + } + Iterable parts = (Iterable) data; + int length = parseInt(valueOf(parameters.get("length")), -1); + int cutOffLength = parseInt(valueOf(parameters.get("cut-off-length")), length); + StringWriter parsedTextWriter = new StringWriter(); + if (length > -1) { + int allPartsLength = 0; + List shortenedParts = new ArrayList(); + for (Part part : parts) { + if (part instanceof PlainTextPart) { + String longText = part.getText(); + if (allPartsLength < cutOffLength) { + if ((allPartsLength + longText.length()) > cutOffLength) { + shortenedParts.add(new PlainTextPart(longText.substring(0, cutOffLength - allPartsLength) + "…")); + } else { + shortenedParts.add(part); + } + } + allPartsLength += longText.length(); + } else if (part instanceof LinkPart) { + if (allPartsLength < cutOffLength) { + shortenedParts.add(part); + } + allPartsLength += part.getText().length(); + } else { + if (allPartsLength < cutOffLength) { + shortenedParts.add(part); + } + } + } + if (allPartsLength >= length) { + parts = shortenedParts; + } + } + render(parsedTextWriter, parts); + return parsedTextWriter.toString(); + } + + private void render(Writer writer, Iterable parts) { + for (Part part : parts) { + render(writer, part); + } + } + + private void render(Writer writer, Part part) { + if (part instanceof PlainTextPart) { + render(writer, (PlainTextPart) part); + } else if (part instanceof FreenetLinkPart) { + render(writer, (FreenetLinkPart) part); + } else if (part instanceof LinkPart) { + render(writer, (LinkPart) part); + } else if (part instanceof SonePart) { + render(writer, (SonePart) part); + } else if (part instanceof PostPart) { + render(writer, (PostPart) part); + } else if (part instanceof AlbumPart) { + render(writer, (AlbumPart) part); + } else if (part instanceof Iterable) { + render(writer, (Iterable) part); + } + } + + private void render(Writer writer, PlainTextPart plainTextPart) { + TemplateContext templateContext = templateContextFactory.createTemplateContext(); + templateContext.set("text", plainTextPart.getText()); + plainTextTemplate.render(templateContext, writer); + } + + private void render(Writer writer, FreenetLinkPart freenetLinkPart) { + renderLink(writer, "/" + freenetLinkPart.getLink(), freenetLinkPart.getText(), freenetLinkPart.getTitle(), + freenetLinkPart.isTrusted() ? "freenet-trusted" : "freenet"); + } + + private void render(Writer writer, LinkPart linkPart) { + try { + renderLink(writer, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode(linkPart.getLink(), "UTF-8"), linkPart.getText(), + linkPart.getTitle(), "internet"); + } catch (UnsupportedEncodingException uee1) { + /* not possible for UTF-8. */ + throw new RuntimeException("The JVM does not support UTF-8 encoding!", uee1); + } + } + + private void render(Writer writer, SonePart sonePart) { + Sone sone = sonePart.getSone(); + if ((sone != null) && (sone.getName() != null)) { + String niceName = SoneAccessor.getNiceName(sone); + renderLink(writer, "viewSone.html?sone=" + sone.getId(), niceName, niceName, "in-sone"); + } else { + renderLink(writer, "/WebOfTrust/ShowIdentity?id=" + sone.getId(), sone.getId(), sone.getId(), "in-sone"); + } + } + + private void render(Writer writer, PostPart postPart) { + SoneTextParser parser = new SoneTextParser(core, core, core); + SoneTextParserContext parserContext = new SoneTextParserContext(null, postPart.getPost().getSone()); + try { + Iterable parts = parser.parse(parserContext, new StringReader(postPart.getPost().getText())); + StringBuilder excerpt = new StringBuilder(); + for (Part part : parts) { + excerpt.append(part.getText()); + if (excerpt.length() > 20) { + int lastSpace = excerpt.lastIndexOf(" ", 20); + if (lastSpace > -1) { + excerpt.setLength(lastSpace); + } else { + excerpt.setLength(20); + } + excerpt.append("…"); + break; + } + } + renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), excerpt.toString(), + SoneAccessor.getNiceName(postPart.getPost().getSone()), postPart.usesDeprecatedLink() ? "internet" : "in-sone"); + } catch (IOException ioe1) { + /* StringReader shouldn’t throw. */ + } + } + + private void render(Writer writer, AlbumPart albumPart) { + Album album = albumPart.getAlbum(); + renderLink(writer, String.format("imageBrowser.html?album=%s", album.getId()), album.getTitle(), album.getDescription(), "in-sone"); + } + + private void renderLink(Writer writer, String link, String text, String title, String cssClass) { + TemplateContext templateContext = templateContextFactory.createTemplateContext(); + templateContext.set("cssClass", cssClass); + templateContext.set("link", link); + templateContext.set("text", text); + templateContext.set("title", title); + linkTemplate.render(templateContext, writer); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/text/AlbumPart.java b/src/main/java/net/pterodactylus/sone/text/AlbumPart.java new file mode 100644 index 0000000..3f38443 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/text/AlbumPart.java @@ -0,0 +1,27 @@ +package net.pterodactylus.sone.text; + +import net.pterodactylus.sone.data.Album; + +/** + * {@link Part} implementation that contains information about the linked {@link Album}. + * + * @author David ‘Bombe’ Roden + */ +public class AlbumPart implements Part { + + private final Album album; + + public AlbumPart(Album album) { + this.album = album; + } + + public Album getAlbum() { + return album; + } + + @Override + public String getText() { + return album.getTitle(); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/text/PostPart.java b/src/main/java/net/pterodactylus/sone/text/PostPart.java index f7177c9..c6ab534 100644 --- a/src/main/java/net/pterodactylus/sone/text/PostPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PostPart.java @@ -26,39 +26,26 @@ import net.pterodactylus.sone.data.Post; */ public class PostPart implements Part { - /** The post this part refers to. */ private final Post post; + private final boolean usesDeprecatedLink; - /** - * Creates a new post part. - * - * @param post - * The referenced post - */ public PostPart(Post post) { - this.post = post; + this(post, false); } - // - // ACCESSORS - // + public PostPart(Post post, boolean usesDeprecatedLink) { + this.post = post; + this.usesDeprecatedLink = usesDeprecatedLink; + } - /** - * Returns the post referenced by this part. - * - * @return The post referenced by this part - */ public Post getPost() { return post; } - // - // PART METHODS - // + public boolean usesDeprecatedLink() { + return usesDeprecatedLink; + } - /** - * {@inheritDoc} - */ @Override public String getText() { return post.getText(); diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index a1675d7..9fcce7a 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -28,9 +28,11 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.impl.IdOnlySone; +import net.pterodactylus.sone.database.AlbumProvider; import net.pterodactylus.sone.database.PostProvider; import net.pterodactylus.sone.database.SoneProvider; import net.pterodactylus.util.io.Closer; @@ -66,7 +68,8 @@ public class SoneTextParser implements Parser { HTTP("http://", false), HTTPS("https://", false), SONE("sone://", false), - POST("post://", false); + POST("post://", false), + ALBUM("album://", false); private final String scheme; private final boolean freenetLink; @@ -91,23 +94,14 @@ public class SoneTextParser implements Parser { } - /** The Sone provider. */ private final SoneProvider soneProvider; - - /** The post provider. */ private final PostProvider postProvider; + private final AlbumProvider albumProvider; - /** - * Creates a new freenet link parser. - * - * @param soneProvider - * The Sone provider - * @param postProvider - * The post provider - */ - public SoneTextParser(SoneProvider soneProvider, PostProvider postProvider) { + public SoneTextParser(SoneProvider soneProvider, PostProvider postProvider, AlbumProvider albumProvider) { this.soneProvider = soneProvider; this.postProvider = postProvider; + this.albumProvider = albumProvider; } // @@ -204,19 +198,23 @@ public class SoneTextParser implements Parser { continue; } if (linkType == LinkType.POST) { - if (line.length() >= (7 + 36)) { - String postId = line.substring(7, 43); - Optional post = postProvider.getPost(postId); - if (post.isPresent()) { - parts.add(new PostPart(post.get())); - } else { - parts.add(new PlainTextPart(line.substring(0, 43))); - } - line = line.substring(43); + Optional post = postProvider.getPost(link.substring(7)); + if (post.isPresent()) { + parts.add(new PostPart(post.get(), link.substring(7).equals(post.get().getInternalId()))); } else { - parts.add(new PlainTextPart(line)); - line = ""; + parts.add(new PlainTextPart(link)); + } + line = line.substring(link.length()); + continue; + } + if (linkType == LinkType.ALBUM) { + Optional album = albumProvider.getAlbum(link.substring(linkType.getScheme().length())); + if (album.isPresent()) { + parts.add(new AlbumPart(album.get())); + } else { + parts.add(new PlainTextPart(link)); } + line = line.substring(link.length()); continue; } diff --git a/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java b/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java index c0a8907..b053b5e 100644 --- a/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java +++ b/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.web; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -53,7 +54,7 @@ public class BookmarkPage extends SoneTemplatePage { 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); + String id = request.getHttpRequest().getPartAsStringFailsafe("post", IdBuilder.ID_STRING_LENGTH); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); Optional post = webInterface.getCore().getPost(id); if (post.isPresent()) { diff --git a/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java index 1c599e4..338056a 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.text.TextFilter; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -26,6 +27,8 @@ import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.web.Method; +import com.google.common.base.Optional; + /** * Page that lets the user create a new album. * @@ -63,12 +66,17 @@ public class CreateAlbumPage extends SoneTemplatePage { } 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); + String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", IdBuilder.ID_STRING_LENGTH); + Optional parent; if (parentId.equals("")) { - parent = currentSone.getRootAlbum(); + parent = Optional.of(currentSone.getRootAlbum()); + } else { + parent = webInterface.getCore().getAlbum(parentId); + if (!parent.isPresent()) { + throw new RedirectException("noPermission.html"); + } } - Album album = webInterface.getCore().createAlbum(currentSone, parent); + Album album = webInterface.getCore().createAlbum(currentSone, parent.get()); try { album.modify().setTitle(name).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); } catch (AlbumTitleMustNotBeEmpty atmnbe) { diff --git a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java index 55903d8..2e59de7 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web; import com.google.common.base.Optional; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.text.TextFilter; @@ -56,7 +57,7 @@ public class CreateReplyPage extends SoneTemplatePage { @Override protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); - String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36); + String postId = request.getHttpRequest().getPartAsStringFailsafe("post", IdBuilder.ID_STRING_LENGTH); String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim(); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); if (request.getMethod() == Method.POST) { diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java index 98e8909..94fbaf1 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java @@ -18,11 +18,14 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.IdBuilder; 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; +import com.google.common.base.Optional; + /** * Page that lets the user delete an {@link Album}. * @@ -49,27 +52,27 @@ public class DeleteAlbumPage extends SoneTemplatePage { 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); - if (album == null) { + String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", IdBuilder.ID_STRING_LENGTH); + Optional album = webInterface.getCore().getAlbum(albumId); + if (!album.isPresent()) { throw new RedirectException("invalid.html"); } - if (!album.getSone().isLocal()) { + if (!album.get().getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if (request.getHttpRequest().isPartSet("abortDelete")) { - throw new RedirectException("imageBrowser.html?album=" + album.getId()); + throw new RedirectException("imageBrowser.html?album=" + album.get().getId()); } - Album parentAlbum = album.getParent(); - webInterface.getCore().deleteAlbum(album); - if (parentAlbum.equals(album.getSone().getRootAlbum())) { - throw new RedirectException("imageBrowser.html?sone=" + album.getSone().getId()); + Album parentAlbum = album.get().getParent(); + webInterface.getCore().deleteAlbum(album.get()); + if (parentAlbum.equals(album.get().getSone().getRootAlbum())) { + throw new RedirectException("imageBrowser.html?sone=" + album.get().getSone().getId()); } throw new RedirectException("imageBrowser.html?album=" + parentAlbum.getId()); } String albumId = request.getHttpRequest().getParam("album"); - Album album = webInterface.getCore().getAlbum(albumId); - if (album == null) { + Optional album = webInterface.getCore().getAlbum(albumId); + if (!album.isPresent()) { 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 index 77f3ab8..9d50bfb 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.web; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -52,7 +53,8 @@ public class DeleteImagePage extends SoneTemplatePage { @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"); + String imageId = (request.getMethod() == Method.POST) ? request.getHttpRequest().getPartAsStringFailsafe("image", IdBuilder.ID_STRING_LENGTH) + : request.getHttpRequest().getParam("image"); Image image = webInterface.getCore().getImage(imageId, false); if (image == null) { throw new RedirectException("invalid.html"); diff --git a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java index 5a309fa..c8e0af0 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web; import com.google.common.base.Optional; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -64,7 +65,7 @@ public class DeletePostPage extends SoneTemplatePage { templateContext.set("post", post.get()); templateContext.set("returnPage", returnPage); } else if (request.getMethod() == Method.POST) { - String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36); + String postId = request.getHttpRequest().getPartAsStringFailsafe("post", IdBuilder.ID_STRING_LENGTH); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); Optional post = webInterface.getCore().getPost(postId); if (!post.isPresent() || !post.get().getSone().isLocal()) { diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java index f7ca132..8e48dd3 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.web; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -54,7 +55,7 @@ public class DeleteReplyPage extends SoneTemplatePage { @Override protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); - String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", 36); + String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", IdBuilder.ID_STRING_LENGTH); Optional reply = webInterface.getCore().getPostReply(replyId); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); if (request.getMethod() == Method.POST) { diff --git a/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java index 66434e5..f54f887 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java @@ -19,12 +19,15 @@ package net.pterodactylus.sone.web; import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.text.TextFilter; 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; +import com.google.common.base.Optional; + /** * Page that lets the user edit the name and description of an album. * @@ -51,37 +54,37 @@ public class EditAlbumPage extends SoneTemplatePage { 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); - if (album == null) { + String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", IdBuilder.ID_STRING_LENGTH); + Optional album = webInterface.getCore().getAlbum(albumId); + if (!album.isPresent()) { throw new RedirectException("invalid.html"); } - if (!album.getSone().isLocal()) { + if (!album.get().getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveLeft", 4))) { - album.getParent().moveAlbumUp(album); + album.get().getParent().moveAlbumUp(album.get()); webInterface.getCore().touchConfiguration(); - throw new RedirectException("imageBrowser.html?album=" + album.getParent().getId()); + throw new RedirectException("imageBrowser.html?album=" + album.get().getParent().getId()); } else if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveRight", 4))) { - album.getParent().moveAlbumDown(album); + album.get().getParent().moveAlbumDown(album.get()); webInterface.getCore().touchConfiguration(); - throw new RedirectException("imageBrowser.html?album=" + album.getParent().getId()); + throw new RedirectException("imageBrowser.html?album=" + album.get().getParent().getId()); } - String albumImageId = request.getHttpRequest().getPartAsStringFailsafe("album-image", 36); + String albumImageId = request.getHttpRequest().getPartAsStringFailsafe("album-image", IdBuilder.ID_STRING_LENGTH); if (webInterface.getCore().getImage(albumImageId, false) == null) { albumImageId = null; } - album.modify().setAlbumImage(albumImageId).update(); + album.get().modify().setAlbumImage(albumImageId).update(); String title = request.getHttpRequest().getPartAsStringFailsafe("title", 100).trim(); String description = request.getHttpRequest().getPartAsStringFailsafe("description", 1000).trim(); try { - album.modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); + album.get().modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); } catch (AlbumTitleMustNotBeEmpty atmnbe) { throw new RedirectException("emptyAlbumTitle.html"); } webInterface.getCore().touchConfiguration(); - throw new RedirectException("imageBrowser.html?album=" + album.getId()); + throw new RedirectException("imageBrowser.html?album=" + album.get().getId()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/EditImagePage.java b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java index 178add1..304b19c 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.web; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.text.TextFilter; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -54,7 +55,7 @@ public class EditImagePage extends SoneTemplatePage { 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 imageId = request.getHttpRequest().getPartAsStringFailsafe("image", IdBuilder.ID_STRING_LENGTH); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); Image image = webInterface.getCore().getImage(imageId, false); if (image == null) { diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java index 162d637..47fd8c3 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java @@ -22,6 +22,7 @@ import static net.pterodactylus.sone.utils.NumberParsers.parseInt; import java.util.List; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.DuplicateField; import net.pterodactylus.sone.data.Profile.Field; @@ -80,12 +81,12 @@ public class EditProfilePage extends SoneTemplatePage { birthDay = parseInt(request.getHttpRequest().getPartAsStringFailsafe("birth-day", 256).trim(), null); birthMonth = parseInt(request.getHttpRequest().getPartAsStringFailsafe("birth-month", 256).trim(), null); birthYear = parseInt(request.getHttpRequest().getPartAsStringFailsafe("birth-year", 256).trim(), null); - avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatarId", 36); + avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatarId", IdBuilder.ID_STRING_LENGTH); profile.setFirstName(firstName.length() > 0 ? firstName : null); profile.setMiddleName(middleName.length() > 0 ? middleName : null); profile.setLastName(lastName.length() > 0 ? lastName : null); profile.setBirthDay(birthDay).setBirthMonth(birthMonth).setBirthYear(birthYear); - profile.setAvatar(webInterface.getCore().getImage(avatarId, false)); + profile.setAvatar(currentSone.getImageByInternalId(avatarId).orNull()); for (Field field : fields) { String value = request.getHttpRequest().getPartAsStringFailsafe("field-" + field.getId(), 400); String filteredValue = filter(request.getHttpRequest().getHeader("Host"), value); diff --git a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java index 60b22d5..36805b3 100644 --- a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java @@ -69,9 +69,9 @@ public class ImageBrowserPage extends SoneTemplatePage { super.processTemplate(request, templateContext); String albumId = request.getHttpRequest().getParam("album", null); if (albumId != null) { - Album album = webInterface.getCore().getAlbum(albumId); + Optional album = webInterface.getCore().getAlbum(albumId); templateContext.set("albumRequested", true); - templateContext.set("album", album); + templateContext.set("album", album.orNull()); templateContext.set("page", request.getHttpRequest().getParam("page")); return; } diff --git a/src/main/java/net/pterodactylus/sone/web/LikePage.java b/src/main/java/net/pterodactylus/sone/web/LikePage.java index 7f08e65..30bf4f0 100644 --- a/src/main/java/net/pterodactylus/sone/web/LikePage.java +++ b/src/main/java/net/pterodactylus/sone/web/LikePage.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.web; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -55,7 +56,7 @@ public class LikePage extends SoneTemplatePage { super.processTemplate(request, templateContext); if (request.getMethod() == Method.POST) { String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16); - String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36); + String id = request.getHttpRequest().getPartAsStringFailsafe(type, IdBuilder.ID_STRING_LENGTH); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); Sone currentSone = getCurrentSone(request.getToadletContext()); if ("post".equals(type)) { diff --git a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java index c38f97b..1332638 100644 --- a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java +++ b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java @@ -22,6 +22,7 @@ import static net.pterodactylus.sone.utils.NumberParsers.parseInt; import java.util.ArrayList; import java.util.List; +import net.pterodactylus.sone.core.CompatibilityMode; import net.pterodactylus.sone.core.Preferences; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; @@ -133,6 +134,14 @@ public class OptionsPage extends SoneTemplatePage { Integer fcpFullAccessRequiredInteger = parseInt(request.getHttpRequest().getPartAsStringFailsafe("fcp-full-access-required", 1), preferences.getFcpFullAccessRequired().ordinal()); FullAccessRequired fcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequiredInteger]; preferences.setFcpFullAccessRequired(fcpFullAccessRequired); + + boolean compatOldElementIds = request.getHttpRequest().isPartSet("compat-old-element-ids"); + if (compatOldElementIds) { + webInterface.getCore().setCompatibilityMode(CompatibilityMode.oldElementIds); + } else { + webInterface.getCore().clearCompatibilityMod(CompatibilityMode.oldElementIds); + } + webInterface.getCore().touchConfiguration(); if (fieldErrors.isEmpty()) { throw new RedirectException(getPath()); @@ -158,6 +167,7 @@ public class OptionsPage extends SoneTemplatePage { templateContext.set("trust-comment", preferences.getTrustComment()); templateContext.set("fcp-interface-active", preferences.isFcpInterfaceActive()); templateContext.set("fcp-full-access-required", preferences.getFcpFullAccessRequired().ordinal()); + templateContext.set("compat-old-element-ids", webInterface.getCore().isCompatibilityMode(CompatibilityMode.oldElementIds)); } } diff --git a/src/main/java/net/pterodactylus/sone/web/SearchPage.java b/src/main/java/net/pterodactylus/sone/web/SearchPage.java index 95b0bba..fd26f44 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 java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; @@ -356,7 +357,7 @@ public class SearchPage extends SoneTemplatePage { */ private String getAlbumId(String phrase) { String albumId = phrase.startsWith("album://") ? phrase.substring(8) : phrase; - return (webInterface.getCore().getAlbum(albumId) != null) ? albumId : null; + return webInterface.getCore().getAlbum(albumId).isPresent() ? albumId : null; } /** diff --git a/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java b/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java index 72ff2fc..3be06ae 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web; import java.util.Set; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -55,7 +56,7 @@ public class UnbookmarkPage extends SoneTemplatePage { 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); + String id = request.getHttpRequest().getPartAsStringFailsafe("post", IdBuilder.ID_STRING_LENGTH); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); Optional post = webInterface.getCore().getPost(id); if (post.isPresent()) { diff --git a/src/main/java/net/pterodactylus/sone/web/UnlikePage.java b/src/main/java/net/pterodactylus/sone/web/UnlikePage.java index 9254a42..16291bf 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnlikePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnlikePage.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.web; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -55,7 +56,7 @@ public class UnlikePage extends SoneTemplatePage { super.processTemplate(request, templateContext); if (request.getMethod() == Method.POST) { String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16); - String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36); + String id = request.getHttpRequest().getPartAsStringFailsafe(type, IdBuilder.ID_STRING_LENGTH); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); Sone currentSone = getCurrentSone(request.getToadletContext()); if ("post".equals(type)) { diff --git a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java index a1e987a..673e439 100644 --- a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java @@ -34,6 +34,7 @@ import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.IdBuilder; import net.pterodactylus.sone.data.Image.Modifier.ImageTitleMustNotBeEmpty; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.TemporaryImage; @@ -44,6 +45,7 @@ import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.web.Method; +import com.google.common.base.Optional; import com.google.common.io.ByteStreams; import freenet.support.api.Bucket; @@ -83,12 +85,12 @@ public class UploadImagePage extends SoneTemplatePage { 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); - if (parent == null) { + String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", IdBuilder.ID_STRING_LENGTH); + Optional parent = webInterface.getCore().getAlbum(parentId); + if (!parent.isPresent()) { throw new RedirectException("noPermission.html"); } - if (!currentSone.equals(parent.getSone())) { + if (!currentSone.equals(parent.get().getSone())) { throw new RedirectException("noPermission.html"); } String name = request.getHttpRequest().getPartAsStringFailsafe("title", 200); @@ -122,7 +124,7 @@ public class UploadImagePage extends SoneTemplatePage { } String mimeType = getMimeType(imageData); TemporaryImage temporaryImage = webInterface.getCore().createTemporaryImage(mimeType, imageData); - net.pterodactylus.sone.data.Image image = webInterface.getCore().createImage(currentSone, parent, temporaryImage); + net.pterodactylus.sone.data.Image image = webInterface.getCore().createImage(currentSone, parent.get(), temporaryImage); image.modify().setTitle(name).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).setWidth(uploadedImage.getWidth(null)).setHeight(uploadedImage.getHeight(null)).update(); } catch (IOException ioe1) { logger.log(Level.WARNING, "Could not read uploaded image!", ioe1); @@ -133,7 +135,7 @@ public class UploadImagePage extends SoneTemplatePage { Closer.close(imageDataInputStream); Closer.flush(uploadedImage); } - throw new RedirectException("imageBrowser.html?album=" + parent.getId()); + throw new RedirectException("imageBrowser.html?album=" + parent.get().getId()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index beba513..06ebf8d 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -73,6 +73,7 @@ import net.pterodactylus.sone.main.ReparseFilter; import net.pterodactylus.sone.main.SonePlugin; import net.pterodactylus.sone.notify.ListNotification; import net.pterodactylus.sone.template.AlbumAccessor; +import net.pterodactylus.sone.template.BuildIdFilter; import net.pterodactylus.sone.template.CollectionAccessor; import net.pterodactylus.sone.template.CssClassNameFilter; import net.pterodactylus.sone.template.HttpRequestAccessor; @@ -83,6 +84,7 @@ import net.pterodactylus.sone.template.JavascriptFilter; import net.pterodactylus.sone.template.ParserFilter; import net.pterodactylus.sone.template.PostAccessor; import net.pterodactylus.sone.template.ProfileAccessor; +import net.pterodactylus.sone.template.RenderFilter; import net.pterodactylus.sone.template.ReplyAccessor; import net.pterodactylus.sone.template.ReplyGroupFilter; import net.pterodactylus.sone.template.RequestChangeFilter; @@ -193,6 +195,7 @@ public class WebInterface { /** The parser filter. */ private final ParserFilter parserFilter; + private final RenderFilter renderFilter; /** The “new Sone” notification. */ private final ListNotification newSoneNotification; @@ -247,7 +250,7 @@ public class WebInterface { this.sonePlugin = sonePlugin; this.loaders = loaders; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); - soneTextParser = new SoneTextParser(getCore(), getCore()); + soneTextParser = new SoneTextParser(getCore(), getCore(), getCore()); templateContextFactory = new TemplateContextFactory(); templateContextFactory.addAccessor(Object.class, new ReflectionAccessor()); @@ -272,8 +275,9 @@ public class WebInterface { templateContextFactory.addFilter("match", new MatchFilter()); templateContextFactory.addFilter("css", new CssClassNameFilter()); templateContextFactory.addFilter("js", new JavascriptFilter()); - templateContextFactory.addFilter("parse", parserFilter = new ParserFilter(getCore(), templateContextFactory, soneTextParser)); + templateContextFactory.addFilter("parse", parserFilter = new ParserFilter(getCore(), soneTextParser)); templateContextFactory.addFilter("reparse", new ReparseFilter()); + templateContextFactory.addFilter("render", renderFilter = new RenderFilter(getCore(), templateContextFactory)); templateContextFactory.addFilter("unknown", new UnknownDateFilter(getL10n(), "View.Sone.Text.UnknownDate")); templateContextFactory.addFilter("format", new FormatFilter()); templateContextFactory.addFilter("sort", new CollectionSortFilter()); @@ -283,6 +287,7 @@ public class WebInterface { templateContextFactory.addFilter("unique", new UniqueElementFilter()); templateContextFactory.addFilter("mod", new ModFilter()); templateContextFactory.addFilter("paginate", new PaginationFilter()); + templateContextFactory.addFilter("build-id", new BuildIdFilter()); templateContextFactory.addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER); templateContextFactory.addProvider(loaders.getTemplateProvider()); templateContextFactory.addTemplateObject("webInterface", this); @@ -704,8 +709,8 @@ public class WebInterface { 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, parserFilter))); + pageToadlets.add(pageToadletFactory.createPageToadlet(new EditAlbumAjaxPage(this, parserFilter, renderFilter))); + pageToadlets.add(pageToadletFactory.createPageToadlet(new EditImageAjaxPage(this, parserFilter, renderFilter))); pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustAjaxPage(this))); pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustAjaxPage(this))); pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustAjaxPage(this))); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java index 238206a..6fe06bc 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java @@ -17,10 +17,20 @@ package net.pterodactylus.sone.web.ajax; +import java.util.Collections; + import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.Image; +import net.pterodactylus.sone.template.ParserFilter; +import net.pterodactylus.sone.template.RenderFilter; +import net.pterodactylus.sone.text.Part; import net.pterodactylus.sone.text.TextFilter; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; +import net.pterodactylus.util.template.TemplateContext; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; /** * Page that stores a user’s album modifications. @@ -29,14 +39,13 @@ import net.pterodactylus.sone.web.page.FreenetRequest; */ public class EditAlbumAjaxPage extends JsonPage { - /** - * Creates a new edit album AJAX page. - * - * @param webInterface - * The Sone web interface - */ - public EditAlbumAjaxPage(WebInterface webInterface) { + private final ParserFilter parserFilter; + private final RenderFilter renderFilter; + + public EditAlbumAjaxPage(WebInterface webInterface, ParserFilter parserFilter, RenderFilter renderFilter) { super("editAlbum.ajax", webInterface); + this.parserFilter = parserFilter; + this.renderFilter = renderFilter; } // @@ -49,32 +58,38 @@ public class EditAlbumAjaxPage extends JsonPage { @Override protected JsonReturnObject createJsonObject(FreenetRequest request) { String albumId = request.getHttpRequest().getParam("album"); - Album album = webInterface.getCore().getAlbum(albumId); - if (album == null) { + Optional album = webInterface.getCore().getAlbum(albumId); + if (!album.isPresent()) { return createErrorJsonObject("invalid-album-id"); } - if (!album.getSone().isLocal()) { + if (!album.get().getSone().isLocal()) { return createErrorJsonObject("not-authorized"); } if ("true".equals(request.getHttpRequest().getParam("moveLeft"))) { - Album swappedAlbum = album.getParent().moveAlbumUp(album); + Album swappedAlbum = album.get().getParent().moveAlbumUp(album.get()); webInterface.getCore().touchConfiguration(); - return createSuccessJsonObject().put("sourceAlbumId", album.getId()).put("destinationAlbumId", swappedAlbum.getId()); + return createSuccessJsonObject().put("sourceAlbumId", album.get().getId()).put("destinationAlbumId", swappedAlbum.getId()); } if ("true".equals(request.getHttpRequest().getParam("moveRight"))) { - Album swappedAlbum = album.getParent().moveAlbumDown(album); + Album swappedAlbum = album.get().getParent().moveAlbumDown(album.get()); webInterface.getCore().touchConfiguration(); - return createSuccessJsonObject().put("sourceAlbumId", album.getId()).put("destinationAlbumId", swappedAlbum.getId()); + return createSuccessJsonObject().put("sourceAlbumId", album.get().getId()).put("destinationAlbumId", swappedAlbum.getId()); } String title = request.getHttpRequest().getParam("title").trim(); String description = request.getHttpRequest().getParam("description").trim(); try { - album.modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); + album.get().modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); webInterface.getCore().touchConfiguration(); - return createSuccessJsonObject().put("albumId", album.getId()).put("title", album.getTitle()).put("description", album.getDescription()); + return createSuccessJsonObject().put("albumId", album.get().getId()).put("title", album.get().getTitle()).put("description", parseDescription(album.get())); } catch (IllegalStateException e) { return createErrorJsonObject("invalid-album-title"); } } + private String parseDescription(Album album) { + Iterable parts = (Iterable) parserFilter.format(new TemplateContext(), album.getDescription(), + ImmutableMap.builder().put("sone", album.getSone()).build()); + return (String) renderFilter.format(new TemplateContext(), parts, Collections.emptyMap()); + } + } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java index 79de200..a40a955 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java @@ -17,8 +17,12 @@ package net.pterodactylus.sone.web.ajax; +import java.util.Collections; + import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.template.ParserFilter; +import net.pterodactylus.sone.template.RenderFilter; +import net.pterodactylus.sone.text.Part; import net.pterodactylus.sone.text.TextFilter; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -33,8 +37,8 @@ import com.google.common.collect.ImmutableMap; */ public class EditImageAjaxPage extends JsonPage { - /** Parser for image descriptions. */ private final ParserFilter parserFilter; + private final RenderFilter renderFilter; /** * Creates a new edit image AJAX page. @@ -44,9 +48,10 @@ public class EditImageAjaxPage extends JsonPage { * @param parserFilter * The parser filter for image descriptions */ - public EditImageAjaxPage(WebInterface webInterface, ParserFilter parserFilter) { + public EditImageAjaxPage(WebInterface webInterface, ParserFilter parserFilter, RenderFilter renderFilter) { super("editImage.ajax", webInterface); this.parserFilter = parserFilter; + this.renderFilter = renderFilter; } // @@ -83,7 +88,16 @@ public class EditImageAjaxPage extends JsonPage { String description = request.getHttpRequest().getParam("description").trim(); image.modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); webInterface.getCore().touchConfiguration(); - return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", (String) parserFilter.format(new TemplateContext(), image.getDescription(), ImmutableMap.builder().put("sone", image.getSone()).build())); + return createSuccessJsonObject().put("imageId", image.getId()) + .put("title", image.getTitle()) + .put("description", image.getDescription()) + .put("parsedDescription", parseDescription(image)); + } + + private String parseDescription(Image image) { + Iterable parts = (Iterable) parserFilter.format(new TemplateContext(), image.getDescription(), + ImmutableMap.builder().put("sone", image.getSone()).build()); + return (String) renderFilter.format(new TemplateContext(), parts, Collections.emptyMap()); } } diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties index 40a12b1..12927a5 100644 --- a/src/main/resources/i18n/sone.en.properties +++ b/src/main/resources/i18n/sone.en.properties @@ -69,6 +69,9 @@ Page.Options.Option.FcpFullAccessRequired.Description=Require FCP connection fro Page.Options.Option.FcpFullAccessRequired.Value.No=No Page.Options.Option.FcpFullAccessRequired.Value.Writing=For Write Access Page.Options.Option.FcpFullAccessRequired.Value.Always=Always +Page.Options.Section.CompatibilityOptions.Title=Compatibility Options +Page.Options.Section.CompatibilityOptions.Description=These options control how much deprecated functionality Sone will support. +Page.Options.Option.CompatibilityOptions.OldElementIds.Description=Support old post IDs in links. Activating this will try to locate a post linked to by an old ID even though it can not be guaranteed that it is the post that was originally linked to. Page.Options.Section.Cleaning.Title=Clean Up Page.Options.Option.ClearOnNextRestart.Description=Resets the configuration of the Sone plugin at the next restart. Warning! {strong}This will destroy all of your Sones{/strong} so make sure you have backed up everyhing you still need! Also, you need to set the next option to true to actually do it. Page.Options.Option.ReallyClearOnNextRestart.Description=This option needs to be set to “yes” if you really, {strong}really{/strong} want to clear the plugin configuration on the next restart. @@ -367,6 +370,7 @@ View.Post.ShowSource=Toggle Parser View.Post.NotDownloaded=This post has not yet been downloaded, or it has been deleted. View.Post.ShowMore=show more View.Post.ShowLess=show less +View.Post.LinkedAlbum.SizeAndAuthor={1,number} {1,choice,0#images|1#image|1 <%foreach currentSone.allImages image>
  • - checked="checked"<%/if>/> + checked="checked"<%/if>/>
    <% image|image-link max-width==48 max-height==48 mode==enlarge title=image.title>
  • <%/foreach> diff --git a/src/main/resources/templates/imageBrowser.html b/src/main/resources/templates/imageBrowser.html index 7dd4c29..a237c7f 100644 --- a/src/main/resources/templates/imageBrowser.html +++ b/src/main/resources/templates/imageBrowser.html @@ -244,7 +244,7 @@ var albumDescriptionField = getAlbum(data.albumId).find(".album-description"); if (data.success) { albumTitleField.text(data.title); - albumDescriptionField.text(data.description); + albumDescriptionField.html(data.description); getAlbum(data.albumId).find(":input[name='title']").attr("defaultValue", title); getAlbum(data.albumId).find(":input[name='description']").attr("defaultValue", description); } else { @@ -341,7 +341,7 @@ <%/foreach> -

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

    +

    <% album.description|parse sone=album.sone|render>

    <%if album.sone.local> @@ -400,7 +400,7 @@
    <% image.title|html>
    -
    <% image.description|parse sone=image.sone>
    +
    <% image.description|parse sone=image.sone|render>
    <%if album.sone.local>
    @@ -512,7 +512,7 @@ <%/if> -

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

    +

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

    <%if image.sone.local> @@ -630,7 +630,7 @@
    <% album.title|html> (<%= View.Sone.Stats.Images|l10n 0=album.images.size>)
    -
    <% album.description|parse sone=album.sone>
    +
    <% album.description|parse sone=album.sone|render>
    <%= false|store key==endRow> diff --git a/src/main/resources/templates/include/browseAlbums.html b/src/main/resources/templates/include/browseAlbums.html index 9aacad3..74bf714 100644 --- a/src/main/resources/templates/include/browseAlbums.html +++ b/src/main/resources/templates/include/browseAlbums.html @@ -1,48 +1,7 @@ <%foreach albums album> <%first>

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

    <%/first> <%if loop.count|mod divisor==3>
    <%/if> -
    - - -
    -
    <% album.title|html> (<%= View.Sone.Stats.Images|l10n 0=album.images.size>)
    -
    <% album.description|parse sone=album.sone>
    -
    - <%if album.sone.local> - - - - - -
    - - -
    - - - - <%/if> -
    + <%include include/viewAlbum.html> <%= false|store key==endRow> <%if loop.count|mod divisor==3 offset==1><%= true|store key==endRow><%/if> <%last><%= true|store key==endRow><%/last> diff --git a/src/main/resources/templates/include/head.html b/src/main/resources/templates/include/head.html index 13b564d..74091ca 100644 --- a/src/main/resources/templates/include/head.html +++ b/src/main/resources/templates/include/head.html @@ -44,7 +44,7 @@ <%ifnull !currentSone> <%ifnull !currentSone.profile.avatar> - <%currentSone.profile.avatar|image-link max-width==80 max-height==80 mode==enlarge title=="Profile Avatar"> + <%currentSone.profile.avatar|build-id sone=currentSone|image-link max-width==80 max-height==80 mode==enlarge title=="Profile Avatar"> <%else> Profile Avatar <%/if> diff --git a/src/main/resources/templates/include/soneMenu.html b/src/main/resources/templates/include/soneMenu.html index fc51e79..a93b0a7 100644 --- a/src/main/resources/templates/include/soneMenu.html +++ b/src/main/resources/templates/include/soneMenu.html @@ -2,7 +2,7 @@