--- /dev/null
+image: alpine/edge
+packages:
+ - openjdk8
+sources:
+ - https://git.sr.ht/~bombe/sone
+tasks:
+ - clean: |
+ cd sone
+ ./gradlew clean
+ - build: |
+ cd sone
+ ./gradlew build
+ - test: |
+ cd sone
+ ./gradlew test
+ - jar: |
+ cd sone
+ ./gradlew fatJar
/target
/src/generated
+
+/.gradle/
+/build/
--- /dev/null
+pipeline {
+ agent any
+
+ options {
+ timestamps()
+ }
+
+ stages {
+ stage('Build') {
+ steps {
+ sh './gradlew clean classes testClasses'
+ }
+ }
+ stage('Test') {
+ steps {
+ sh './gradlew test'
+ }
+ post {
+ always {
+ junit 'build/test-results/test/*.xml'
+ }
+ }
+ }
+ stage('Binary') {
+ steps {
+ sh './gradlew fatJar'
+ archiveArtifacts artifacts: 'build/libs/sone*-jar-with-dependencies.jar', fingerprint: true
+ }
+ }
+ stage('Reports') {
+ steps {
+ sh './gradlew jacocoTestReport findbugsMain countLines'
+ jacoco classPattern: 'build/classes/*/main', sourcePattern: '**/src/main/'
+ findbugs canComputeNew: false, defaultEncoding: '', excludePattern: '', healthy: '', includePattern: '', pattern: '**/findbugs/main.xml', unHealthy: ''
+ sloccountPublish encoding: '', pattern: 'build/reports/cloc/*.xml'
+ }
+ }
+ }
+}
group = 'net.pterodactylus'
-version = '0.9.8'
+version = '79'
buildscript {
- ext.kotlinVersion = '1.2.0'
+ ext.kotlinVersion = '1.2.71'
repositories {
mavenCentral()
}
dependencies {
- classpath group: 'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: '1.1.11'
+ classpath group: 'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: '1.4.0'
classpath group: 'org.jetbrains.kotlin', name: 'kotlin-gradle-plugin', version: kotlinVersion
classpath group: 'org.jetbrains.kotlin', name: 'kotlin-noarg', version: kotlinVersion
}
}
repositories {
- maven { url "http://maven.pterodactylus.net/" }
mavenCentral()
+ maven { url "https://maven.pterodactylus.net/" }
}
apply plugin: 'java'
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib'
compile group: 'net.pterodactylus', name: 'utils', version: '0.12.4'
- compile group: 'com.google.inject', name: 'guice', version: '3.0'
- compile group: 'com.google.guava', name: 'guava', version: '14.0.1'
+ compile group: 'com.google.inject', name: 'guice', version: '4.2.2'
+ compile group: 'com.google.guava', name: 'guava', version: '27.0.1-android'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.1'
compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.9.1'
compile group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
compile group: 'org.jsoup', name: 'jsoup', version: '1.10.2'
- testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test'
+ testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit'
testCompile group: 'junit', name: 'junit', version: '4.11'
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.10.0'
testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'
apply from: 'version.gradle'
+test {
+ maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
+}
+
task fatJar(type: Jar) {
- archiveName = project.name + '-jar-with-dependencies.jar'
+ archiveName = project.name.toLowerCase() + '-jar-with-dependencies.jar'
from { (configurations.runtime - configurations.provided).collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes('Plugin-Main-Class': 'net.pterodactylus.sone.main.SonePlugin')
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip
-rootProject.name = 'sone'
+rootProject.name = 'Sone'
/**
* Parses a {@link Sone}’s data from a {@link Configuration}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ConfigurationSoneParser {
private final Configuration configuration;
private final Sone sone;
private final String sonePrefix;
- private final Map<String, Album> albums = new HashMap<String, Album>();
- private final List<Album> topLevelAlbums = new ArrayList<Album>();
- private final Map<String, Image> images = new HashMap<String, Image>();
+ private final Map<String, Album> albums = new HashMap<>();
+ private final List<Album> topLevelAlbums = new ArrayList<>();
+ private final Map<String, Image> images = new HashMap<>();
public ConfigurationSoneParser(Configuration configuration, Sone sone) {
this.configuration = configuration;
public Set<Post> parsePosts(PostBuilderFactory postBuilderFactory)
throws InvalidPostFound {
- Set<Post> posts = new HashSet<Post>();
+ Set<Post> posts = new HashSet<>();
while (true) {
String postPrefix = "/Posts/" + posts.size();
String postId = getString(postPrefix + "/ID", null);
public Set<PostReply> parsePostReplies(
PostReplyBuilderFactory postReplyBuilderFactory) {
- Set<PostReply> replies = new HashSet<PostReply>();
+ Set<PostReply> replies = new HashSet<>();
while (true) {
String replyPrefix = "/Replies/" + replies.size();
String replyId = getString(replyPrefix + "/ID", null);
}
public Set<String> parseLikedPostIds() {
- Set<String> likedPostIds = new HashSet<String>();
+ Set<String> likedPostIds = new HashSet<>();
while (true) {
String likedPostId =
getString("/Likes/Post/" + likedPostIds.size() + "/ID",
}
public Set<String> parseLikedPostReplyIds() {
- Set<String> likedPostReplyIds = new HashSet<String>();
+ Set<String> likedPostReplyIds = new HashSet<>();
while (true) {
String likedReplyId = getString(
"/Likes/Reply/" + likedPostReplyIds.size() + "/ID", null);
}
public Set<String> parseFriends() {
- Set<String> friends = new HashSet<String>();
+ Set<String> friends = new HashSet<>();
while (true) {
String friendId =
getString("/Friends/" + friends.size() + "/ID", null);
/*
- * Sone - Core.java - Copyright © 2010–2016 David Roden
+ * Sone - Core.java - Copyright © 2010–2019 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
import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound;
import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound;
import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound;
-import net.pterodactylus.sone.core.SoneChangeDetector.PostProcessor;
-import net.pterodactylus.sone.core.SoneChangeDetector.PostReplyProcessor;
import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent;
import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
import net.pterodactylus.sone.core.event.MarkPostKnownEvent;
/**
* The Sone core.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider {
/** The trust updater. */
private final WebOfTrustUpdater webOfTrustUpdater;
- /** The times Sones were followed. */
- private final Map<String, Long> soneFollowingTimes = new HashMap<String, Long>();
-
/** Locked local Sones. */
/* synchronize on itself. */
- private final Set<Sone> lockedSones = new HashSet<Sone>();
+ private final Set<Sone> lockedSones = new HashSet<>();
/** Sone inserters. */
/* synchronize access on this on sones. */
- private final Map<Sone, SoneInserter> soneInserters = new HashMap<Sone, SoneInserter>();
+ private final Map<Sone, SoneInserter> soneInserters = new HashMap<>();
/** Sone rescuers. */
/* synchronize access on this on sones. */
- private final Map<Sone, SoneRescuer> soneRescuers = new HashMap<Sone, SoneRescuer>();
+ private final Map<Sone, SoneRescuer> soneRescuers = new HashMap<>();
/** All known Sones. */
- private final Set<String> knownSones = new HashSet<String>();
+ private final Set<String> knownSones = new HashSet<>();
/** The post database. */
private final Database database;
private final Multimap<OwnIdentity, Identity> trustedIdentities = Multimaps.synchronizedSetMultimap(HashMultimap.<OwnIdentity, Identity>create());
/** All temporary images. */
- private final Map<String, TemporaryImage> temporaryImages = new HashMap<String, TemporaryImage>();
+ private final Map<String, TemporaryImage> temporaryImages = new HashMap<>();
/** Ticker for threads that mark own elements as known. */
private final ScheduledExecutorService localElementTicker = Executors.newScheduledThreadPool(1);
* The database
*/
@Inject
- public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) {
- super("Sone Core");
- this.configuration = configuration;
- this.freenetInterface = freenetInterface;
- this.identityManager = identityManager;
- this.soneDownloader = new SoneDownloaderImpl(this, freenetInterface);
- this.imageInserter = new ImageInserter(freenetInterface, freenetInterface.new InsertTokenSupplier());
- this.updateChecker = updateChecker;
- this.webOfTrustUpdater = webOfTrustUpdater;
- this.eventBus = eventBus;
- this.database = database;
- preferences = new Preferences(eventBus);
- }
-
- @VisibleForTesting
- protected Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) {
+ public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) {
super("Sone Core");
this.configuration = configuration;
this.freenetInterface = freenetInterface;
}
/**
- * Returns the time when the given was first followed by any local Sone.
- *
- * @param sone
- * The Sone to get the time for
- * @return The time (in milliseconds since Jan 1, 1970) the Sone has first
- * been followed, or {@link Long#MAX_VALUE}
- */
- public long getSoneFollowingTime(Sone sone) {
- synchronized (soneFollowingTimes) {
- return Optional.fromNullable(soneFollowingTimes.get(sone.getId())).or(Long.MAX_VALUE);
- }
- }
-
- /**
* Returns a post builder.
*
* @return A new post builder
* @return The Sones that like the given post
*/
public Set<Sone> getLikes(Post post) {
- Set<Sone> sones = new HashSet<Sone>();
+ Set<Sone> sones = new HashSet<>();
for (Sone sone : getSones()) {
if (sone.getLikedPostIds().contains(post.getId())) {
sones.add(sone);
* @return The Sones that like the given reply
*/
public Set<Sone> getLikes(PostReply reply) {
- Set<Sone> sones = new HashSet<Sone>();
+ Set<Sone> sones = new HashSet<>();
for (Sone sone : getSones()) {
if (sone.getLikedReplyIds().contains(reply.getId())) {
sones.add(sone);
}
database.storeSone(sone);
soneDownloader.addSone(sone);
- soneDownloaders.execute(soneDownloader.fetchSoneWithUriAction(sone));
+ soneDownloaders.execute(soneDownloader.fetchSoneAsUskAction(sone));
return sone;
}
checkNotNull(sone, "sone must not be null");
checkNotNull(soneId, "soneId must not be null");
database.addFriend(sone, soneId);
- synchronized (soneFollowingTimes) {
- if (!soneFollowingTimes.containsKey(soneId)) {
- long now = System.currentTimeMillis();
- soneFollowingTimes.put(soneId, now);
- Sone followedSone = getSone(soneId);
- if (followedSone == null) {
- return;
- }
- for (Post post : followedSone.getPosts()) {
- if (post.getTime() < now) {
- markPostKnown(post);
- }
- }
- for (PostReply reply : followedSone.getReplies()) {
- if (reply.getTime() < now) {
- markReplyKnown(reply);
- }
- }
+ @SuppressWarnings("ConstantConditions") // we just followed, this can’t be null.
+ long now = database.getFollowingTime(soneId);
+ Sone followedSone = getSone(soneId);
+ if (followedSone == null) {
+ return;
+ }
+ for (Post post : followedSone.getPosts()) {
+ if (post.getTime() < now) {
+ markPostKnown(post);
+ }
+ }
+ for (PostReply reply : followedSone.getReplies()) {
+ if (reply.getTime() < now) {
+ markReplyKnown(reply);
}
}
touchConfiguration();
checkNotNull(sone, "sone must not be null");
checkNotNull(soneId, "soneId must not be null");
database.removeFriend(sone, soneId);
- boolean unfollowedSoneStillFollowed = false;
- for (Sone localSone : getLocalSones()) {
- unfollowedSoneStillFollowed |= localSone.hasFriend(soneId);
- }
- if (!unfollowedSoneStillFollowed) {
- synchronized (soneFollowingTimes) {
- soneFollowingTimes.remove(soneId);
- }
- }
touchConfiguration();
}
}
}
- private List<Object> collectEventsForChangesInSone(Sone oldSone,
- final Sone newSone) {
- final List<Object> events = new ArrayList<Object>();
- SoneChangeDetector soneChangeDetector = new SoneChangeDetector(
- oldSone);
- soneChangeDetector.onNewPosts(new PostProcessor() {
- @Override
- public void processPost(Post post) {
- if (post.getTime() < getSoneFollowingTime(newSone)) {
- post.setKnown(true);
- } else if (!post.isKnown()) {
- events.add(new NewPostFoundEvent(post));
- }
- }
- });
- soneChangeDetector.onRemovedPosts(new PostProcessor() {
- @Override
- public void processPost(Post post) {
- events.add(new PostRemovedEvent(post));
+ private List<Object> collectEventsForChangesInSone(Sone oldSone, Sone newSone) {
+ List<Object> events = new ArrayList<>();
+ SoneComparison soneComparison = new SoneComparison(oldSone, newSone);
+ for (Post newPost : soneComparison.getNewPosts()) {
+ if (newPost.getSone().equals(newSone)) {
+ newPost.setKnown(true);
+ } else if (newPost.getTime() < database.getFollowingTime(newSone.getId())) {
+ newPost.setKnown(true);
+ } else if (!newPost.isKnown()) {
+ events.add(new NewPostFoundEvent(newPost));
}
- });
- soneChangeDetector.onNewPostReplies(new PostReplyProcessor() {
- @Override
- public void processPostReply(PostReply postReply) {
- if (postReply.getTime() < getSoneFollowingTime(newSone)) {
- postReply.setKnown(true);
- } else if (!postReply.isKnown()) {
- events.add(new NewPostReplyFoundEvent(postReply));
- }
- }
- });
- soneChangeDetector.onRemovedPostReplies(new PostReplyProcessor() {
- @Override
- public void processPostReply(PostReply postReply) {
- events.add(new PostReplyRemovedEvent(postReply));
+ }
+ for (Post post : soneComparison.getRemovedPosts()) {
+ events.add(new PostRemovedEvent(post));
+ }
+ for (PostReply postReply : soneComparison.getNewPostReplies()) {
+ if (postReply.getSone().equals(newSone)) {
+ postReply.setKnown(true);
+ } else if (postReply.getTime() < database.getFollowingTime(newSone.getId())) {
+ postReply.setKnown(true);
+ } else if (!postReply.isKnown()) {
+ events.add(new NewPostReplyFoundEvent(postReply));
}
- });
- soneChangeDetector.detectChanges(newSone);
+ }
+ for (PostReply postReply : soneComparison.getRemovedPostReplies()) {
+ events.add(new PostReplyRemovedEvent(postReply));
+ }
return events;
}
identityManager.start();
webOfTrustUpdater.init();
webOfTrustUpdater.start();
- database.start();
+ database.startAsync();
}
/**
}
}
saveConfiguration();
- database.stop();
+ database.stopAsync();
webOfTrustUpdater.stop();
updateChecker.stop();
soneDownloader.stop();
configuration.getStringValue("KnownSone/" + soneCounter + "/ID").setValue(null);
}
- /* save Sone following times. */
- soneCounter = 0;
- synchronized (soneFollowingTimes) {
- for (Entry<String, Long> soneFollowingTime : soneFollowingTimes.entrySet()) {
- configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(soneFollowingTime.getKey());
- configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").setValue(soneFollowingTime.getValue());
- ++soneCounter;
- }
- configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(null);
- }
-
/* save known posts. */
database.save();
knownSones.add(knownSoneId);
}
}
-
- /* load Sone following times. */
- soneCounter = 0;
- while (true) {
- String soneId = configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").getValue(null);
- if (soneId == null) {
- break;
- }
- long time = configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").getValue(Long.MAX_VALUE);
- synchronized (soneFollowingTimes) {
- soneFollowingTimes.put(soneId, time);
- }
- ++soneCounter;
- }
}
/**
}
}
soneDownloader.addSone(sone);
- soneDownloaders.execute(soneDownloader.fetchSoneAction(sone));
+ soneDownloaders.execute(soneDownloader.fetchSoneAsSskAction(sone));
}
/**
/*
- * Sone - FreenetInterface.java - Copyright © 2010–2016 David Roden
+ * Sone - FreenetInterface.java - Copyright © 2010–2019 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
import java.util.logging.Logger;
import javax.annotation.Nonnull;
+import javax.inject.Inject;
import net.pterodactylus.sone.core.event.ImageInsertAbortedEvent;
import net.pterodactylus.sone.core.event.ImageInsertFailedEvent;
import com.google.common.base.Function;
import com.google.common.eventbus.EventBus;
-import com.google.inject.Inject;
import com.google.inject.Singleton;
import freenet.client.ClientMetadata;
/**
* Contains all necessary functionality for interacting with the Freenet node.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class FreenetInterface {
/** The high-level client to use for requests. */
private final HighLevelSimpleClient client;
+ private final RequestClient requestClient = new RequestClientBuilder().realTime().build();
/** The USK callbacks. */
- private final Map<String, USKCallback> soneUskCallbacks = new HashMap<String, USKCallback>();
+ private final Map<String, USKCallback> soneUskCallbacks = new HashMap<>();
/** The not-Sone-related USK callbacks. */
private final Map<FreenetURI, USKCallback> uriUskCallbacks = Collections.synchronizedMap(new HashMap<FreenetURI, USKCallback>());
try {
soneUskCallbacks.put(routingKey(requestUri), uskCallback);
node.clientCore.uskManager.subscribe(create(requestUri),
- uskCallback, true, (RequestClient) client);
+ uskCallback, true, requestClient);
} catch (MalformedURLException mue1) {
logger.log(WARNING, format("Could not subscribe USK “%s”!",
requestUri), mue1);
soneUskCallbacks.put(routingKey(requestUri), uskCallback);
node.clientCore
.uskManager
- .subscribe(create(requestUri), uskCallback, false,
- (RequestClient) client);
+ .subscribe(create(requestUri), uskCallback, false, requestClient);
} catch (MalformedURLException mue1) {
logger.log(WARNING,
format("Could not subscribe USK “%s”!", requestUri),
};
try {
- node.clientCore.uskManager.subscribe(USK.create(uri), uskCallback, true, (RequestClient) client);
+ node.clientCore.uskManager.subscribe(USK.create(uri), uskCallback, true, requestClient);
uriUskCallbacks.put(uri, uskCallback);
} catch (MalformedURLException mue1) {
logger.log(Level.WARNING, String.format("Could not subscribe to USK: %s", uri), mue1);
}
/**
- * Container for a fetched URI and the {@link FetchResult}.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
- */
- public static class Fetched {
-
- /** The fetched URI. */
- private final FreenetURI freenetUri;
-
- /** The fetch result. */
- private final FetchResult fetchResult;
-
- /**
- * Creates a new fetched URI.
- *
- * @param freenetUri
- * The URI that was fetched
- * @param fetchResult
- * The fetch result
- */
- public Fetched(FreenetURI freenetUri, FetchResult fetchResult) {
- this.freenetUri = freenetUri;
- this.fetchResult = fetchResult;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns the fetched URI.
- *
- * @return The fetched URI
- */
- public FreenetURI getFreenetUri() {
- return freenetUri;
- }
-
- /**
- * Returns the fetch result.
- *
- * @return The fetch result
- */
- public FetchResult getFetchResult() {
- return fetchResult;
- }
-
- }
-
- /**
* Callback for USK watcher events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public static interface Callback {
* @see ImageInsertStartedEvent
* @see ImageInsertFailedEvent
* @see ImageInsertFinishedEvent
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class InsertToken implements ClientPutCallback {
}
- public class InsertTokenSupplier implements Function<Image, InsertToken> {
+ public static class InsertTokenSupplier implements Function<Image, InsertToken> {
+
+ private final FreenetInterface freenetInterface;
+
+ @Inject
+ public InsertTokenSupplier(FreenetInterface freenetInterface) {
+ this.freenetInterface = freenetInterface;
+ }
@Override
public InsertToken apply(Image image) {
- return new InsertToken(image);
+ return freenetInterface.new InsertToken(image);
}
}
/*
- * Sone - ImageInserter.java - Copyright © 2011–2016 David Roden
+ * Sone - ImageInserter.java - Copyright © 2011–2019 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
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.inject.Inject;
+
import net.pterodactylus.sone.core.FreenetInterface.InsertToken;
+import net.pterodactylus.sone.core.FreenetInterface.InsertTokenSupplier;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.TemporaryImage;
* {@link FreenetInterface#insertImage(TemporaryImage, Image, InsertToken)} and
* also tracks running inserts, giving the possibility to abort a running
* insert.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageInserter {
* @param insertTokenSupplier
* The supplier for insert tokens
*/
- public ImageInserter(FreenetInterface freenetInterface, Function<Image, InsertToken> insertTokenSupplier) {
+ @Inject
+ public ImageInserter(FreenetInterface freenetInterface, InsertTokenSupplier insertTokenSupplier) {
this.freenetInterface = freenetInterface;
this.insertTokenSupplier = insertTokenSupplier;
}
/*
- * Sone - Options.java - Copyright © 2010–2016 David Roden
+ * Sone - Options.java - Copyright © 2010–2019 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
/**
* Stores various options that influence Sone’s behaviour.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Options {
--- /dev/null
+package net.pterodactylus.sone.core
+
+data class PreferenceChangedEvent(val preferenceName: String, val newValue: Any)
+++ /dev/null
-/*
- * Sone - Preferences.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.core;
-
-import static com.google.common.base.Predicates.equalTo;
-import static java.lang.Integer.MAX_VALUE;
-import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS;
-import static net.pterodactylus.sone.utils.IntegerRangePredicate.range;
-
-import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
-import net.pterodactylus.sone.fcp.FcpInterface;
-import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
-import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent;
-import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent;
-import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged;
-import net.pterodactylus.sone.utils.DefaultOption;
-import net.pterodactylus.sone.utils.Option;
-import net.pterodactylus.util.config.Configuration;
-import net.pterodactylus.util.config.ConfigurationException;
-
-import com.google.common.base.Predicates;
-import com.google.common.eventbus.EventBus;
-
-/**
- * Convenience interface for external classes that want to access the core’s
- * configuration.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class Preferences {
-
- private final EventBus eventBus;
- private final Option<Integer> insertionDelay =
- new DefaultOption<Integer>(60, range(0, MAX_VALUE));
- private final Option<Integer> postsPerPage =
- new DefaultOption<Integer>(10, range(1, MAX_VALUE));
- private final Option<Integer> imagesPerPage =
- new DefaultOption<Integer>(9, range(1, MAX_VALUE));
- private final Option<Integer> charactersPerPost =
- new DefaultOption<Integer>(400, Predicates.<Integer>or(
- range(50, MAX_VALUE), equalTo(-1)));
- private final Option<Integer> postCutOffLength =
- new DefaultOption<Integer>(200, range(50, MAX_VALUE));
- private final Option<Boolean> requireFullAccess =
- new DefaultOption<Boolean>(false);
- private final Option<Integer> positiveTrust =
- new DefaultOption<Integer>(75, range(0, 100));
- private final Option<Integer> negativeTrust =
- new DefaultOption<Integer>(-25, range(-100, 100));
- private final Option<String> trustComment =
- new DefaultOption<String>("Set from Sone Web Interface");
- private final Option<Boolean> activateFcpInterface =
- new DefaultOption<Boolean>(false);
- private final Option<FullAccessRequired> fcpFullAccessRequired =
- new DefaultOption<FullAccessRequired>(ALWAYS);
-
- public Preferences(EventBus eventBus) {
- this.eventBus = eventBus;
- }
-
- /**
- * Returns the insertion delay.
- *
- * @return The insertion delay
- */
- public int getInsertionDelay() {
- return insertionDelay.get();
- }
-
- /**
- * Validates the given insertion delay.
- *
- * @param insertionDelay
- * The insertion delay to validate
- * @return {@code true} if the given insertion delay was valid,
- * {@code false} otherwise
- */
- public boolean validateInsertionDelay(Integer insertionDelay) {
- return this.insertionDelay.validate(insertionDelay);
- }
-
- /**
- * Sets the insertion delay
- *
- * @param insertionDelay
- * The new insertion delay, or {@code null} to restore it to
- * the default value
- * @return This preferences
- */
- public Preferences setInsertionDelay(Integer insertionDelay) {
- this.insertionDelay.set(insertionDelay);
- eventBus.post(new InsertionDelayChangedEvent(getInsertionDelay()));
- return this;
- }
-
- /**
- * Returns the number of posts to show per page.
- *
- * @return The number of posts to show per page
- */
- public int getPostsPerPage() {
- return postsPerPage.get();
- }
-
- /**
- * Validates the number of posts per page.
- *
- * @param postsPerPage
- * The number of posts per page
- * @return {@code true} if the number of posts per page was valid,
- * {@code false} otherwise
- */
- public boolean validatePostsPerPage(Integer postsPerPage) {
- return this.postsPerPage.validate(postsPerPage);
- }
-
- /**
- * Sets the number of posts to show per page.
- *
- * @param postsPerPage
- * The number of posts to show per page
- * @return This preferences object
- */
- public Preferences setPostsPerPage(Integer postsPerPage) {
- this.postsPerPage.set(postsPerPage);
- return this;
- }
-
- /**
- * Returns the number of images to show per page.
- *
- * @return The number of images to show per page
- */
- public int getImagesPerPage() {
- return imagesPerPage.get();
- }
-
- /**
- * Validates the number of images per page.
- *
- * @param imagesPerPage
- * The number of images per page
- * @return {@code true} if the number of images per page was valid,
- * {@code false} otherwise
- */
- public boolean validateImagesPerPage(Integer imagesPerPage) {
- return this.imagesPerPage.validate(imagesPerPage);
- }
-
- /**
- * Sets the number of images per page.
- *
- * @param imagesPerPage
- * The number of images per page
- * @return This preferences object
- */
- public Preferences setImagesPerPage(Integer imagesPerPage) {
- this.imagesPerPage.set(imagesPerPage);
- return this;
- }
-
- /**
- * Returns the number of characters per post, or <code>-1</code> if the
- * posts should not be cut off.
- *
- * @return The numbers of characters per post
- */
- public int getCharactersPerPost() {
- return charactersPerPost.get();
- }
-
- /**
- * Validates the number of characters per post.
- *
- * @param charactersPerPost
- * The number of characters per post
- * @return {@code true} if the number of characters per post was valid,
- * {@code false} otherwise
- */
- public boolean validateCharactersPerPost(Integer charactersPerPost) {
- return this.charactersPerPost.validate(charactersPerPost);
- }
-
- /**
- * Sets the number of characters per post.
- *
- * @param charactersPerPost
- * The number of characters per post, or <code>-1</code> to
- * not cut off the posts
- * @return This preferences objects
- */
- public Preferences setCharactersPerPost(Integer charactersPerPost) {
- this.charactersPerPost.set(charactersPerPost);
- return this;
- }
-
- /**
- * Returns the number of characters the shortened post should have.
- *
- * @return The number of characters of the snippet
- */
- public int getPostCutOffLength() {
- return postCutOffLength.get();
- }
-
- /**
- * Validates the number of characters after which to cut off the post.
- *
- * @param postCutOffLength
- * The number of characters of the snippet
- * @return {@code true} if the number of characters of the snippet is
- * valid, {@code false} otherwise
- */
- public boolean validatePostCutOffLength(Integer postCutOffLength) {
- return this.postCutOffLength.validate(postCutOffLength);
- }
-
- /**
- * Sets the number of characters the shortened post should have.
- *
- * @param postCutOffLength
- * The number of characters of the snippet
- * @return This preferences
- */
- public Preferences setPostCutOffLength(Integer postCutOffLength) {
- this.postCutOffLength.set(postCutOffLength);
- return this;
- }
-
- /**
- * Returns whether Sone requires full access to be even visible.
- *
- * @return {@code true} if Sone requires full access, {@code false}
- * otherwise
- */
- public boolean isRequireFullAccess() {
- return requireFullAccess.get();
- }
-
- /**
- * Sets whether Sone requires full access to be even visible.
- *
- * @param requireFullAccess
- * {@code true} if Sone requires full access, {@code false}
- * otherwise
- */
- public void setRequireFullAccess(Boolean requireFullAccess) {
- this.requireFullAccess.set(requireFullAccess);
- }
-
- /**
- * Returns the positive trust.
- *
- * @return The positive trust
- */
- public int getPositiveTrust() {
- return positiveTrust.get();
- }
-
- /**
- * Validates the positive trust.
- *
- * @param positiveTrust
- * The positive trust to validate
- * @return {@code true} if the positive trust was valid, {@code false}
- * otherwise
- */
- public boolean validatePositiveTrust(Integer positiveTrust) {
- return this.positiveTrust.validate(positiveTrust);
- }
-
- /**
- * Sets the positive trust.
- *
- * @param positiveTrust
- * The new positive trust, or {@code null} to restore it to
- * the default vlaue
- * @return This preferences
- */
- public Preferences setPositiveTrust(Integer positiveTrust) {
- this.positiveTrust.set(positiveTrust);
- return this;
- }
-
- /**
- * Returns the negative trust.
- *
- * @return The negative trust
- */
- public int getNegativeTrust() {
- return negativeTrust.get();
- }
-
- /**
- * Validates the negative trust.
- *
- * @param negativeTrust
- * The negative trust to validate
- * @return {@code true} if the negative trust was valid, {@code false}
- * otherwise
- */
- public boolean validateNegativeTrust(Integer negativeTrust) {
- return this.negativeTrust.validate(negativeTrust);
- }
-
- /**
- * Sets the negative trust.
- *
- * @param negativeTrust
- * The negative trust, or {@code null} to restore it to the
- * default value
- * @return The preferences
- */
- public Preferences setNegativeTrust(Integer negativeTrust) {
- this.negativeTrust.set(negativeTrust);
- return this;
- }
-
- /**
- * Returns the trust comment. This is the comment that is set in the web
- * of trust when a trust value is assigned to an identity.
- *
- * @return The trust comment
- */
- public String getTrustComment() {
- return trustComment.get();
- }
-
- /**
- * Sets the trust comment.
- *
- * @param trustComment
- * The trust comment, or {@code null} to restore it to the
- * default value
- * @return This preferences
- */
- public Preferences setTrustComment(String trustComment) {
- this.trustComment.set(trustComment);
- return this;
- }
-
- /**
- * Returns whether the {@link FcpInterface FCP interface} is currently
- * active.
- *
- * @see FcpInterface#setActive(boolean)
- * @return {@code true} if the FCP interface is currently active,
- * {@code false} otherwise
- */
- public boolean isFcpInterfaceActive() {
- return activateFcpInterface.get();
- }
-
- /**
- * Sets whether the {@link FcpInterface FCP interface} is currently
- * active.
- *
- * @see FcpInterface#setActive(boolean)
- * @param fcpInterfaceActive
- * {@code true} to activate the FCP interface, {@code false}
- * to deactivate the FCP interface
- * @return This preferences object
- */
- public Preferences setFcpInterfaceActive(Boolean fcpInterfaceActive) {
- this.activateFcpInterface.set(fcpInterfaceActive);
- if (isFcpInterfaceActive()) {
- eventBus.post(new FcpInterfaceActivatedEvent());
- } else {
- eventBus.post(new FcpInterfaceDeactivatedEvent());
- }
- return this;
- }
-
- /**
- * Returns the action level for which full access to the FCP interface
- * is required.
- *
- * @return The action level for which full access to the FCP interface
- * is required
- */
- public FullAccessRequired getFcpFullAccessRequired() {
- return fcpFullAccessRequired.get();
- }
-
- /**
- * Sets the action level for which full access to the FCP interface is
- * required
- *
- * @param fcpFullAccessRequired
- * The action level
- * @return This preferences
- */
- public Preferences setFcpFullAccessRequired(
- FullAccessRequired fcpFullAccessRequired) {
- this.fcpFullAccessRequired.set(fcpFullAccessRequired);
- eventBus.post(new FullAccessRequiredChanged(getFcpFullAccessRequired()));
- return this;
- }
-
- public void saveTo(Configuration configuration) throws ConfigurationException {
- configuration.getIntValue("Option/ConfigurationVersion").setValue(0);
- configuration.getIntValue("Option/InsertionDelay").setValue(insertionDelay.getReal());
- configuration.getIntValue("Option/PostsPerPage").setValue(postsPerPage.getReal());
- configuration.getIntValue("Option/ImagesPerPage").setValue(imagesPerPage.getReal());
- configuration.getIntValue("Option/CharactersPerPost").setValue(charactersPerPost.getReal());
- configuration.getIntValue("Option/PostCutOffLength").setValue(postCutOffLength.getReal());
- configuration.getBooleanValue("Option/RequireFullAccess").setValue(requireFullAccess.getReal());
- configuration.getIntValue("Option/PositiveTrust").setValue(positiveTrust.getReal());
- configuration.getIntValue("Option/NegativeTrust").setValue(negativeTrust.getReal());
- configuration.getStringValue("Option/TrustComment").setValue(trustComment.getReal());
- configuration.getBooleanValue("Option/ActivateFcpInterface").setValue(activateFcpInterface.getReal());
- configuration.getIntValue("Option/FcpFullAccessRequired").setValue(toInt(fcpFullAccessRequired.getReal()));
- }
-
- private Integer toInt(FullAccessRequired fullAccessRequired) {
- return (fullAccessRequired == null) ? null : fullAccessRequired.ordinal();
- }
-
-}
/**
* Loads preferences stored in a {@link Configuration} into a {@link
* Preferences} object.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PreferencesLoader {
}
private void loadInsertionDelay(Configuration configuration) {
- preferences.setInsertionDelay(configuration.getIntValue(
+ preferences.setNewInsertionDelay(configuration.getIntValue(
"Option/InsertionDelay").getValue(null));
}
private void loadPostsPerPage(Configuration configuration) {
- preferences.setPostsPerPage(
+ preferences.setNewPostsPerPage(
configuration.getIntValue("Option/PostsPerPage")
.getValue(null));
}
private void loadImagesPerPage(Configuration configuration) {
- preferences.setImagesPerPage(
+ preferences.setNewImagesPerPage(
configuration.getIntValue("Option/ImagesPerPage")
.getValue(null));
}
private void loadCharactersPerPost(Configuration configuration) {
- preferences.setCharactersPerPost(
+ preferences.setNewCharactersPerPost(
configuration.getIntValue("Option/CharactersPerPost")
.getValue(null));
}
private void loadPostCutOffLength(Configuration configuration) {
try {
- preferences.setPostCutOffLength(
+ preferences.setNewPostCutOffLength(
configuration.getIntValue("Option/PostCutOffLength")
.getValue(null));
} catch (IllegalArgumentException iae1) {
}
private void loadRequireFullAccess(Configuration configuration) {
- preferences.setRequireFullAccess(
+ preferences.setNewRequireFullAccess(
configuration.getBooleanValue("Option/RequireFullAccess")
.getValue(null));
}
private void loadPositiveTrust(Configuration configuration) {
- preferences.setPositiveTrust(
+ preferences.setNewPositiveTrust(
configuration.getIntValue("Option/PositiveTrust")
.getValue(null));
}
private void loadNegativeTrust(Configuration configuration) {
- preferences.setNegativeTrust(
+ preferences.setNewNegativeTrust(
configuration.getIntValue("Option/NegativeTrust")
.getValue(null));
}
private void loadTrustComment(Configuration configuration) {
- preferences.setTrustComment(
+ preferences.setNewTrustComment(
configuration.getStringValue("Option/TrustComment")
.getValue(null));
}
private void loadFcpInterfaceActive(Configuration configuration) {
- preferences.setFcpInterfaceActive(configuration.getBooleanValue(
+ preferences.setNewFcpInterfaceActive(configuration.getBooleanValue(
"Option/ActivateFcpInterface").getValue(null));
}
private void loadFcpFullAccessRequired(Configuration configuration) {
Integer fullAccessRequiredInteger = configuration
.getIntValue("Option/FcpFullAccessRequired").getValue(null);
- preferences.setFcpFullAccessRequired(
+ preferences.setNewFcpFullAccessRequired(
(fullAccessRequiredInteger == null) ? null :
FullAccessRequired.values()[fullAccessRequiredInteger]);
}
+++ /dev/null
-package net.pterodactylus.sone.core;
-
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.fromNullable;
-import static com.google.common.collect.FluentIterable.from;
-
-import java.util.Collection;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-
-/**
- * Compares the contents of two {@link Sone}s and fires events for new and
- * removed elements.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class SoneChangeDetector {
-
- private final Sone oldSone;
- private Optional<PostProcessor> newPostProcessor = absent();
- private Optional<PostProcessor> removedPostProcessor = absent();
- private Optional<PostReplyProcessor> newPostReplyProcessor = absent();
- private Optional<PostReplyProcessor> removedPostReplyProcessor = absent();
-
- public SoneChangeDetector(Sone oldSone) {
- this.oldSone = oldSone;
- }
-
- public void onNewPosts(PostProcessor newPostProcessor) {
- this.newPostProcessor = fromNullable(newPostProcessor);
- }
-
- public void onRemovedPosts(PostProcessor removedPostProcessor) {
- this.removedPostProcessor = fromNullable(removedPostProcessor);
- }
-
- public void onNewPostReplies(PostReplyProcessor newPostReplyProcessor) {
- this.newPostReplyProcessor = fromNullable(newPostReplyProcessor);
- }
-
- public void onRemovedPostReplies(
- PostReplyProcessor removedPostReplyProcessor) {
- this.removedPostReplyProcessor = fromNullable(removedPostReplyProcessor);
- }
-
- public void detectChanges(Sone newSone) {
- processPosts(from(newSone.getPosts()).filter(
- notContainedIn(oldSone.getPosts())), newPostProcessor);
- processPosts(from(oldSone.getPosts()).filter(
- notContainedIn(newSone.getPosts())), removedPostProcessor);
- processPostReplies(from(newSone.getReplies()).filter(
- notContainedIn(oldSone.getReplies())), newPostReplyProcessor);
- processPostReplies(from(oldSone.getReplies()).filter(
- notContainedIn(newSone.getReplies())), removedPostReplyProcessor);
- }
-
- private void processPostReplies(FluentIterable<PostReply> postReplies,
- Optional<PostReplyProcessor> postReplyProcessor) {
- for (PostReply postReply : postReplies) {
- notifyPostReplyProcessor(postReplyProcessor, postReply);
- }
- }
-
- private void notifyPostReplyProcessor(
- Optional<PostReplyProcessor> postReplyProcessor,
- PostReply postReply) {
- if (postReplyProcessor.isPresent()) {
- postReplyProcessor.get()
- .processPostReply(postReply);
- }
- }
-
- private void processPosts(FluentIterable<Post> posts,
- Optional<PostProcessor> newPostProcessor) {
- for (Post post : posts) {
- notifyPostProcessor(newPostProcessor, post);
- }
- }
-
- private void notifyPostProcessor(Optional<PostProcessor> postProcessor,
- Post newPost) {
- if (postProcessor.isPresent()) {
- postProcessor.get().processPost(newPost);
- }
- }
-
- private <T> Predicate<T> notContainedIn(final Collection<T> posts) {
- return new Predicate<T>() {
- @Override
- public boolean apply(T element) {
- return !posts.contains(element);
- }
- };
- }
-
- public interface PostProcessor {
-
- void processPost(Post post);
-
- }
-
- public interface PostReplyProcessor {
-
- void processPostReply(PostReply postReply);
-
- }
-
-}
import freenet.keys.FreenetURI;
+import com.google.inject.ImplementedBy;
+
/**
* Downloads and parses Sone and {@link Core#updateSone(Sone) updates the
* core}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
+@ImplementedBy(SoneDownloaderImpl.class)
public interface SoneDownloader extends Service {
void addSone(Sone sone);
- void fetchSone(Sone sone, FreenetURI soneUri);
Sone fetchSone(Sone sone, FreenetURI soneUri, boolean fetchOnly);
- Runnable fetchSoneWithUriAction(Sone sone);
- Runnable fetchSoneAction(Sone sone);
+ Runnable fetchSoneAsUskAction(Sone sone);
+ Runnable fetchSoneAsSskAction(Sone sone);
}
/*
- * Sone - SoneDownloaderImpl.java - Copyright © 2010–2016 David Roden
+ * Sone - SoneDownloaderImpl.java - Copyright © 2010–2019 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
import java.util.logging.Level;
import java.util.logging.Logger;
-import net.pterodactylus.sone.core.FreenetInterface.Fetched;
+import javax.inject.Inject;
+
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.data.Sone.SoneStatus;
import net.pterodactylus.util.service.AbstractService;
import freenet.keys.USK;
import freenet.node.RequestStarter;
import freenet.support.api.Bucket;
-import freenet.support.io.Closer;
-import com.db4o.ObjectContainer;
-
-import com.google.common.annotations.VisibleForTesting;
/**
* The Sone downloader is responsible for download Sones as they are updated.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneDownloaderImpl extends AbstractService implements SoneDownloader {
private static final int MAX_PROTOCOL_VERSION = 0;
/** The core. */
- private final Core core;
+ private final UpdatedSoneProcessor updatedSoneProcessor;
private final SoneParser soneParser;
/** The Freenet interface. */
private final FreenetInterface freenetInterface;
/** The sones to update. */
- private final Set<Sone> sones = new HashSet<Sone>();
-
- /**
- * Creates a new Sone downloader.
- *
- * @param core
- * The core
- * @param freenetInterface
- * The Freenet interface
- */
- public SoneDownloaderImpl(Core core, FreenetInterface freenetInterface) {
- this(core, freenetInterface, new SoneParser(core));
- }
+ private final Set<Sone> sones = new HashSet<>();
- /**
- * Creates a new Sone downloader.
- *
- * @param core
- * The core
- * @param freenetInterface
- * The Freenet interface
- * @param soneParser
- */
- @VisibleForTesting
- SoneDownloaderImpl(Core core, FreenetInterface freenetInterface, SoneParser soneParser) {
+ @Inject
+ SoneDownloaderImpl(UpdatedSoneProcessor updatedSoneProcessor, FreenetInterface freenetInterface, SoneParser soneParser) {
super("Sone Downloader", false);
- this.core = core;
+ this.updatedSoneProcessor = updatedSoneProcessor;
this.freenetInterface = freenetInterface;
this.soneParser = soneParser;
}
sone, key, newKnownGood, newSlotToo));
if (edition > sone.getLatestEdition()) {
sone.setLatestEdition(edition);
- new Thread(fetchSoneAction(sone),
+ new Thread(fetchSoneAsSskAction(sone),
"Sone Downloader").start();
}
}
return (currentTimeMillis() - sone.getTime()) < DAYS.toMillis(7);
}
- private void fetchSone(Sone sone) {
- fetchSone(sone, sone.getRequestUri().sskForUSK());
- }
-
- /**
- * Fetches the updated Sone. This method can be used to fetch a Sone from a
- * specific URI.
- *
- * @param sone
- * The Sone to fetch
- * @param soneUri
- * The URI to fetch the Sone from
- */
- @Override
- public void fetchSone(Sone sone, FreenetURI soneUri) {
- fetchSone(sone, soneUri, false);
+ private void fetchSoneAsSsk(Sone sone) {
+ fetchSone(sone, sone.getRequestUri().sskForUSK(), false);
}
/**
if (parsedSone != null) {
if (!fetchOnly) {
parsedSone.setStatus((parsedSone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle);
- core.updateSone(parsedSone);
+ updatedSoneProcessor.updateSone(parsedSone);
addSone(parsedSone);
}
}
Sone parsedSone = soneParser.parseSone(originalSone,
soneInputStream);
if (parsedSone != null) {
+ logger.log(Level.FINER, "Sone %s was successfully parsed.", parsedSone);
parsedSone.setLatestEdition(requestUri.getEdition());
}
return parsedSone;
}
@Override
- public Runnable fetchSoneWithUriAction(final Sone sone) {
+ public Runnable fetchSoneAsUskAction(final Sone sone) {
return new Runnable() {
@Override
public void run() {
- fetchSone(sone, sone.getRequestUri());
+ fetchSone(sone, sone.getRequestUri(), false);
}
};
}
@Override
- public Runnable fetchSoneAction(final Sone sone) {
+ public Runnable fetchSoneAsSskAction(final Sone sone) {
return new Runnable() {
@Override
public void run() {
- fetchSone(sone);
+ fetchSoneAsSsk(sone);
}
};
}
/*
- * Sone - SoneException.java - Copyright © 2010–2016 David Roden
+ * Sone - SoneException.java - Copyright © 2010–2019 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
/**
* A Sone exception.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneException extends Exception {
/*
- * Sone - SoneInsertException.java - Copyright © 2011–2016 David Roden
+ * Sone - SoneInsertException.java - Copyright © 2011–2019 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
/**
* Exception that signals a problem with an insert.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneInsertException extends SoneException {
/*
- * Sone - SoneInserter.java - Copyright © 2010–2016 David Roden
+ * Sone - SoneInserter.java - Copyright © 2010–2019 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
/**
* A Sone inserter is responsible for inserting a Sone if it has changed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneInserter extends AbstractService {
* Container for information that are required to insert a Sone. This
* container merely exists to copy all relevant data without holding a lock
* on the {@link Sone} object for too long.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
class InsertInformation implements Closeable {
/** All properties of the Sone, copied for thread safety. */
- private final Map<String, Object> soneProperties = new HashMap<String, Object>();
+ private final Map<String, Object> soneProperties = new HashMap<>();
private final String fingerprint;
private final ManifestCreator manifestCreator;
*/
public InsertInformation(Sone sone) {
this.fingerprint = sone.getFingerprint();
- Map<String, Object> soneProperties = new HashMap<String, Object>();
+ Map<String, Object> soneProperties = new HashMap<>();
soneProperties.put("id", sone.getId());
soneProperties.put("name", sone.getName());
soneProperties.put("time", currentTimeMillis());
soneProperties.put("profile", sone.getProfile());
soneProperties.put("posts", Ordering.from(Post.NEWEST_FIRST).sortedCopy(sone.getPosts()));
soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies()));
- soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
- soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
+ soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds()));
+ soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds()));
soneProperties.put("albums", FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).filter(NOT_EMPTY).toList());
manifestCreator = new ManifestCreator(core, soneProperties);
}
* @return The manifest entries for the Sone insert
*/
public HashMap<String, Object> generateManifestEntries() {
- HashMap<String, Object> manifestEntries = new HashMap<String, Object>();
+ HashMap<String, Object> manifestEntries = new HashMap<>();
/* first, create an index.html. */
manifestEntries.put("index.html", manifestCreator.createManifestElement(
/**
* Creates manifest elements for an insert by rendering a template.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
static class ManifestCreator implements Closeable {
private final Core core;
private final Map<String, Object> soneProperties;
- private final Set<Bucket> buckets = new HashSet<Bucket>();
+ private final Set<Bucket> buckets = new HashSet<>();
ManifestCreator(Core core, Map<String, Object> soneProperties) {
this.core = core;
* Class that detects {@link Sone} modifications (as per their {@link
* Sone#getFingerprint() fingerprints} and determines when a modified Sone may
* be inserted.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
class SoneModificationDetector {
/**
* Provider for a fingerprint and the information if a {@link Sone} is locked. This
* prevents us from having to lug a Sone object around.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
static interface LockableFingerprintProvider {
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.inject.Inject;
+
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Client;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Profile.DuplicateField;
import net.pterodactylus.sone.data.Profile.EmptyFieldName;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.database.Database;
import net.pterodactylus.sone.database.PostBuilder;
import net.pterodactylus.sone.database.PostReplyBuilder;
import net.pterodactylus.sone.database.SoneBuilder;
/**
* Parses a {@link Sone} from an XML {@link InputStream}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneParser {
private static final Logger logger = getLogger(SoneParser.class.getName());
private static final int MAX_PROTOCOL_VERSION = 0;
- private final Core core;
+ private final Database database;
- public SoneParser(Core core) {
- this.core = core;
+ @Inject
+ public SoneParser(Database database) {
+ this.database = database;
}
public Sone parseSone(Sone originalSone, InputStream soneInputStream) throws SoneException {
return null;
}
- SoneBuilder soneBuilder = core.soneBuilder().from(originalSone.getIdentity());
+ SoneBuilder soneBuilder = database.newSoneBuilder().from(originalSone.getIdentity());
if (originalSone.isLocal()) {
soneBuilder = soneBuilder.local();
}
/* parse posts. */
SimpleXML postsXml = soneXml.getNode("posts");
- Set<Post> posts = new HashSet<Post>();
+ Set<Post> posts = new HashSet<>();
if (postsXml == null) {
/* TODO - mark Sone as bad. */
logger.log(Level.WARNING, String.format("Downloaded Sone %s has no posts!", sone));
return null;
}
try {
- PostBuilder postBuilder = core.postBuilder();
+ PostBuilder postBuilder = database.newPostBuilder();
/* TODO - parse time correctly. */
postBuilder.withId(postId).from(sone.getId()).withTime(Long.parseLong(postTime)).withText(postText);
if ((postRecipientId != null) && (postRecipientId.length() == 43)) {
/* parse replies. */
SimpleXML repliesXml = soneXml.getNode("replies");
- Set<PostReply> replies = new HashSet<PostReply>();
+ Set<PostReply> replies = new HashSet<>();
if (repliesXml == null) {
/* TODO - mark Sone as bad. */
logger.log(Level.WARNING, String.format("Downloaded Sone %s has no replies!", sone));
return null;
}
try {
- PostReplyBuilder postReplyBuilder = core.postReplyBuilder();
+ PostReplyBuilder postReplyBuilder = database.newPostReplyBuilder();
/* TODO - parse time correctly. */
postReplyBuilder.withId(replyId).from(sone.getId()).to(replyPostId).withTime(Long.parseLong(replyTime)).withText(replyText);
replies.add(postReplyBuilder.build());
/* parse liked post IDs. */
SimpleXML likePostIdsXml = soneXml.getNode("post-likes");
- Set<String> likedPostIds = new HashSet<String>();
+ Set<String> likedPostIds = new HashSet<>();
if (likePostIdsXml == null) {
/* TODO - mark Sone as bad. */
logger.log(Level.WARNING, String.format("Downloaded Sone %s has no post likes!", sone));
/* parse liked reply IDs. */
SimpleXML likeReplyIdsXml = soneXml.getNode("reply-likes");
- Set<String> likedReplyIds = new HashSet<String>();
+ Set<String> likedReplyIds = new HashSet<>();
if (likeReplyIdsXml == null) {
/* TODO - mark Sone as bad. */
logger.log(Level.WARNING, String.format("Downloaded Sone %s has no reply likes!", sone));
/* parse albums. */
SimpleXML albumsXml = soneXml.getNode("albums");
- Map<String, Image> allImages = new HashMap<String, Image>();
- List<Album> topLevelAlbums = new ArrayList<Album>();
+ Map<String, Image> allImages = new HashMap<>();
+ List<Album> topLevelAlbums = new ArrayList<>();
if (albumsXml != null) {
for (SimpleXML albumXml : albumsXml.getNodes("album")) {
String id = albumXml.getValue("id", null);
}
Album parent = null;
if (parentId != null) {
- parent = core.getAlbum(parentId);
+ parent = database.getAlbum(parentId);
if (parent == null) {
logger.log(Level.WARNING, String.format("Downloaded Sone %s has album with invalid parent!", sone));
return null;
}
}
- Album album = core.albumBuilder()
+ Album album = database.newAlbumBuilder()
.withId(id)
.by(sone)
.build()
logger.log(Level.WARNING, String.format("Downloaded Sone %s contains image %s with invalid dimensions (%s, %s)!", sone, imageId, imageWidthString, imageHeightString));
return null;
}
- Image image = core.imageBuilder().withId(imageId).build().modify().setSone(sone).setKey(imageKey).setCreationTime(creationTime).update();
+ Image image = database.newImageBuilder().withId(imageId).build().modify().setSone(sone).setKey(imageKey).setCreationTime(creationTime).update();
image = image.modify().setTitle(imageTitle).setDescription(imageDescription).update();
image = image.modify().setWidth(imageWidth).setHeight(imageHeight).update();
album.addImage(image);
/*
- * Sone - SoneRescuer.java - Copyright © 2011–2016 David Roden
+ * Sone - SoneRescuer.java - Copyright © 2011–2019 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 Sone rescuer downloads older editions of a Sone and updates the currently
* stored Sone with it.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneRescuer extends AbstractService {
/*
- * Sone - SoneUri.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneUri.java - Copyright © 2013–2019 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
/**
* Helper class that creates {@link FreenetURI}s for Sone to insert to and
* request from.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneUri {
/*
- * Sone - UpdateChecker.java - Copyright © 2011–2016 David Roden
+ * Sone - UpdateChecker.java - Copyright © 2011–2019 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
import javax.inject.Singleton;
-import net.pterodactylus.sone.core.FreenetInterface.Fetched;
import net.pterodactylus.sone.core.event.UpdateFoundEvent;
+import net.pterodactylus.sone.main.PluginHomepage;
import net.pterodactylus.sone.main.SonePlugin;
import net.pterodactylus.util.io.Closer;
import net.pterodactylus.util.version.Version;
/**
* Watches the official Sone homepage for new releases.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class UpdateChecker {
/** The release date of the latest version. */
private long latestVersionDate;
+ private final PluginHomepage pluginHomepage;
+
/**
* Creates a new update checker.
*
* The freenet interface to use
*/
@Inject
- public UpdateChecker(EventBus eventBus, FreenetInterface freenetInterface, Version currentVersion) {
+ public UpdateChecker(EventBus eventBus, FreenetInterface freenetInterface, Version currentVersion, PluginHomepage pluginHomepage) {
this.eventBus = eventBus;
this.freenetInterface = freenetInterface;
this.currentRunningVersion = currentVersion;
this.currentLatestVersion = currentVersion;
+ this.pluginHomepage = pluginHomepage;
}
//
*/
public void start() {
try {
- currentUri = new FreenetURI(SonePlugin.getHomepage());
+ currentUri = new FreenetURI(pluginHomepage.getHomepage());
} catch (MalformedURLException mue1) {
/* this can not really happen unless I screw up. */
logger.log(Level.SEVERE, "Sone Homepage URI invalid!", mue1);
/**
* Updates WebOfTrust identity data.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@ImplementedBy(WebOfTrustUpdaterImpl.class)
public interface WebOfTrustUpdater extends Service {
/*
- * Sone - WebOfTrustUpdaterImpl.java - Copyright © 2013–2016 David Roden
+ * Sone - WebOfTrustUpdaterImpl.java - Copyright © 2013–2019 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
/**
* Updates WebOfTrust identity data in a background thread because communicating
* with the WebOfTrust plugin can potentially last quite long.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class WebOfTrustUpdaterImpl extends AbstractService implements WebOfTrustUpdater {
private final WebOfTrustConnector webOfTrustConnector;
/** The queue for jobs. */
- private final BlockingQueue<WebOfTrustUpdateJob> updateJobs = new LinkedBlockingQueue<WebOfTrustUpdateJob>();
+ private final BlockingQueue<WebOfTrustUpdateJob> updateJobs = new LinkedBlockingQueue<>();
/**
* Creates a new trust updater.
/**
* Base class for WebOfTrust update jobs.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
class WebOfTrustUpdateJob implements Runnable {
/**
* Update job that sets the trust relation between two identities.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
class SetTrustJob extends WebOfTrustUpdateJob {
/**
* Base class for context updates of an {@link OwnIdentity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
class WebOfTrustContextUpdateJob extends WebOfTrustUpdateJob {
/**
* Job that adds a context to an {@link OwnIdentity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
class AddContextJob extends WebOfTrustContextUpdateJob {
/**
* Job that removes a context from an {@link OwnIdentity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
class RemoveContextJob extends WebOfTrustContextUpdateJob {
/**
* WebOfTrust update job that sets a property on an {@link OwnIdentity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@VisibleForTesting
class SetPropertyJob extends WebOfTrustUpdateJob {
/*
- * Sone - ImageEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageEvent.java - Copyright © 2013–2019 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
/**
* Base class for {@link Image} events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class ImageEvent {
/*
- * Sone - ImageInsertAbortedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageInsertAbortedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link Image} insert is aborted.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageInsertAbortedEvent extends ImageEvent {
/*
- * Sone - ImageInsertFailedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageInsertFailedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link Image} insert has failed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageInsertFailedEvent extends ImageEvent {
/*
- * Sone - ImageInsertFinishedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageInsertFinishedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link Image} insert is finished.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageInsertFinishedEvent extends ImageEvent {
/*
- * Sone - ImageInsertStartedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageInsertStartedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link Image} is not being inserted.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageInsertStartedEvent extends ImageEvent {
+++ /dev/null
-package net.pterodactylus.sone.core.event;
-
-import com.google.common.eventbus.EventBus;
-
-/**
- * Notifies interested {@link EventBus} clients that the Sone insertion delay
- * has changed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class InsertionDelayChangedEvent {
-
- private final int insertionDelay;
-
- public InsertionDelayChangedEvent(int insertionDelay) {
- this.insertionDelay = insertionDelay;
- }
-
- public int getInsertionDelay() {
- return insertionDelay;
- }
-
-}
/*
- * Sone - MarkPostKnownEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - MarkPostKnownEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Post} has been marked as
* {@link Post#isKnown() known}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class MarkPostKnownEvent extends PostEvent {
/*
- * Sone - MarkPostReplyKnownEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - MarkPostReplyKnownEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link PostReply} has been marked as
* {@link PostReply#isKnown() known}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class MarkPostReplyKnownEvent extends PostReplyEvent {
/*
- * Sone - MarkSoneKnownEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - MarkSoneKnownEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Sone} has been marked as
* {@link Sone#isKnown() known}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class MarkSoneKnownEvent extends SoneEvent {
+++ /dev/null
-/*
- * Sone - NewPostFoundEvent.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.core.event;
-
-import net.pterodactylus.sone.data.Post;
-
-/**
- * Event that signals that a new post was found.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class NewPostFoundEvent extends PostEvent {
-
- /**
- * Creates a new “new post found” event.
- *
- * @param post
- * The post that was found
- */
- public NewPostFoundEvent(Post post) {
- super(post);
- }
-
-}
+++ /dev/null
-/*
- * Sone - NewPostReplyFoundEvent.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.core.event;
-
-import net.pterodactylus.sone.data.PostReply;
-
-/**
- * Event that signals that a new {@link PostReply} was found.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class NewPostReplyFoundEvent extends PostReplyEvent {
-
- /**
- * Creates a new “new post found” event.
- *
- * @param postReply
- * The post reply that was found
- */
- public NewPostReplyFoundEvent(PostReply postReply) {
- super(postReply);
- }
-
-}
/*
- * Sone - NewSoneFoundEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - NewSoneFoundEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a new remote Sone was found.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class NewSoneFoundEvent extends SoneEvent {
/*
- * Sone - PostEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - PostEvent.java - Copyright © 2013–2019 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
/**
* Base class for post events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PostEvent {
+++ /dev/null
-/*
- * Sone - PostRemovedEvent.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.core.event;
-
-import net.pterodactylus.sone.data.Post;
-
-/**
- * Event that signals that a {@link Post} was removed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PostRemovedEvent extends PostEvent {
-
- /**
- * Creates a new “post removed” event.
- *
- * @param post
- * The post that was removed
- */
- public PostRemovedEvent(Post post) {
- super(post);
- }
-
-}
/*
- * Sone - PostReplyEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - PostReplyEvent.java - Copyright © 2013–2019 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
/**
* Base class for {@link PostReply} events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PostReplyEvent {
+++ /dev/null
-/*
- * Sone - PostReplyRemovedEvent.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.core.event;
-
-import net.pterodactylus.sone.data.PostReply;
-
-/**
- * Event that signals that a {@link PostReply} was removed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PostReplyRemovedEvent extends PostReplyEvent {
-
- /**
- * Creates a new “post reply removed” event.
- *
- * @param postReply
- * The post reply that was removed
- */
- public PostReplyRemovedEvent(PostReply postReply) {
- super(postReply);
- }
-
-}
/*
- * Sone - SoneEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneEvent.java - Copyright © 2013–2019 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
/**
* Base class for Sone events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class SoneEvent {
/*
- * Sone - SoneInsertAbortedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneInsertAbortedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Sone} insert was aborted.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneInsertAbortedEvent extends SoneEvent {
/*
- * Sone - SoneInsertedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneInsertedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Sone} was inserted.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneInsertedEvent extends SoneEvent {
/*
- * Sone - SoneInsertingEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneInsertingEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Sone} is now being inserted.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneInsertingEvent extends SoneEvent {
/*
- * Sone - SoneLockedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneLockedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Sone} was locked. Only
* {@link Sone#isLocal() local Sones} can be locked.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneLockedEvent extends SoneEvent {
/*
- * Sone - SoneRemovedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneRemovedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Sone} was removed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneRemovedEvent extends SoneEvent {
/*
- * Sone - SoneUnlockedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - SoneUnlockedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a {@link Sone} was unlocked. Only
* {@link Sone#isLocal() local Sones} can be locked.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneUnlockedEvent extends SoneEvent {
/*
- * Sone - UpdateFoundEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - UpdateFoundEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an update for Sone was found.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class UpdateFoundEvent {
/*
- * Sone - Album.java - Copyright © 2011–2016 David Roden
+ * Sone - Album.java - Copyright © 2011–2019 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
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Container for images that can also contain nested {@link Album}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Album extends Identified, Fingerprintable {
- /** Compares two {@link Album}s by {@link #getTitle()}. */
- Comparator<Album> TITLE_COMPARATOR = new Comparator<Album>() {
-
- @Override
- public int compare(Album leftAlbum, Album rightAlbum) {
- return leftAlbum.getTitle().compareToIgnoreCase(rightAlbum.getTitle());
- }
- };
-
/** Function that flattens the given album and all albums beneath it. */
Function<Album, List<Album>> FLATTENER = new Function<Album, List<Album>>() {
if (album == null) {
return emptyList();
}
- List<Album> albums = new ArrayList<Album>();
+ List<Album> albums = new ArrayList<>();
albums.add(album);
for (Album subAlbum : album.getAlbums()) {
albums.addAll(FluentIterable.from(ImmutableList.of(subAlbum)).transformAndConcat(FLATTENER).toList());
* Allows modifying an album. Modifications are only performed once {@link
* #update()} has succesfully returned a new album with the modifications
* made.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
interface Modifier {
/*
- * Sone - Client.java - Copyright © 2010–2016 David Roden
+ * Sone - Client.java - Copyright © 2010–2019 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
/**
* Container for the client information of a {@link Sone}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Client {
+++ /dev/null
-/*
- * Sone - Fingerprintable.java - Copyright © 2011–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data;
-
-/**
- * Interface for objects that can create a fingerprint of themselves, e.g. to
- * detect modifications. The fingerprint should only contain original
- * information; derived information should not be included.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface Fingerprintable {
-
- /**
- * Returns the fingerprint of this object.
- *
- * @return The fingerprint of this object
- */
- public String getFingerprint();
-
-}
+++ /dev/null
-/*
- * Sone - Identified.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data;
-
-import javax.annotation.Nonnull;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-
-/**
- * Interface for all objects that expose an ID.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface Identified {
-
- /** Function to extract the ID from an optional. */
- public static final Function<Optional<? extends Identified>, Optional<String>> GET_ID = new Function<Optional<? extends Identified>, Optional<String>>() {
-
- @Override
- @Nonnull
- public Optional<String> apply(Optional<? extends Identified> identified) {
- return (identified == null) ? Optional.<String>absent() : (identified.isPresent() ? Optional.of(identified.get().getId()) : Optional.<String>absent());
- }
- };
-
- /**
- * Returns the ID of this element.
- *
- * @return The ID of this element
- */
- public String getId();
-
-}
/*
- * Sone - Image.java - Copyright © 2011–2016 David Roden
+ * Sone - Image.java - Copyright © 2011–2019 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
/**
* Container for image metadata.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
*/
public interface Image extends Identified, Fingerprintable {
/*
- * Sone - Post.java - Copyright © 2010–2016 David Roden
+ * Sone - Post.java - Copyright © 2010–2019 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
/**
* A post is a short message that a user writes in his Sone to let other users
* know what is going on.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Post extends Identified {
/**
* Shell for a post that has not yet been loaded.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’
- * Roden</a>
*/
public static class EmptyPost implements Post {
/*
- * Sone - PostReply.java - Copyright © 2010–2016 David Roden
+ * Sone - PostReply.java - Copyright © 2010–2019 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
/**
* A reply is like a {@link Post} but can never be posted on its own, it always
* refers to another {@link Post}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface PostReply extends Reply<PostReply> {
/*
- * Sone - Profile.java - Copyright © 2010–2016 David Roden
+ * Sone - Profile.java - Copyright © 2010–2019 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
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
+import static java.nio.charset.StandardCharsets.UTF_8;
import java.util.ArrayList;
import java.util.Collections;
/**
* A profile stores personal information about a {@link Sone}. All information
* is optional and can be {@code null}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Profile implements Fingerprintable {
*/
@Nonnull
public Profile setFirstName(@Nullable String firstName) {
- this.firstName = firstName;
+ this.firstName = "".equals(firstName) ? null : firstName;
return this;
}
*/
@Nonnull
public Profile setMiddleName(@Nullable String middleName) {
- this.middleName = middleName;
+ this.middleName = "".equals(middleName) ? null : middleName;
return this;
}
*/
@Nonnull
public Profile setLastName(@Nullable String lastName) {
- this.lastName = lastName;
+ this.lastName = "".equals(lastName) ? null : lastName;
return this;
}
*/
@Nonnull
public List<Field> getFields() {
- return new ArrayList<Field>(fields);
+ return new ArrayList<>(fields);
}
/**
@Override
public String getFingerprint() {
Hasher hash = Hashing.sha256().newHasher();
- hash.putString("Profile(");
+ hash.putString("Profile(", UTF_8);
if (firstName != null) {
- hash.putString("FirstName(").putString(firstName).putString(")");
+ hash.putString("FirstName(", UTF_8).putString(firstName, UTF_8).putString(")", UTF_8);
}
if (middleName != null) {
- hash.putString("MiddleName(").putString(middleName).putString(")");
+ hash.putString("MiddleName(", UTF_8).putString(middleName, UTF_8).putString(")", UTF_8);
}
if (lastName != null) {
- hash.putString("LastName(").putString(lastName).putString(")");
+ hash.putString("LastName(", UTF_8).putString(lastName, UTF_8).putString(")", UTF_8);
}
if (birthDay != null) {
- hash.putString("BirthDay(").putInt(birthDay).putString(")");
+ hash.putString("BirthDay(", UTF_8).putInt(birthDay).putString(")", UTF_8);
}
if (birthMonth != null) {
- hash.putString("BirthMonth(").putInt(birthMonth).putString(")");
+ hash.putString("BirthMonth(", UTF_8).putInt(birthMonth).putString(")", UTF_8);
}
if (birthYear != null) {
- hash.putString("BirthYear(").putInt(birthYear).putString(")");
+ hash.putString("BirthYear(", UTF_8).putInt(birthYear).putString(")", UTF_8);
}
if (avatar != null) {
- hash.putString("Avatar(").putString(avatar).putString(")");
+ hash.putString("Avatar(", UTF_8).putString(avatar, UTF_8).putString(")", UTF_8);
}
- hash.putString("ContactInformation(");
+ hash.putString("ContactInformation(", UTF_8);
for (Field field : fields) {
- hash.putString(field.getName()).putString("(").putString(field.getValue()).putString(")");
+ hash.putString(field.getName(), UTF_8).putString("(", UTF_8).putString(field.getValue(), UTF_8).putString(")", UTF_8);
}
- hash.putString(")");
- hash.putString(")");
+ hash.putString(")", UTF_8);
+ hash.putString(")", UTF_8);
return hash.hash().toString();
}
/**
* Container for a profile field.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Field {
/**
* Exception that signals the addition of a field with an empty name.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public static class EmptyFieldName extends IllegalArgumentException { }
/**
* Exception that signals the addition of a field that already exists.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public static class DuplicateField extends IllegalArgumentException { }
/*
- * Sone - Reply.java - Copyright © 2010–2016 David Roden
+ * Sone - Reply.java - Copyright © 2010–2019 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
*
* @param <T>
* The type of the reply
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Reply<T extends Reply<T>> extends Identified {
/*
- * Sone - Sone.java - Copyright © 2010–2016 David Roden
+ * Sone - Sone.java - Copyright © 2010–2019 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
/**
* A Sone defines everything about a user: her profile, her status updates, her
* replies, her likes and dislikes, etc.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Sone extends Identified, Fingerprintable, Comparable<Sone> {
/**
* Enumeration for the possible states of a {@link Sone}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public enum SoneStatus {
/**
* All Sone-specific options.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface SoneOptions {
/**
* Possible values for all options that are related to loading external content.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
enum LoadExternalContent {
/**
* {@link SoneOptions} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultSoneOptions implements SoneOptions {
/*
- * Sone - TemporaryImage.java - Copyright © 2011–2016 David Roden
+ * Sone - TemporaryImage.java - Copyright © 2011–2019 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
/**
* A temporary image stores an uploaded image in memory until it has been
* inserted into Freenet and is subsequently loaded from there.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TemporaryImage {
/*
- * Sone - AbstractAlbumBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - AbstractAlbumBuilder.java - Copyright © 2013–2019 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
/**
* Abstract {@link AlbumBuilder} implementation. It stores the state of the new
* album and performs validation, you only need to implement {@link #build()}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class AbstractAlbumBuilder implements AlbumBuilder {
/*
- * Sone - AbstractImageBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - AbstractImageBuilder.java - Copyright © 2013–2019 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
/**
* Abstract {@link ImageBuilder} implementation. It stores the state of the new
* album and performs validation, you only need to implement {@link #build()}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class AbstractImageBuilder implements ImageBuilder {
/*
- * Sone - AbstractPostBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - AbstractPostBuilder.java - Copyright © 2013–2019 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
/**
* Abstract {@link PostBuilder} implementation. It stores the state of the new
* post and performs validation, you only need to implement {@link #build()}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class AbstractPostBuilder implements PostBuilder {
/*
- * Sone - AbstractPostReplyBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - AbstractPostReplyBuilder.java - Copyright © 2013–2019 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
* Abstract {@link PostReplyBuilder} implementation. It stores the state of the
* new post and performs validation, implementations only need to implement
* {@link #build()}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class AbstractPostReplyBuilder extends AbstractReplyBuilder<PostReplyBuilder> implements PostReplyBuilder {
/*
- * Sone - AbstractReplyBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - AbstractReplyBuilder.java - Copyright © 2013–2019 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
*
* @param <B>
* The interface implemented and exposed by the builder
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class AbstractReplyBuilder<B extends ReplyBuilder<B>> implements ReplyBuilder<B> {
/**
* Abstract {@link SoneBuilder} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class AbstractSoneBuilder implements SoneBuilder {
/*
- * Sone - AlbumBuilderImpl.java - Copyright © 2013–2016 David Roden
+ * Sone - AlbumBuilderImpl.java - Copyright © 2013–2019 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
/**
* {@link AlbumBuilder} implementation that creates {@link AlbumImpl} objects.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class AlbumBuilderImpl extends AbstractAlbumBuilder {
/*
- * Sone - AlbumImpl.java - Copyright © 2011–2016 David Roden
+ * Sone - AlbumImpl.java - Copyright © 2011–2019 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
import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.nio.charset.StandardCharsets.UTF_8;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Container for images that can also contain nested {@link AlbumImpl}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class AlbumImpl implements Album {
private final Sone sone;
/** Nested albums. */
- private final List<Album> albums = new ArrayList<Album>();
+ private final List<Album> albums = new ArrayList<>();
/** The image IDs in order. */
- private final List<String> imageIds = new ArrayList<String>();
+ private final List<String> imageIds = new ArrayList<>();
/** The images in this album. */
- private final Map<String, Image> images = new HashMap<String, Image>();
+ private final Map<String, Image> images = new HashMap<>();
/** The parent album. */
private Album parent;
@Override
public List<Album> getAlbums() {
- return new ArrayList<Album>(albums);
+ return new ArrayList<>(albums);
}
@Override
@Override
public List<Image> getImages() {
- return new ArrayList<Image>(Collections2.filter(Collections2.transform(imageIds, new Function<String, Image>() {
+ return new ArrayList<>(Collections2.filter(Collections2.transform(imageIds, new Function<String, Image>() {
@Override
@SuppressWarnings("synthetic-access")
@Override
public String getFingerprint() {
Hasher hash = Hashing.sha256().newHasher();
- hash.putString("Album(");
- hash.putString("ID(").putString(id).putString(")");
- hash.putString("Title(").putString(title).putString(")");
- hash.putString("Description(").putString(description).putString(")");
+ hash.putString("Album(", UTF_8);
+ hash.putString("ID(", UTF_8).putString(id, UTF_8).putString(")", UTF_8);
+ hash.putString("Title(", UTF_8).putString(title, UTF_8).putString(")", UTF_8);
+ hash.putString("Description(", UTF_8).putString(description, UTF_8).putString(")", UTF_8);
/* add nested albums. */
- hash.putString("Albums(");
+ hash.putString("Albums(", UTF_8);
for (Album album : albums) {
- hash.putString(album.getFingerprint());
+ hash.putString(album.getFingerprint(), UTF_8);
}
- hash.putString(")");
+ hash.putString(")", UTF_8);
/* add images. */
- hash.putString("Images(");
+ hash.putString("Images(", UTF_8);
for (Image image : getImages()) {
if (image.isInserted()) {
- hash.putString(image.getFingerprint());
+ hash.putString(image.getFingerprint(), UTF_8);
}
}
- hash.putString(")");
+ hash.putString(")", UTF_8);
- hash.putString(")");
+ hash.putString(")", UTF_8);
return hash.hash().toString();
}
+++ /dev/null
-/*
- * Sone - DefaultPostBuilderFactory.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data.impl;
-
-import net.pterodactylus.sone.database.PostBuilder;
-import net.pterodactylus.sone.database.PostBuilderFactory;
-import net.pterodactylus.sone.database.SoneProvider;
-
-import com.google.inject.Inject;
-
-/**
- * {@link PostBuilderFactory} implementation that creates
- * {@link PostBuilderImpl}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class DefaultPostBuilderFactory implements PostBuilderFactory {
-
- /** The Sone provider. */
- private final SoneProvider soneProvider;
-
- /**
- * Creates a new default post builder factory.
- *
- * @param soneProvider
- * The Sone provider
- */
- @Inject
- public DefaultPostBuilderFactory(SoneProvider soneProvider) {
- this.soneProvider = soneProvider;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public PostBuilder newPostBuilder() {
- return new PostBuilderImpl(soneProvider);
- }
-
-}
+++ /dev/null
-/*
- * Sone - DefaultPostReplyBuilderFactory.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data.impl;
-
-import net.pterodactylus.sone.database.PostProvider;
-import net.pterodactylus.sone.database.PostReplyBuilder;
-import net.pterodactylus.sone.database.PostReplyBuilderFactory;
-import net.pterodactylus.sone.database.SoneProvider;
-
-import com.google.inject.Inject;
-
-/**
- * {@link PostReplyBuilderFactory} that creates {@link PostReplyBuilderImpl}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class DefaultPostReplyBuilderFactory implements PostReplyBuilderFactory {
-
- /** The Sone provider. */
- private final SoneProvider soneProvider;
-
- /** The post provider. */
- private final PostProvider postProvider;
-
- /**
- * Creates a new default post reply builder factory.
- *
- * @param soneProvider
- * The Sone provider
- * @param postProvider
- * The post provider
- */
- @Inject
- public DefaultPostReplyBuilderFactory(SoneProvider soneProvider, PostProvider postProvider) {
- this.soneProvider = soneProvider;
- this.postProvider = postProvider;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public PostReplyBuilder newPostReplyBuilder() {
- return new PostReplyBuilderImpl(soneProvider, postProvider);
- }
-
-}
/**
* {@link Sone} implementation that only stores the ID of a Sone and returns
* {@code null}, {@code 0}, or empty collections where appropriate.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdOnlySone implements Sone {
/*
- * Sone - ImageBuilderImpl.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageBuilderImpl.java - Copyright © 2013–2019 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
/**
* {@link ImageBuilder} implementation that creates {@link ImageImpl} objects.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
*/
public class ImageBuilderImpl extends AbstractImageBuilder {
/*
- * Sone - ImageImpl.java - Copyright © 2011–2016 David Roden
+ * Sone - ImageImpl.java - Copyright © 2011–2019 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
import static com.google.common.base.Optional.of;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
+import static java.nio.charset.StandardCharsets.UTF_8;
import java.util.UUID;
/**
* Container for image metadata.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageImpl implements Image {
@Override
public String getFingerprint() {
Hasher hash = Hashing.sha256().newHasher();
- hash.putString("Image(");
- hash.putString("ID(").putString(id).putString(")");
- hash.putString("Title(").putString(title).putString(")");
- hash.putString("Description(").putString(description).putString(")");
- hash.putString(")");
+ hash.putString("Image(", UTF_8);
+ hash.putString("ID(", UTF_8).putString(id, UTF_8).putString(")", UTF_8);
+ hash.putString("Title(", UTF_8).putString(title, UTF_8).putString(")", UTF_8);
+ hash.putString("Description(", UTF_8).putString(description, UTF_8).putString(")", UTF_8);
+ hash.putString(")", UTF_8);
return hash.hash().toString();
}
+++ /dev/null
-/*
- * Sone - PostBuilderImpl.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data.impl;
-
-import java.util.UUID;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.database.PostBuilder;
-import net.pterodactylus.sone.database.SoneProvider;
-
-/**
- * {@link PostBuilder} implementation that creates {@link PostImpl} objects.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PostBuilderImpl extends AbstractPostBuilder {
-
- /**
- * Creates a new post builder.
- *
- * @param soneProvider
- * The Sone provider
- */
- public PostBuilderImpl(SoneProvider soneProvider) {
- super(soneProvider);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Post build() {
- validate();
- return new PostImpl(soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, recipientId, currentTime ? System.currentTimeMillis() : time, text);
- }
-
-}
+++ /dev/null
-/*
- * Sone - PostImpl.java - Copyright © 2010–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data.impl;
-
-import static com.google.common.base.Optional.fromNullable;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.database.SoneProvider;
-
-import com.google.common.base.Optional;
-
-/**
- * A post is a short message that a user writes in his Sone to let other users
- * know what is going on.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PostImpl implements Post {
-
- /** The Sone provider. */
- private final SoneProvider soneProvider;
-
- /** The GUID of the post. */
- private final String id;
-
- /** The ID of the owning Sone. */
- private final String soneId;
-
- /** The ID of the recipient Sone. */
- private final String recipientId;
-
- /** The time of the post (in milliseconds since Jan 1, 1970 UTC). */
- private final long time;
-
- /** The text of the post. */
- private final String text;
-
- /** Whether the post is known. */
- private volatile boolean known;
-
- /**
- * Creates a new post.
- *
- * @param soneProvider
- * The Sone provider
- * @param id
- * The ID of the post
- * @param soneId
- * The ID of the Sone this post belongs to
- * @param recipientId
- * The ID of the recipient of the post
- * @param time
- * The time of the post (in milliseconds since Jan 1, 1970 UTC)
- * @param text
- * The text of the post
- */
- public PostImpl(SoneProvider soneProvider, String id, String soneId, String recipientId, long time, String text) {
- this.soneProvider = soneProvider;
- this.id = id;
- this.soneId = soneId;
- this.recipientId = recipientId;
- this.time = time;
- this.text = text;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getId() {
- return id;
- }
-
- @Override
- public boolean isLoaded() {
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Sone getSone() {
- return soneProvider.getSone(soneId);
- }
-
- /**
- * {@inheritDocs}
- */
- @Override
- public Optional<String> getRecipientId() {
- return fromNullable(recipientId);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Optional<Sone> getRecipient() {
- return fromNullable(soneProvider.getSone(recipientId));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long getTime() {
- return time;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getText() {
- return text;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isKnown() {
- return known;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public PostImpl setKnown(boolean known) {
- this.known = known;
- return this;
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof PostImpl)) {
- return false;
- }
- PostImpl post = (PostImpl) object;
- return post.id.equals(id);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return String.format("%s[id=%s,sone=%s,recipient=%s,time=%d,text=%s]", getClass().getName(), id, soneId, recipientId, time, text);
- }
-
-}
+++ /dev/null
-/*
- * Sone - PostReplyBuilderImpl.java - Copyright © 2013–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data.impl;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import java.util.UUID;
-
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.database.PostProvider;
-import net.pterodactylus.sone.database.PostReplyBuilder;
-import net.pterodactylus.sone.database.SoneProvider;
-
-/**
- * {@link PostReplyBuilder} implementation that creates {@link PostReplyImpl}
- * objects.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PostReplyBuilderImpl extends AbstractPostReplyBuilder {
-
- /** The Sone provider. */
- private final SoneProvider soneProvider;
-
- /** The post provider. */
- private final PostProvider postProvider;
-
- /**
- * Creates a new post reply builder.
- *
- * @param soneProvider
- * The Sone provider
- * @param postProvider
- * The post provider
- */
- public PostReplyBuilderImpl(SoneProvider soneProvider, PostProvider postProvider) {
- this.soneProvider = soneProvider;
- this.postProvider = postProvider;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public PostReply build() {
- checkState((randomId && (id == null)) || (!randomId && (id != null)), "either random ID nor custom ID must be set");
- checkState(senderId != null, "sender must not be null");
- checkState((currentTime && (time == 0)) || (!currentTime && (time >= 0)), "either current time or custom time must be set");
- checkState((text != null) && !text.trim().isEmpty(), "text must not be empty");
- checkState(postId != null, "post must not be null");
-
- /* create new post reply. */
- return new PostReplyImpl(soneProvider, postProvider, randomId ? UUID.randomUUID().toString() : id, senderId, currentTime ? System.currentTimeMillis() : time, text, postId);
- }
-
-}
+++ /dev/null
-/*
- * Sone - PostReplyImpl.java - Copyright © 2010–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data.impl;
-
-import static com.google.common.base.Optional.fromNullable;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.database.PostProvider;
-import net.pterodactylus.sone.database.SoneProvider;
-
-import com.google.common.base.Optional;
-
-/**
- * Simple {@link PostReply} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PostReplyImpl extends ReplyImpl<PostReply> implements PostReply {
-
- /** The post provider. */
- private final PostProvider postProvider;
-
- /** The Post this reply refers to. */
- private final String postId;
-
- /**
- * Creates a new reply.
- *
- * @param soneProvider
- * The Sone provider
- * @param postProvider
- * The post provider
- * @param id
- * The ID of the reply
- * @param soneId
- * The ID of the Sone of the reply
- * @param time
- * The time of the reply
- * @param text
- * The text of the reply
- * @param postId
- * The ID of the post this reply refers to
- */
- public PostReplyImpl(SoneProvider soneProvider, PostProvider postProvider, String id, String soneId, long time, String text, String postId) {
- super(soneProvider, id, soneId, time, text);
- this.postProvider = postProvider;
- this.postId = postId;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * {@inheritDocs}
- */
- @Override
- public String getPostId() {
- return postId;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Optional<Post> getPost() {
- return fromNullable(postProvider.getPost(postId));
- }
-
-}
+++ /dev/null
-/*
- * Sone - ReplyImpl.java - Copyright © 2011–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.data.impl;
-
-import net.pterodactylus.sone.data.Reply;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.database.SoneProvider;
-
-/**
- * Abstract base class for all replies.
- *
- * @param <T>
- * The type of the reply
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public abstract class ReplyImpl<T extends Reply<T>> implements Reply<T> {
-
- /** The Sone provider. */
- private final SoneProvider soneProvider;
-
- /** The ID of the reply. */
- private final String id;
-
- /** The Sone that created this reply. */
- private final String soneId;
-
- /** The time of the reply. */
- private final long time;
-
- /** The text of the reply. */
- private final String text;
-
- /** Whether the reply is known. */
- private volatile boolean known;
-
- /**
- * Creates a new reply.
- *
- * @param soneProvider
- * The Sone provider
- * @param id
- * The ID of the reply
- * @param soneId
- * The ID of the Sone of the reply
- * @param time
- * The time of the reply
- * @param text
- * The text of the reply
- */
- protected ReplyImpl(SoneProvider soneProvider, String id, String soneId, long time, String text) {
- this.soneProvider = soneProvider;
- this.id = id;
- this.soneId = soneId;
- this.time = time;
- this.text = text;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getId() {
- return id;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Sone getSone() {
- return soneProvider.getSone(soneId);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long getTime() {
- return time;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getText() {
- return text;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isKnown() {
- return known;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- @SuppressWarnings("unchecked")
- public T setKnown(boolean known) {
- this.known = known;
- return (T) this;
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof Reply<?>)) {
- return false;
- }
- Reply<?> reply = (Reply<?>) object;
- return reply.getId().equals(id);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return String.format("%s[id=%s,sone=%s,time=%d,text=%s]", getClass().getName(), id, soneId, time, text);
- }
-
-}
/*
- * Sone - SoneImpl.java - Copyright © 2010–2016 David Roden
+ * Sone - SoneImpl.java - Copyright © 2010–2019 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
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.logging.Logger.getLogger;
import java.net.MalformedURLException;
* {@link Sone} implementation.
* <p/>
* Operations that modify the Sone need to synchronize on the Sone in question.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneImpl implements Sone {
private volatile boolean known;
/** All posts. */
- private final Set<Post> posts = new CopyOnWriteArraySet<Post>();
+ private final Set<Post> posts = new CopyOnWriteArraySet<>();
/** All replies. */
- private final Set<PostReply> replies = new CopyOnWriteArraySet<PostReply>();
+ private final Set<PostReply> replies = new CopyOnWriteArraySet<>();
/** The IDs of all liked posts. */
- private final Set<String> likedPostIds = new CopyOnWriteArraySet<String>();
+ private final Set<String> likedPostIds = new CopyOnWriteArraySet<>();
/** The IDs of all liked replies. */
- private final Set<String> likedReplyIds = new CopyOnWriteArraySet<String>();
+ private final Set<String> likedReplyIds = new CopyOnWriteArraySet<>();
/** The root album containing all albums. */
private final Album rootAlbum = new AlbumImpl(this);
public List<Post> getPosts() {
List<Post> sortedPosts;
synchronized (this) {
- sortedPosts = new ArrayList<Post>(posts);
+ sortedPosts = new ArrayList<>(posts);
}
Collections.sort(sortedPosts, Post.NEWEST_FIRST);
return sortedPosts;
@Override
public synchronized String getFingerprint() {
Hasher hash = Hashing.sha256().newHasher();
- hash.putString(profile.getFingerprint());
+ hash.putString(profile.getFingerprint(), UTF_8);
- hash.putString("Posts(");
+ hash.putString("Posts(", UTF_8);
for (Post post : getPosts()) {
- hash.putString("Post(").putString(post.getId()).putString(")");
+ hash.putString("Post(", UTF_8).putString(post.getId(), UTF_8).putString(")", UTF_8);
}
- hash.putString(")");
+ hash.putString(")", UTF_8);
- List<PostReply> replies = new ArrayList<PostReply>(getReplies());
+ List<PostReply> replies = new ArrayList<>(getReplies());
Collections.sort(replies, Reply.TIME_COMPARATOR);
- hash.putString("Replies(");
+ hash.putString("Replies(", UTF_8);
for (PostReply reply : replies) {
- hash.putString("Reply(").putString(reply.getId()).putString(")");
+ hash.putString("Reply(", UTF_8).putString(reply.getId(), UTF_8).putString(")", UTF_8);
}
- hash.putString(")");
+ hash.putString(")", UTF_8);
- List<String> likedPostIds = new ArrayList<String>(getLikedPostIds());
+ List<String> likedPostIds = new ArrayList<>(getLikedPostIds());
Collections.sort(likedPostIds);
- hash.putString("LikedPosts(");
+ hash.putString("LikedPosts(", UTF_8);
for (String likedPostId : likedPostIds) {
- hash.putString("Post(").putString(likedPostId).putString(")");
+ hash.putString("Post(", UTF_8).putString(likedPostId, UTF_8).putString(")", UTF_8);
}
- hash.putString(")");
+ hash.putString(")", UTF_8);
- List<String> likedReplyIds = new ArrayList<String>(getLikedReplyIds());
+ List<String> likedReplyIds = new ArrayList<>(getLikedReplyIds());
Collections.sort(likedReplyIds);
- hash.putString("LikedReplies(");
+ hash.putString("LikedReplies(", UTF_8);
for (String likedReplyId : likedReplyIds) {
- hash.putString("Reply(").putString(likedReplyId).putString(")");
+ hash.putString("Reply(", UTF_8).putString(likedReplyId, UTF_8).putString(")", UTF_8);
}
- hash.putString(")");
+ hash.putString(")", UTF_8);
- hash.putString("Albums(");
+ hash.putString("Albums(", UTF_8);
for (Album album : rootAlbum.getAlbums()) {
if (!Album.NOT_EMPTY.apply(album)) {
continue;
}
- hash.putString(album.getFingerprint());
+ hash.putString(album.getFingerprint(), UTF_8);
}
- hash.putString(")");
+ hash.putString(")", UTF_8);
return hash.hash().toString();
}
+++ /dev/null
-/*
- * Sone - AlbumBuilder.java - Copyright © 2013–2016 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.database
-
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Sone
-
-/**
- * Builder for [Album] objects.
- */
-interface AlbumBuilder {
-
- fun randomId(): AlbumBuilder
- fun withId(id: String): AlbumBuilder
-
- fun by(sone: Sone): AlbumBuilder
-
- fun build(): Album
-
-}
+++ /dev/null
-/*
- * Sone - AlbumBuilderFactory.java - Copyright © 2013–2016 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.database
-
-/**
- * Factory for [AlbumBuilder]s.
- */
-interface AlbumBuilderFactory {
-
- fun newAlbumBuilder(): AlbumBuilder
-
-}
+++ /dev/null
-/*
- * Sone - AlbumDatabase.java - Copyright © 2013–2016 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.database
-
-/**
- * Combines an [AlbumProvider] and an [AlbumStore] into an album
- * database.
- */
-interface AlbumDatabase : AlbumProvider, AlbumBuilderFactory, AlbumStore
+++ /dev/null
-/*
- * Sone - AlbumProvider.java - Copyright © 2013–2016 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.database
-
-import net.pterodactylus.sone.data.Album
-
-import com.google.common.base.Optional
-
-/**
- * Interface for objects that can provide [Album]s by their ID.
- */
-interface AlbumProvider {
-
- fun getAlbum(albumId: String): Album?
-
-}
+++ /dev/null
-/*
- * Sone - AlbumStore.java - Copyright © 2013–2016 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.database
-
-import net.pterodactylus.sone.data.Album
-
-/**
- * Interface for a store of albums.
- */
-interface AlbumStore {
-
- fun storeAlbum(album: Album)
- fun removeAlbum(album: Album)
-
-}
/*
- * Sone - DatabaseException.java - Copyright © 2013–2016 David Roden
+ * Sone - DatabaseException.java - Copyright © 2013–2019 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
/**
* Exception that signals a database error.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DatabaseException extends Exception {
import static java.util.logging.Level.WARNING;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import net.pterodactylus.util.config.Configuration;
import net.pterodactylus.util.config.ConfigurationException;
/**
* Helper class for interacting with a {@link Configuration}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ConfigurationLoader {
return loadIds("Bookmarks/Post");
}
+ @Nullable
+ public synchronized Long getSoneFollowingTime(@Nonnull String soneId) {
+ return loadSoneFollowingTimes().get(soneId);
+ }
+
+ public synchronized void removeSoneFollowingTime(@Nonnull String soneId) {
+ Map<String, Long> soneFollowingTimes = loadSoneFollowingTimes();
+ soneFollowingTimes.remove(soneId);
+ storeSoneFollowingTimes(soneFollowingTimes);
+ }
+
+ public synchronized void setSoneFollowingTime(@Nonnull String soneId, long time) {
+ Map<String, Long> soneFollowingTimes = loadSoneFollowingTimes();
+ soneFollowingTimes.put(soneId, time);
+ storeSoneFollowingTimes(soneFollowingTimes);
+ }
+
+ private synchronized Map<String, Long> loadSoneFollowingTimes() {
+ Map<String, Long> soneFollowingTimes = new HashMap<>();
+ int soneCounter = 0;
+ while (true) {
+ String soneId = configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").getValue(null);
+ if (soneId == null) {
+ break;
+ }
+ soneFollowingTimes.put(soneId, configuration.getLongValue("SoneFollowingTimes/" + soneCounter++ + "/Time").getValue(null));
+ }
+ return soneFollowingTimes;
+ }
+
+ private synchronized void storeSoneFollowingTimes(Map<String, Long> soneFollowingTimes) {
+ int soneCounter = 0;
+ try {
+ for (Entry<String, Long> soneFollowingTime : soneFollowingTimes.entrySet()) {
+ configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(soneFollowingTime.getKey());
+ configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").setValue(soneFollowingTime.getValue());
+ ++soneCounter;
+ }
+ configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(null);
+ } catch (ConfigurationException ce1) {
+ logger.log(WARNING, "Could not save Sone following times!", ce1);
+ }
+ }
+
private Set<String> loadIds(String prefix) {
- Set<String> ids = new HashSet<String>();
+ Set<String> ids = new HashSet<>();
int idCounter = 0;
while (true) {
String id = configuration
/**
* Memory-based {@link BookmarkDatabase} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class MemoryBookmarkDatabase implements BookmarkDatabase {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final MemoryDatabase memoryDatabase;
private final ConfigurationLoader configurationLoader;
- private final Set<String> bookmarkedPosts = new HashSet<String>();
+ private final Set<String> bookmarkedPosts = new HashSet<>();
public MemoryBookmarkDatabase(MemoryDatabase memoryDatabase,
ConfigurationLoader configurationLoader) {
+++ /dev/null
-/*
- * Sone - MemoryDatabase.java - Copyright © 2013–2016 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.database.memory;
-
-import static com.google.common.base.Optional.fromNullable;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Predicates.not;
-import static com.google.common.collect.FluentIterable.from;
-import static net.pterodactylus.sone.data.Reply.TIME_COMPARATOR;
-import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER;
-import static net.pterodactylus.sone.data.Sone.toAllAlbums;
-import static net.pterodactylus.sone.data.Sone.toAllImages;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.data.impl.AlbumBuilderImpl;
-import net.pterodactylus.sone.data.impl.ImageBuilderImpl;
-import net.pterodactylus.sone.database.AlbumBuilder;
-import net.pterodactylus.sone.database.Database;
-import net.pterodactylus.sone.database.DatabaseException;
-import net.pterodactylus.sone.database.ImageBuilder;
-import net.pterodactylus.sone.database.PostBuilder;
-import net.pterodactylus.sone.database.PostDatabase;
-import net.pterodactylus.sone.database.PostReplyBuilder;
-import net.pterodactylus.sone.database.SoneBuilder;
-import net.pterodactylus.sone.database.SoneProvider;
-import net.pterodactylus.util.config.Configuration;
-import net.pterodactylus.util.config.ConfigurationException;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.SortedSetMultimap;
-import com.google.common.collect.TreeMultimap;
-import com.google.common.util.concurrent.AbstractService;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import kotlin.jvm.functions.Function1;
-
-/**
- * Memory-based {@link PostDatabase} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-@Singleton
-public class MemoryDatabase extends AbstractService implements Database {
-
- /** The lock. */
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- /** The Sone provider. */
- private final SoneProvider soneProvider;
-
- /** The configuration. */
- private final Configuration configuration;
- private final ConfigurationLoader configurationLoader;
-
- private final Map<String, Sone> allSones = new HashMap<String, Sone>();
-
- /** All posts by their ID. */
- private final Map<String, Post> allPosts = new HashMap<String, Post>();
-
- /** All posts by their Sones. */
- private final Multimap<String, Post> sonePosts = HashMultimap.create();
-
- /** Whether posts are known. */
- private final Set<String> knownPosts = new HashSet<String>();
-
- /** All post replies by their ID. */
- private final Map<String, PostReply> allPostReplies = new HashMap<String, PostReply>();
-
- /** Replies sorted by Sone. */
- private final SortedSetMultimap<String, PostReply> sonePostReplies = TreeMultimap.create(new Comparator<String>() {
-
- @Override
- public int compare(String leftString, String rightString) {
- return leftString.compareTo(rightString);
- }
- }, TIME_COMPARATOR);
-
- /** Whether post replies are known. */
- private final Set<String> knownPostReplies = new HashSet<String>();
-
- private final Map<String, Album> allAlbums = new HashMap<String, Album>();
- private final Multimap<String, Album> soneAlbums = HashMultimap.create();
-
- private final Map<String, Image> allImages = new HashMap<String, Image>();
- private final Multimap<String, Image> soneImages = HashMultimap.create();
-
- private final MemoryBookmarkDatabase memoryBookmarkDatabase;
- private final MemoryFriendDatabase memoryFriendDatabase;
-
- /**
- * Creates a new memory database.
- *
- * @param soneProvider
- * The Sone provider
- * @param configuration
- * The configuration for loading and saving elements
- */
- @Inject
- public MemoryDatabase(SoneProvider soneProvider, Configuration configuration) {
- this.soneProvider = soneProvider;
- this.configuration = configuration;
- this.configurationLoader = new ConfigurationLoader(configuration);
- memoryBookmarkDatabase =
- new MemoryBookmarkDatabase(this, configurationLoader);
- memoryFriendDatabase = new MemoryFriendDatabase(configurationLoader);
- }
-
- //
- // DATABASE METHODS
- //
-
- /**
- * Saves the database.
- *
- * @throws DatabaseException
- * if an error occurs while saving
- */
- @Override
- public void save() throws DatabaseException {
- saveKnownPosts();
- saveKnownPostReplies();
- }
-
- //
- // SERVICE METHODS
- //
-
- /** {@inheritDocs} */
- @Override
- protected void doStart() {
- memoryBookmarkDatabase.start();
- loadKnownPosts();
- loadKnownPostReplies();
- notifyStarted();
- }
-
- /** {@inheritDocs} */
- @Override
- protected void doStop() {
- try {
- memoryBookmarkDatabase.stop();
- save();
- notifyStopped();
- } catch (DatabaseException de1) {
- notifyFailed(de1);
- }
- }
-
- @Override
- public SoneBuilder newSoneBuilder() {
- return new MemorySoneBuilder(this);
- }
-
- @Override
- public void storeSone(Sone sone) {
- lock.writeLock().lock();
- try {
- removeSone(sone);
-
- allSones.put(sone.getId(), sone);
- sonePosts.putAll(sone.getId(), sone.getPosts());
- for (Post post : sone.getPosts()) {
- allPosts.put(post.getId(), post);
- }
- sonePostReplies.putAll(sone.getId(), sone.getReplies());
- for (PostReply postReply : sone.getReplies()) {
- allPostReplies.put(postReply.getId(), postReply);
- }
- soneAlbums.putAll(sone.getId(), toAllAlbums.apply(sone));
- for (Album album : toAllAlbums.apply(sone)) {
- allAlbums.put(album.getId(), album);
- }
- soneImages.putAll(sone.getId(), toAllImages.apply(sone));
- for (Image image : toAllImages.apply(sone)) {
- allImages.put(image.getId(), image);
- }
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- @Override
- public void removeSone(Sone sone) {
- lock.writeLock().lock();
- try {
- allSones.remove(sone.getId());
- Collection<Post> removedPosts = sonePosts.removeAll(sone.getId());
- for (Post removedPost : removedPosts) {
- allPosts.remove(removedPost.getId());
- }
- Collection<PostReply> removedPostReplies =
- sonePostReplies.removeAll(sone.getId());
- for (PostReply removedPostReply : removedPostReplies) {
- allPostReplies.remove(removedPostReply.getId());
- }
- Collection<Album> removedAlbums =
- soneAlbums.removeAll(sone.getId());
- for (Album removedAlbum : removedAlbums) {
- allAlbums.remove(removedAlbum.getId());
- }
- Collection<Image> removedImages =
- soneImages.removeAll(sone.getId());
- for (Image removedImage : removedImages) {
- allImages.remove(removedImage.getId());
- }
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- @Nonnull
- @Override
- public Function1<String, Sone> getSoneLoader() {
- return new Function1<String, Sone>() {
- @Override
- public Sone invoke(String soneId) {
- return getSone(soneId);
- }
- };
- }
-
- @Override
- public Sone getSone(String soneId) {
- lock.readLock().lock();
- try {
- return allSones.get(soneId);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- @Override
- public Collection<Sone> getSones() {
- lock.readLock().lock();
- try {
- return new HashSet<Sone>(allSones.values());
- } finally {
- lock.readLock().unlock();
- }
- }
-
- @Override
- public Collection<Sone> getLocalSones() {
- lock.readLock().lock();
- try {
- return from(allSones.values()).filter(LOCAL_SONE_FILTER).toSet();
- } finally {
- lock.readLock().unlock();
- }
- }
-
- @Override
- public Collection<Sone> getRemoteSones() {
- lock.readLock().lock();
- try {
- return from(allSones.values())
- .filter(not(LOCAL_SONE_FILTER)) .toSet();
- } finally {
- lock.readLock().unlock();
- }
- }
-
- @Override
- public Collection<String> getFriends(Sone localSone) {
- if (!localSone.isLocal()) {
- return Collections.emptySet();
- }
- return memoryFriendDatabase.getFriends(localSone.getId());
- }
-
- @Override
- public boolean isFriend(Sone localSone, String friendSoneId) {
- if (!localSone.isLocal()) {
- return false;
- }
- return memoryFriendDatabase.isFriend(localSone.getId(), friendSoneId);
- }
-
- @Override
- public void addFriend(Sone localSone, String friendSoneId) {
- if (!localSone.isLocal()) {
- return;
- }
- memoryFriendDatabase.addFriend(localSone.getId(), friendSoneId);
- }
-
- @Override
- public void removeFriend(Sone localSone, String friendSoneId) {
- if (!localSone.isLocal()) {
- return;
- }
- memoryFriendDatabase.removeFriend(localSone.getId(), friendSoneId);
- }
-
- //
- // POSTPROVIDER METHODS
- //
-
- @Nullable
- @Override
- public Post getPost(@Nonnull String postId) {
- lock.readLock().lock();
- try {
- return allPosts.get(postId);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /** {@inheritDocs} */
- @Override
- public Collection<Post> getPosts(String soneId) {
- return new HashSet<Post>(getPostsFrom(soneId));
- }
-
- /** {@inheritDocs} */
- @Override
- public Collection<Post> getDirectedPosts(final String recipientId) {
- lock.readLock().lock();
- try {
- return from(sonePosts.values()).filter(new Predicate<Post>() {
- @Override
- public boolean apply(Post post) {
- return post.getRecipientId().asSet().contains(recipientId);
- }
- }).toSet();
- } finally {
- lock.readLock().unlock();
- }
- }
-
- //
- // POSTBUILDERFACTORY METHODS
- //
-
- /** {@inheritDocs} */
- @Override
- public PostBuilder newPostBuilder() {
- return new MemoryPostBuilder(this, soneProvider);
- }
-
- //
- // POSTSTORE METHODS
- //
-
- /** {@inheritDocs} */
- @Override
- public void storePost(Post post) {
- checkNotNull(post, "post must not be null");
- lock.writeLock().lock();
- try {
- allPosts.put(post.getId(), post);
- getPostsFrom(post.getSone().getId()).add(post);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /** {@inheritDocs} */
- @Override
- public void removePost(Post post) {
- checkNotNull(post, "post must not be null");
- lock.writeLock().lock();
- try {
- allPosts.remove(post.getId());
- getPostsFrom(post.getSone().getId()).remove(post);
- post.getSone().removePost(post);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- //
- // POSTREPLYPROVIDER METHODS
- //
-
- @Nullable
- @Override
- public PostReply getPostReply(String id) {
- lock.readLock().lock();
- try {
- return allPostReplies.get(id);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /** {@inheritDocs} */
- @Override
- public List<PostReply> getReplies(final String postId) {
- lock.readLock().lock();
- try {
- return from(allPostReplies.values())
- .filter(new Predicate<PostReply>() {
- @Override
- public boolean apply(PostReply postReply) {
- return postReply.getPostId().equals(postId);
- }
- }).toSortedList(TIME_COMPARATOR);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- //
- // POSTREPLYBUILDERFACTORY METHODS
- //
-
- /** {@inheritDocs} */
- @Override
- public PostReplyBuilder newPostReplyBuilder() {
- return new MemoryPostReplyBuilder(this, soneProvider);
- }
-
- //
- // POSTREPLYSTORE METHODS
- //
-
- /** {@inheritDocs} */
- @Override
- public void storePostReply(PostReply postReply) {
- lock.writeLock().lock();
- try {
- allPostReplies.put(postReply.getId(), postReply);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /** {@inheritDocs} */
- @Override
- public void removePostReply(PostReply postReply) {
- lock.writeLock().lock();
- try {
- allPostReplies.remove(postReply.getId());
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- //
- // ALBUMPROVDER METHODS
- //
-
- @Nullable
- @Override
- public Album getAlbum(@Nonnull String albumId) {
- lock.readLock().lock();
- try {
- return allAlbums.get(albumId);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- //
- // ALBUMBUILDERFACTORY METHODS
- //
-
- @Override
- public AlbumBuilder newAlbumBuilder() {
- return new AlbumBuilderImpl();
- }
-
- //
- // ALBUMSTORE METHODS
- //
-
- @Override
- public void storeAlbum(Album album) {
- lock.writeLock().lock();
- try {
- allAlbums.put(album.getId(), album);
- soneAlbums.put(album.getSone().getId(), album);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- @Override
- public void removeAlbum(Album album) {
- lock.writeLock().lock();
- try {
- allAlbums.remove(album.getId());
- soneAlbums.remove(album.getSone().getId(), album);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- //
- // IMAGEPROVIDER METHODS
- //
-
- @Nullable
- @Override
- public Image getImage(@Nonnull String imageId) {
- lock.readLock().lock();
- try {
- return allImages.get(imageId);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- //
- // IMAGEBUILDERFACTORY METHODS
- //
-
- @Override
- public ImageBuilder newImageBuilder() {
- return new ImageBuilderImpl();
- }
-
- //
- // IMAGESTORE METHODS
- //
-
- @Override
- public void storeImage(Image image) {
- lock.writeLock().lock();
- try {
- allImages.put(image.getId(), image);
- soneImages.put(image.getSone().getId(), image);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- @Override
- public void removeImage(Image image) {
- lock.writeLock().lock();
- try {
- allImages.remove(image.getId());
- soneImages.remove(image.getSone().getId(), image);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- @Override
- public void bookmarkPost(Post post) {
- memoryBookmarkDatabase.bookmarkPost(post);
- }
-
- @Override
- public void unbookmarkPost(Post post) {
- memoryBookmarkDatabase.unbookmarkPost(post);
- }
-
- @Override
- public boolean isPostBookmarked(Post post) {
- return memoryBookmarkDatabase.isPostBookmarked(post);
- }
-
- @Override
- public Set<Post> getBookmarkedPosts() {
- return memoryBookmarkDatabase.getBookmarkedPosts();
- }
-
- //
- // PACKAGE-PRIVATE METHODS
- //
-
- /**
- * Returns whether the given post is known.
- *
- * @param post
- * The post
- * @return {@code true} if the post is known, {@code false} otherwise
- */
- boolean isPostKnown(Post post) {
- lock.readLock().lock();
- try {
- return knownPosts.contains(post.getId());
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /**
- * Sets whether the given post is known.
- *
- * @param post
- * The post
- * @param known
- * {@code true} if the post is known, {@code false} otherwise
- */
- void setPostKnown(Post post, boolean known) {
- lock.writeLock().lock();
- try {
- if (known) {
- knownPosts.add(post.getId());
- } else {
- knownPosts.remove(post.getId());
- }
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * Returns whether the given post reply is known.
- *
- * @param postReply
- * The post reply
- * @return {@code true} if the given post reply is known, {@code false}
- * otherwise
- */
- boolean isPostReplyKnown(PostReply postReply) {
- lock.readLock().lock();
- try {
- return knownPostReplies.contains(postReply.getId());
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /**
- * Sets whether the given post reply is known.
- *
- * @param postReply
- * The post reply
- * @param known
- * {@code true} if the post reply is known, {@code false} otherwise
- */
- void setPostReplyKnown(PostReply postReply, boolean known) {
- lock.writeLock().lock();
- try {
- if (known) {
- knownPostReplies.add(postReply.getId());
- } else {
- knownPostReplies.remove(postReply.getId());
- }
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- //
- // PRIVATE METHODS
- //
-
- /**
- * Gets all posts for the given Sone, creating a new collection if there is
- * none yet.
- *
- * @param soneId
- * The ID of the Sone to get the posts for
- * @return All posts
- */
- private Collection<Post> getPostsFrom(String soneId) {
- lock.readLock().lock();
- try {
- return sonePosts.get(soneId);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /** Loads the known posts. */
- private void loadKnownPosts() {
- Set<String> knownPosts = configurationLoader.loadKnownPosts();
- lock.writeLock().lock();
- try {
- this.knownPosts.clear();
- this.knownPosts.addAll(knownPosts);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * Saves the known posts to the configuration.
- *
- * @throws DatabaseException
- * if a configuration error occurs
- */
- private void saveKnownPosts() throws DatabaseException {
- lock.readLock().lock();
- try {
- int postCounter = 0;
- for (String knownPostId : knownPosts) {
- configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").setValue(knownPostId);
- }
- configuration.getStringValue("KnownPosts/" + postCounter + "/ID").setValue(null);
- } catch (ConfigurationException ce1) {
- throw new DatabaseException("Could not save database.", ce1);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /** Loads the known post replies. */
- private void loadKnownPostReplies() {
- Set<String> knownPostReplies = configurationLoader.loadKnownPostReplies();
- lock.writeLock().lock();
- try {
- this.knownPostReplies.clear();
- this.knownPostReplies.addAll(knownPostReplies);
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * Saves the known post replies to the configuration.
- *
- * @throws DatabaseException
- * if a configuration error occurs
- */
- private void saveKnownPostReplies() throws DatabaseException {
- lock.readLock().lock();
- try {
- int replyCounter = 0;
- for (String knownReplyId : knownPostReplies) {
- configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").setValue(knownReplyId);
- }
- configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null);
- } catch (ConfigurationException ce1) {
- throw new DatabaseException("Could not save database.", ce1);
- } finally {
- lock.readLock().unlock();
- }
- }
-
-}
--- /dev/null
+/*
+ * Sone - MemoryDatabase.kt - Copyright © 2013–2019 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.database.memory
+
+import com.google.common.base.Preconditions.checkNotNull
+import com.google.common.collect.HashMultimap
+import com.google.common.collect.Multimap
+import com.google.common.collect.TreeMultimap
+import com.google.common.util.concurrent.AbstractService
+import com.google.inject.Inject
+import com.google.inject.Singleton
+import net.pterodactylus.sone.data.Album
+import net.pterodactylus.sone.data.Image
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.data.Reply.TIME_COMPARATOR
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.data.Sone.toAllAlbums
+import net.pterodactylus.sone.data.Sone.toAllImages
+import net.pterodactylus.sone.data.impl.AlbumBuilderImpl
+import net.pterodactylus.sone.data.impl.ImageBuilderImpl
+import net.pterodactylus.sone.database.AlbumBuilder
+import net.pterodactylus.sone.database.Database
+import net.pterodactylus.sone.database.DatabaseException
+import net.pterodactylus.sone.database.ImageBuilder
+import net.pterodactylus.sone.database.PostBuilder
+import net.pterodactylus.sone.database.PostDatabase
+import net.pterodactylus.sone.database.PostReplyBuilder
+import net.pterodactylus.sone.utils.unit
+import net.pterodactylus.util.config.Configuration
+import net.pterodactylus.util.config.ConfigurationException
+import java.util.concurrent.locks.ReentrantReadWriteLock
+import kotlin.concurrent.withLock
+
+/**
+ * Memory-based [PostDatabase] implementation.
+ */
+@Singleton
+class MemoryDatabase @Inject constructor(private val configuration: Configuration) : AbstractService(), Database {
+
+ private val lock = ReentrantReadWriteLock()
+ private val readLock by lazy { lock.readLock()!! }
+ private val writeLock by lazy { lock.writeLock()!! }
+ private val configurationLoader = ConfigurationLoader(configuration)
+ private val allSones = mutableMapOf<String, Sone>()
+ private val allPosts = mutableMapOf<String, Post>()
+ private val sonePosts: Multimap<String, Post> = HashMultimap.create<String, Post>()
+ private val knownPosts = mutableSetOf<String>()
+ private val allPostReplies = mutableMapOf<String, PostReply>()
+ private val sonePostReplies: Multimap<String, PostReply> = TreeMultimap.create<String, PostReply>(Comparator { leftString, rightString -> leftString.compareTo(rightString) }, TIME_COMPARATOR)
+ private val knownPostReplies = mutableSetOf<String>()
+ private val allAlbums = mutableMapOf<String, Album>()
+ private val soneAlbums: Multimap<String, Album> = HashMultimap.create<String, Album>()
+ private val allImages = mutableMapOf<String, Image>()
+ private val soneImages: Multimap<String, Image> = HashMultimap.create<String, Image>()
+ private val memoryBookmarkDatabase = MemoryBookmarkDatabase(this, configurationLoader)
+ private val memoryFriendDatabase = MemoryFriendDatabase(configurationLoader)
+
+ override val soneLoader get() = this::getSone
+
+ override val sones get() = readLock.withLock { allSones.values.toSet() }
+
+ override val localSones get() = readLock.withLock { allSones.values.filter(Sone::isLocal) }
+
+ override val remoteSones get() = readLock.withLock { allSones.values.filterNot(Sone::isLocal) }
+
+ override val bookmarkedPosts get() = memoryBookmarkDatabase.bookmarkedPosts
+
+ override fun save() {
+ saveKnownPosts()
+ saveKnownPostReplies()
+ }
+
+ override fun doStart() {
+ memoryBookmarkDatabase.start()
+ loadKnownPosts()
+ loadKnownPostReplies()
+ notifyStarted()
+ }
+
+ override fun doStop() {
+ try {
+ memoryBookmarkDatabase.stop()
+ save()
+ notifyStopped()
+ } catch (de1: DatabaseException) {
+ notifyFailed(de1)
+ }
+ }
+
+ override fun newSoneBuilder() = MemorySoneBuilder(this)
+
+ override fun storeSone(sone: Sone) {
+ writeLock.withLock {
+ removeSone(sone)
+
+ allSones[sone.id] = sone
+ sonePosts.putAll(sone.id, sone.posts)
+ for (post in sone.posts) {
+ allPosts[post.id] = post
+ }
+ sonePostReplies.putAll(sone.id, sone.replies)
+ for (postReply in sone.replies) {
+ allPostReplies[postReply.id] = postReply
+ }
+ soneAlbums.putAll(sone.id, toAllAlbums.apply(sone)!!)
+ for (album in toAllAlbums.apply(sone)!!) {
+ allAlbums[album.id] = album
+ }
+ soneImages.putAll(sone.id, toAllImages.apply(sone)!!)
+ for (image in toAllImages.apply(sone)!!) {
+ allImages[image.id] = image
+ }
+ }
+ }
+
+ override fun removeSone(sone: Sone) {
+ writeLock.withLock {
+ allSones.remove(sone.id)
+ val removedPosts = sonePosts.removeAll(sone.id)
+ for (removedPost in removedPosts) {
+ allPosts.remove(removedPost.id)
+ }
+ val removedPostReplies = sonePostReplies.removeAll(sone.id)
+ for (removedPostReply in removedPostReplies) {
+ allPostReplies.remove(removedPostReply.id)
+ }
+ val removedAlbums = soneAlbums.removeAll(sone.id)
+ for (removedAlbum in removedAlbums) {
+ allAlbums.remove(removedAlbum.id)
+ }
+ val removedImages = soneImages.removeAll(sone.id)
+ for (removedImage in removedImages) {
+ allImages.remove(removedImage.id)
+ }
+ }
+ }
+
+ override fun getSone(soneId: String) = readLock.withLock { allSones[soneId] }
+
+ override fun getFriends(localSone: Sone): Collection<String> =
+ if (!localSone.isLocal) {
+ emptySet()
+ } else {
+ memoryFriendDatabase.getFriends(localSone.id)
+ }
+
+ override fun isFriend(localSone: Sone, friendSoneId: String) =
+ if (!localSone.isLocal) {
+ false
+ } else {
+ memoryFriendDatabase.isFriend(localSone.id, friendSoneId)
+ }
+
+ override fun addFriend(localSone: Sone, friendSoneId: String) {
+ if (!localSone.isLocal) {
+ return
+ }
+ memoryFriendDatabase.addFriend(localSone.id, friendSoneId)
+ }
+
+ override fun removeFriend(localSone: Sone, friendSoneId: String) {
+ if (!localSone.isLocal) {
+ return
+ }
+ memoryFriendDatabase.removeFriend(localSone.id, friendSoneId)
+ }
+
+ override fun getFollowingTime(friendSoneId: String) =
+ memoryFriendDatabase.getFollowingTime(friendSoneId)
+
+ override fun getPost(postId: String) =
+ readLock.withLock { allPosts[postId] }
+
+ override fun getPosts(soneId: String): Collection<Post> =
+ sonePosts[soneId].toSet()
+
+ override fun getDirectedPosts(recipientId: String) =
+ readLock.withLock {
+ allPosts.values.filter {
+ it.recipientId.orNull() == recipientId
+ }
+ }
+
+ override fun newPostBuilder(): PostBuilder = MemoryPostBuilder(this, this)
+
+ override fun storePost(post: Post) {
+ checkNotNull(post, "post must not be null")
+ writeLock.withLock {
+ allPosts[post.id] = post
+ sonePosts[post.sone.id].add(post)
+ }
+ }
+
+ override fun removePost(post: Post) {
+ checkNotNull(post, "post must not be null")
+ writeLock.withLock {
+ allPosts.remove(post.id)
+ sonePosts[post.sone.id].remove(post)
+ post.sone.removePost(post)
+ }
+ }
+
+ override fun getPostReply(id: String) = readLock.withLock { allPostReplies[id] }
+
+ override fun getReplies(postId: String) =
+ readLock.withLock {
+ allPostReplies.values
+ .filter { it.postId == postId }
+ .sortedWith(TIME_COMPARATOR)
+ }
+
+ override fun newPostReplyBuilder(): PostReplyBuilder =
+ MemoryPostReplyBuilder(this, this)
+
+ override fun storePostReply(postReply: PostReply) =
+ writeLock.withLock {
+ allPostReplies[postReply.id] = postReply
+ }
+
+ override fun removePostReply(postReply: PostReply) =
+ writeLock.withLock {
+ allPostReplies.remove(postReply.id)
+ }.unit
+
+ override fun getAlbum(albumId: String) = readLock.withLock { allAlbums[albumId] }
+
+ override fun newAlbumBuilder(): AlbumBuilder = AlbumBuilderImpl()
+
+ override fun storeAlbum(album: Album) =
+ writeLock.withLock {
+ allAlbums[album.id] = album
+ soneAlbums.put(album.sone.id, album)
+ }.unit
+
+ override fun removeAlbum(album: Album) =
+ writeLock.withLock {
+ allAlbums.remove(album.id)
+ soneAlbums.remove(album.sone.id, album)
+ }.unit
+
+ override fun getImage(imageId: String) = readLock.withLock { allImages[imageId] }
+
+ override fun newImageBuilder(): ImageBuilder = ImageBuilderImpl()
+
+ override fun storeImage(image: Image): Unit =
+ writeLock.withLock {
+ allImages[image.id] = image
+ soneImages.put(image.sone.id, image)
+ }
+
+ override fun removeImage(image: Image): Unit =
+ writeLock.withLock {
+ allImages.remove(image.id)
+ soneImages.remove(image.sone.id, image)
+ }
+
+ override fun bookmarkPost(post: Post) =
+ memoryBookmarkDatabase.bookmarkPost(post)
+
+ override fun unbookmarkPost(post: Post) =
+ memoryBookmarkDatabase.unbookmarkPost(post)
+
+ override fun isPostBookmarked(post: Post) =
+ memoryBookmarkDatabase.isPostBookmarked(post)
+
+ protected fun isPostKnown(post: Post) = readLock.withLock { post.id in knownPosts }
+
+ fun setPostKnown(post: Post, known: Boolean): Unit =
+ writeLock.withLock {
+ if (known)
+ knownPosts.add(post.id)
+ else
+ knownPosts.remove(post.id)
+ saveKnownPosts()
+ }
+
+ protected fun isPostReplyKnown(postReply: PostReply) = readLock.withLock { postReply.id in knownPostReplies }
+
+ fun setPostReplyKnown(postReply: PostReply, known: Boolean): Unit =
+ writeLock.withLock {
+ if (known)
+ knownPostReplies.add(postReply.id)
+ else
+ knownPostReplies.remove(postReply.id)
+ saveKnownPostReplies()
+ }
+
+ private fun loadKnownPosts() =
+ configurationLoader.loadKnownPosts()
+ .let {
+ writeLock.withLock {
+ knownPosts.clear()
+ knownPosts.addAll(it)
+ }
+ }
+
+ private fun saveKnownPosts() =
+ try {
+ readLock.withLock {
+ knownPosts.forEachIndexed { index, knownPostId ->
+ configuration.getStringValue("KnownPosts/$index/ID").value = knownPostId
+ }
+ configuration.getStringValue("KnownPosts/${knownPosts.size}/ID").value = null
+ }
+ } catch (ce1: ConfigurationException) {
+ throw DatabaseException("Could not save database.", ce1)
+ }
+
+ private fun loadKnownPostReplies(): Unit =
+ configurationLoader.loadKnownPostReplies().let { knownPostReplies ->
+ writeLock.withLock {
+ this.knownPostReplies.clear()
+ this.knownPostReplies.addAll(knownPostReplies)
+ }
+ }
+
+ private fun saveKnownPostReplies() =
+ try {
+ readLock.withLock {
+ knownPostReplies.forEachIndexed { index, knownPostReply ->
+ configuration.getStringValue("KnownReplies/$index/ID").value = knownPostReply
+ }
+ configuration.getStringValue("KnownReplies/${knownPostReplies.size}/ID").value = null
+ }
+ } catch (ce1: ConfigurationException) {
+ throw DatabaseException("Could not save database.", ce1)
+ }
+
+}
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* In-memory implementation of friend-related functionality.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
class MemoryFriendDatabase {
try {
if (soneFriends.put(localSoneId, friendSoneId)) {
configurationLoader.saveFriends(localSoneId, soneFriends.get(localSoneId));
+ if (configurationLoader.getSoneFollowingTime(friendSoneId) == null) {
+ configurationLoader.setSoneFollowingTime(friendSoneId, System.currentTimeMillis());
+ }
}
} finally {
lock.writeLock().unlock();
try {
if (soneFriends.remove(localSoneId, friendSoneId)) {
configurationLoader.saveFriends(localSoneId, soneFriends.get(localSoneId));
+ boolean unfollowedSoneStillFollowed = false;
+ for (String soneId : soneFriends.keys()) {
+ unfollowedSoneStillFollowed |= getFriends(soneId).contains(friendSoneId);
+ }
+ if (!unfollowedSoneStillFollowed) {
+ configurationLoader.removeSoneFollowingTime(friendSoneId);
+ }
}
} finally {
lock.writeLock().unlock();
}
}
+ @Nullable
+ Long getFollowingTime(@Nonnull String soneId) {
+ return configurationLoader.getSoneFollowingTime(soneId);
+ }
+
private void loadFriends(String localSoneId) {
lock.writeLock().lock();
try {
/*
- * Sone - MemoryPost.java - Copyright © 2010–2016 David Roden
+ * Sone - MemoryPost.java - Copyright © 2010–2019 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
/**
* A post is a short message that a user writes in his Sone to let other users
* know what is going on.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
class MemoryPost implements Post {
*/
@Override
public Optional<Sone> getRecipient() {
- return fromNullable(soneProvider.getSone(recipientId));
+ return recipientId == null ? Optional.<Sone>absent() : fromNullable(soneProvider.getSone(recipientId));
}
/**
/*
- * Sone - MemoryPostBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - MemoryPostBuilder.java - Copyright © 2013–2019 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
/**
* {@link PostBuilder} implementation that creates a {@link MemoryPost}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
class MemoryPostBuilder extends AbstractPostBuilder {
/*
- * Sone - MemoryPostReply.java - Copyright © 2013–2016 David Roden
+ * Sone - MemoryPostReply.java - Copyright © 2013–2019 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
/**
* Memory-based {@link PostReply} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
class MemoryPostReply implements PostReply {
/*
- * Sone - MemoryPostReplyBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - MemoryPostReplyBuilder.java - Copyright © 2013–2019 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
/**
* {@link PostReplyBuilder} implementation that creates {@link MemoryPostReply}
* objects.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
class MemoryPostReplyBuilder extends AbstractPostReplyBuilder {
/**
* Memory-based {@link AbstractSoneBuilder} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class MemorySoneBuilder extends AbstractSoneBuilder {
+++ /dev/null
-/*
- * Sone - AbstractSoneCommand.java - Copyright © 2011–2016 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.fcp;
-
-import java.util.Collection;
-import java.util.List;
-
-import net.pterodactylus.sone.core.Core;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Profile;
-import net.pterodactylus.sone.data.Profile.Field;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
-import net.pterodactylus.sone.freenet.fcp.AbstractCommand;
-import net.pterodactylus.sone.freenet.fcp.Command;
-import net.pterodactylus.sone.freenet.fcp.FcpException;
-import net.pterodactylus.sone.template.SoneAccessor;
-
-import freenet.node.FSParseException;
-import freenet.support.SimpleFieldSet;
-
-import com.google.common.base.Optional;
-
-/**
- * Abstract base implementation of a {@link Command} with Sone-related helper
- * methods.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public abstract class AbstractSoneCommand extends AbstractCommand {
-
- /** The Sone core. */
- private final Core core;
-
- /** Whether this command needs write access. */
- private final boolean writeAccess;
-
- /**
- * Creates a new abstract Sone FCP command.
- *
- * @param core
- * The Sone core
- */
- protected AbstractSoneCommand(Core core) {
- this(core, false);
- }
-
- /**
- * Creates a new abstract Sone FCP command.
- *
- * @param core
- * The Sone core
- * @param writeAccess
- * {@code true} if this command requires write access,
- * {@code false} otherwise
- */
- protected AbstractSoneCommand(Core core, boolean writeAccess) {
- this.core = core;
- this.writeAccess = writeAccess;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns the Sone core.
- *
- * @return The Sone core
- */
- protected Core getCore() {
- return core;
- }
-
- /**
- * Returns whether this command requires write access.
- *
- * @return {@code true} if this command require write access, {@code false}
- * otherwise
- */
- public boolean requiresWriteAccess() {
- return writeAccess;
- }
-
- //
- // PROTECTED METHODS
- //
-
- /**
- * Encodes text in a way that makes it possible for the text to be stored in
- * a {@link SimpleFieldSet}. Backslashes, CR, and LF are prepended with a
- * backslash.
- *
- * @param text
- * The text to encode
- * @return The encoded text
- */
- protected static String encodeString(String text) {
- return text.replaceAll("\\\\", "\\\\\\\\").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r");
- }
-
- /**
- * Returns a Sone whose ID is a parameter in the given simple field set.
- *
- * @param simpleFieldSet
- * The simple field set containing the ID of the Sone
- * @param parameterName
- * The name under which the Sone ID is stored in the simple field
- * set
- * @param localOnly
- * {@code true} to only return local Sones, {@code false} to
- * return any Sones
- * @return The Sone
- * @throws FcpException
- * if there is no Sone ID stored under the given parameter name,
- * or if the Sone ID is invalid
- */
- protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly) throws FcpException {
- return getSone(simpleFieldSet, parameterName, localOnly, true).get();
- }
-
- /**
- * Returns a Sone whose ID is a parameter in the given simple field set.
- *
- * @param simpleFieldSet
- * The simple field set containing the ID of the Sone
- * @param parameterName
- * The name under which the Sone ID is stored in the simple field
- * set
- * @param localOnly
- * {@code true} to only return local Sones, {@code false} to
- * return any Sones
- * @param mandatory
- * {@code true} if a valid Sone ID is required, {@code false}
- * otherwise
- * @return The Sone, or {@code null} if {@code mandatory} is {@code false}
- * and the Sone ID is invalid
- * @throws FcpException
- * if there is no Sone ID stored under the given parameter name,
- * or if {@code mandatory} is {@code true} and the Sone ID is
- * invalid
- */
- protected Optional<Sone> getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly, boolean mandatory) throws FcpException {
- String soneId = simpleFieldSet.get(parameterName);
- if (mandatory && (soneId == null)) {
- throw new FcpException("Could not load Sone ID from “" + parameterName + "”.");
- }
- Sone sone = core.getSone(soneId);
- if ((mandatory && (sone == null)) || ((sone != null) && localOnly && !sone.isLocal())) {
- throw new FcpException("Could not load Sone from “" + soneId + "”.");
- }
- return Optional.fromNullable(sone);
- }
-
- /**
- * Returns a post whose ID is a parameter in the given simple field set.
- *
- * @param simpleFieldSet
- * The simple field set containing the ID of the post
- * @param parameterName
- * The name under which the post ID is stored in the simple field
- * set
- * @return The post
- * @throws FcpException
- * if there is no post ID stored under the given parameter name,
- * or if the post ID is invalid
- */
- protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
- try {
- String postId = simpleFieldSet.getString(parameterName);
- Post post = core.getPost(postId);
- if (post == null) {
- throw new FcpException("Could not load post from “" + postId + "”.");
- }
- return post;
- } catch (FSParseException fspe1) {
- throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1);
- }
- }
-
- /**
- * Returns a reply whose ID is a parameter in the given simple field set.
- *
- * @param simpleFieldSet
- * The simple field set containing the ID of the reply
- * @param parameterName
- * The name under which the reply ID is stored in the simple
- * field set
- * @return The reply
- * @throws FcpException
- * if there is no reply ID stored under the given parameter
- * name, or if the reply ID is invalid
- */
- protected PostReply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
- try {
- String replyId = simpleFieldSet.getString(parameterName);
- PostReply reply = core.getPostReply(replyId);
- if (reply == null) {
- throw new FcpException("Could not load reply from “" + replyId + "”.");
- }
- return reply;
- } catch (FSParseException fspe1) {
- throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1);
- }
- }
-
- /**
- * Creates a simple field set from the given Sone, including {@link Profile}
- * information.
- *
- * @param sone
- * The Sone to encode
- * @param prefix
- * The prefix for the field names (may be empty but not {@code
- * null})
- * @param localSone
- * An optional local Sone that is used for Sone-specific data,
- * such as if the Sone is followed by the local Sone
- * @return The simple field set containing the given Sone
- */
- protected static SimpleFieldSet encodeSone(Sone sone, String prefix, Optional<Sone> localSone) {
- SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
-
- soneBuilder.put(prefix + "ID", sone.getId());
- soneBuilder.put(prefix + "Name", sone.getName());
- soneBuilder.put(prefix + "NiceName", SoneAccessor.getNiceName(sone));
- soneBuilder.put(prefix + "LastUpdated", sone.getTime());
- if (localSone.isPresent()) {
- soneBuilder.put(prefix + "Followed", String.valueOf(localSone.get().hasFriend(sone.getId())));
- }
- Profile profile = sone.getProfile();
- soneBuilder.put(prefix + "Field.Count", profile.getFields().size());
- int fieldIndex = 0;
- for (Field field : profile.getFields()) {
- soneBuilder.put(prefix + "Field." + fieldIndex + ".Name", field.getName());
- soneBuilder.put(prefix + "Field." + fieldIndex + ".Value", field.getValue());
- ++fieldIndex;
- }
-
- return soneBuilder.get();
- }
-
- /**
- * Creates a simple field set from the given collection of Sones.
- *
- * @param sones
- * The Sones to encode
- * @param prefix
- * The prefix for the field names (may be empty but not
- * {@code null})
- * @return The simple field set containing the given Sones
- */
- protected static SimpleFieldSet encodeSones(Collection<? extends Sone> sones, String prefix) {
- SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
-
- int soneIndex = 0;
- soneBuilder.put(prefix + "Count", sones.size());
- for (Sone sone : sones) {
- String sonePrefix = prefix + soneIndex++ + ".";
- soneBuilder.put(encodeSone(sone, sonePrefix, Optional.<Sone>absent()));
- }
-
- return soneBuilder.get();
- }
-
- /**
- * Creates a simple field set from the given post.
- *
- * @param post
- * The post to encode
- * @param prefix
- * The prefix for the field names (may be empty but not
- * {@code null})
- * @param includeReplies
- * {@code true} to include replies, {@code false} to not include
- * replies
- * @return The simple field set containing the post
- */
- protected SimpleFieldSet encodePost(Post post, String prefix, boolean includeReplies) {
- SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
-
- postBuilder.put(prefix + "ID", post.getId());
- postBuilder.put(prefix + "Sone", post.getSone().getId());
- if (post.getRecipientId().isPresent()) {
- postBuilder.put(prefix + "Recipient", post.getRecipientId().get());
- }
- postBuilder.put(prefix + "Time", post.getTime());
- postBuilder.put(prefix + "Text", encodeString(post.getText()));
- postBuilder.put(encodeLikes(core.getLikes(post), prefix + "Likes."));
-
- if (includeReplies) {
- List<PostReply> replies = core.getReplies(post.getId());
- postBuilder.put(encodeReplies(replies, prefix));
- }
-
- return postBuilder.get();
- }
-
- /**
- * Creates a simple field set from the given collection of posts.
- *
- * @param posts
- * The posts to encode
- * @param prefix
- * The prefix for the field names (may be empty but not
- * {@code null})
- * @param includeReplies
- * {@code true} to include the replies, {@code false} to not
- * include the replies
- * @return The simple field set containing the posts
- */
- protected SimpleFieldSet encodePosts(Collection<? extends Post> posts, String prefix, boolean includeReplies) {
- SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
-
- int postIndex = 0;
- postBuilder.put(prefix + "Count", posts.size());
- for (Post post : posts) {
- String postPrefix = prefix + postIndex++;
- postBuilder.put(encodePost(post, postPrefix + ".", includeReplies));
- }
-
- return postBuilder.get();
- }
-
- /**
- * Creates a simple field set from the given collection of replies.
- *
- * @param replies
- * The replies to encode
- * @param prefix
- * The prefix for the field names (may be empty, but not
- * {@code null})
- * @return The simple field set containing the replies
- */
- protected SimpleFieldSet encodeReplies(Collection<? extends PostReply> replies, String prefix) {
- SimpleFieldSetBuilder replyBuilder = new SimpleFieldSetBuilder();
-
- int replyIndex = 0;
- replyBuilder.put(prefix + "Replies.Count", replies.size());
- for (PostReply reply : replies) {
- String replyPrefix = prefix + "Replies." + replyIndex++ + ".";
- replyBuilder.put(replyPrefix + "ID", reply.getId());
- replyBuilder.put(replyPrefix + "Sone", reply.getSone().getId());
- replyBuilder.put(replyPrefix + "Time", reply.getTime());
- replyBuilder.put(replyPrefix + "Text", encodeString(reply.getText()));
- replyBuilder.put(encodeLikes(core.getLikes(reply), replyPrefix + "Likes."));
- }
-
- return replyBuilder.get();
- }
-
- /**
- * Creates a simple field set from the given collection of Sones that like
- * an element.
- *
- * @param likes
- * The liking Sones
- * @param prefix
- * The prefix for the field names (may be empty but not
- * {@code null})
- * @return The simple field set containing the likes
- */
- protected static SimpleFieldSet encodeLikes(Collection<? extends Sone> likes, String prefix) {
- SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder();
-
- int likeIndex = 0;
- likesBuilder.put(prefix + "Count", likes.size());
- for (Sone sone : likes) {
- String sonePrefix = prefix + likeIndex++ + ".";
- likesBuilder.put(sonePrefix + "ID", sone.getId());
- }
-
- return likesBuilder.get();
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return getClass().getName() + "[writeAccess=" + writeAccess + "]";
- }
-
-}
/*
- * Sone - CreatePostCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - CreatePostCommand.java - Copyright © 2011–2019 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
* FCP command that creates a new {@link Post}.
*
* @see Core#createPost(Sone, Optional, String)
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class CreatePostCommand extends AbstractSoneCommand {
/*
- * Sone - CreateReplyCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - CreateReplyCommand.java - Copyright © 2011–2019 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
* FCP command that creates a new {@link Reply}.
*
* @see Core#createReply(Sone, Post, String)
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class CreateReplyCommand extends AbstractSoneCommand {
/*
- * Sone - DeletePostCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - DeletePostCommand.java - Copyright © 2011–2019 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
* FCP command that deletes a {@link Post}.
*
* @see Core#deletePost(Post)
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DeletePostCommand extends AbstractSoneCommand {
/*
- * Sone - DeleteReplyCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - DeleteReplyCommand.java - Copyright © 2011–2019 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
* FCP command that deletes a {@link PostReply}.
*
* @see Core#deleteReply(PostReply)
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DeleteReplyCommand extends AbstractSoneCommand {
/*
- * Sone - FcpInterface.java - Copyright © 2011–2016 David Roden
+ * Sone - FcpInterface.java - Copyright © 2011–2019 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
/**
* Implementation of an FCP interface for other clients or plugins to
* communicate with Sone.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class FcpInterface {
/**
* The action level that full access for the FCP connection is required.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public enum FullAccessRequired {
private final AtomicBoolean active = new AtomicBoolean();
/** What function full access is required for. */
- private final AtomicReference<FullAccessRequired> fullAccessRequired = new AtomicReference<FullAccessRequired>(FullAccessRequired.ALWAYS);
+ private final AtomicReference<FullAccessRequired> fullAccessRequired = new AtomicReference<>(FullAccessRequired.ALWAYS);
/** All available FCP commands. */
private final Map<String, AbstractSoneCommand> commands;
/*
- * Sone - GetLocalSonesCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - GetLocalSonesCommand.java - Copyright © 2011–2019 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
package net.pterodactylus.sone.fcp;
+import static net.pterodactylus.sone.fcp.AbstractSoneCommandKt.encodeSones;
+
import net.pterodactylus.sone.core.Core;
import freenet.support.SimpleFieldSet;
/**
* Implements the “GetLocalSones” FCP command that returns the list of local
* Sones to the sender.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetLocalSonesCommand extends AbstractSoneCommand {
/*
- * Sone - GetPostCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - GetPostCommand.java - Copyright © 2011–2019 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 “GetPost” FCP command returns a single {@link Post} to an FCP client.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetPostCommand extends AbstractSoneCommand {
/*
- * Sone - GetPostFeedCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - GetPostFeedCommand.java - Copyright © 2011–2019 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
/**
* Implementation of an FCP interface for other clients or plugins to
* communicate with Sone.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetPostFeedCommand extends AbstractSoneCommand {
int startPost = getInt(parameters, "StartPost", 0);
int maxPosts = getInt(parameters, "MaxPosts", -1);
- Collection<Post> allPosts = new HashSet<Post>();
+ Collection<Post> allPosts = new HashSet<>();
allPosts.addAll(sone.getPosts());
for (String friendSoneId : sone.getFriends()) {
Sone friendSone = getCore().getSone(friendSoneId);
allPosts.addAll(getCore().getDirectedPosts(sone.getId()));
allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER);
- List<Post> sortedPosts = new ArrayList<Post>(allPosts);
+ List<Post> sortedPosts = new ArrayList<>(allPosts);
Collections.sort(sortedPosts, Post.NEWEST_FIRST);
if (sortedPosts.size() < startPost) {
/*
- * Sone - GetPostsCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - GetPostsCommand.java - Copyright © 2011–2019 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
/**
* Implements the “GetPosts” FCP command that returns the list of posts a Sone
* made.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetPostsCommand extends AbstractSoneCommand {
/*
- * Sone - GetSoneCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - GetSoneCommand.java - Copyright © 2011–2019 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
package net.pterodactylus.sone.fcp;
+import static net.pterodactylus.sone.fcp.AbstractSoneCommandKt.encodeSone;
+
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Sone;
/**
* Implements the “GetSone“ FCP command which returns {@link Profile}
* information about a {@link Sone}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetSoneCommand extends AbstractSoneCommand {
/*
- * Sone - GetSonesCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - GetSonesCommand.java - Copyright © 2011–2019 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
package net.pterodactylus.sone.fcp;
+import static net.pterodactylus.sone.fcp.AbstractSoneCommandKt.encodeSones;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Implements the “GetSones” FCP command that returns the list of known Sones.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetSonesCommand extends AbstractSoneCommand {
public Response execute(SimpleFieldSet parameters) {
int startSone = getInt(parameters, "StartSone", 0);
int maxSones = getInt(parameters, "MaxSones", -1);
- List<Sone> sones = new ArrayList<Sone>(getCore().getSones());
+ List<Sone> sones = new ArrayList<>(getCore().getSones());
if (sones.size() < startSone) {
return new Response("Sones", encodeSones(Collections.<Sone> emptyList(), "Sones."));
}
/*
- * Sone - LikePostCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - LikePostCommand.java - Copyright © 2011–2019 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
/**
* Implements the “LikePost” FCP command which allows the user to like a post.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class LikePostCommand extends AbstractSoneCommand {
/*
- * Sone - LikeReplyCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - LikeReplyCommand.java - Copyright © 2011–2019 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
/**
* Implements the “LikeReply” FCP command which allows the user to like a reply.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class LikeReplyCommand extends AbstractSoneCommand {
/*
- * Sone - LockSoneCommand.java - Copyright © 2013–2016 David Roden
+ * Sone - LockSoneCommand.java - Copyright © 2013–2019 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
* Implements the “LockSone” FCP command. If a valid local Sone was given as
* parameter “Sone,” this command will always lock the Sone and reply with
* “SoneLocked.”
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class LockSoneCommand extends AbstractSoneCommand {
/*
- * Sone - UnlockSoneCommand.java - Copyright © 2013–2016 David Roden
+ * Sone - UnlockSoneCommand.java - Copyright © 2013–2019 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
* Implements the “UnlockSone” FCP command. If a valid local Sone was given as
* parameter “Sone,” this command will always unlock the Sone and reply with
* “SoneUnlocked.”
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class UnlockSoneCommand extends AbstractSoneCommand {
/*
- * Sone - VersionCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - VersionCommand.java - Copyright © 2011–2019 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
/**
* Returns version information about the Sone plugin.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class VersionCommand extends AbstractSoneCommand {
/**
* Event that signals that the {@link FcpInterface} was activated in the
* configuration.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FcpInterfaceActivatedEvent {
/**
* Event that signals that the {@link FcpInterface} was deactivated in the
* configuration.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FcpInterfaceDeactivatedEvent {
/**
* Event that signals that the {@link FcpInterface}’s {@link
* FullAccessRequired} parameter was changed in the configuration.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FullAccessRequiredChanged {
/**
* Encapsulates the parts of a {@link FreenetURI} that do not change while
* being converted from SSK to USK and/or back.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Key {
/*
- * Sone - L10nFilter.java - Copyright © 2010–2016 David Roden
+ * Sone - L10nFilter.java - Copyright © 2010–2019 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
import javax.annotation.Nonnull;
-import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.template.Filter;
import net.pterodactylus.util.template.TemplateContext;
+import freenet.l10n.BaseL10n;
+
/**
* {@link Filter} implementation replaces {@link String} values with their
* translated equivalents.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class L10nFilter implements Filter {
- /** The web interface. */
- private final WebInterface webInterface;
+ private final BaseL10n l10n;
- /**
- * Creates a new L10n filter.
- *
- * @param webInterface
- * The Sone web interface
- */
- public L10nFilter(WebInterface webInterface) {
- this.webInterface = webInterface;
+ public L10nFilter(BaseL10n l10n) {
+ this.l10n = l10n;
}
/**
List<Object> parameterValues = getParameters(data, parameters);
String text = getText(data);
if (parameterValues.isEmpty()) {
- return webInterface.getL10n().getString(text);
+ return l10n.getString(text);
}
- return new MessageFormat(webInterface.getL10n().getString(text), new Locale(webInterface.getL10n().getSelectedLanguage().shortCode)).format(parameterValues.toArray());
+ return new MessageFormat(l10n.getString(text), new Locale(l10n.getSelectedLanguage().shortCode)).format(parameterValues.toArray());
}
@Nonnull
if (data instanceof L10nText) {
return ((L10nText) data).getParameters();
}
- List<Object> parameterValues = new ArrayList<Object>();
+ List<Object> parameterValues = new ArrayList<>();
int parameterIndex = 0;
while (parameters.containsKey(String.valueOf(parameterIndex))) {
Object value = parameters.get(String.valueOf(parameterIndex));
/*
- * Sone - PluginStoreConfigurationBackend.java - Copyright © 2010–2016 David Roden
+ * Sone - PluginStoreConfigurationBackend.java - Copyright © 2010–2019 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
/**
* Backend for a {@link Configuration} that is based on a {@link PluginStore}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PluginStoreConfigurationBackend implements ExtendedConfigurationBackend {
/*
- * Sone - SimpleFieldSetBuilder.java - Copyright © 2011–2016 David Roden
+ * Sone - SimpleFieldSetBuilder.java - Copyright © 2011–2019 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
/**
* Helper class to construct {@link SimpleFieldSet} objects in a single call.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SimpleFieldSetBuilder {
/*
- * Sone - AbstractCommand.java - Copyright © 2011–2016 David Roden
+ * Sone - AbstractCommand.java - Copyright © 2011–2019 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
/**
* Basic implementation of a {@link Command} with various helper methods to
* simplify processing of input parameters.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class AbstractCommand implements Command {
/*
- * Sone - Command.java - Copyright © 2011–2016 David Roden
+ * Sone - Command.java - Copyright © 2011–2019 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
/**
* Implementation of an FCP interface for other clients or plugins to
* communicate with Sone.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Command {
/**
* The access type of the request.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public static enum AccessType {
/**
* Interface for command replies.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public static class Response {
/**
* Response implementation that can return an error message and an optional
* error code.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ErrorResponse extends Response {
/*
- * Sone - FcpException.java - Copyright © 2011–2016 David Roden
+ * Sone - FcpException.java - Copyright © 2011–2019 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
/**
* Base exception for FCP communication.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FcpException extends Exception {
/*
- * Sone - PluginConnector.java - Copyright © 2010–2016 David Roden
+ * Sone - PluginConnector.java - Copyright © 2010–2019 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
/**
* Interface for talking to other plugins. Other plugins are identified by their
* name and a unique connection identifier.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class PluginConnector implements FredPluginTalker {
/*
- * Sone - PluginException.java - Copyright © 2010–2016 David Roden
+ * Sone - PluginException.java - Copyright © 2010–2019 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
/**
* Exception that signals an error when communicating with a plugin.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PluginException extends WebOfTrustException {
/*
- * Sone - ReceivedReplyEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - ReceivedReplyEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that a plugin reply was received.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ReceivedReplyEvent {
/*
- * Sone - Context.java - Copyright © 2014–2016 David Roden
+ * Sone - Context.java - Copyright © 2014–2019 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
/**
* Custom container for the Web of Trust context. This allows easier
* configuration of dependency injection.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Context {
/*
- * Sone - DefaultIdentity.java - Copyright © 2010–2016 David Roden
+ * Sone - DefaultIdentity.java - Copyright © 2010–2019 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
/**
* A Web of Trust identity.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultIdentity implements Identity {
/*
- * Sone - DefaultOwnIdentity.java - Copyright © 2010–2016 David Roden
+ * Sone - DefaultOwnIdentity.java - Copyright © 2010–2019 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
/**
* An own identity is an identity that the owner of the node has full control
* over.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity {
/*
- * Sone - Identity.java - Copyright © 2010–2016 David Roden
+ * Sone - Identity.java - Copyright © 2010–2019 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
* Interface for web of trust identities, defining all functions that can be
* performed on an identity. An identity is only a container for identity data
* and will not perform any updating in the WebOfTrust plugin itself.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Identity {
/*
- * Sone - IdentityChangeDetector.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityChangeDetector.java - Copyright © 2013–2019 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
* added and removed identities, and for identities that exist in both list
* their contexts and properties are checked for added, removed, or (in case of
* properties) changed values.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityChangeDetector {
/*
- * Sone - IdentityChangeEventSender.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityChangeEventSender.java - Copyright © 2013–2019 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
* Detects changes in {@link Identity}s trusted my multiple {@link
* OwnIdentity}s.
*
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
* @see IdentityChangeDetector
*/
public class IdentityChangeEventSender {
/*
- * Sone - IdentityLoader.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityLoader.java - Copyright © 2013–2019 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
/**
* Loads {@link OwnIdentity}s and the {@link Identity}s they trust.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityLoader {
}
private Map<OwnIdentity, Collection<Identity>> loadTrustedIdentitiesForOwnIdentities(Collection<OwnIdentity> ownIdentities) throws PluginException {
- Map<OwnIdentity, Collection<Identity>> currentIdentities = new HashMap<OwnIdentity, Collection<Identity>>();
+ Map<OwnIdentity, Collection<Identity>> currentIdentities = new HashMap<>();
for (OwnIdentity ownIdentity : ownIdentities) {
if (identityDoesNotHaveTheCorrectContext(ownIdentity)) {
/**
* Connects to a {@link WebOfTrustConnector} and sends identity events to an
* {@link EventBus}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@ImplementedBy(IdentityManagerImpl.class)
public interface IdentityManager extends Service {
/*
- * Sone - IdentityManagerImpl.java - Copyright © 2010–2016 David Roden
+ * Sone - IdentityManagerImpl.java - Copyright © 2010–2019 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
* It is also responsible for polling identities from the Web of Trust plugin
* and sending events to the {@link EventBus} when {@link Identity}s and
* {@link OwnIdentity}s are discovered or disappearing.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class IdentityManagerImpl extends AbstractService implements IdentityManager {
@Override
public Set<OwnIdentity> getAllOwnIdentities() {
synchronized (currentOwnIdentities) {
- return new HashSet<OwnIdentity>(currentOwnIdentities);
+ return new HashSet<>(currentOwnIdentities);
}
}
*/
@Override
protected void serviceRun() {
- Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<OwnIdentity, Collection<Identity>>();
+ Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<>();
while (!shouldStop()) {
try {
/*
- * Sone - OwnIdentity.java - Copyright © 2010–2016 David Roden
+ * Sone - OwnIdentity.java - Copyright © 2010–2019 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
/**
* Defines a local identity, an own identity.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface OwnIdentity extends Identity {
/*
- * Sone - Trust.java - Copyright © 2010–2016 David Roden
+ * Sone - Trust.java - Copyright © 2010–2019 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
/**
* Container class for trust in the web of trust.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Trust {
/*
- * Sone - WebOfTrustConnector.java - Copyright © 2010–2016 David Roden
+ * Sone - WebOfTrustConnector.java - Copyright © 2010–2019 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
/**
* Connector for the Web of Trust plugin.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class WebOfTrustConnector {
Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get());
SimpleFieldSet fields = reply.getFields();
int ownIdentityCounter = -1;
- Set<OwnIdentity> ownIdentities = new HashSet<OwnIdentity>();
+ Set<OwnIdentity> ownIdentities = new HashSet<>();
while (true) {
String id = fields.get("Identity" + ++ownIdentityCounter);
if (id == null) {
public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity, Optional<String> context) throws PluginException {
Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", context.or("")).put("WantTrustValues", "true").get());
SimpleFieldSet fields = reply.getFields();
- Set<Identity> identities = new HashSet<Identity>();
+ Set<Identity> identities = new HashSet<>();
int identityCounter = -1;
while (true) {
String id = fields.get("Identity" + ++identityCounter);
* @return The parsed contexts
*/
private static Set<String> parseContexts(String prefix, SimpleFieldSet fields) {
- Set<String> contexts = new HashSet<String>();
+ Set<String> contexts = new HashSet<>();
int contextCounter = -1;
while (true) {
String context = fields.get(prefix + "Context" + ++contextCounter);
* @return The parsed properties
*/
private static Map<String, String> parseProperties(String prefix, SimpleFieldSet fields) {
- Map<String, String> properties = new HashMap<String, String>();
+ Map<String, String> properties = new HashMap<>();
int propertiesCounter = -1;
while (true) {
String propertyName = fields.get(prefix + "Property" + ++propertiesCounter + ".Name");
/**
* Container for the data of the reply from a plugin.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
private static class Reply {
/**
* Helper method to create {@link SimpleFieldSet}s with terser code.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
private static class SimpleFieldSetConstructor {
/**
* Container for identifying plugins. Plugins are identified by their plugin
* name and their unique identifier.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
*/
private static class PluginIdentifier {
/*
- * Sone - WebOfTrustException.java - Copyright © 2010–2016 David Roden
+ * Sone - WebOfTrustException.java - Copyright © 2010–2019 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
/**
* Exception that signals an error processing web of trust identities, mostly
* when communicating with the web of trust plugin.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class WebOfTrustException extends Exception {
/*
- * Sone - IdentityAddedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityAddedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link Identity} was added.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityAddedEvent extends IdentityEvent {
/*
- * Sone - IdentityEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityEvent.java - Copyright © 2013–2019 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
/**
* Base class for {@link Identity} events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class IdentityEvent {
/*
- * Sone - IdentityRemovedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityRemovedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link Identity} was removed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityRemovedEvent extends IdentityEvent {
/*
- * Sone - IdentityUpdatedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityUpdatedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link Identity} was updated.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityUpdatedEvent extends IdentityEvent {
/*
- * Sone - OwnIdentityAddedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - OwnIdentityAddedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link OwnIdentity} was added.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class OwnIdentityAddedEvent extends OwnIdentityEvent {
/*
- * Sone - OwnIdentityEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - OwnIdentityEvent.java - Copyright © 2013–2019 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
/**
* Base class for {@link OwnIdentity} events.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public abstract class OwnIdentityEvent {
/*
- * Sone - OwnIdentityRemovedEvent.java - Copyright © 2013–2016 David Roden
+ * Sone - OwnIdentityRemovedEvent.java - Copyright © 2013–2019 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
/**
* Event that signals that an {@link OwnIdentity} was removed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class OwnIdentityRemovedEvent extends OwnIdentityEvent {
/**
* {@link Loaders} implementation that loads all resources from the filesystem.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DebugLoaders implements Loaders {
@Override
public <REQ extends Request> Page<REQ> loadStaticPage(String basePath, String prefix, String mimeType) {
- return new ReloadingPage<REQ>(basePath, new File(filesystemPath, prefix).getAbsolutePath(), mimeType);
+ return new ReloadingPage<>(basePath, new File(filesystemPath, prefix).getAbsolutePath(), mimeType);
}
@Override
/**
* Default {@link Loaders} implementation that loads resources from the classpath.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultLoaders implements Loaders {
/**
* Defines loaders for resources that can be loaded from various locations.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@ImplementedBy(DefaultLoaders.class)
public interface Loaders {
/**
* Takes the input and parses it as a new {@link Template}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ReparseFilter implements Filter {
/*
- * Sone - SonePlugin.java - Copyright © 2010–2016 David Roden
+ * Sone - SonePlugin.java - Copyright © 2010–2019 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
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.database.Database;
+import net.pterodactylus.sone.database.PostProvider;
+import net.pterodactylus.sone.database.SoneProvider;
import net.pterodactylus.sone.database.memory.MemoryDatabase;
import net.pterodactylus.sone.fcp.FcpInterface;
import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend;
import net.pterodactylus.sone.freenet.wot.Context;
import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector;
import net.pterodactylus.sone.web.WebInterface;
+import net.pterodactylus.sone.web.WebInterfaceModule;
import net.pterodactylus.util.config.Configuration;
import net.pterodactylus.util.config.ConfigurationException;
import net.pterodactylus.util.config.MapConfigurationBackend;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
+import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeListener;
import freenet.client.async.PersistenceDisabledException;
+import freenet.l10n.BaseL10n;
import freenet.l10n.BaseL10n.LANGUAGE;
import freenet.l10n.PluginL10n;
import freenet.pluginmanager.FredPlugin;
/**
* This class interfaces with Freenet. It is the class that is loaded by the
* node and starts up the whole Sone system.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, FredPluginBaseL10n, FredPluginThreadless, FredPluginVersioned {
}
/** The current year at time of release. */
- private static final int YEAR = 2017;
+ private static final int YEAR = 2019;
private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
- private static final int LATEST_EDITION = 77;
+ private static final int LATEST_EDITION = 78;
/** The logger. */
private static final Logger logger = getLogger(SonePlugin.class.getName());
return (version == null) ? "unknown" : version.getNice();
}
- public static int getYear() {
+ public int getYear() {
return YEAR;
}
- public static String getHomepage() {
+ public String getHomepage() {
return SONE_HOMEPAGE + LATEST_EDITION;
}
bind(PluginYear.class).toInstance(new PluginYear(getYear()));
bind(PluginHomepage.class).toInstance(new PluginHomepage(getHomepage()));
bind(Database.class).to(MemoryDatabase.class).in(Singleton.class);
+ bind(BaseL10n.class).toInstance(l10n.getBase());
+ bind(SoneProvider.class).to(Core.class).in(Singleton.class);
+ bind(PostProvider.class).to(Core.class).in(Singleton.class);
if (startConfiguration.getBooleanValue("Developer.LoadFromFilesystem").getValue(false)) {
String path = startConfiguration.getStringValue("Developer.FilesystemPath").getValue(null);
if (path != null) {
}
};
- Injector injector = Guice.createInjector(freenetModule, soneModule);
+ Module webInterfaceModule = new WebInterfaceModule();
+ Injector injector = Guice.createInjector(freenetModule, soneModule, webInterfaceModule);
core = injector.getInstance(Core.class);
/* create web of trust connector. */
return getPluginVersion();
}
- public static class PluginVersion {
-
- private final String version;
-
- public PluginVersion(String version) {
- this.version = version;
- }
-
- public String getVersion() {
- return version;
- }
-
- }
-
- public static class PluginYear {
-
- private final int year;
-
- public PluginYear(int year) {
- this.year = year;
- }
-
- public int getYear() {
- return year;
- }
-
- }
-
- public static class PluginHomepage {
-
- private final String homepage;
-
- public PluginHomepage(String homepage) {
- this.homepage = homepage;
- }
-
- public String getHomepage() {
- return homepage;
- }
-
- }
-
}
--- /dev/null
+package net.pterodactylus.sone.main
+
+data class PluginVersion(val version: String)
+
+data class PluginYear(val year: Int)
+
+data class PluginHomepage(val homepage: String)
/*
- * Sone - ListNotification.java - Copyright © 2010–2016 David Roden
+ * Sone - ListNotification.java - Copyright © 2010–2019 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
*
* @param <T>
* The type of the items
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ListNotification<T> extends TemplateNotification {
private final String key;
/** The list of new elements. */
- private final List<T> elements = new CopyOnWriteArrayList<T>();
+ private final List<T> elements = new CopyOnWriteArrayList<>();
/**
* Creates a new list notification.
* @return The current list of elements
*/
public List<T> getElements() {
- return new ArrayList<T>(elements);
+ return new ArrayList<>(elements);
}
/**
/*
- * Sone - ListNotificationFilter.java - Copyright © 2010–2016 David Roden
+ * Sone - ListNotificationFilter.java - Copyright © 2010–2019 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
/**
* Filter for {@link ListNotification}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class ListNotificationFilter {
*/
@SuppressWarnings("unchecked")
public List<Notification> filterNotifications(Collection<? extends Notification> notifications, Sone currentSone) {
- List<Notification> filteredNotifications = new ArrayList<Notification>();
+ List<Notification> filteredNotifications = new ArrayList<>();
for (Notification notification : notifications) {
if (notification.getId().equals("new-sone-notification")) {
if ((currentSone != null) && !currentSone.getOptions().isShowNewSoneNotifications()) {
if (newPosts.size() == postNotification.getElements().size()) {
return Optional.of(postNotification);
}
- ListNotification<Post> filteredNotification = new ListNotification<Post>(postNotification);
+ ListNotification<Post> filteredNotification = new ListNotification<>(postNotification);
filteredNotification.setElements(newPosts);
filteredNotification.setLastUpdateTime(postNotification.getLastUpdatedTime());
return Optional.of(filteredNotification);
if (newReplies.size() == newReplyNotification.getElements().size()) {
return Optional.of(newReplyNotification);
}
- ListNotification<PostReply> filteredNotification = new ListNotification<PostReply>(newReplyNotification);
+ ListNotification<PostReply> filteredNotification = new ListNotification<>(newReplyNotification);
filteredNotification.setElements(newReplies);
filteredNotification.setLastUpdateTime(newReplyNotification.getLastUpdatedTime());
return Optional.of(filteredNotification);
/**
* Filters {@link Notification}s involving {@link Post}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class PostVisibilityFilter {
/**
* Filter that checks a {@link PostReply} for visibility.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class ReplyVisibilityFilter {
/*
- * Sone - AlbumAccessor.java - Copyright © 2011–2016 David Roden
+ * Sone - AlbumAccessor.java - Copyright © 2011–2019 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
* {@link Accessor} implementation for {@link Album}s. A property named
* “backlinks” is added, it returns links to all parents and the owner Sone of
* an album.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class AlbumAccessor extends ReflectionAccessor {
public Object get(TemplateContext templateContext, Object object, String member) {
Album album = (Album) object;
if ("backlinks".equals(member)) {
- List<Link> backlinks = new ArrayList<Link>();
+ List<Link> backlinks = new ArrayList<>();
Album currentAlbum = album;
while (!currentAlbum.isRoot()) {
backlinks.add(0, new Link("imageBrowser.html?album=" + currentAlbum.getId(), currentAlbum.getTitle()));
/**
* Container for links.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
private static class Link {
/*
- * Sone - CollectionAccessor.java - Copyright © 2010–2016 David Roden
+ * Sone - CollectionAccessor.java - Copyright © 2010–2019 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
* <dt>Returns the nice names of all {@link Sone}s in the collection, sorted
* ascending by their nice names.</dt>
* </dl>
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class CollectionAccessor extends ReflectionAccessor {
public Object get(TemplateContext templateContext, Object object, String member) {
Collection<?> collection = (Collection<?>) object;
if (member.equals("soneNames")) {
- List<Sone> sones = new ArrayList<Sone>();
+ List<Sone> sones = new ArrayList<>();
for (Object sone : collection) {
if (!(sone instanceof Sone)) {
continue;
/*
- * Sone - CssClassNameFilter.java - Copyright © 2010–2016 David Roden
+ * Sone - CssClassNameFilter.java - Copyright © 2010–2019 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
* Converts the {@link String} {@link String#valueOf(Object) representation} of
* an object to a valid CSS class name by converting all characters that are not
* US-ASCII letters or numbers to an underscore.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class CssClassNameFilter implements Filter {
/**
* {@link Template} implementation that can be reloaded from the filesystem.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FilesystemTemplate extends Template {
private final String filename;
- private final AtomicReference<LastLoadedTemplate> lastTemplate = new AtomicReference<LastLoadedTemplate>();
+ private final AtomicReference<LastLoadedTemplate> lastTemplate = new AtomicReference<>();
private final TemplateContext initialContext = new TemplateContext();
- private final List<Part> parts = new ArrayList<Part>();
+ private final List<Part> parts = new ArrayList<>();
public FilesystemTemplate(String filename) {
this.filename = filename;
/**
* Exception that signals that a template file could not be found.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public static class TemplateFileNotFoundException extends RuntimeException {
/*
- * Sone - GetPagePlugin.java - Copyright © 2010–2016 David Roden
+ * Sone - GetPagePlugin.java - Copyright © 2010–2019 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
/**
* Extracts a page number from a {@link Request}’s parameters and stores it in
* the {@link TemplateContext}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetPagePlugin implements Plugin {
/*
- * Sone - HttpRequestAccessor.java - Copyright © 2011–2016 David Roden
+ * Sone - HttpRequestAccessor.java - Copyright © 2011–2019 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
* {@link HTTPRequest}s.
*
* @see HTTPRequest#getHeader(String)
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class HttpRequestAccessor extends ReflectionAccessor {
/*
- * Sone - IdentityAccessor.java - Copyright © 2010–2016 David Roden
+ * Sone - IdentityAccessor.java - Copyright © 2010–2019 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
/**
* {@link Accessor} implementation that adds a “uniqueNickname” member to an
* {@link Identity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
@Singleton
public class IdentityAccessor extends ReflectionAccessor {
/*
- * Sone - ImageAccessor.java - Copyright © 2011–2016 David Roden
+ * Sone - ImageAccessor.java - Copyright © 2011–2019 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
* <li>{@code next}: returns the next image in the image’s album, or {@code
* null} if the image is the last image of its album.</li>
* </ul>
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageAccessor extends ReflectionAccessor {
/*
- * Sone - ImageLinkFilter.java - Copyright © 2011–2016 David Roden
+ * Sone - ImageLinkFilter.java - Copyright © 2011–2019 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
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.HtmlFilter;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
import net.pterodactylus.util.template.TemplateContextFactory;
/**
* Template filter that turns an {@link Image} into an HTML <img> tag,
* using some parameters to influence parameters of the image.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageLinkFilter implements Filter {
private final Core core;
/** The template context factory. */
- private final TemplateContextFactory templateContextFactory;
+ private final TemplateContextFactory templateContextFactory = new TemplateContextFactory();
- /**
- * Creates a new image link filter.
- *
- * @param core
- * The core
- * @param templateContextFactory
- * The template context factory
- */
- public ImageLinkFilter(Core core, TemplateContextFactory templateContextFactory) {
+ public ImageLinkFilter(Core core) {
this.core = core;
- this.templateContextFactory = templateContextFactory;
+ templateContextFactory.addFilter("html", new HtmlFilter());
+ templateContextFactory.addFilter("css", new CssClassNameFilter());
}
/**
/*
- * Sone - JavascriptFilter.java - Copyright © 2011–2016 David Roden
+ * Sone - JavascriptFilter.java - Copyright © 2011–2019 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
* Escapes double quotes, backslashes, carriage returns and line feeds, and
* additionally encloses a given string with double quotes to make it possible
* to use a string in Javascript.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class JavascriptFilter implements Filter {
/*
- * Sone - PostAccessor.java - Copyright © 2010–2016 David Roden
+ * Sone - PostAccessor.java - Copyright © 2010–2019 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
* <dd>replies</dd>
* <dt>All replies to this post, sorted by time, oldest first</dt>
* </dl>
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PostAccessor extends ReflectionAccessor {
/*
- * Sone - ProfileAccessor.java - Copyright © 2011–2016 David Roden
+ * Sone - ProfileAccessor.java - Copyright © 2011–2019 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
* {@link Accessor} for {@link Profile} objects that overwrites the original
* “avatar” member to include checks for whether the custom avatar should
* actually be shown.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ProfileAccessor extends ReflectionAccessor {
/*
- * Sone - ReplyAccessor.java - Copyright © 2010–2016 David Roden
+ * Sone - ReplyAccessor.java - Copyright © 2010–2019 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
/**
* {@link Accessor} implementation that adds a couple of properties to
* {@link Reply}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ReplyAccessor extends ReflectionAccessor {
/*
- * Sone - ReplyGroupFilter.java - Copyright © 2010–2016 David Roden
+ * Sone - ReplyGroupFilter.java - Copyright © 2010–2019 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
* {@link Filter} implementation that groups replies by the post the are in
* reply to, returning a map with the post as key and the list of replies as
* values.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ReplyGroupFilter implements Filter {
/*
- * Sone - RequestChangeFilter.java - Copyright © 2010–2016 David Roden
+ * Sone - RequestChangeFilter.java - Copyright © 2010–2019 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
* This filter expects a {@link FreenetRequest} as input and outputs a
* {@link URI} that is modified by the parameters. The name of the parameter is
* handed in as “name”, the new value is stored in “value”.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class RequestChangeFilter implements Filter {
String name = String.valueOf(parameters.get("name"));
String value = String.valueOf(parameters.get("value"));
- Map<String, String> values = new HashMap<String, String>();
+ Map<String, String> values = new HashMap<>();
Collection<String> parameterNames = request.getHttpRequest().getParameterNames();
for (String parameterName : parameterNames) {
values.put(parameterName, request.getHttpRequest().getParam(parameterName));
/*
- * Sone - SoneAccessor.java - Copyright © 2010–2016 David Roden
+ * Sone - SoneAccessor.java - Copyright © 2010–2019 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
* <dd>Will return {@code true} if the sone in question is the currently logged
* in Sone.</dd>
* </dl>
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneAccessor extends ReflectionAccessor {
/*
- * Sone - SubstringFilter.java - Copyright © 2010–2016 David Roden
+ * Sone - SubstringFilter.java - Copyright © 2010–2019 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
* “start” and “length.” “length” is optional and defaults to “the rest of the
* string.” “start” starts at {@code 0} and can be negative to denote starting
* at the end of the string.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SubstringFilter implements Filter {
/*
- * Sone - TrustAccessor.java - Copyright © 2010–2016 David Roden
+ * Sone - TrustAccessor.java - Copyright © 2010–2019 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
* <dd>{@link Boolean} that indicates whether this trust relationship has an
* explicit value assigned to it.</dd>
* </dl>
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TrustAccessor extends ReflectionAccessor {
/*
- * Sone - UniqueElementFilter.java - Copyright © 2011–2016 David Roden
+ * Sone - UniqueElementFilter.java - Copyright © 2011–2019 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
/**
* Filter that reduces a collection to a {@link Set}, removing duplicates.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class UniqueElementFilter implements Filter {
if (!(data instanceof Collection<?>)) {
return data;
}
- return new HashSet<Object>((Collection<?>) data);
+ return new HashSet<>((Collection<?>) data);
}
}
/*
- * Sone - UnknownDateFilter.java - Copyright © 2011–2016 David Roden
+ * Sone - UnknownDateFilter.java - Copyright © 2011–2019 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
/**
* {@link Filter} implementation that replaces a {@link Long} with a value of
* {@code 0} by a {@link String} from an {@link BaseL10n l10n handler}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class UnknownDateFilter implements Filter {
/**
* {@link Part} implementation that holds a freemail address.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FreemailPart implements Part {
/*
- * Sone - Parser.java - Copyright © 2010–2016 David Roden
+ * Sone - Parser.java - Copyright © 2010–2019 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
*
* @param <C>
* The type of the parser context
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Parser<C extends ParserContext> {
/*
- * Sone - ParserContext.java - Copyright © 2010–2016 David Roden
+ * Sone - ParserContext.java - Copyright © 2010–2019 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
* Context for the {@link Parser}. This interface needs to be implemented by
* {@link Parser}s that need to provide more information than just the text to
* parse to {@link Parser#parse(String, ParserContext)}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface ParserContext {
/*
- * Sone - PostPart.java - Copyright © 2011–2016 David Roden
+ * Sone - PostPart.java - Copyright © 2011–2019 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
/**
* {@link Part} implementation that stores a reference to a {@link Post}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PostPart implements Part {
/*
- * Sone - SoneTextParserContext.java - Copyright © 2011–2016 David Roden
+ * Sone - SoneTextParserContext.java - Copyright © 2011–2019 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
* {@link ParserContext} implementation for the {@link SoneTextParser}. It
* stores the {@link Sone} that provided the parsed text so that certain links
* can be marked in a different way.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneTextParserContext implements ParserContext {
/*
- * Sone - TextFilter.java - Copyright © 2011–2016 David Roden
+ * Sone - TextFilter.java - Copyright © 2011–2019 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
* will be converted to “KSK@gpl.txt”. This will only work for links that point
* to the same address Sone is accessed by, so if you access Sone using
* localhost:8888, links to 127.0.0.1:8888 will not be removed.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TextFilter {
*
* @param <T>
* The type of the option
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultOption<T> implements Option<T> {
/*
- * Sone - IntegerRangePredicate.java - Copyright © 2013–2016 David Roden
+ * Sone - IntegerRangePredicate.java - Copyright © 2013–2019 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
* {@link Predicate} that verifies that an {@link Integer} value is not
* {@code null} and is between a lower and an upper bound. Both bounds are
* inclusive.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IntegerRangePredicate implements Predicate<Integer> {
/**
* Parses numbers from strings.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class NumberParsers {
*
* @param <T>
* The type of the option
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public interface Option<T> {
--- /dev/null
+package net.pterodactylus.sone.web
+
+import net.pterodactylus.sone.web.pages.*
+import javax.inject.Inject
+
+/**
+ * Container for all web pages. This uses field injection because there are way too many pages
+ * to sensibly use constructor injection.
+ */
+class AllPages {
+
+ @Inject lateinit var aboutPage: AboutPage
+ @Inject lateinit var bookmarkPage: BookmarkPage
+ @Inject lateinit var bookmarksPage: BookmarksPage
+ @Inject lateinit var createAlbumPage: CreateAlbumPage
+ @Inject lateinit var createPostPage: CreatePostPage
+ @Inject lateinit var createReplyPage: CreateReplyPage
+ @Inject lateinit var createSonePage: CreateSonePage
+ @Inject lateinit var deleteAlbumPage: DeleteAlbumPage
+ @Inject lateinit var deleteImagePage: DeleteImagePage
+ @Inject lateinit var deletePostPage: DeletePostPage
+ @Inject lateinit var deleteProfileFieldPage: DeleteProfileFieldPage
+ @Inject lateinit var deleteReplyPage: DeleteReplyPage
+ @Inject lateinit var deleteSonePage: DeleteSonePage
+ @Inject lateinit var dismissNotificationPage: DismissNotificationPage
+ @Inject lateinit var distrustPage: DistrustPage
+ @Inject lateinit var editAlbumPage: EditAlbumPage
+ @Inject lateinit var editImagePage: EditImagePage
+ @Inject lateinit var editProfileFieldPage: EditProfileFieldPage
+ @Inject lateinit var editProfilePage: EditProfilePage
+ @Inject lateinit var followSonePage: FollowSonePage
+ @Inject lateinit var getImagePage: GetImagePage
+ @Inject lateinit var imageBrowserPage: ImageBrowserPage
+ @Inject lateinit var indexPage: IndexPage
+ @Inject lateinit var knownSonesPage: KnownSonesPage
+ @Inject lateinit var likePage: LikePage
+ @Inject lateinit var lockSonePage: LockSonePage
+ @Inject lateinit var loginPage: LoginPage
+ @Inject lateinit var logoutPage: LogoutPage
+ @Inject lateinit var markAsKnownPage: MarkAsKnownPage
+ @Inject lateinit var newPage: NewPage
+ @Inject lateinit var optionsPage: OptionsPage
+ @Inject lateinit var rescuePage: RescuePage
+ @Inject lateinit var searchPage: SearchPage
+ @Inject lateinit var trustPage: TrustPage
+ @Inject lateinit var unbookmarkPage: UnbookmarkPage
+ @Inject lateinit var unfollowSonePage: UnfollowSonePage
+ @Inject lateinit var unlikePage: UnlikePage
+ @Inject lateinit var unlockSonePage: UnlockSonePage
+ @Inject lateinit var untrustPage: UntrustPage
+ @Inject lateinit var uploadImagePage: UploadImagePage
+ @Inject lateinit var viewPostPage: ViewPostPage
+ @Inject lateinit var viewSonePage: ViewSonePage
+
+}
/*
- * Sone - WebInterface.java - Copyright © 2010–2016 David Roden
+ * Sone - WebInterface.java - Copyright © 2010–2019 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
import static net.pterodactylus.util.template.TemplateParser.parse;
import java.io.StringReader;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import net.pterodactylus.sone.core.event.SoneRemovedEvent;
import net.pterodactylus.sone.core.event.SoneUnlockedEvent;
import net.pterodactylus.sone.core.event.UpdateFoundEvent;
-import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Profile;
-import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.L10nFilter;
-import net.pterodactylus.sone.freenet.wot.Identity;
-import net.pterodactylus.sone.freenet.wot.Trust;
import net.pterodactylus.sone.main.Loaders;
-import net.pterodactylus.sone.main.ReparseFilter;
+import net.pterodactylus.sone.main.PluginHomepage;
+import net.pterodactylus.sone.main.PluginVersion;
+import net.pterodactylus.sone.main.PluginYear;
import net.pterodactylus.sone.main.SonePlugin;
-import net.pterodactylus.sone.main.SonePlugin.PluginHomepage;
-import net.pterodactylus.sone.main.SonePlugin.PluginVersion;
-import net.pterodactylus.sone.main.SonePlugin.PluginYear;
import net.pterodactylus.sone.notify.ListNotification;
import net.pterodactylus.sone.notify.ListNotificationFilter;
import net.pterodactylus.sone.notify.PostVisibilityFilter;
import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
-import net.pterodactylus.sone.template.AlbumAccessor;
-import net.pterodactylus.sone.template.CollectionAccessor;
-import net.pterodactylus.sone.template.CssClassNameFilter;
-import net.pterodactylus.sone.template.HttpRequestAccessor;
-import net.pterodactylus.sone.template.IdentityAccessor;
-import net.pterodactylus.sone.template.ImageAccessor;
-import net.pterodactylus.sone.template.ImageLinkFilter;
-import net.pterodactylus.sone.template.JavascriptFilter;
import net.pterodactylus.sone.template.LinkedElementRenderFilter;
-import net.pterodactylus.sone.template.LinkedElementsFilter;
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;
import net.pterodactylus.sone.template.ShortenFilter;
-import net.pterodactylus.sone.template.SoneAccessor;
-import net.pterodactylus.sone.template.SubstringFilter;
-import net.pterodactylus.sone.template.TrustAccessor;
-import net.pterodactylus.sone.template.UniqueElementFilter;
-import net.pterodactylus.sone.template.UnknownDateFilter;
import net.pterodactylus.sone.text.Part;
import net.pterodactylus.sone.text.SonePart;
import net.pterodactylus.sone.text.SoneTextParser;
import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage;
import net.pterodactylus.sone.web.ajax.UntrustAjaxPage;
import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.sone.web.page.PageToadlet;
-import net.pterodactylus.sone.web.page.PageToadletFactory;
+import net.pterodactylus.sone.web.page.TemplateRenderer;
import net.pterodactylus.sone.web.pages.AboutPage;
import net.pterodactylus.sone.web.pages.BookmarkPage;
import net.pterodactylus.sone.web.pages.BookmarksPage;
import net.pterodactylus.sone.web.pages.EditImagePage;
import net.pterodactylus.sone.web.pages.EditProfileFieldPage;
import net.pterodactylus.sone.web.pages.EditProfilePage;
+import net.pterodactylus.sone.web.pages.EmptyAlbumTitlePage;
+import net.pterodactylus.sone.web.pages.EmptyImageTitlePage;
import net.pterodactylus.sone.web.pages.FollowSonePage;
import net.pterodactylus.sone.web.pages.GetImagePage;
import net.pterodactylus.sone.web.pages.ImageBrowserPage;
import net.pterodactylus.sone.web.pages.IndexPage;
+import net.pterodactylus.sone.web.pages.InvalidPage;
import net.pterodactylus.sone.web.pages.KnownSonesPage;
import net.pterodactylus.sone.web.pages.LikePage;
import net.pterodactylus.sone.web.pages.LockSonePage;
import net.pterodactylus.sone.web.pages.LogoutPage;
import net.pterodactylus.sone.web.pages.MarkAsKnownPage;
import net.pterodactylus.sone.web.pages.NewPage;
+import net.pterodactylus.sone.web.pages.NoPermissionPage;
import net.pterodactylus.sone.web.pages.OptionsPage;
import net.pterodactylus.sone.web.pages.RescuePage;
import net.pterodactylus.sone.web.pages.SearchPage;
import net.pterodactylus.util.notify.Notification;
import net.pterodactylus.util.notify.NotificationManager;
import net.pterodactylus.util.notify.TemplateNotification;
-import net.pterodactylus.util.template.CollectionSortFilter;
-import net.pterodactylus.util.template.ContainsFilter;
-import net.pterodactylus.util.template.DateFilter;
-import net.pterodactylus.util.template.FormatFilter;
-import net.pterodactylus.util.template.HtmlFilter;
-import net.pterodactylus.util.template.MatchFilter;
-import net.pterodactylus.util.template.ModFilter;
-import net.pterodactylus.util.template.PaginationFilter;
-import net.pterodactylus.util.template.ReflectionAccessor;
-import net.pterodactylus.util.template.ReplaceFilter;
-import net.pterodactylus.util.template.StoreFilter;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContextFactory;
-import net.pterodactylus.util.template.TemplateProvider;
-import net.pterodactylus.util.template.XmlFilter;
import net.pterodactylus.util.web.RedirectPage;
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.l10n.BaseL10n;
-import freenet.support.api.HTTPRequest;
import com.google.common.base.Optional;
import com.google.common.collect.Collections2;
/**
* Bundles functionality that a web interface of a Freenet plugin needs, e.g.
* references to l10n helpers.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class WebInterface implements SessionProvider {
/** The Sone plugin. */
private final SonePlugin sonePlugin;
- /** The registered toadlets. */
- private final List<PageToadlet> pageToadlets = new ArrayList<PageToadlet>();
-
/** The form password. */
private final String formPassword;
/** The template context factory. */
private final TemplateContextFactory templateContextFactory;
+ private final TemplateRenderer templateRenderer;
/** The Sone text parser. */
private final SoneTextParser soneTextParser;
private final ElementLoader elementLoader;
private final LinkedElementRenderFilter linkedElementRenderFilter;
private final TimeTextConverter timeTextConverter = new TimeTextConverter();
- private final L10nFilter l10nFilter = new L10nFilter(this);
+ private final L10nFilter l10nFilter;
+
+ private final PageToadletRegistry pageToadletRegistry;
/** The “new Sone” notification. */
private final ListNotification<Sone> newSoneNotification;
private final ListNotification<Post> mentionNotification;
/** Notifications for sone inserts. */
- private final Map<Sone, TemplateNotification> soneInsertNotifications = new HashMap<Sone, TemplateNotification>();
+ private final Map<Sone, TemplateNotification> soneInsertNotifications = new HashMap<>();
/** Sone locked notification ticker objects. */
private final Map<Sone, ScheduledFuture<?>> lockedSonesTickerObjects = Collections.synchronizedMap(new HashMap<Sone, ScheduledFuture<?>>());
/** Scheduled executor for time-based notifications. */
private final ScheduledExecutorService ticker = Executors.newScheduledThreadPool(1);
- /**
- * Creates a new web interface.
- *
- * @param sonePlugin
- * The Sone plugin
- */
@Inject
- public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter, PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter, ElementLoader elementLoader) {
+ public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter,
+ PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter,
+ ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
+ TemplateRenderer templateRenderer,
+ ParserFilter parserFilter, ShortenFilter shortenFilter,
+ RenderFilter renderFilter,
+ LinkedElementRenderFilter linkedElementRenderFilter,
+ PageToadletRegistry pageToadletRegistry) {
this.sonePlugin = sonePlugin;
this.loaders = loaders;
this.listNotificationFilter = listNotificationFilter;
this.postVisibilityFilter = postVisibilityFilter;
this.replyVisibilityFilter = replyVisibilityFilter;
this.elementLoader = elementLoader;
+ this.templateRenderer = templateRenderer;
+ this.parserFilter = parserFilter;
+ this.shortenFilter = shortenFilter;
+ this.renderFilter = renderFilter;
+ this.linkedElementRenderFilter = linkedElementRenderFilter;
+ this.pageToadletRegistry = pageToadletRegistry;
formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
soneTextParser = new SoneTextParser(getCore(), getCore());
+ l10nFilter = new L10nFilter(getL10n());
- templateContextFactory = new TemplateContextFactory();
- templateContextFactory.addAccessor(Object.class, new ReflectionAccessor());
- templateContextFactory.addAccessor(Collection.class, new CollectionAccessor());
- templateContextFactory.addAccessor(Sone.class, new SoneAccessor(getCore(), new TimeTextConverter()));
- templateContextFactory.addAccessor(Post.class, new PostAccessor(getCore()));
- templateContextFactory.addAccessor(Reply.class, new ReplyAccessor(getCore()));
- templateContextFactory.addAccessor(Album.class, new AlbumAccessor());
- templateContextFactory.addAccessor(Image.class, new ImageAccessor());
- templateContextFactory.addAccessor(Identity.class, new IdentityAccessor(getCore()));
- templateContextFactory.addAccessor(Trust.class, new TrustAccessor());
- templateContextFactory.addAccessor(HTTPRequest.class, new HttpRequestAccessor());
- templateContextFactory.addAccessor(Profile.class, new ProfileAccessor(getCore()));
- templateContextFactory.addFilter("date", new DateFilter());
- templateContextFactory.addFilter("html", new HtmlFilter());
- templateContextFactory.addFilter("replace", new ReplaceFilter());
- templateContextFactory.addFilter("store", new StoreFilter());
- templateContextFactory.addFilter("l10n", new L10nFilter(this));
- templateContextFactory.addFilter("substring", new SubstringFilter());
- templateContextFactory.addFilter("xml", new XmlFilter());
- templateContextFactory.addFilter("change", new RequestChangeFilter());
- templateContextFactory.addFilter("match", new MatchFilter());
- templateContextFactory.addFilter("css", new CssClassNameFilter());
- templateContextFactory.addFilter("js", new JavascriptFilter());
- templateContextFactory.addFilter("parse", parserFilter = new ParserFilter(getCore(), soneTextParser));
- templateContextFactory.addFilter("shorten", shortenFilter = new ShortenFilter());
- templateContextFactory.addFilter("render", renderFilter = new RenderFilter(getCore(), templateContextFactory));
- templateContextFactory.addFilter("linked-elements", new LinkedElementsFilter(elementLoader));
- templateContextFactory.addFilter("render-linked-element", linkedElementRenderFilter = new LinkedElementRenderFilter(templateContextFactory));
- templateContextFactory.addFilter("reparse", new ReparseFilter());
- 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(getCore(), templateContextFactory));
- templateContextFactory.addFilter("replyGroup", new ReplyGroupFilter());
- templateContextFactory.addFilter("in", new ContainsFilter());
- templateContextFactory.addFilter("unique", new UniqueElementFilter());
- templateContextFactory.addFilter("mod", new ModFilter());
- templateContextFactory.addFilter("paginate", new PaginationFilter());
- templateContextFactory.addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER);
- templateContextFactory.addProvider(loaders.getTemplateProvider());
+ this.templateContextFactory = templateContextFactory;
templateContextFactory.addTemplateObject("webInterface", this);
templateContextFactory.addTemplateObject("formPassword", formPassword);
/* create notifications. */
Template newSoneNotificationTemplate = loaders.loadTemplate("/templates/notify/newSoneNotification.html");
- newSoneNotification = new ListNotification<Sone>("new-sone-notification", "sones", newSoneNotificationTemplate, false);
+ newSoneNotification = new ListNotification<>("new-sone-notification", "sones", newSoneNotificationTemplate, false);
Template newPostNotificationTemplate = loaders.loadTemplate("/templates/notify/newPostNotification.html");
- newPostNotification = new ListNotification<Post>("new-post-notification", "posts", newPostNotificationTemplate, false);
+ newPostNotification = new ListNotification<>("new-post-notification", "posts", newPostNotificationTemplate, false);
Template localPostNotificationTemplate = loaders.loadTemplate("/templates/notify/newPostNotification.html");
- localPostNotification = new ListNotification<Post>("local-post-notification", "posts", localPostNotificationTemplate, false);
+ localPostNotification = new ListNotification<>("local-post-notification", "posts", localPostNotificationTemplate, false);
Template newReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
- newReplyNotification = new ListNotification<PostReply>("new-reply-notification", "replies", newReplyNotificationTemplate, false);
+ newReplyNotification = new ListNotification<>("new-reply-notification", "replies", newReplyNotificationTemplate, false);
Template localReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
- localReplyNotification = new ListNotification<PostReply>("local-reply-notification", "replies", localReplyNotificationTemplate, false);
+ localReplyNotification = new ListNotification<>("local-reply-notification", "replies", localReplyNotificationTemplate, false);
Template mentionNotificationTemplate = loaders.loadTemplate("/templates/notify/mentionNotification.html");
- mentionNotification = new ListNotification<Post>("mention-notification", "posts", mentionNotificationTemplate, false);
+ mentionNotification = new ListNotification<>("mention-notification", "posts", mentionNotificationTemplate, false);
Template lockedSonesTemplate = loaders.loadTemplate("/templates/notify/lockedSonesNotification.html");
- lockedSonesNotification = new ListNotification<Sone>("sones-locked-notification", "sones", lockedSonesTemplate);
+ lockedSonesNotification = new ListNotification<>("sones-locked-notification", "sones", lockedSonesTemplate);
Template newVersionTemplate = loaders.loadTemplate("/templates/notify/newVersionNotification.html");
newVersionNotification = new TemplateNotification("new-version-notification", newVersionTemplate);
Template insertingImagesTemplate = loaders.loadTemplate("/templates/notify/inserting-images-notification.html");
- insertingImagesNotification = new ListNotification<Image>("inserting-images-notification", "images", insertingImagesTemplate);
+ insertingImagesNotification = new ListNotification<>("inserting-images-notification", "images", insertingImagesTemplate);
Template insertedImagesTemplate = loaders.loadTemplate("/templates/notify/inserted-images-notification.html");
- insertedImagesNotification = new ListNotification<Image>("inserted-images-notification", "images", insertedImagesTemplate);
+ insertedImagesNotification = new ListNotification<>("inserted-images-notification", "images", insertedImagesTemplate);
Template imageInsertFailedTemplate = loaders.loadTemplate("/templates/notify/image-insert-failed-notification.html");
- imageInsertFailedNotification = new ListNotification<Image>("image-insert-failed-notification", "images", imageInsertFailedTemplate);
+ imageInsertFailedNotification = new ListNotification<>("image-insert-failed-notification", "images", imageInsertFailedTemplate);
}
//
* Stops the web interface and unregisters all toadlets.
*/
public void stop() {
- unregisterToadlets();
+ pageToadletRegistry.unregisterToadlets();
ticker.shutdownNow();
}
* Register all toadlets.
*/
private void registerToadlets() {
- Template emptyTemplate = parse(new StringReader(""));
- Template loginTemplate = loaders.loadTemplate("/templates/login.html");
- Template indexTemplate = loaders.loadTemplate("/templates/index.html");
- Template newTemplate = loaders.loadTemplate("/templates/new.html");
- Template knownSonesTemplate = loaders.loadTemplate("/templates/knownSones.html");
- Template createSoneTemplate = loaders.loadTemplate("/templates/createSone.html");
- Template createPostTemplate = loaders.loadTemplate("/templates/createPost.html");
- Template createReplyTemplate = loaders.loadTemplate("/templates/createReply.html");
- Template bookmarksTemplate = loaders.loadTemplate("/templates/bookmarks.html");
- Template searchTemplate = loaders.loadTemplate("/templates/search.html");
- Template editProfileTemplate = loaders.loadTemplate("/templates/editProfile.html");
- Template editProfileFieldTemplate = loaders.loadTemplate("/templates/editProfileField.html");
- Template deleteProfileFieldTemplate = loaders.loadTemplate("/templates/deleteProfileField.html");
- Template viewSoneTemplate = loaders.loadTemplate("/templates/viewSone.html");
- Template viewPostTemplate = loaders.loadTemplate("/templates/viewPost.html");
- Template deletePostTemplate = loaders.loadTemplate("/templates/deletePost.html");
- Template deleteReplyTemplate = loaders.loadTemplate("/templates/deleteReply.html");
- Template deleteSoneTemplate = loaders.loadTemplate("/templates/deleteSone.html");
- Template imageBrowserTemplate = loaders.loadTemplate("/templates/imageBrowser.html");
- Template createAlbumTemplate = loaders.loadTemplate("/templates/createAlbum.html");
- Template deleteAlbumTemplate = loaders.loadTemplate("/templates/deleteAlbum.html");
- Template deleteImageTemplate = loaders.loadTemplate("/templates/deleteImage.html");
- Template noPermissionTemplate = loaders.loadTemplate("/templates/noPermission.html");
- Template emptyImageTitleTemplate = loaders.loadTemplate("/templates/emptyImageTitle.html");
- Template emptyAlbumTitleTemplate = loaders.loadTemplate("/templates/emptyAlbumTitle.html");
- Template optionsTemplate = loaders.loadTemplate("/templates/options.html");
- Template rescueTemplate = loaders.loadTemplate("/templates/rescue.html");
- Template aboutTemplate = loaders.loadTemplate("/templates/about.html");
- Template invalidTemplate = loaders.loadTemplate("/templates/invalid.html");
Template postTemplate = loaders.loadTemplate("/templates/include/viewPost.html");
Template replyTemplate = loaders.loadTemplate("/templates/include/viewReply.html");
Template openSearchTemplate = loaders.loadTemplate("/templates/xml/OpenSearch.xml");
- PageToadletFactory pageToadletFactory = new PageToadletFactory(sonePlugin.pluginRespirator().getHLSimpleClient(), "/Sone/");
- pageToadlets.add(pageToadletFactory.createPageToadlet(new RedirectPage<FreenetRequest>("", "index.html")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new IndexPage(indexTemplate, this, postVisibilityFilter), "Index"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new NewPage(newTemplate, this), "New"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateSonePage(createSoneTemplate, this), "CreateSone"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new KnownSonesPage(knownSonesTemplate, this), "KnownSones"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new EditProfilePage(editProfileTemplate, this), "EditProfile"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new EditProfileFieldPage(editProfileFieldTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteProfileFieldPage(deleteProfileFieldTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new CreatePostPage(createPostTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateReplyPage(createReplyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new ViewSonePage(viewSoneTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new ViewPostPage(viewPostTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new LikePage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlikePage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeletePostPage(deletePostTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteReplyPage(deleteReplyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new LockSonePage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlockSonePage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new FollowSonePage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnfollowSonePage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new ImageBrowserPage(imageBrowserTemplate, this), "ImageBrowser"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateAlbumPage(createAlbumTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new EditAlbumPage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteAlbumPage(deleteAlbumTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UploadImagePage(invalidTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new EditImagePage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteImagePage(deleteImageTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustPage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustPage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustPage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new MarkAsKnownPage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new BookmarkPage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnbookmarkPage(emptyTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new BookmarksPage(bookmarksTemplate, this), "Bookmarks"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new SearchPage(searchTemplate, this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteSonePage(deleteSoneTemplate, this), "DeleteSone"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new LoginPage(loginTemplate, this), "Login"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new LogoutPage(emptyTemplate, this), "Logout"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new OptionsPage(optionsTemplate, this), "Options"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new RescuePage(rescueTemplate, this), "Rescue"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new AboutPage(aboutTemplate, this, new PluginVersion(SonePlugin.getPluginVersion()), new PluginYear(SonePlugin.getYear()), new PluginHomepage(SonePlugin.getHomepage())), "About"));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("noPermission.html", noPermissionTemplate, "Page.NoPermission.Title", this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("emptyImageTitle.html", emptyImageTitleTemplate, "Page.EmptyImageTitle.Title", this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("emptyAlbumTitle.html", emptyAlbumTitleTemplate, "Page.EmptyAlbumTitle.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(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(loaders.<FreenetRequest>loadStaticPage("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 GetTranslationAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new GetStatusAjaxPage(this, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault())));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new GetNotificationsAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DismissNotificationAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new CreatePostAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateReplyAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new GetReplyAjaxPage(this, replyTemplate)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new GetPostAjaxPage(this, postTemplate)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter, TimeZone.getDefault())));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new MarkAsKnownAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeletePostAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteReplyAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new LockSoneAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlockSoneAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new FollowSoneAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnfollowSoneAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new EditAlbumAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new LikeAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnlikeAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new GetLikesAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new BookmarkAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new UnbookmarkAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new EditProfileFieldAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new DeleteProfileFieldAjaxPage(this)));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new MoveProfileFieldAjaxPage(this)));
-
- ToadletContainer toadletContainer = sonePlugin.pluginRespirator().getToadletContainer();
- toadletContainer.getPageMaker().addNavigationCategory("/Sone/index.html", "Navigation.Menu.Sone.Name", "Navigation.Menu.Sone.Tooltip", sonePlugin);
- for (PageToadlet toadlet : pageToadlets) {
- String menuName = toadlet.getMenuName();
- if (menuName != null) {
- toadletContainer.register(toadlet, "Navigation.Menu.Sone.Name", toadlet.path(), true, "Navigation.Menu.Sone.Item." + menuName + ".Name", "Navigation.Menu.Sone.Item." + menuName + ".Tooltip", false, toadlet);
- } else {
- toadletContainer.register(toadlet, null, toadlet.path(), true, false);
- }
- }
- }
-
- /**
- * Unregisters all toadlets.
- */
- private void unregisterToadlets() {
- ToadletContainer toadletContainer = sonePlugin.pluginRespirator().getToadletContainer();
- for (PageToadlet pageToadlet : pageToadlets) {
- toadletContainer.unregister(pageToadlet);
- }
- toadletContainer.getPageMaker().removeNavigationCategory("Navigation.Menu.Sone.Name");
+ pageToadletRegistry.addPage(new RedirectPage<FreenetRequest>("", "index.html"));
+ pageToadletRegistry.addPage(new IndexPage(this, loaders, templateRenderer, postVisibilityFilter));
+ pageToadletRegistry.addPage(new NewPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new CreateSonePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new KnownSonesPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new EditProfilePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new EditProfileFieldPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DeleteProfileFieldPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new CreatePostPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new CreateReplyPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new ViewSonePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new ViewPostPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new LikePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new UnlikePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DeletePostPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DeleteReplyPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new LockSonePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new UnlockSonePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new FollowSonePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new UnfollowSonePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new ImageBrowserPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new CreateAlbumPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new EditAlbumPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DeleteAlbumPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new UploadImagePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new EditImagePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DeleteImagePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new TrustPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DistrustPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new UntrustPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new MarkAsKnownPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new BookmarkPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new UnbookmarkPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new BookmarksPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new SearchPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DeleteSonePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new LoginPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new LogoutPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new OptionsPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new RescuePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new AboutPage(this, loaders, templateRenderer, new PluginVersion(SonePlugin.getPluginVersion()), new PluginYear(sonePlugin.getYear()), new PluginHomepage(sonePlugin.getHomepage())));
+ pageToadletRegistry.addPage(new InvalidPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new NoPermissionPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new EmptyImageTitlePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new EmptyAlbumTitlePage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer));
+ pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css"));
+ pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript"));
+ pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png"));
+ pageToadletRegistry.addPage(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate));
+ pageToadletRegistry.addPage(new GetImagePage(this));
+ pageToadletRegistry.addPage(new GetTranslationAjaxPage(this));
+ pageToadletRegistry.addPage(new GetStatusAjaxPage(this, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault()));
+ pageToadletRegistry.addPage(new GetNotificationsAjaxPage(this));
+ pageToadletRegistry.addPage(new DismissNotificationAjaxPage(this));
+ pageToadletRegistry.addPage(new CreatePostAjaxPage(this));
+ pageToadletRegistry.addPage(new CreateReplyAjaxPage(this));
+ pageToadletRegistry.addPage(new GetReplyAjaxPage(this, replyTemplate));
+ pageToadletRegistry.addPage(new GetPostAjaxPage(this, postTemplate));
+ pageToadletRegistry.addPage(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter));
+ pageToadletRegistry.addPage(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter, TimeZone.getDefault()));
+ pageToadletRegistry.addPage(new MarkAsKnownAjaxPage(this));
+ pageToadletRegistry.addPage(new DeletePostAjaxPage(this));
+ pageToadletRegistry.addPage(new DeleteReplyAjaxPage(this));
+ pageToadletRegistry.addPage(new LockSoneAjaxPage(this));
+ pageToadletRegistry.addPage(new UnlockSoneAjaxPage(this));
+ pageToadletRegistry.addPage(new FollowSoneAjaxPage(this));
+ pageToadletRegistry.addPage(new UnfollowSoneAjaxPage(this));
+ pageToadletRegistry.addPage(new EditAlbumAjaxPage(this));
+ pageToadletRegistry.addPage(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter));
+ pageToadletRegistry.addPage(new TrustAjaxPage(this));
+ pageToadletRegistry.addPage(new DistrustAjaxPage(this));
+ pageToadletRegistry.addPage(new UntrustAjaxPage(this));
+ pageToadletRegistry.addPage(new LikeAjaxPage(this));
+ pageToadletRegistry.addPage(new UnlikeAjaxPage(this));
+ pageToadletRegistry.addPage(new GetLikesAjaxPage(this));
+ pageToadletRegistry.addPage(new BookmarkAjaxPage(this));
+ pageToadletRegistry.addPage(new UnbookmarkAjaxPage(this));
+ pageToadletRegistry.addPage(new EditProfileFieldAjaxPage(this));
+ pageToadletRegistry.addPage(new DeleteProfileFieldAjaxPage(this));
+ pageToadletRegistry.addPage(new MoveProfileFieldAjaxPage(this));
+
+ pageToadletRegistry.registerToadlets();
}
/**
*/
private Collection<Sone> getMentionedSones(String text) {
/* we need no context to find mentioned Sones. */
- Set<Sone> mentionedSones = new HashSet<Sone>();
+ Set<Sone> mentionedSones = new HashSet<>();
for (Part part : soneTextParser.parse(text, null)) {
if (part instanceof SonePart) {
mentionedSones.add(((SonePart) part).getSone());
+++ /dev/null
-/*
- * Sone - FreenetPage.java - Copyright © 2011–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-import java.net.URI;
-
-import net.pterodactylus.util.web.Page;
-
-/**
- * Freenet-specific {@link Page} extension that adds the capability to allow a
- * link to a page to be unharmed by Freenet’s content filter.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface FreenetPage extends Page<FreenetRequest> {
-
- /**
- * Returns whether the given should be excepted from being filtered.
- *
- * @param link
- * The link to check
- * @return {@code true} if the link should not be filtered, {@code false} if
- * it should be filtered
- */
- public boolean isLinkExcepted(URI link);
-
-}
+++ /dev/null
-/*
- * Sone - FreenetRequest.java - Copyright © 2011–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-import java.net.URI;
-
-import net.pterodactylus.util.web.Method;
-import net.pterodactylus.util.web.Request;
-import freenet.clients.http.ToadletContext;
-import freenet.support.api.HTTPRequest;
-
-/**
- * Encapsulates all Freenet-specific properties of a request.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class FreenetRequest extends Request {
-
- /** The underlying HTTP request from Freenet. */
- private final HTTPRequest httpRequest;
-
- /** The toadlet context. */
- private final ToadletContext toadletContext;
-
- /**
- * Creates a new freenet request.
- *
- * @param uri
- * The URI that is being accessed
- * @param method
- * The method used to access this page
- * @param httpRequest
- * The underlying HTTP request from Freenet
- * @param toadletContext
- * The toadlet context
- */
- public FreenetRequest(URI uri, Method method, HTTPRequest httpRequest, ToadletContext toadletContext) {
- super(uri, method);
- this.httpRequest = httpRequest;
- this.toadletContext = toadletContext;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns the underlying HTTP request from Freenet.
- *
- * @return The underlying HTTP request from Freenet
- */
- public HTTPRequest getHttpRequest() {
- return httpRequest;
- }
-
- /**
- * Returns the toadlet context.
- *
- * @return The toadlet context
- */
- public ToadletContext getToadletContext() {
- return toadletContext;
- }
-
-}
+++ /dev/null
-/*
- * Sone - FreenetTemplatePage.java - Copyright © 2010–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-import static java.lang.String.format;
-import static java.util.logging.Logger.getLogger;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.net.URI;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
-import net.pterodactylus.util.template.TemplateContextFactory;
-import net.pterodactylus.util.web.Method;
-import net.pterodactylus.util.web.Page;
-import net.pterodactylus.util.web.RedirectResponse;
-import net.pterodactylus.util.web.Response;
-import freenet.clients.http.LinkEnabledCallback;
-import freenet.clients.http.PageMaker;
-import freenet.clients.http.PageNode;
-import freenet.clients.http.ToadletContext;
-import freenet.support.HTMLNode;
-
-/**
- * Base class for all {@link Page}s that are rendered with {@link Template}s and
- * fit into Freenet’s web interface.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
-
- /** The logger. */
- private static final Logger logger = getLogger(FreenetTemplatePage.class.getName());
-
- /** The path of the page. */
- private final String path;
-
- /** The template context factory. */
- private final TemplateContextFactory templateContextFactory;
-
- /** The template to render. */
- private final Template template;
-
- /** Where to redirect for invalid form passwords. */
- private final String invalidFormPasswordRedirectTarget;
-
- /**
- * Creates a new template page.
- *
- * @param path
- * The path of the page
- * @param templateContextFactory
- * The template context factory
- * @param template
- * The template to render
- * @param invalidFormPasswordRedirectTarget
- * The target to redirect to if a POST request does not contain
- * the correct form password
- */
- public FreenetTemplatePage(String path, TemplateContextFactory templateContextFactory, Template template, String invalidFormPasswordRedirectTarget) {
- this.path = path;
- this.templateContextFactory = templateContextFactory;
- this.template = template;
- this.invalidFormPasswordRedirectTarget = invalidFormPasswordRedirectTarget;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getPath() {
- return path;
- }
-
- /**
- * Returns the title of the page.
- *
- * @param request
- * The request to serve
- * @return The title of the page
- */
- @SuppressWarnings("static-method")
- protected String getPageTitle(FreenetRequest request) {
- return null;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isPrefixPage() {
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public final Response handleRequest(FreenetRequest request, Response response) throws IOException {
- String redirectTarget = getRedirectTarget(request);
- if (redirectTarget != null) {
- return new RedirectResponse(redirectTarget);
- }
-
- if (isFullAccessOnly() && !request.getToadletContext().isAllowedFullAccess()) {
- return response.setStatusCode(401).setStatusText("Not authorized").setContentType("text/html");
- }
- ToadletContext toadletContext = request.getToadletContext();
- if (request.getMethod() == Method.POST) {
- /* require form password. */
- String formPassword = request.getHttpRequest().getPartAsStringFailsafe("formPassword", 32);
- if (!formPassword.equals(toadletContext.getContainer().getFormPassword())) {
- return new RedirectResponse(invalidFormPasswordRedirectTarget);
- }
- }
- PageMaker pageMaker = toadletContext.getPageMaker();
- PageNode pageNode = pageMaker.getPageNode(getPageTitle(request), toadletContext);
- for (String styleSheet : getStyleSheets()) {
- pageNode.addCustomStyleSheet(styleSheet);
- }
- for (Map<String, String> linkNodeParameters : getAdditionalLinkNodes(request)) {
- HTMLNode linkNode = pageNode.headNode.addChild("link");
- for (Entry<String, String> parameter : linkNodeParameters.entrySet()) {
- linkNode.addAttribute(parameter.getKey(), parameter.getValue());
- }
- }
- String shortcutIcon = getShortcutIcon();
- if (shortcutIcon != null) {
- pageNode.addForwardLink("icon", shortcutIcon);
- }
-
- TemplateContext templateContext = templateContextFactory.createTemplateContext();
- templateContext.mergeContext(template.getInitialContext());
- try {
- long start = System.nanoTime();
- processTemplate(request, templateContext);
- long finish = System.nanoTime();
- logger.log(Level.FINEST, format("Template was rendered in %.2fms.", (finish - start) / 1000000.0));
- } catch (RedirectException re1) {
- return new RedirectResponse(re1.getTarget());
- }
-
- StringWriter stringWriter = new StringWriter();
- template.render(templateContext, stringWriter);
- pageNode.content.addChild("%", stringWriter.toString());
-
- postProcess(request, templateContext);
-
- return response.setStatusCode(200).setStatusText("OK").setContentType("text/html").write(pageNode.outer.generate());
- }
-
- /**
- * Can be overridden to return a custom set of style sheets that are to be
- * included in the page’s header.
- *
- * @return Additional style sheets to load
- */
- @SuppressWarnings("static-method")
- protected Collection<String> getStyleSheets() {
- return Collections.emptySet();
- }
-
- /**
- * Returns the name of the shortcut icon to include in the page’s header.
- *
- * @return The URL of the shortcut icon, or {@code null} for no icon
- */
- @SuppressWarnings("static-method")
- protected String getShortcutIcon() {
- return null;
- }
-
- /**
- * Can be overridden when extending classes need to set variables in the
- * template before it is rendered.
- *
- * @param request
- * The request that is rendered
- * @param templateContext
- * The template context to set variables in
- * @throws RedirectException
- * if the processing page wants to redirect after processing
- */
- protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
- /* do nothing. */
- }
-
- /**
- * This method will be called after
- * {@link #processTemplate(FreenetRequest, TemplateContext)} has processed
- * the template and the template was rendered. This method will not be
- * called if {@link #processTemplate(FreenetRequest, TemplateContext)}
- * throws a {@link RedirectException}!
- *
- * @param request
- * The request being processed
- * @param templateContext
- * The template context that supplied the rendered data
- */
- protected void postProcess(FreenetRequest request, TemplateContext templateContext) {
- /* do nothing. */
- }
-
- /**
- * Can be overridden to redirect the user to a different page, in case a log
- * in is required, or something else is wrong.
- *
- * @param request
- * The request that is processed
- * @return The URL to redirect to, or {@code null} to not redirect
- */
- @SuppressWarnings("static-method")
- protected String getRedirectTarget(FreenetRequest request) {
- return null;
- }
-
- /**
- * Returns additional <link> nodes for the HTML’s <head> node.
- *
- * @param request
- * The request for which to return the link nodes
- * @return All link nodes that should be added to the HTML head
- */
- @SuppressWarnings("static-method")
- protected List<Map<String, String>> getAdditionalLinkNodes(FreenetRequest request) {
- return Collections.emptyList();
- }
-
- /**
- * Returns whether this page should only be allowed for requests from hosts
- * with full access.
- *
- * @return {@code true} if this page should only be allowed for hosts with
- * full access, {@code false} to allow this page for any host
- */
- @SuppressWarnings("static-method")
- protected boolean isFullAccessOnly() {
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isLinkExcepted(URI link) {
- return false;
- }
-
- //
- // INTERFACE LinkEnabledCallback
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isEnabled(ToadletContext toadletContext) {
- return !isFullAccessOnly();
- }
-
- /**
- * Exception that can be thrown to signal that a subclassed {@link Page}
- * wants to redirect the user during the
- * {@link FreenetTemplatePage#processTemplate(FreenetRequest, TemplateContext)}
- * method call.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- public static class RedirectException extends Exception {
-
- /** The target to redirect to. */
- private final String target;
-
- /**
- * Creates a new redirect exception.
- *
- * @param target
- * The target of the redirect
- */
- public RedirectException(String target) {
- this.target = target;
- }
-
- /**
- * Returns the target to redirect to.
- *
- * @return The target to redirect to
- */
- public String getTarget() {
- return target;
- }
-
- @Override
- public String toString() {
- return format("RedirectException{target='%s'}", target);
- }
-
- }
-
-}
/*
- * Sone - PageToadlet.java - Copyright © 2010–2016 David Roden
+ * Sone - PageToadlet.java - Copyright © 2010–2019 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
import freenet.client.HighLevelSimpleClient;
import freenet.clients.http.LinkEnabledCallback;
import freenet.clients.http.LinkFilterExceptedToadlet;
+import freenet.clients.http.SessionManager;
import freenet.clients.http.Toadlet;
import freenet.clients.http.ToadletContext;
import freenet.clients.http.ToadletContextClosedException;
+import freenet.l10n.NodeL10n;
import freenet.support.MultiValueTable;
import freenet.support.api.HTTPRequest;
/**
* {@link Toadlet} implementation that is wrapped around a {@link Page}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PageToadlet extends Toadlet implements LinkEnabledCallback, LinkFilterExceptedToadlet {
+ private final SessionManager sessionManager;
+
/** The name of the menu item. */
private final String menuName;
* Prefix that is prepended to all {@link Page#getPath()} return
* values
*/
- protected PageToadlet(HighLevelSimpleClient highLevelSimpleClient, String menuName, Page<FreenetRequest> page, String pathPrefix) {
+ protected PageToadlet(HighLevelSimpleClient highLevelSimpleClient, SessionManager sessionManager, String menuName, Page<FreenetRequest> page, String pathPrefix) {
super(highLevelSimpleClient);
+ this.sessionManager = sessionManager;
this.menuName = menuName;
this.page = page;
this.pathPrefix = pathPrefix;
* if the toadlet context is closed
*/
public void handleMethodGET(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
- handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext));
+ handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext, NodeL10n.getBase(), sessionManager));
}
/**
* if the toadlet context is closed
*/
public void handleMethodPOST(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
- handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext));
+ handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext, NodeL10n.getBase(), sessionManager));
}
/**
try (AutoCloseableBucket pageBucket = new AutoCloseableBucket(pageRequest.getToadletContext().getBucketFactory().makeBucket(-1));
OutputStream pageBucketOutputStream = pageBucket.getBucket().getOutputStream()) {
Response pageResponse = page.handleRequest(pageRequest, new Response(pageBucketOutputStream));
- MultiValueTable<String, String> headers = new MultiValueTable<String, String>();
+ MultiValueTable<String, String> headers = new MultiValueTable<>();
if (pageResponse.getHeaders() != null) {
for (Header header : pageResponse.getHeaders()) {
for (String value : header) {
+++ /dev/null
-/*
- * Sone - PageToadletFactory.java - Copyright © 2010–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.web.page;
-
-import net.pterodactylus.util.web.Page;
-import freenet.client.HighLevelSimpleClient;
-
-/**
- * Factory that creates {@link PageToadlet}s using a given
- * {@link HighLevelSimpleClient}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PageToadletFactory {
-
- /** The client to use when creating the toadlets. */
- private final HighLevelSimpleClient highLevelSimpleClient;
-
- /** The prefix for all pages’ paths. */
- private final String pathPrefix;
-
- /**
- * Creates a new {@link PageToadlet} factory.
- *
- * @param highLevelSimpleClient
- * The client to use when creating the toadlets
- * @param pathPrefix
- * The path that is prepended to all pages’ paths
- */
- public PageToadletFactory(HighLevelSimpleClient highLevelSimpleClient, String pathPrefix) {
- this.highLevelSimpleClient = highLevelSimpleClient;
- this.pathPrefix = pathPrefix;
- }
-
- /**
- * Creates a {@link PageToadlet} that wraps the given page and does not
- * appear in the node’s menu.
- *
- * @param page
- * The page to wrap
- * @return The toadlet wrapped around the page
- */
- public PageToadlet createPageToadlet(Page<FreenetRequest> page) {
- return createPageToadlet(page, null);
- }
-
- /**
- * Creates a {@link PageToadlet} that wraps the given page and appears in
- * the node’s menu under the given name.
- *
- * @param page
- * The page to wrap
- * @param menuName
- * The name of the menu item
- * @return The toadlet wrapped around the page
- */
- public PageToadlet createPageToadlet(Page<FreenetRequest> page, String menuName) {
- return new PageToadlet(highLevelSimpleClient, menuName, page, pathPrefix);
- }
-
-}
--- /dev/null
+package net.pterodactylus.sone.core
+
+import freenet.client.FetchResult
+import freenet.keys.FreenetURI
+
+/**
+ * Container for a fetched URI and the [FetchResult].
+ */
+data class Fetched(val freenetUri: FreenetURI, val fetchResult: FetchResult)
--- /dev/null
+/*
+ * Sone - Preferences.kt - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core
+
+import com.google.common.base.Predicates.*
+import com.google.common.eventbus.*
+import net.pterodactylus.sone.core.event.*
+import net.pterodactylus.sone.fcp.FcpInterface.*
+import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.*
+import net.pterodactylus.sone.fcp.event.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.utils.IntegerRangePredicate.*
+import net.pterodactylus.util.config.*
+import java.lang.Integer.*
+
+/**
+ * Convenience interface for external classes that want to access the core’s
+ * configuration.
+ */
+class Preferences(private val eventBus: EventBus) {
+
+ private val _insertionDelay = DefaultOption(60, range(0, MAX_VALUE))
+ val insertionDelay: Int get() = _insertionDelay.get()
+ var newInsertionDelay: Int?
+ get() = unsupported
+ set(value) {
+ _insertionDelay.set(value)
+ eventBus.post(InsertionDelayChangedEvent(insertionDelay))
+ eventBus.post(PreferenceChangedEvent("InsertionDelay", insertionDelay))
+ }
+
+ private val _postsPerPage = DefaultOption(10, range(1, MAX_VALUE))
+ val postsPerPage: Int get() = _postsPerPage.get()
+ var newPostsPerPage: Int?
+ get() = unsupported
+ set(value) {
+ _postsPerPage.set(value)
+ eventBus.post(PreferenceChangedEvent("PostsPerPage", postsPerPage))
+ }
+
+ private val _imagesPerPage = DefaultOption(9, range(1, MAX_VALUE))
+ val imagesPerPage: Int get() = _imagesPerPage.get()
+ var newImagesPerPage: Int?
+ get() = unsupported
+ set (value: Int?) = _imagesPerPage.set(value)
+
+ private val _charactersPerPost = DefaultOption(400, or(range(50, MAX_VALUE), equalTo(-1)))
+ val charactersPerPost: Int get() = _charactersPerPost.get()
+ var newCharactersPerPost: Int?
+ get() = unsupported
+ set(value) = _charactersPerPost.set(value)
+
+ private val _postCutOffLength = DefaultOption(200, range(50, MAX_VALUE))
+ val postCutOffLength: Int get() = _postCutOffLength.get()
+ var newPostCutOffLength: Int?
+ get() = unsupported
+ set(value) = _postCutOffLength.set(value)
+
+ private val _requireFullAccess = DefaultOption(false)
+ val requireFullAccess: Boolean get() = _requireFullAccess.get()
+ var newRequireFullAccess: Boolean?
+ get() = unsupported
+ set(value) = _requireFullAccess.set(value)
+
+ private val _positiveTrust = DefaultOption(75, range(0, 100))
+ val positiveTrust: Int get() = _positiveTrust.get()
+ var newPositiveTrust: Int?
+ get() = unsupported
+ set(value) = _positiveTrust.set(value)
+
+ private val _negativeTrust = DefaultOption(-25, range(-100, 100))
+ val negativeTrust: Int get() = _negativeTrust.get()
+ var newNegativeTrust: Int?
+ get() = unsupported
+ set(value) = _negativeTrust.set(value)
+
+ private val _trustComment = DefaultOption("Set from Sone Web Interface")
+ val trustComment: String get() = _trustComment.get()
+ var newTrustComment: String?
+ get() = unsupported
+ set(value) = _trustComment.set(value)
+
+ private val _fcpInterfaceActive = DefaultOption(false)
+ val fcpInterfaceActive: Boolean get() = _fcpInterfaceActive.get()
+ var newFcpInterfaceActive: Boolean?
+ get() = unsupported
+ set(value) {
+ _fcpInterfaceActive.set(value)
+ when (value) {
+ true -> eventBus.post(FcpInterfaceActivatedEvent())
+ else -> eventBus.post(FcpInterfaceDeactivatedEvent())
+ }
+ }
+
+ private val _fcpFullAccessRequired = DefaultOption(ALWAYS)
+ val fcpFullAccessRequired: FullAccessRequired get() = _fcpFullAccessRequired.get()
+ var newFcpFullAccessRequired: FullAccessRequired?
+ get() = unsupported
+ set(value) {
+ _fcpFullAccessRequired.set(value)
+ eventBus.post(FullAccessRequiredChanged(fcpFullAccessRequired))
+ }
+
+ @Throws(ConfigurationException::class)
+ fun saveTo(configuration: Configuration) {
+ configuration.getIntValue("Option/ConfigurationVersion").value = 0
+ configuration.getIntValue("Option/InsertionDelay").value = _insertionDelay.real
+ configuration.getIntValue("Option/PostsPerPage").value = _postsPerPage.real
+ configuration.getIntValue("Option/ImagesPerPage").value = _imagesPerPage.real
+ configuration.getIntValue("Option/CharactersPerPost").value = _charactersPerPost.real
+ configuration.getIntValue("Option/PostCutOffLength").value = _postCutOffLength.real
+ configuration.getBooleanValue("Option/RequireFullAccess").value = _requireFullAccess.real
+ configuration.getIntValue("Option/PositiveTrust").value = _positiveTrust.real
+ configuration.getIntValue("Option/NegativeTrust").value = _negativeTrust.real
+ configuration.getStringValue("Option/TrustComment").value = _trustComment.real
+ configuration.getBooleanValue("Option/ActivateFcpInterface").value = _fcpInterfaceActive.real
+ configuration.getIntValue("Option/FcpFullAccessRequired").value = toInt(_fcpFullAccessRequired.real)
+ }
+
+ private fun toInt(fullAccessRequired: FullAccessRequired?): Int? {
+ return fullAccessRequired?.ordinal
+ }
+
+}
+
+private val unsupported: Nothing get() = throw UnsupportedOperationException()
--- /dev/null
+package net.pterodactylus.sone.core
+
+import net.pterodactylus.sone.data.*
+
+class SoneComparison(private val oldSone: Sone, private val newSone: Sone) {
+
+ val newPosts: Collection<Post> get() = newSone.posts - oldSone.posts
+ val removedPosts: Collection<Post> get() = oldSone.posts - newSone.posts
+ val newPostReplies: Collection<PostReply> get() = newSone.replies - oldSone.replies
+ val removedPostReplies: Collection<PostReply> get() = oldSone.replies - newSone.replies
+
+}
--- /dev/null
+package net.pterodactylus.sone.core
+
+import com.google.common.eventbus.*
+import com.google.inject.*
+import net.pterodactylus.sone.core.event.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Sone.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.util.logging.*
+import javax.inject.Inject
+
+/**
+ * An `UpdatedSoneProcessor` is called to process a [Sone] after it has been
+ * downloaded from Freenet.
+ */
+@ImplementedBy(DefaultUpdateSoneProcessor::class)
+interface UpdatedSoneProcessor {
+
+ fun updateSone(sone: Sone)
+
+}
+
+abstract class BasicUpdateSoneProcessor(private val database: Database, private val eventBus: EventBus) :
+ UpdatedSoneProcessor {
+
+ private val logger = Logging.getLogger(UpdatedSoneProcessor::javaClass.name)!!
+
+ override fun updateSone(sone: Sone) {
+ val storedSone = database.getSone(sone.id) ?: return
+ if (!soneCanBeUpdated(storedSone, sone)) {
+ logger.fine("Downloaded Sone $sone can not update stored Sone $storedSone.")
+ return
+ }
+
+ SoneComparison(storedSone, sone).apply {
+ newPosts
+ .onEach { post -> if (post.time <= sone.followingTime) post.isKnown = true }
+ .mapNotNull { post -> post.isKnown.ifFalse { NewPostFoundEvent(post) } }
+ .forEach(eventBus::post)
+ removedPosts
+ .map { PostRemovedEvent(it) }
+ .forEach(eventBus::post)
+ newPostReplies
+ .onEach { postReply -> if (postReply.time <= sone.followingTime) postReply.isKnown = true }
+ .mapNotNull { postReply -> postReply.isKnown.ifFalse { NewPostReplyFoundEvent(postReply) } }
+ .forEach(eventBus::post)
+ removedPostReplies
+ .map { PostReplyRemovedEvent(it) }
+ .forEach(eventBus::post)
+ }
+ database.storeSone(sone)
+ sone.options = storedSone.options
+ sone.isKnown = storedSone.isKnown
+ sone.status = if (sone.time != 0L) SoneStatus.idle else SoneStatus.unknown
+ }
+
+ protected abstract fun soneCanBeUpdated(storedSone: Sone, newSone: Sone): Boolean
+
+ private val Sone.followingTime get() = database.getFollowingTime(id) ?: 0
+
+}
+
+class DefaultUpdateSoneProcessor @Inject constructor(database: Database, eventBus: EventBus) :
+ BasicUpdateSoneProcessor(database, eventBus) {
+
+ override fun soneCanBeUpdated(storedSone: Sone, newSone: Sone) =
+ newSone.time > storedSone.time
+
+}
--- /dev/null
+package net.pterodactylus.sone.core.event
+
+data class InsertionDelayChangedEvent(val insertionDelay: Int)
--- /dev/null
+/*
+ * Sone - NewPostFoundEvent.kt - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core.event
+
+import net.pterodactylus.sone.data.Post
+
+/**
+ * Event that signals that a new post was found.
+ */
+data class NewPostFoundEvent(val post: Post) {
+
+ @Deprecated(message = "will go away", replaceWith = ReplaceWith("post"))
+ fun post() = post
+
+}
--- /dev/null
+/*
+ * Sone - NewPostReplyFoundEvent.kt - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core.event
+
+import net.pterodactylus.sone.data.PostReply
+
+/**
+ * Event that signals that a new [PostReply] was found.
+ */
+data class NewPostReplyFoundEvent(val postReply: PostReply) {
+
+ @Deprecated(message = "will go away", replaceWith = ReplaceWith("postReply"))
+ fun postReply() = postReply
+
+}
--- /dev/null
+/*
+ * Sone - PostRemovedEvent.kt - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core.event
+
+import net.pterodactylus.sone.data.Post
+
+/**
+ * Event that signals that a [Post] was removed.
+ */
+data class PostRemovedEvent(val post: Post) {
+
+ @Deprecated(message = "will go away", replaceWith = ReplaceWith("post"))
+ fun post() = post
+
+}
--- /dev/null
+/*
+ * Sone - PostReplyRemovedEvent.kt - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core.event
+
+import net.pterodactylus.sone.data.PostReply
+
+/**
+ * Event that signals that a [PostReply] was removed.
+ */
+data class PostReplyRemovedEvent(val postReply: PostReply) {
+
+ @Deprecated(message = "will go away", replaceWith = ReplaceWith("postReply"))
+ fun postReply() = postReply
+
+}
--- /dev/null
+/*
+ * Sone - Fingerprintable.kt - Copyright © 2011–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data
+
+/**
+ * Interface for objects that can create a fingerprint of themselves, e.g. to
+ * detect modifications. The fingerprint should only contain original
+ * information; derived information should not be included.
+ */
+interface Fingerprintable {
+
+ val fingerprint: String
+
+}
--- /dev/null
+/*
+ * Sone - Identified.kt - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data
+
+/**
+ * Interface for all objects that expose an ID.
+ */
+interface Identified {
+
+ val id: String
+
+}
--- /dev/null
+/*
+ * Sone - AlbumBuilder.kt - Copyright © 2013–2019 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.database
+
+import net.pterodactylus.sone.data.Album
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Builder for [Album] objects.
+ */
+interface AlbumBuilder {
+
+ fun randomId(): AlbumBuilder
+ fun withId(id: String): AlbumBuilder
+
+ fun by(sone: Sone): AlbumBuilder
+
+ fun build(): Album
+
+}
--- /dev/null
+/*
+ * Sone - AlbumBuilderFactory.kt - Copyright © 2013–2019 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.database
+
+/**
+ * Factory for [AlbumBuilder]s.
+ */
+interface AlbumBuilderFactory {
+
+ fun newAlbumBuilder(): AlbumBuilder
+
+}
--- /dev/null
+/*
+ * Sone - AlbumDatabase.kt - Copyright © 2013–2019 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.database
+
+/**
+ * Combines an [AlbumProvider] and an [AlbumStore] into an album
+ * database.
+ */
+interface AlbumDatabase : AlbumProvider, AlbumBuilderFactory, AlbumStore
--- /dev/null
+/*
+ * Sone - AlbumProvider.kt - Copyright © 2013–2019 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.database
+
+import net.pterodactylus.sone.data.*
+
+/**
+ * Interface for objects that can provide [Album]s by their ID.
+ */
+interface AlbumProvider {
+
+ fun getAlbum(albumId: String): Album?
+
+}
--- /dev/null
+/*
+ * Sone - AlbumStore.kt - Copyright © 2013–2019 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.database
+
+import net.pterodactylus.sone.data.Album
+
+/**
+ * Interface for a store of albums.
+ */
+interface AlbumStore {
+
+ fun storeAlbum(album: Album)
+ fun removeAlbum(album: Album)
+
+}
/*
- * Sone - Database.java - Copyright © 2013–2016 David Roden
+ * Sone - Database.kt - Copyright © 2013–2019 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
fun getFriends(localSone: Sone): Collection<String>
fun isFriend(localSone: Sone, friendSoneId: String): Boolean
+ fun getFollowingTime(friendSoneId: String): Long?
}
/*
- * Sone - ImageBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageBuilder.kt - Copyright © 2013–2019 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
/*
- * Sone - ImageBuilderFactory.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageBuilderFactory.kt - Copyright © 2013–2019 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
/*
- * Sone - ImageDatabase.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageDatabase.kt - Copyright © 2013–2019 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
/*
- * Sone - ImageProvider.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageProvider.kt - Copyright © 2013–2019 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
package net.pterodactylus.sone.database
-import net.pterodactylus.sone.data.Image
-
-import com.google.common.base.Optional
+import net.pterodactylus.sone.data.*
/**
* Provides [Image]s.
/*
- * Sone - ImageStore.java - Copyright © 2013–2016 David Roden
+ * Sone - ImageStore.kt - Copyright © 2013–2019 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
/*
- * Sone - PostBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - PostBuilder.kt - Copyright © 2013–2019 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
/*
- * Sone - PostBuilderFactory.java - Copyright © 2013–2016 David Roden
+ * Sone - PostBuilderFactory.kt - Copyright © 2013–2019 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
/*
- * Sone - PostDatabase.java - Copyright © 2013–2016 David Roden
+ * Sone - PostDatabase.kt - Copyright © 2013–2019 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
/*
- * Sone - PostProvider.java - Copyright © 2011–2016 David Roden
+ * Sone - PostProvider.kt - Copyright © 2011–2019 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
package net.pterodactylus.sone.database
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.database.memory.MemoryDatabase
-
-import com.google.common.base.Optional
-import com.google.inject.ImplementedBy
+import com.google.inject.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.database.memory.*
/**
* Interface for objects that can provide [Post]s by their ID.
/*
- * Sone - PostReplyBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - PostReplyBuilder.kt - Copyright © 2013–2019 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
/*
- * Sone - PostReplyBuilderFactory.java - Copyright © 2013–2016 David Roden
+ * Sone - PostReplyBuilderFactory.kt - Copyright © 2013–2019 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
/*
- * Sone - PostReplyDatabase.java - Copyright © 2013–2016 David Roden
+ * Sone - PostReplyDatabase.kt - Copyright © 2013–2019 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
/*
- * Sone - PostReplyProvider.java - Copyright © 2013–2016 David Roden
+ * Sone - PostReplyProvider.kt - Copyright © 2013–2019 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
package net.pterodactylus.sone.database
-import com.google.common.base.Optional
-import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.data.*
/**
* Interface for objects that can provide [PostReply]s.
/*
- * Sone - PostReplyStore.java - Copyright © 2013–2016 David Roden
+ * Sone - PostReplyStore.kt - Copyright © 2013–2019 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
package net.pterodactylus.sone.database
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.data.*
/**
* Defines a store for [post replies][PostReply].
/*
- * Sone - PostStore.java - Copyright © 2013–2016 David Roden
+ * Sone - PostStore.kt - Copyright © 2013–2019 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
package net.pterodactylus.sone.database
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.data.*
/**
* Interface for a store for posts.
/*
- * Sone - ReplyBuilder.java - Copyright © 2013–2016 David Roden
+ * Sone - ReplyBuilder.kt - Copyright © 2013–2019 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
package net.pterodactylus.sone.database
-import net.pterodactylus.sone.data.Reply
-import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.data.*
/**
* Methods that all reply builders need to implement in order to be able to
/*
- * Sone - SoneProvider.java - Copyright © 2011–2016 David Roden
+ * Sone - SoneProvider.kt - Copyright © 2011–2019 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
package net.pterodactylus.sone.database
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Sone
-
-import com.google.common.base.Function
-import com.google.common.base.Optional
-import com.google.inject.ImplementedBy
+import com.google.inject.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
/**
* Interface for objects that can provide [Sone]s by their ID.
--- /dev/null
+/*
+ * Sone - AbstractSoneCommand.kt - Copyright © 2011–2019 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.fcp
+
+import com.google.common.base.Optional
+import freenet.node.FSParseException
+import freenet.support.SimpleFieldSet
+import net.pterodactylus.sone.core.Core
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder
+import net.pterodactylus.sone.freenet.fcp.AbstractCommand
+import net.pterodactylus.sone.freenet.fcp.Command
+import net.pterodactylus.sone.freenet.fcp.FcpException
+import net.pterodactylus.sone.template.SoneAccessor
+import net.pterodactylus.sone.utils.asOptional
+import net.pterodactylus.sone.utils.let
+import net.pterodactylus.sone.utils.throwOnNullIf
+
+/**
+ * Abstract base implementation of a [Command] with Sone-related helper
+ * methods.
+ */
+abstract class AbstractSoneCommand
+@JvmOverloads protected constructor(
+ protected val core: Core,
+ @get:JvmName("requiresWriteAccess")
+ val requiresWriteAccess: Boolean = false) : AbstractCommand() {
+
+ @Throws(FcpException::class)
+ protected fun getSone(simpleFieldSet: SimpleFieldSet, parameterName: String, localOnly: Boolean): Sone =
+ getSone(simpleFieldSet, parameterName, localOnly, true).get()
+
+ @Throws(FcpException::class)
+ protected fun getSone(simpleFieldSet: SimpleFieldSet, parameterName: String, localOnly: Boolean, mandatory: Boolean): Optional<Sone> {
+ val soneId = simpleFieldSet.get(parameterName)
+ .throwOnNullIf(mandatory) { FcpException("Could not load Sone ID from “$parameterName”.") }
+ ?: return Optional.absent()
+ val sone = core.getSone(soneId)
+ if (mandatory && sone == null || sone != null && localOnly && !sone.isLocal) {
+ throw FcpException("Could not load Sone from “$soneId”.")
+ }
+ return sone.asOptional()
+ }
+
+ @Throws(FcpException::class)
+ protected fun getPost(simpleFieldSet: SimpleFieldSet, parameterName: String): Post {
+ try {
+ val postId = simpleFieldSet.getString(parameterName)
+ return core.getPost(postId)
+ ?: throw FcpException("Could not load post from “$postId”.")
+ } catch (fspe1: FSParseException) {
+ throw FcpException("Could not post ID from “$parameterName”.", fspe1)
+ }
+ }
+
+ @Throws(FcpException::class)
+ protected fun getReply(simpleFieldSet: SimpleFieldSet, parameterName: String): PostReply {
+ try {
+ val replyId = simpleFieldSet.getString(parameterName)
+ return core.getPostReply(replyId)
+ ?: throw FcpException("Could not load reply from “$replyId”.")
+ } catch (fspe1: FSParseException) {
+ throw FcpException("Could not reply ID from “$parameterName”.", fspe1)
+ }
+ }
+
+ protected fun encodePost(post: Post, prefix: String, includeReplies: Boolean): SimpleFieldSet = SimpleFieldSetBuilder().apply {
+ put(prefix + "ID", post.id)
+ put(prefix + "Sone", post.sone.id)
+ post.recipientId.let { put(prefix + "Recipient", it) }
+ put(prefix + "Time", post.time)
+ put(prefix + "Text", encodeString(post.text))
+ put(encodeLikes(core.getLikes(post), "${prefix}Likes."))
+ if (includeReplies) {
+ val replies = core.getReplies(post.id)
+ put(encodeReplies(replies, prefix))
+ }
+ }.get()
+
+ protected fun encodePosts(posts: Collection<Post>, prefix: String, includeReplies: Boolean): SimpleFieldSet = SimpleFieldSetBuilder().apply {
+ put(prefix + "Count", posts.size)
+ posts.forEachIndexed { postIndex, post ->
+ put(encodePost(post, "$prefix$postIndex.", includeReplies))
+ }
+ }.get()
+
+ private fun encodeReplies(replies: Collection<PostReply>, prefix: String): SimpleFieldSet = SimpleFieldSetBuilder().apply {
+ put(prefix + "Replies.Count", replies.size)
+ replies.forEachIndexed { replyIndex, reply ->
+ val replyPrefix = "${prefix}Replies.$replyIndex."
+ put(replyPrefix + "ID", reply.id)
+ put(replyPrefix + "Sone", reply.sone.id)
+ put(replyPrefix + "Time", reply.time)
+ put(replyPrefix + "Text", encodeString(reply.text))
+ put(encodeLikes(core.getLikes(reply), "${replyPrefix}Likes."))
+ }
+ }.get()
+
+ override fun toString() = "${javaClass.name}[requiresWriteAccess=$requiresWriteAccess]"
+}
+
+fun encodeString(text: String) = text
+ .replace("\\\\".toRegex(), "\\\\\\\\")
+ .replace("\n".toRegex(), "\\\\n")
+ .replace("\r".toRegex(), "\\\\r")
+
+fun encodeSone(sone: Sone, prefix: String, localSone: Optional<Sone>): SimpleFieldSet = SimpleFieldSetBuilder().apply {
+ put(prefix + "ID", sone.id)
+ put(prefix + "Name", sone.name)
+ put(prefix + "NiceName", SoneAccessor.getNiceName(sone))
+ put(prefix + "LastUpdated", sone.time)
+ localSone.let { put(prefix + "Followed", it.hasFriend(sone.id).toString()) }
+ val profile = sone.profile
+ put(prefix + "Field.Count", profile.fields.size)
+ profile.fields.forEachIndexed { fieldIndex, field ->
+ put(prefix + "Field." + fieldIndex + ".Name", field.name)
+ put(prefix + "Field." + fieldIndex + ".Value", field.value)
+ }
+}.get()
+
+fun encodeSones(sones: Collection<Sone>, prefix: String): SimpleFieldSet = SimpleFieldSetBuilder().apply {
+ put(prefix + "Count", sones.size)
+ sones.forEachIndexed { soneIndex, sone ->
+ put(encodeSone(sone, "$prefix$soneIndex.", Optional.absent()))
+ }
+}.get()
+
+fun encodeLikes(likes: Collection<Sone>, prefix: String): SimpleFieldSet = SimpleFieldSetBuilder().apply {
+ put(prefix + "Count", likes.size)
+ likes.forEachIndexed { index, sone -> put("$prefix$index.ID", sone.id) }
+}.get()
package net.pterodactylus.sone.main
-import com.google.inject.Binder
-import com.google.inject.Module
-import com.google.inject.Provides
-import freenet.client.HighLevelSimpleClient
-import freenet.node.Node
-import freenet.pluginmanager.PluginRespirator
+import com.google.inject.*
+import freenet.client.*
+import freenet.clients.http.*
+import freenet.node.*
+import freenet.pluginmanager.*
+import javax.inject.Provider
import javax.inject.Singleton
/**
* Guice [Module] that supplies some objects that are in fact supplied by the Freenet node.
*/
-class FreenetModule(private val pluginRespirator: PluginRespirator): Module {
+class FreenetModule(private val pluginRespirator: PluginRespirator) : Module {
override fun configure(binder: Binder): Unit = binder.run {
- bind(PluginRespirator::class.java).toProvider { pluginRespirator }
- pluginRespirator.node!!.let { node -> bind(Node::class.java).toProvider { node } }
- bind(HighLevelSimpleClient::class.java).toProvider { pluginRespirator.hlSimpleClient!! }
+ bind(PluginRespirator::class.java).toProvider(Provider<PluginRespirator> { pluginRespirator })
+ pluginRespirator.node!!.let { node -> bind(Node::class.java).toProvider(Provider<Node> { node }) }
+ bind(HighLevelSimpleClient::class.java).toProvider(Provider<HighLevelSimpleClient> { pluginRespirator.hlSimpleClient!! })
+ bind(ToadletContainer::class.java).toProvider(Provider<ToadletContainer> { pluginRespirator.toadletContainer })
+ bind(PageMaker::class.java).toProvider(Provider<PageMaker> { pluginRespirator.pageMaker })
}
- @Provides @Singleton
+ @Provides
+ @Singleton
fun getSessionManager() = pluginRespirator.getSessionManager("Sone")!!
}
--- /dev/null
+package net.pterodactylus.sone.main
+
+import com.google.common.base.*
+import com.google.common.eventbus.*
+import com.google.inject.*
+import com.google.inject.matcher.*
+import com.google.inject.name.Names.*
+import com.google.inject.spi.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.database.memory.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.util.config.*
+import net.pterodactylus.util.config.ConfigurationException
+import net.pterodactylus.util.version.Version
+import java.io.*
+
+class SoneModuleCreator {
+
+ fun createModule(sonePlugin: SonePlugin) = object : AbstractModule() {
+ override fun configure() {
+ val sonePropertiesFile = File("sone.properties")
+ val firstStart = !sonePropertiesFile.exists()
+ var newConfig = false
+ val configuration = try {
+ Configuration(MapConfigurationBackend(sonePropertiesFile, false))
+ } catch (ce: ConfigurationException) {
+ sonePropertiesFile.delete()
+ newConfig = true
+ Configuration(MapConfigurationBackend(sonePropertiesFile, true))
+ }
+ val context = Context("Sone")
+ val loaders = configuration.getStringValue("Developer.LoadFromFilesystem")
+ .getValue(null)
+ ?.let {
+ configuration.getStringValue("Developer.FilesystemPath")
+ .getValue(null)
+ ?.let { DebugLoaders(it) }
+ }
+ val eventBus = EventBus()
+
+ bind(Configuration::class.java).toInstance(configuration)
+ bind(EventBus::class.java).toInstance(eventBus)
+ bind(Boolean::class.java).annotatedWith(named("FirstStart")).toInstance(firstStart)
+ bind(Boolean::class.java).annotatedWith(named("NewConfig")).toInstance(newConfig)
+ bind(Context::class.java).toInstance(context)
+ bind(object : TypeLiteral<Optional<Context>>() {}).toInstance(Optional.of(context))
+ bind(SonePlugin::class.java).toInstance(sonePlugin)
+ bind(Version::class.java).toInstance(sonePlugin.version.parseVersion())
+ bind(PluginVersion::class.java).toInstance(PluginVersion(sonePlugin.version))
+ bind(PluginYear::class.java).toInstance(PluginYear(sonePlugin.year))
+ bind(PluginHomepage::class.java).toInstance(PluginHomepage(sonePlugin.homepage))
+ bind(Database::class.java).to(MemoryDatabase::class.java).`in`(Singleton::class.java)
+ loaders?.let { bind(Loaders::class.java).toInstance(it) }
+
+ bindListener(Matchers.any(), object : TypeListener {
+ override fun <I> hear(typeLiteral: TypeLiteral<I>, typeEncounter: TypeEncounter<I>) {
+ typeEncounter.register(InjectionListener { injectee -> eventBus.register(injectee) })
+ }
+ })
+ }
+ }
+
+}
+
+private fun String.parseVersion(): Version = Version.parse(this)
package net.pterodactylus.sone.template
-import net.pterodactylus.sone.core.LinkedElement
-import net.pterodactylus.sone.utils.asTemplate
-import net.pterodactylus.util.template.Filter
-import net.pterodactylus.util.template.TemplateContext
-import net.pterodactylus.util.template.TemplateContextFactory
-import java.io.StringWriter
-import javax.inject.Inject
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.util.template.*
+import java.io.*
/**
* Renders all kinds of [LinkedElement]s.
*/
-class LinkedElementRenderFilter @Inject constructor(private val templateContextFactory: TemplateContextFactory): Filter {
+class LinkedElementRenderFilter : Filter {
- companion object {
- private val loadedImageTemplate = """<%include linked/image.html>""".asTemplate()
- private val loadedHtmlPageTemplate = """<%include linked/html-page.html>""".asTemplate()
- private val notLoadedImageTemplate = """<%include linked/notLoaded.html>""".asTemplate()
+ private val templateContextFactory = TemplateContextFactory().apply {
+ addFilter("html", HtmlFilter())
+ addProvider(ClassPathTemplateProvider(LinkedElementRenderFilter::class.java, "/templates/"))
}
override fun format(templateContext: TemplateContext?, data: Any?, parameters: Map<String, Any?>?) =
}.toString()
}
+
+private val loadedImageTemplate = """<%include linked/image.html>""".asTemplate()
+private val loadedHtmlPageTemplate = """<%include linked/html-page.html>""".asTemplate()
+private val notLoadedImageTemplate = """<%include linked/notLoaded.html>""".asTemplate()
?.filterIsInstance<FreenetLinkPart>()
?.map { elementLoader.loadElement(it.link) }
?.filter { !it.failed }
- ?: listOf<LinkedElement>()
+ ?: listOf()
} else {
- listOf<LinkedElement>()
+ listOf()
}
private fun showLinkedImages(currentSone: Sone?, sone: Sone?): Boolean {
package net.pterodactylus.sone.template
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.text.FreemailPart
-import net.pterodactylus.sone.text.FreenetLinkPart
-import net.pterodactylus.sone.text.LinkPart
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.text.*
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.utils.asTemplate
-import net.pterodactylus.util.template.Filter
-import net.pterodactylus.util.template.TemplateContext
-import net.pterodactylus.util.template.TemplateContextFactory
-import java.io.StringWriter
-import java.io.Writer
-import java.net.URLEncoder
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.util.template.*
+import java.io.*
+import java.net.*
/**
* Renders a number of pre-parsed [Part] into a [String].
- *
- * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
*/
-class RenderFilter(private val core: Core, private val templateContextFactory: TemplateContextFactory) : Filter {
+class RenderFilter(private val soneProvider: SoneProvider, private val soneTextParser: SoneTextParser, htmlFilter: HtmlFilter) : Filter {
- companion object {
- private val plainTextTemplate = "<%text|html>".asTemplate()
- private val linkTemplate = "<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>".asTemplate()
+ private val templateContextFactory = TemplateContextFactory().apply {
+ addFilter("html", htmlFilter)
}
override fun format(templateContext: TemplateContext?, data: Any?, parameters: MutableMap<String, Any?>?): Any? {
}
private fun render(writer: Writer, postPart: PostPart) {
- val parser = SoneTextParser(core, core)
val parserContext = SoneTextParserContext(postPart.post.sone)
- val parts = parser.parse(postPart.post.text, parserContext)
+ val parts = soneTextParser.parse(postPart.post.text, parserContext)
val excerpt = StringBuilder()
for (part in parts) {
excerpt.append(part.text)
}
private fun render(writer: Writer, freemailPart: FreemailPart) {
- val sone = core.getSone(freemailPart.identityId)
+ val sone = soneProvider.getSone(freemailPart.identityId)
val soneName = sone?.let(SoneAccessor::getNiceName) ?: freemailPart.identityId
renderLink(writer,
"/Freemail/NewMessage?to=${freemailPart.identityId}",
}
}
+
+private val plainTextTemplate = "<%text|html>".asTemplate()
+private val linkTemplate = "<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>".asTemplate()
/**
* [Part] implementation that holds a single piece of text.
- *
- * @author [David Roden](mailto:d.roden@emetriq.com)
*/
data class PlainTextPart(override val text: String) : Part
package net.pterodactylus.sone.text
-import freenet.keys.FreenetURI
-import freenet.support.Base64
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.data.impl.IdOnlySone
-import net.pterodactylus.sone.database.PostProvider
-import net.pterodactylus.sone.database.SoneProvider
-import net.pterodactylus.sone.text.LinkType.CHK
-import net.pterodactylus.sone.text.LinkType.FREEMAIL
-import net.pterodactylus.sone.text.LinkType.HTTP
-import net.pterodactylus.sone.text.LinkType.HTTPS
-import net.pterodactylus.sone.text.LinkType.KSK
-import net.pterodactylus.sone.text.LinkType.POST
-import net.pterodactylus.sone.text.LinkType.SONE
-import net.pterodactylus.sone.text.LinkType.SSK
+import freenet.keys.*
+import freenet.support.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.impl.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.text.LinkType.*
import net.pterodactylus.sone.text.LinkType.USK
-import net.pterodactylus.sone.utils.let
-import org.bitpedia.util.Base32
-import java.net.MalformedURLException
+import org.bitpedia.util.*
+import java.net.*
+import javax.inject.*
/**
* [Parser] implementation that can recognize Freenet URIs.
*/
-class SoneTextParser(private val soneProvider: SoneProvider?, private val postProvider: PostProvider?) {
+class SoneTextParser @Inject constructor(private val soneProvider: SoneProvider?, private val postProvider: PostProvider?) {
fun parse(source: String, context: SoneTextParserContext?) =
source.split("\n")
}
}.map { it.first }.toList()
+ private val NextLink.linkWithoutBacklink: String
+ get() {
+ val backlink = link.indexOf("/../")
+ val query = link.indexOf("?")
+ return if ((backlink > -1) && ((query == -1) || (query > -1) && (backlink < query)))
+ link.substring(0, backlink)
+ else
+ link
+ }
+
private fun NextLink.toPart(context: SoneTextParserContext?) = when (linkType) {
KSK, CHK -> try {
- FreenetURI(link).let { freenetUri ->
+ FreenetURI(linkWithoutBacklink).let { freenetUri ->
FreenetLinkPart(
- link,
- if (freenetUri.isKSK) {
- freenetUri.guessableKey
- } else {
- freenetUri.metaString ?: freenetUri.docName ?: link.substring(0, 9)
- },
- link.split('?').first()
+ linkWithoutBacklink,
+ freenetUri.allMetaStrings?.lastOrNull { it != "" } ?: freenetUri.docName ?: linkWithoutBacklink.substring(0, 9),
+ linkWithoutBacklink.split('?').first()
)
}
} catch (e: MalformedURLException) {
- PlainTextPart(link)
+ PlainTextPart(linkWithoutBacklink)
}
SSK, USK ->
try {
- FreenetLinkPart(link, FreenetURI(link).docName, trusted = context?.routingKey?.contentEquals(FreenetURI(link).routingKey) == true)
+ FreenetURI(linkWithoutBacklink).let { uri ->
+ uri.allMetaStrings
+ ?.takeIf { (it.size > 1) || ((it.size == 1) && (it.single() != "")) }
+ ?.lastOrNull()
+ ?: uri.docName
+ ?: "${uri.keyType}@${uri.routingKey.freenetBase64}"
+ }.let { FreenetLinkPart(linkWithoutBacklink.removeSuffix("/"), it, trusted = context?.routingKey?.contentEquals(FreenetURI(linkWithoutBacklink).routingKey) == true) }
} catch (e: MalformedURLException) {
- PlainTextPart(link)
+ PlainTextPart(linkWithoutBacklink)
}
SONE -> link.substring(7).let { SonePart(soneProvider?.getSone(it) ?: IdOnlySone(it)) }
POST -> postProvider?.getPost(link.substring(7))?.let { PostPart(it) } ?: PlainTextPart(link)
private val whitespace = Regex("[\\u000a\u0020\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u2800\u3000]")
private data class NextLink(val position: Int, val linkType: LinkType, val link: String, val remainder: String)
+
+private val ByteArray.freenetBase64 get() = Base64.encode(this)!!
package net.pterodactylus.sone.utils
-fun <T> T?.asList() = this?.let(::listOf) ?: emptyList<T>()
+fun <T> T?.asList() = this?.let(::listOf) ?: emptyList()
+val Any?.unit get() = Unit
+
+fun <T> T?.throwOnNullIf(throwCondition: Boolean, exception: () -> Throwable) =
+ if (this == null && throwCondition) throw exception() else this
override fun iterator() = items.iterator()
+ fun turnTo(page: Int) = apply { this.page = page }
+
}
-fun <T> Iterable<T>.paginate(pageSize: Int) = Pagination<T>(toList(), pageSize)
+fun <T> Iterable<T>.paginate(pageSize: Int) = Pagination(toList(), pageSize)
+fun <T> Sequence<T>.paginate(pageSize: Int) = Pagination(toList(), pageSize)
--- /dev/null
+package net.pterodactylus.sone.web
+
+import freenet.clients.http.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.*
+import java.util.concurrent.atomic.*
+import javax.inject.*
+
+private const val soneMenu = "Navigation.Menu.Sone"
+private const val soneMenuName = "$soneMenu.Name"
+
+class PageToadletRegistry @Inject constructor(
+ private val pageMaker: PageMaker,
+ private val toadletContainer: ToadletContainer,
+ private val sonePlugin: SonePlugin,
+ private val pageToadletFactory: PageToadletFactory
+) {
+
+ private val pages = mutableListOf<Page<FreenetRequest>>()
+ private val registeredToadlets = mutableListOf<PageToadlet>()
+ private val registered = AtomicBoolean(false)
+
+ fun addPage(page: Page<FreenetRequest>) {
+ if (registered.get()) throw IllegalStateException()
+ pages += page
+ }
+
+ fun registerToadlets() {
+ registered.set(true)
+ pageMaker.addNavigationCategory("/Sone/index.html", soneMenuName, "$soneMenu.Tooltip", sonePlugin)
+ addPages()
+ }
+
+ private fun addPages() =
+ pages
+ .map { pageToadletFactory.createPageToadlet(it) }
+ .onEach(registeredToadlets::plusAssign)
+ .forEach { pageToadlet ->
+ if (pageToadlet.menuName == null) {
+ registerToadletWithoutMenuname(pageToadlet)
+ } else {
+ registerToadletWithMenuname(pageToadlet)
+ }
+ }
+
+ private fun registerToadletWithoutMenuname(pageToadlet: PageToadlet) =
+ toadletContainer.register(pageToadlet, null, pageToadlet.path(), true, false)
+
+ private fun registerToadletWithMenuname(pageToadlet: PageToadlet) =
+ toadletContainer.register(pageToadlet, soneMenuName, pageToadlet.path(), true, "$soneMenu.Item.${pageToadlet.menuName}.Name", "$soneMenu.Item.${pageToadlet.menuName}.Tooltip", false, pageToadlet)
+
+ fun unregisterToadlets() {
+ pageMaker.removeNavigationCategory(soneMenuName)
+ registeredToadlets.forEach(toadletContainer::unregister)
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.web
+
+import com.google.inject.*
+import freenet.l10n.*
+import freenet.support.api.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.template.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
+import javax.inject.Singleton
+
+class WebInterfaceModule : AbstractModule() {
+
+ @Provides
+ @Singleton
+ fun getTemplateContextFactory(
+ soneAccessor: SoneAccessor,
+ postAccessor: PostAccessor,
+ replyAccessor: ReplyAccessor,
+ identityAccessor: IdentityAccessor,
+ profileAccessor: ProfileAccessor,
+ l10nFilter: L10nFilter,
+ parserFilter: ParserFilter,
+ renderFilter: RenderFilter,
+ linkedElementsFilter: LinkedElementsFilter,
+ unknownDateFilter: UnknownDateFilter,
+ imageLinkFilter: ImageLinkFilter,
+ loaders: Loaders
+ ) =
+ TemplateContextFactory().apply {
+ addAccessor(Any::class.java, ReflectionAccessor())
+ addAccessor(Collection::class.java, CollectionAccessor())
+ addAccessor(Sone::class.java, soneAccessor)
+ addAccessor(Post::class.java, postAccessor)
+ addAccessor(Reply::class.java, replyAccessor)
+ addAccessor(Album::class.java, AlbumAccessor())
+ addAccessor(Image::class.java, ImageAccessor())
+ addAccessor(Identity::class.java, identityAccessor)
+ addAccessor(Trust::class.java, TrustAccessor())
+ addAccessor(HTTPRequest::class.java, HttpRequestAccessor())
+ addAccessor(Profile::class.java, profileAccessor)
+
+ addFilter("date", DateFilter())
+ addFilter("html", HtmlFilter())
+ addFilter("replace", ReplaceFilter())
+ addFilter("store", StoreFilter())
+ addFilter("l10n", l10nFilter)
+ addFilter("substring", SubstringFilter())
+ addFilter("xml", XmlFilter())
+ addFilter("change", RequestChangeFilter())
+ addFilter("match", MatchFilter())
+ addFilter("css", CssClassNameFilter())
+ addFilter("js", JavascriptFilter())
+ addFilter("parse", parserFilter)
+ addFilter("shorten", ShortenFilter())
+ addFilter("render", renderFilter)
+ addFilter("linked-elements", linkedElementsFilter)
+ addFilter("render-linked-element", LinkedElementRenderFilter())
+ addFilter("reparse", ReparseFilter())
+ addFilter("unknown", unknownDateFilter)
+ addFilter("format", FormatFilter())
+ addFilter("sort", CollectionSortFilter())
+ addFilter("image-link", imageLinkFilter)
+ addFilter("replyGroup", ReplyGroupFilter())
+ addFilter("in", ContainsFilter())
+ addFilter("unique", UniqueElementFilter())
+ addFilter("mod", ModFilter())
+ addFilter("paginate", PaginationFilter())
+
+ addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER)
+ addProvider(loaders.templateProvider)
+ }
+
+ @Provides
+ fun getSoneAccessor(core: Core, timeTextConverter: TimeTextConverter) =
+ SoneAccessor(core, timeTextConverter)
+
+ @Provides
+ fun getPostAccessor(core: Core) =
+ PostAccessor(core)
+
+ @Provides
+ fun getReplyAccessor(core: Core) =
+ ReplyAccessor(core)
+
+ @Provides
+ fun getIdentityAccessor(core: Core) =
+ IdentityAccessor(core)
+
+ @Provides
+ fun getProfileAccessor(core: Core) =
+ ProfileAccessor(core)
+
+ @Provides
+ fun getL10nFilter(l10n: BaseL10n) =
+ L10nFilter(l10n)
+
+ @Provides
+ fun getParserFilter(core: Core, soneTextParser: SoneTextParser) =
+ ParserFilter(core, soneTextParser)
+
+ @Provides
+ fun getRenderFilter(soneProvider: SoneProvider, soneTextParser: SoneTextParser, htmlFilter: HtmlFilter) =
+ RenderFilter(soneProvider, soneTextParser, htmlFilter)
+
+ @Provides
+ fun getLinkedElementsFilter(elementLoader: ElementLoader) =
+ LinkedElementsFilter(elementLoader)
+
+ @Provides
+ fun getUnknownDateFilter(l10n: BaseL10n) =
+ UnknownDateFilter(l10n, "View.Sone.Text.UnknownDate")
+
+ @Provides
+ fun getImageLinkFilter(core: Core) =
+ ImageLinkFilter(core)
+
+ @Provides
+ @Named("toadletPathPrefix")
+ fun getPathPrefix(): String = "/Sone/"
+
+}
import net.pterodactylus.sone.utils.emptyToNull
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user bookmark a post.
*/
-class BookmarkAjaxPage(webInterface: WebInterface) : JsonPage("bookmark.ajax", webInterface) {
+@ToadletPath("bookmark.ajax")
+class BookmarkAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val requiresLogin = false
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.asOptional
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.utils.headers
-import net.pterodactylus.sone.utils.let
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import javax.inject.*
/**
* AJAX handler that creates a new post.
*/
-class CreatePostAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("createPost.ajax", webInterface) {
+@ToadletPath("createPost.ajax")
+class CreatePostAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["text"].emptyToNull
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.utils.headers
-import net.pterodactylus.sone.utils.let
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import javax.inject.*
/**
* This AJAX page create a reply.
*/
-class CreateReplyAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("createReply.ajax", webInterface) {
+@ToadletPath("createReply.ajax")
+class CreateReplyAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest): JsonReturnObject =
request.parameters["post"].emptyToNull
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.ifTrue
-import net.pterodactylus.sone.utils.let
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import javax.inject.*
/**
* This AJAX page deletes a post.
*/
-class DeletePostAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("deletePost.ajax", webInterface) {
+@ToadletPath("deletePost.ajax")
+class DeletePostAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["post"]
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user delete a profile field.
*/
-class DeleteProfileFieldAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("deleteProfileField.ajax", webInterface) {
+@ToadletPath("deleteProfileField.ajax")
+class DeleteProfileFieldAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
currentSone.profile.let { profile ->
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.ifTrue
-import net.pterodactylus.sone.utils.let
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import javax.inject.*
/**
* This AJAX page deletes a reply.
*/
-class DeleteReplyAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("deleteReply.ajax", webInterface) {
+@ToadletPath("deleteReply.ajax")
+class DeleteReplyAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["reply"]
import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user dismiss a notification.
*/
-class DismissNotificationAjaxPage(webInterface: WebInterface) : JsonPage("dismissNotification.ajax", webInterface) {
+@ToadletPath("dismissNotification.ajax")
+class DismissNotificationAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val requiresLogin = false
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.let
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import javax.inject.*
/**
* AJAX page that lets the user distrust a Sone.
*
* @see Core.distrustSone(Sone, Sone)
*/
-class DistrustAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("distrustSone.ajax", webInterface) {
+@ToadletPath("distrustSone.ajax")
+class DistrustAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
import net.pterodactylus.sone.utils.ifTrue
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* Page that stores a user’s album modifications.
*/
-class EditAlbumAjaxPage(webInterface: WebInterface) : JsonPage("editAlbum.ajax", webInterface) {
+@ToadletPath("editAlbum.ajax")
+class EditAlbumAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override fun createJsonObject(request: FreenetRequest) =
request.parameters["album"]!!
import net.pterodactylus.sone.utils.ifTrue
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
import net.pterodactylus.util.template.TemplateContext
+import javax.inject.Inject
/**
* Page that stores a user’s image modifications.
*/
-class EditImageAjaxPage(webInterface: WebInterface,
+@ToadletPath("editImage.ajax")
+class EditImageAjaxPage @Inject constructor(webInterface: WebInterface,
private val parserFilter: ParserFilter,
private val shortenFilter: ShortenFilter,
- private val renderFilter: RenderFilter) : JsonPage("editImage.ajax", webInterface) {
+ private val renderFilter: RenderFilter) : JsonPage(webInterface) {
override fun createJsonObject(request: FreenetRequest) =
request.parameters["image"]
import net.pterodactylus.sone.utils.ifFalse
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user rename a profile field.
*/
-class EditProfileFieldAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("editProfileField.ajax", webInterface) {
+@ToadletPath("editProfileField.ajax")
+class EditProfileFieldAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
currentSone.profile.let { profile ->
package net.pterodactylus.sone.web.ajax
import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.also
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets a Sone follow another Sone.
*/
-class FollowSoneAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("followSone.ajax", webInterface) {
+@ToadletPath("followSone.ajax")
+class FollowSoneAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
import net.pterodactylus.sone.template.SoneAccessor
import net.pterodactylus.sone.utils.jsonArray
import net.pterodactylus.sone.utils.jsonObject
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that retrieves the number of “likes” a [net.pterodactylus.sone.data.Post]
* or [net.pterodactylus.sone.data.PostReply] has.
*/
-class GetLikesAjaxPage(webInterface: WebInterface) : JsonPage("getLikes.ajax", webInterface) {
+@ToadletPath("getLikes.ajax")
+class GetLikesAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val needsFormPassword = false
import net.pterodactylus.sone.utils.jsonArray
import net.pterodactylus.sone.utils.jsonObject
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* Renders linked elements after they have been loaded.
*/
-class GetLinkedElementAjaxPage(webInterface: WebInterface, private val elementLoader: ElementLoader, private val linkedElementRenderFilter: LinkedElementRenderFilter):
- JsonPage("getLinkedElement.ajax", webInterface) {
+@ToadletPath("getLinkedElement.ajax")
+class GetLinkedElementAjaxPage @Inject constructor(webInterface: WebInterface, private val elementLoader: ElementLoader, private val linkedElementRenderFilter: LinkedElementRenderFilter):
+ JsonPage(webInterface) {
override val needsFormPassword = false
override val requiresLogin = false
import net.pterodactylus.sone.utils.jsonArray
import net.pterodactylus.sone.utils.jsonObject
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
import net.pterodactylus.util.notify.Notification
import net.pterodactylus.util.notify.TemplateNotification
import java.io.StringWriter
+import javax.inject.Inject
/**
* AJAX handler to return all current notifications.
*/
-class GetNotificationsAjaxPage(webInterface: WebInterface) : JsonPage("getNotifications.ajax", webInterface) {
+@ToadletPath("getNotifications.ajax")
+class GetNotificationsAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val needsFormPassword = false
override val requiresLogin = false
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.jsonObject
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.utils.render
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
import net.pterodactylus.util.template.Template
+import javax.inject.Inject
/**
* This AJAX handler retrieves information and rendered representation of a [Post].
*/
-class GetPostAjaxPage(webInterface: WebInterface, private val postTemplate: Template) : LoggedInJsonPage("getPost.ajax", webInterface) {
+@ToadletPath("getPost.ajax")
+class GetPostAjaxPage @Inject constructor(webInterface: WebInterface, private val postTemplate: Template) : LoggedInJsonPage(webInterface) {
override val needsFormPassword = false
import net.pterodactylus.sone.data.PostReply
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.jsonObject
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.utils.render
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
import net.pterodactylus.util.template.Template
+import javax.inject.Inject
/**
* This AJAX page returns the details of a reply.
*/
-class GetReplyAjaxPage(webInterface: WebInterface, private val template: Template) : LoggedInJsonPage("getReply.ajax", webInterface) {
+@ToadletPath("getReply.ajax")
+class GetReplyAjaxPage @Inject constructor(webInterface: WebInterface, private val template: Template) : LoggedInJsonPage(webInterface) {
override val needsFormPassword = false
import net.pterodactylus.sone.template.SoneAccessor
import net.pterodactylus.sone.text.TimeTextConverter
import net.pterodactylus.sone.utils.jsonObject
-import net.pterodactylus.sone.utils.mapPresent
import net.pterodactylus.sone.utils.toArray
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
import java.text.SimpleDateFormat
import java.util.TimeZone
+import javax.inject.Inject
/**
* The “get status” AJAX handler returns all information that is necessary to
* update the web interface in real-time.
*/
-class GetStatusAjaxPage(webInterface: WebInterface, private val elementLoader: ElementLoader, private val timeTextConverter: TimeTextConverter, private val l10nFilter: L10nFilter, timeZone: TimeZone = TimeZone.getDefault()):
- JsonPage("getStatus.ajax", webInterface) {
+@ToadletPath("getStatus.ajax")
+class GetStatusAjaxPage(webInterface: WebInterface, private val elementLoader: ElementLoader, private val timeTextConverter: TimeTextConverter, private val l10nFilter: L10nFilter, timeZone: TimeZone):
+ JsonPage(webInterface) {
+
+ @Inject constructor(webInterface: WebInterface, elementLoader: ElementLoader, timeTextConverter: TimeTextConverter, l10nFilter: L10nFilter):
+ this(webInterface, elementLoader, timeTextConverter, l10nFilter, TimeZone.getDefault())
private val dateFormatter = SimpleDateFormat("MMM d, yyyy, HH:mm:ss").apply {
this.timeZone = timeZone
import net.pterodactylus.sone.utils.jsonObject
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
import java.text.SimpleDateFormat
import java.util.TimeZone
+import javax.inject.Inject
/**
* Ajax page that returns a formatted, relative timestamp for replies or posts.
*/
-class GetTimesAjaxPage(webInterface: WebInterface,
+@ToadletPath("getTimes.ajax")
+class GetTimesAjaxPage @Inject constructor(webInterface: WebInterface,
private val timeTextConverter: TimeTextConverter,
private val l10nFilter: L10nFilter,
- timeZone: TimeZone) : JsonPage("getTimes.ajax", webInterface) {
+ timeZone: TimeZone) : JsonPage(webInterface) {
private val dateTimeFormatter = SimpleDateFormat("MMM d, yyyy, HH:mm:ss").apply {
this.timeZone = timeZone
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* Returns the translation for a given key as JSON object.
*/
-class GetTranslationAjaxPage(webInterface: WebInterface) : JsonPage("getTranslation.ajax", webInterface) {
+@ToadletPath("getTranslation.ajax")
+class GetTranslationAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val needsFormPassword = false
override val requiresLogin = false
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.SessionProvider
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
import net.pterodactylus.util.web.Page
import net.pterodactylus.util.web.Response
import java.io.ByteArrayOutputStream
* A JSON page is a specialized [Page] that will always return a JSON
* object to the browser, e.g. for use with AJAX or other scripting frameworks.
*/
-abstract class JsonPage(private val path: String, protected val webInterface: WebInterface) : Page<FreenetRequest> {
+abstract class JsonPage(protected val webInterface: WebInterface) : Page<FreenetRequest> {
private val objectMapper = ObjectMapper()
private val sessionProvider: SessionProvider = webInterface
protected val core = webInterface.core
- override fun getPath() = path
+ override fun getPath() = toadletPath
override fun isPrefixPage() = false
open val needsFormPassword = true
sessionProvider.getCurrentSone(toadletContext, createSession)
override fun handleRequest(request: FreenetRequest, response: Response): Response {
- if (core.preferences.isRequireFullAccess && !request.toadletContext.isAllowedFullAccess) {
+ if (core.preferences.requireFullAccess && !request.toadletContext.isAllowedFullAccess) {
return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(createErrorJsonObject("auth-required").asJsonString())
}
if (needsFormPassword && request.parameters["formPassword"] != webInterface.formPassword) {
package net.pterodactylus.sone.web.ajax
import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user like a [net.pterodactylus.sone.data.Post].
*/
-class LikeAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("like.ajax", webInterface) {
+@ToadletPath("like.ajax")
+class LikeAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
when (request.parameters["type"]) {
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* Lets the user [lock][net.pterodactylus.sone.core.Core.lockSone] a [Sone][net.pterodactylus.sone.data.Sone].
*/
-class LockSoneAjaxPage(webInterface: WebInterface) : JsonPage("lockSone.ajax", webInterface) {
+@ToadletPath("lockSone.ajax")
+class LockSoneAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val requiresLogin = false
/**
* Base JSON page for all pages that require the user to be logged in.
*/
-open class LoggedInJsonPage(path: String, webInterface: WebInterface) : JsonPage(path, webInterface) {
+open class LoggedInJsonPage(webInterface: WebInterface) : JsonPage(webInterface) {
final override val requiresLogin = true
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user mark a number of [net.pterodactylus.sone.data.Sone]s,
* [net.pterodactylus.sone.data.Post]s, or [net.pterodactylus.sone.data.Reply]s as known.
*/
-class MarkAsKnownAjaxPage(webInterface: WebInterface) : JsonPage("markAsKnown.ajax", webInterface) {
+@ToadletPath("markAsKnown.ajax")
+class MarkAsKnownAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val requiresLogin = false
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user move a profile field up or down.
* @see net.pterodactylus.sone.data.Profile#moveFieldUp(Field)
* @see net.pterodactylus.sone.data.Profile#moveFieldDown(Field)
*/
-class MoveProfileFieldAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("moveProfileField.ajax", webInterface) {
+@ToadletPath("moveProfileField.ajax")
+class MoveProfileFieldAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
currentSone.profile.let { profile ->
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.let
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import javax.inject.*
/**
* AJAX page that lets the user trust a Sone.
*
* @see net.pterodactylus.sone.core.Core.trustSone
*/
-class TrustAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("trustSone.ajax", webInterface) {
+@ToadletPath("trustSone.ajax")
+class TrustAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.utils.also
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user unbookmark a post.
*/
-class UnbookmarkAjaxPage(webInterface: WebInterface) : JsonPage("unbookmark.ajax", webInterface) {
+@ToadletPath("unbookmark.ajax")
+class UnbookmarkAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val requiresLogin = false
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets a Sone unfollow another Sone.
*/
-class UnfollowSoneAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("unfollowSone.ajax", webInterface) {
+@ToadletPath("unfollowSone.ajax")
+class UnfollowSoneAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
import net.pterodactylus.sone.utils.emptyToNull
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user unlike a [net.pterodactylus.sone.data.Post].
*/
-class UnlikeAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("unlike.ajax", webInterface) {
+@ToadletPath("unlike.ajax")
+class UnlikeAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) = when (request.parameters["type"]) {
"post" -> request.processEntity("post", currentSone::removeLikedPostId)
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* Lets the user [unlock][net.pterodactylus.sone.core.Core.unlockSone] a [Sone][net.pterodactylus.sone.data.Sone].
*/
-class UnlockSoneAjaxPage(webInterface: WebInterface) : JsonPage("unlockSone.ajax", webInterface) {
+@ToadletPath("unlockSone.ajax")
+class UnlockSoneAjaxPage @Inject constructor(webInterface: WebInterface) : JsonPage(webInterface) {
override val requiresLogin = false
package net.pterodactylus.sone.web.ajax
import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.also
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.web.page.*
+import javax.inject.Inject
/**
* AJAX page that lets the user [untrust][net.pterodactylus.sone.core.Core.untrustSone] a [Sone].
*/
-class UntrustAjaxPage(webInterface: WebInterface) : LoggedInJsonPage("untrustSone.ajax", webInterface) {
+@ToadletPath("untrustSone.ajax")
+class UntrustAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import net.pterodactylus.util.web.*
+
+annotation class MenuName(val value: String)
+
+val Page<*>.menuName get() = javaClass.getAnnotation(MenuName::class.java)?.value
+
+annotation class TemplatePath(val value: String)
+
+val Page<*>.templatePath get() = javaClass.getAnnotation(TemplatePath::class.java)?.value
+
+annotation class ToadletPath(val value: String)
+
+val Page<*>.toadletPath get() = javaClass.getAnnotation(ToadletPath::class.java)?.value
--- /dev/null
+/*
+ * Sone - FreenetPage.kt - Copyright © 2011–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.page
+
+import net.pterodactylus.util.web.*
+import java.net.*
+
+interface FreenetPage : Page<FreenetRequest> {
+
+ fun isLinkExcepted(link: URI): Boolean
+
+}
--- /dev/null
+/*
+ * Sone - FreenetRequest.kt - Copyright © 2011–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.page
+
+import freenet.clients.http.*
+import freenet.clients.http.SessionManager.*
+import freenet.l10n.*
+import freenet.support.api.*
+import net.pterodactylus.util.web.*
+import java.net.*
+import java.util.UUID.*
+
+open class FreenetRequest(uri: URI, method: Method,
+ val httpRequest: HTTPRequest,
+ val toadletContext: ToadletContext,
+ val l10n: BaseL10n,
+ val sessionManager: SessionManager
+) : Request(uri, method) {
+
+ val session: Session
+ get() =
+ sessionManager.useSession(toadletContext)
+ ?: sessionManager.createSession(randomUUID().toString(), toadletContext)
+
+ val existingSession: Session? get() = sessionManager.useSession(toadletContext)
+
+}
--- /dev/null
+/*
+ * Sone - FreenetTemplatePage.kt - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.page
+
+import freenet.clients.http.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.util.template.*
+import net.pterodactylus.util.web.*
+import java.lang.String.*
+import java.net.*
+import java.util.logging.*
+import java.util.logging.Logger.*
+
+/**
+ * Base class for all [Page]s that are rendered with [Template]s and
+ * fit into Freenet’s web interface.
+ */
+open class FreenetTemplatePage(
+ private val templateRenderer: TemplateRenderer,
+ loaders: Loaders,
+ private val invalidFormPasswordRedirectTarget: String
+) : FreenetPage, LinkEnabledCallback {
+
+ private val pageMakerInteractionFactory: PageMakerInteractionFactory = DefaultPageMakerInteractionFactory()
+ open val styleSheets: Collection<String> = emptySet()
+ open val shortcutIcon: String? get() = null
+ open val isFullAccessOnly get() = false
+
+ override fun getPath() = toadletPath
+
+ open fun getPageTitle(request: FreenetRequest) = ""
+
+ override fun isPrefixPage() = false
+
+ open fun getRedirectTarget(request: FreenetRequest): String? = null
+
+ open fun getAdditionalLinkNodes(request: FreenetRequest): List<Map<String, String>> = emptyList()
+
+ override fun isLinkExcepted(link: URI) = false
+
+ override fun isEnabled(toadletContext: ToadletContext) = !isFullAccessOnly
+
+ private val template = templatePath?.let(loaders::loadTemplate) ?: Template()
+
+ override fun handleRequest(request: FreenetRequest, response: Response): Response {
+ getRedirectTarget(request)?.let { redirectTarget -> return RedirectResponse(redirectTarget) }
+
+ if (isFullAccessOnly && !request.toadletContext.isAllowedFullAccess) {
+ return response.setStatusCode(401).setStatusText("Not authorized").setContentType("text/html")
+ }
+ val toadletContext = request.toadletContext
+ if (request.method == Method.POST) {
+ /* require form password. */
+ val formPassword = request.httpRequest.getPartAsStringFailsafe("formPassword", 32)
+ if (formPassword != toadletContext.container.formPassword) {
+ return RedirectResponse(invalidFormPasswordRedirectTarget)
+ }
+ }
+
+ val pageMakerInteraction = pageMakerInteractionFactory.createPageMaker(toadletContext, getPageTitle(request))
+ styleSheets.forEach(pageMakerInteraction::addStyleSheet)
+ getAdditionalLinkNodes(request).forEach(pageMakerInteraction::addLinkNode)
+ shortcutIcon?.let(pageMakerInteraction::addShortcutIcon)
+
+ val output = try {
+ val start = System.nanoTime()
+ templateRenderer.render(template) { templateContext ->
+ processTemplate(request, templateContext)
+ }.also {
+ val finish = System.nanoTime()
+ logger.log(Level.FINEST, format("Template was rendered in %.2fms.", (finish - start) / 1000000.0))
+ }
+ } catch (re1: RedirectException) {
+ return RedirectResponse(re1.target ?: "")
+ }
+
+ pageMakerInteraction.setContent(output)
+
+ return response.setStatusCode(200).setStatusText("OK").setContentType("text/html").write(pageMakerInteraction.renderPage())
+ }
+
+ open fun processTemplate(request: FreenetRequest, templateContext: TemplateContext) {
+ /* do nothing. */
+ }
+
+ class RedirectException(val target: String?) : Exception() {
+ override fun toString(): String = format("RedirectException{target='%s'}", target)
+ }
+
+}
+
+private val logger: Logger = getLogger(FreenetTemplatePage::class.java.name)
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import freenet.clients.http.*
+
+class PageMakerInteraction(toadletContext: ToadletContext, pageTitle: String) {
+
+ private val pageMaker: PageMaker = toadletContext.pageMaker
+ private val pageNode: PageNode = pageMaker.getPageNode(pageTitle, toadletContext)
+
+ fun addStyleSheet(styleSheet: String) {
+ pageNode.addCustomStyleSheet(styleSheet)
+ }
+
+ fun addLinkNode(linkAttributes: Map<String, String>) {
+ pageNode.headNode.addChild("link").let {
+ linkAttributes.forEach(it::addAttribute)
+ }
+ }
+
+ fun addShortcutIcon(shortcutIcon: String) {
+ pageNode.addForwardLink("icon", shortcutIcon)
+ }
+
+ fun setContent(content: String) {
+ pageNode.content.addChild("%", content)
+ }
+
+ fun renderPage(): String =
+ pageNode.outer.generate()
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import com.google.inject.*
+import freenet.clients.http.*
+
+@ImplementedBy(DefaultPageMakerInteractionFactory::class)
+interface PageMakerInteractionFactory {
+
+ fun createPageMaker(toadletContext: ToadletContext, pageTitle: String): PageMakerInteraction
+
+}
+
+class DefaultPageMakerInteractionFactory : PageMakerInteractionFactory {
+
+ override fun createPageMaker(toadletContext: ToadletContext, pageTitle: String) =
+ PageMakerInteraction(toadletContext, pageTitle)
+
+}
--- /dev/null
+/*
+ * Sone - PageToadletFactory.kt - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web.page
+
+import freenet.client.*
+import freenet.clients.http.*
+import net.pterodactylus.util.web.*
+import javax.inject.*
+
+class PageToadletFactory @Inject constructor(
+ private val highLevelSimpleClient: HighLevelSimpleClient,
+ private val sessionManager: SessionManager,
+ @Named("toadletPathPrefix") private val pathPrefix: String
+) {
+
+ @JvmOverloads
+ fun createPageToadlet(page: Page<FreenetRequest>, menuName: String? = null) =
+ PageToadlet(highLevelSimpleClient, sessionManager, menuName ?: page.menuName, page, pathPrefix)
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import freenet.clients.http.*
+import freenet.l10n.*
+import freenet.support.api.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.*
+import java.net.*
+
+class SoneRequest(uri: URI, method: Method, httpRequest: HTTPRequest, toadletContext: ToadletContext, l10n: BaseL10n, sessionManager: SessionManager,
+ val core: Core,
+ val webInterface: WebInterface
+) : FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager)
+
+fun FreenetRequest.toSoneRequest(core: Core, webInterface: WebInterface) = SoneRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager, core, webInterface)
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import net.pterodactylus.util.template.*
+import java.io.*
+import javax.inject.*
+
+class TemplateRenderer @Inject constructor(private val templateContextFactory: TemplateContextFactory) {
+
+ fun render(template: Template, processor: (TemplateContext) -> Unit = {}): String =
+ templateContextFactory.createTemplateContext().let { templateContext ->
+ templateContext.mergeContext(template.initialContext)
+ processor(templateContext)
+ StringWriter().also { template.render(templateContext, it) }.toString()
+ }
+
+}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.main.SonePlugin.PluginHomepage
-import net.pterodactylus.sone.main.SonePlugin.PluginVersion
-import net.pterodactylus.sone.main.SonePlugin.PluginYear
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* A [SoneTemplatePage] that stores information about Sone in the [TemplateContext].
*/
-class AboutPage(template: Template, webInterface: WebInterface,
+@MenuName("About")
+@TemplatePath("/templates/about.html")
+@ToadletPath("about.html")
+class AboutPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer,
private val pluginVersion: PluginVersion,
private val pluginYear: PluginYear,
- private val pluginHomepage: PluginHomepage): SoneTemplatePage("about.html", template, "Page.About.Title", webInterface, false) {
+ private val pluginHomepage: PluginHomepage) : SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.About.Title") {
override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
templateContext["version"] = pluginVersion.version
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user bookmark a post.
*/
-class BookmarkPage(template: Template, webInterface: WebInterface)
- : SoneTemplatePage("bookmark.html", template, "Page.Bookmark.Title", webInterface) {
+@ToadletPath("bookmark.html")
+class BookmarkPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer)
+ : SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.Bookmark.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
- val postId = freenetRequest.httpRequest.getPartAsStringFailsafe("post", 36)
- webInterface.core.getPost(postId)?.let {
- webInterface.core.bookmarkPost(it)
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
+ val postId = soneRequest.httpRequest.getPartAsStringFailsafe("post", 36)
+ soneRequest.core.getPost(postId)?.let {
+ soneRequest.core.bookmarkPost(it)
}
throw RedirectException(returnPage)
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user browse all his bookmarked posts.
*/
-class BookmarksPage(template: Template, webInterface: WebInterface): SoneTemplatePage("bookmarks.html", template, "Page.Bookmarks.Title", webInterface) {
+@MenuName("Bookmarks")
+@TemplatePath("/templates/bookmarks.html")
+@ToadletPath("bookmarks.html")
+class BookmarksPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.Bookmarks.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- webInterface.core.bookmarkedPosts.let { posts ->
- val pagination = Pagination<Post>(posts.filter { it.isLoaded }.sortedByDescending { it.time }, webInterface.core.preferences.postsPerPage)
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ soneRequest.core.bookmarkedPosts.let { posts ->
+ val pagination = posts.filter(Post::isLoaded).sortedByDescending { it.time }.paginate(soneRequest.core.preferences.postsPerPage)
templateContext["pagination"] = pagination
templateContext["posts"] = pagination.items
templateContext["postsNotLoaded"] = posts.any { !it.isLoaded }
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Album.Modifier.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user create a new album.
*/
-class CreateAlbumPage(template: Template, webInterface: WebInterface):
- LoggedInPage("createAlbum.html", template, "Page.CreateAlbum.Title", webInterface) {
+@TemplatePath("/templates/createAlbum.html")
+@ToadletPath("createAlbum.html")
+class CreateAlbumPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.CreateAlbum.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val name = freenetRequest.httpRequest.getPartAsStringFailsafe("name", 64).trim()
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val name = soneRequest.httpRequest.getPartAsStringFailsafe("name", 64).trim()
if (name.isEmpty()) {
templateContext["nameMissing"] = true
return
}
- val description = freenetRequest.httpRequest.getPartAsStringFailsafe("description", 256).trim()
- val parentId = freenetRequest.httpRequest.getPartAsStringFailsafe("parent", 36)
- val parent = if (parentId == "") currentSone.rootAlbum else webInterface.core.getAlbum(parentId)
- val album = webInterface.core.createAlbum(currentSone, parent)
+ val description = soneRequest.httpRequest.getPartAsStringFailsafe("description", 256).trim()
+ val parentId = soneRequest.httpRequest.getPartAsStringFailsafe("parent", 36)
+ val parent = if (parentId == "") currentSone.rootAlbum else soneRequest.core.getAlbum(parentId)
+ val album = soneRequest.core.createAlbum(currentSone, parent)
try {
album.modify().apply {
setTitle(name)
- setDescription(TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), description))
+ setDescription(TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), description))
}.update()
} catch (e: AlbumTitleMustNotBeEmpty) {
throw RedirectException("emptyAlbumTitle.html")
}
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
throw RedirectException("imageBrowser.html?album=${album.id}")
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.asOptional
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user create a new [Post].
*/
-class CreatePostPage(template: Template, webInterface: WebInterface):
- LoggedInPage("createPost.html", template, "Page.CreatePost.Title", webInterface) {
+@TemplatePath("/templates/createPost.html")
+@ToadletPath("createPost.html")
+class CreatePostPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.CreatePost.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
templateContext["returnPage"] = returnPage
- if (freenetRequest.isPOST) {
- val text = freenetRequest.httpRequest.getPartAsStringFailsafe("text", 65536).trim()
+ if (soneRequest.isPOST) {
+ val text = soneRequest.httpRequest.getPartAsStringFailsafe("text", 65536).trim()
if (text == "") {
templateContext["errorTextEmpty"] = true
return
}
- val sender = webInterface.core.getLocalSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone
- val recipient = webInterface.core.getSone(freenetRequest.httpRequest.getPartAsStringFailsafe("recipient", 43))
- webInterface.core.createPost(sender, recipient.asOptional(), TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), text))
+ val sender = soneRequest.core.getLocalSone(soneRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone
+ val recipient = soneRequest.core.getSone(soneRequest.httpRequest.getPartAsStringFailsafe("recipient", 43))
+ soneRequest.core.createPost(sender, recipient.asOptional(), TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), text))
throw RedirectException(returnPage)
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user post a reply to a post.
*/
-class CreateReplyPage(template: Template, webInterface: WebInterface):
- LoggedInPage("createReply.html", template, "Page.CreateReply.Title", webInterface) {
+@TemplatePath("/templates/createReply.html")
+@ToadletPath("createReply.html")
+class CreateReplyPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.CreateReply.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- val postId = freenetRequest.httpRequest.getPartAsStringFailsafe("post", 36).apply { templateContext["postId"] = this }
- val text = freenetRequest.httpRequest.getPartAsStringFailsafe("text", 65536).trim().apply { templateContext["text"] = this }
- val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256).apply { templateContext["returnPage"] = this }
- if (freenetRequest.isPOST) {
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ val postId = soneRequest.httpRequest.getPartAsStringFailsafe("post", 36).apply { templateContext["postId"] = this }
+ val text = soneRequest.httpRequest.getPartAsStringFailsafe("text", 65536).trim().apply { templateContext["text"] = this }
+ val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256).apply { templateContext["returnPage"] = this }
+ if (soneRequest.isPOST) {
if (text == "") {
templateContext["errorTextEmpty"] = true
return
}
- val post = webInterface.core.getPost(postId) ?: throw RedirectException("noPermission.html")
- val sender = webInterface.core.getLocalSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone
- webInterface.core.createReply(sender, post, TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), text))
+ val post = soneRequest.core.getPost(postId) ?: throw RedirectException("noPermission.html")
+ val sender = soneRequest.core.getLocalSone(soneRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone
+ soneRequest.core.createReply(sender, post, TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), text))
throw RedirectException(returnPage)
}
}
package net.pterodactylus.sone.web.pages
-import freenet.clients.http.ToadletContext
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import java.util.logging.Level
-import java.util.logging.Logger
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import java.util.logging.*
+import javax.inject.*
/**
* The “create Sone” page lets the user create a new Sone.
*/
-class CreateSonePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("createSone.html", template, "Page.CreateSone.Title", webInterface, false) {
+@MenuName("CreateSone")
+@TemplatePath("/templates/createSone.html")
+@ToadletPath("createSone.html")
+class CreateSonePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.CreateSone.Title") {
private val logger = Logger.getLogger(CreateSonePage::class.java.name)
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- templateContext["sones"] = webInterface.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR)
- templateContext["identitiesWithoutSone"] = webInterface.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}".toLowerCase() }
- if (freenetRequest.isPOST) {
- val identity = freenetRequest.httpRequest.getPartAsStringFailsafe("identity", 43)
- webInterface.core.identityManager.allOwnIdentities.firstOrNull { it.id == identity }?.let { ownIdentity ->
- val sone = webInterface.core.createSone(ownIdentity)
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ templateContext["sones"] = soneRequest.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR)
+ templateContext["identitiesWithoutSone"] = soneRequest.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}".toLowerCase() }
+ if (soneRequest.isPOST) {
+ val identity = soneRequest.httpRequest.getPartAsStringFailsafe("identity", 43)
+ soneRequest.core.identityManager.allOwnIdentities.firstOrNull { it.id == identity }?.let { ownIdentity ->
+ val sone = soneRequest.core.createSone(ownIdentity)
if (sone == null) {
logger.log(Level.SEVERE, "Could not create Sone for OwnIdentity: $ownIdentity")
}
- setCurrentSone(freenetRequest.toadletContext, sone)
+ setCurrentSone(soneRequest.toadletContext, sone)
throw RedirectException("index.html")
}
templateContext["errorNoIdentity"] = true
}
}
- override fun isEnabled(toadletContext: ToadletContext) =
- if (webInterface.core.preferences.isRequireFullAccess && !toadletContext.isAllowedFullAccess) {
+ override fun isEnabled(soneRequest: SoneRequest) =
+ if (soneRequest.core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess) {
false
} else {
- (getCurrentSone(toadletContext) == null) || (webInterface.core.localSones.size == 1)
+ (getCurrentSone(soneRequest.toadletContext) == null) || (soneRequest.core.localSones.size == 1)
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user delete an {@link Album}.
*/
-class DeleteAlbumPage(template: Template, webInterface: WebInterface):
- LoggedInPage("deleteAlbum.html", template, "Page.DeleteAlbum.Title", webInterface) {
+@TemplatePath("/templates/deleteAlbum.html")
+@ToadletPath("deleteAlbum.html")
+class DeleteAlbumPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.DeleteAlbum.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val album = webInterface.core.getAlbum(freenetRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html")
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html")
if (!album.sone.isLocal) {
throw RedirectException("noPermission.html")
}
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("abortDelete", 4) == "true") {
+ if (soneRequest.httpRequest.getPartAsStringFailsafe("abortDelete", 4) == "true") {
throw RedirectException("imageBrowser.html?album=${album.id}")
}
- webInterface.core.deleteAlbum(album)
+ soneRequest.core.deleteAlbum(album)
throw RedirectException(if (album.parent.isRoot) "imageBrowser.html?sone=${album.sone.id}" else "imageBrowser.html?album=${album.parent.id}")
}
- val album = webInterface.core.getAlbum(freenetRequest.httpRequest.getParam("album"))
+ val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getParam("album"))
templateContext["album"] = album ?: throw RedirectException("invalid.html")
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user delete an {@link Image}.
*/
-class DeleteImagePage(template: Template, webInterface: WebInterface):
- LoggedInPage("deleteImage.html", template, "Page.DeleteImage.Title", webInterface) {
+@TemplatePath("/templates/deleteImage.html")
+@ToadletPath("deleteImage.html")
+class DeleteImagePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.DeleteImage.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val image = webInterface.core.getImage(freenetRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html")
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val image = soneRequest.core.getImage(soneRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html")
if (!image.sone.isLocal) {
throw RedirectException("noPermission.html")
}
- if (freenetRequest.httpRequest.isPartSet("abortDelete")) {
+ if (soneRequest.httpRequest.isPartSet("abortDelete")) {
throw RedirectException("imageBrowser.html?image=${image.id}")
}
- webInterface.core.deleteImage(image)
+ soneRequest.core.deleteImage(image)
throw RedirectException("imageBrowser.html?album=${image.album.id}")
}
- val image = webInterface.core.getImage(freenetRequest.httpRequest.getParam("image")) ?: throw RedirectException("invalid.html")
+ val image = soneRequest.core.getImage(soneRequest.httpRequest.getParam("image")) ?: throw RedirectException("invalid.html")
if (!image.sone.isLocal) {
throw RedirectException("noPermission.html")
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Lets the user delete a post they made.
*/
-class DeletePostPage(template: Template, webInterface: WebInterface):
- LoggedInPage("deletePost.html", template, "Page.DeletePost.Title", webInterface) {
+@TemplatePath("/templates/deletePost.html")
+@ToadletPath("deletePost.html")
+class DeletePostPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.DeletePost.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val post = webInterface.core.getPost(freenetRequest.httpRequest.getPartAsStringFailsafe("post", 36)) ?: throw RedirectException("noPermission.html")
- val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val post = soneRequest.core.getPost(soneRequest.httpRequest.getPartAsStringFailsafe("post", 36)) ?: throw RedirectException("noPermission.html")
+ val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
if (!post.sone.isLocal) {
throw RedirectException("noPermission.html")
}
- if (freenetRequest.httpRequest.isPartSet("confirmDelete")) {
- webInterface.core.deletePost(post)
+ if (soneRequest.httpRequest.isPartSet("confirmDelete")) {
+ soneRequest.core.deletePost(post)
throw RedirectException(returnPage)
- } else if (freenetRequest.httpRequest.isPartSet("abortDelete")) {
+ } else if (soneRequest.httpRequest.isPartSet("abortDelete")) {
throw RedirectException(returnPage)
}
templateContext["post"] = post
templateContext["returnPage"] = returnPage
return
}
- templateContext["post"] = webInterface.core.getPost(freenetRequest.httpRequest.getParam("post")) ?: throw RedirectException("noPermission.html")
- templateContext["returnPage"] = freenetRequest.httpRequest.getParam("returnPage")
+ templateContext["post"] = soneRequest.core.getPost(soneRequest.httpRequest.getParam("post")) ?: throw RedirectException("noPermission.html")
+ templateContext["returnPage"] = soneRequest.httpRequest.getParam("returnPage")
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user confirm the deletion of a profile field.
*/
-class DeleteProfileFieldPage(template: Template, webInterface: WebInterface):
- LoggedInPage("deleteProfileField.html", template, "Page.DeleteProfileField.Title", webInterface) {
+@TemplatePath("/templates/deleteProfileField.html")
+@ToadletPath("deleteProfileField.html")
+class DeleteProfileFieldPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.DeleteProfileField.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val field = currentSone.profile.getFieldById(freenetRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html")
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("confirm", 4) == "true") {
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val field = currentSone.profile.getFieldById(soneRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html")
+ if (soneRequest.httpRequest.getPartAsStringFailsafe("confirm", 4) == "true") {
currentSone.profile = currentSone.profile.apply { removeField(field) }
}
throw RedirectException("editProfile.html#profile-fields")
}
- val field = currentSone.profile.getFieldById(freenetRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html")
+ val field = currentSone.profile.getFieldById(soneRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html")
templateContext["field"] = field
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user delete a reply.
*/
-class DeleteReplyPage(template: Template, webInterface: WebInterface):
- LoggedInPage("deleteReply.html", template, "Page.DeleteReply.Title", webInterface) {
+@TemplatePath("/templates/deleteReply.html")
+@ToadletPath("deleteReply.html")
+class DeleteReplyPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.DeleteReply.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val replyId = freenetRequest.httpRequest.getPartAsStringFailsafe("reply", 36)
- val reply = webInterface.core.getPostReply(replyId) ?: throw RedirectException("noPermission.html")
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val replyId = soneRequest.httpRequest.getPartAsStringFailsafe("reply", 36)
+ val reply = soneRequest.core.getPostReply(replyId) ?: throw RedirectException("noPermission.html")
if (!reply.sone.isLocal) {
throw RedirectException("noPermission.html")
}
- val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
- if (freenetRequest.httpRequest.isPartSet("confirmDelete")) {
- webInterface.core.deleteReply(reply)
+ val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
+ if (soneRequest.httpRequest.isPartSet("confirmDelete")) {
+ soneRequest.core.deleteReply(reply)
throw RedirectException(returnPage)
}
- if (freenetRequest.httpRequest.isPartSet("abortDelete")) {
+ if (soneRequest.httpRequest.isPartSet("abortDelete")) {
throw RedirectException(returnPage)
}
templateContext["reply"] = replyId
templateContext["returnPage"] = returnPage
return
}
- templateContext["reply"] = freenetRequest.httpRequest.getParam("reply")
- templateContext["returnPage"] = freenetRequest.httpRequest.getParam("returnPage")
+ templateContext["reply"] = soneRequest.httpRequest.getParam("reply")
+ templateContext["returnPage"] = soneRequest.httpRequest.getParam("returnPage")
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Lets the user delete a Sone. Of course the Sone is not really deleted from
* Freenet; merely all references to it are removed from the local plugin
* installation.
*/
-class DeleteSonePage(template: Template, webInterface: WebInterface):
- LoggedInPage("deleteSone.html", template, "Page.DeleteSone.Title", webInterface) {
+@MenuName("DeleteSone")
+@TemplatePath("/templates/deleteSone.html")
+@ToadletPath("deleteSone.html")
+class DeleteSonePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.DeleteSone.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- if (freenetRequest.httpRequest.isPartSet("deleteSone")) {
- webInterface.core.deleteSone(currentSone)
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ if (soneRequest.httpRequest.isPartSet("deleteSone")) {
+ soneRequest.core.deleteSone(currentSone)
}
throw RedirectException("index.html")
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user dismiss a notification.
*/
-class DismissNotificationPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("dismissNotification.html", template, "Page.DismissNotification.Title", webInterface) {
+@ToadletPath("dismissNotification.html")
+class DismissNotificationPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.DismissNotification.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
- val notificationId = freenetRequest.httpRequest.getPartAsStringFailsafe("notification", 36)
- webInterface.getNotification(notificationId).orNull()?.takeIf { it.isDismissable }?.dismiss()
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
+ val notificationId = soneRequest.httpRequest.getPartAsStringFailsafe("notification", 36)
+ soneRequest.webInterface.getNotification(notificationId).orNull()?.takeIf { it.isDismissable }?.dismiss()
throw RedirectException(returnPage)
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user distrust another Sone. This will assign a
*
* @see net.pterodactylus.sone.core.Core#distrustSone(Sone, Sone)
*/
-class DistrustPage(template: Template, webInterface: WebInterface):
- LoggedInPage("distrust.html", template, "Page.Distrust.Title", webInterface) {
+@ToadletPath("distrust.html")
+class DistrustPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.Distrust.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- webInterface.core.getSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sone", 44))
- ?.run { webInterface.core.distrustSone(currentSone, this) }
- throw RedirectException(freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256))
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ soneRequest.core.getSone(soneRequest.httpRequest.getPartAsStringFailsafe("sone", 44))
+ ?.run { soneRequest.core.distrustSone(currentSone, this) }
+ throw RedirectException(soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256))
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Album.Modifier.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user edit the name and description of an album.
*/
-class EditAlbumPage(template: Template, webInterface: WebInterface):
- LoggedInPage("editAlbum.html", template, "Page.EditAlbum.Title", webInterface) {
+@ToadletPath("editAlbum.html")
+class EditAlbumPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.EditAlbum.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val album = webInterface.core.getAlbum(freenetRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html")
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html")
album.takeUnless { it.sone.isLocal }?.run { throw RedirectException("noPermission.html") }
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("moveLeft", 4) == "true") {
+ if (soneRequest.httpRequest.getPartAsStringFailsafe("moveLeft", 4) == "true") {
album.parent?.moveAlbumUp(album)
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
throw RedirectException("imageBrowser.html?album=${album.parent?.id}")
- } else if (freenetRequest.httpRequest.getPartAsStringFailsafe("moveRight", 4) == "true") {
+ } else if (soneRequest.httpRequest.getPartAsStringFailsafe("moveRight", 4) == "true") {
album.parent?.moveAlbumDown(album)
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
throw RedirectException("imageBrowser.html?album=${album.parent?.id}")
} else {
try {
album.modify()
- .setTitle(freenetRequest.httpRequest.getPartAsStringFailsafe("title", 100))
- .setDescription(freenetRequest.httpRequest.getPartAsStringFailsafe("description", 1000))
+ .setTitle(soneRequest.httpRequest.getPartAsStringFailsafe("title", 100))
+ .setDescription(soneRequest.httpRequest.getPartAsStringFailsafe("description", 1000))
.update()
} catch (e: AlbumTitleMustNotBeEmpty) {
throw RedirectException("emptyAlbumTitle.html")
}
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
throw RedirectException("imageBrowser.html?album=${album.id}")
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Image.Modifier.ImageTitleMustNotBeEmpty
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Image.Modifier.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user edit title and description of an {@link Image}.
*/
-class EditImagePage(template: Template, webInterface: WebInterface):
- LoggedInPage("editImage.html", template, "Page.EditImage.Title", webInterface) {
+@ToadletPath("editImage.html")
+class EditImagePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.EditImage.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val image = webInterface.core.getImage(freenetRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html")
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val image = soneRequest.core.getImage(soneRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html")
if (!image.sone.isLocal) {
throw RedirectException("noPermission.html")
}
- freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256).let { returnPage ->
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("moveLeft", 4) == "true") {
+ soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256).let { returnPage ->
+ if (soneRequest.httpRequest.getPartAsStringFailsafe("moveLeft", 4) == "true") {
image.album.moveImageUp(image)
- webInterface.core.touchConfiguration()
- } else if (freenetRequest.httpRequest.getPartAsStringFailsafe("moveRight", 4) == "true") {
+ soneRequest.core.touchConfiguration()
+ } else if (soneRequest.httpRequest.getPartAsStringFailsafe("moveRight", 4) == "true") {
image.album.moveImageDown(image)
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
} else {
try {
image.modify()
- .setTitle(freenetRequest.httpRequest.getPartAsStringFailsafe("title", 100))
- .setDescription(TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), freenetRequest.httpRequest.getPartAsStringFailsafe("description", 1024)))
+ .setTitle(soneRequest.httpRequest.getPartAsStringFailsafe("title", 100))
+ .setDescription(TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), soneRequest.httpRequest.getPartAsStringFailsafe("description", 1024)))
.update()
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
} catch (e: ImageTitleMustNotBeEmpty) {
throw RedirectException("emptyImageTitle.html")
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user edit the name of a profile field.
*/
-class EditProfileFieldPage(template: Template, webInterface: WebInterface) :
- LoggedInPage("editProfileField.html", template, "Page.EditProfileField.Title", webInterface) {
+@TemplatePath("/templates/editProfileField.html")
+@ToadletPath("editProfileField.html")
+class EditProfileFieldPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.EditProfileField.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
currentSone.profile.let { profile ->
- if (freenetRequest.isPOST) {
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("cancel", 4) == "true") {
+ if (soneRequest.isPOST) {
+ if (soneRequest.httpRequest.getPartAsStringFailsafe("cancel", 4) == "true") {
throw RedirectException("editProfile.html#profile-fields")
}
- val field = profile.getFieldById(freenetRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html")
- freenetRequest.httpRequest.getPartAsStringFailsafe("name", 256).let { name ->
+ val field = profile.getFieldById(soneRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html")
+ soneRequest.httpRequest.getPartAsStringFailsafe("name", 256).let { name ->
try {
if (name != field.name) {
field.name = name
}
}
}
- templateContext["field"] = profile.getFieldById(freenetRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html")
+ templateContext["field"] = profile.getFieldById(soneRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html")
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Profile.DuplicateField
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Profile.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user edit her profile.
*/
-class EditProfilePage(template: Template, webInterface: WebInterface) :
- LoggedInPage("editProfile.html", template, "Page.EditProfile.Title", webInterface) {
+@MenuName("EditProfile")
+@TemplatePath("/templates/editProfile.html")
+@ToadletPath("editProfile.html")
+class EditProfilePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.EditProfile.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
currentSone.profile.let { profile ->
templateContext["firstName"] = profile.firstName
templateContext["middleName"] = profile.middleName
templateContext["birthYear"] = profile.birthYear
templateContext["avatarId"] = profile.avatar
templateContext["fields"] = profile.fields
- if (freenetRequest.isPOST) {
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("save-profile", 4) == "true") {
- profile.firstName = freenetRequest.httpRequest.getPartAsStringFailsafe("first-name", 256).trim()
- profile.middleName = freenetRequest.httpRequest.getPartAsStringFailsafe("middle-name", 256).trim()
- profile.lastName = freenetRequest.httpRequest.getPartAsStringFailsafe("last-name", 256).trim()
- profile.birthDay = freenetRequest.httpRequest.getPartAsStringFailsafe("birth-day", 256).trim().toIntOrNull()
- profile.birthMonth = freenetRequest.httpRequest.getPartAsStringFailsafe("birth-month", 256).trim().toIntOrNull()
- profile.birthYear = freenetRequest.httpRequest.getPartAsStringFailsafe("birth-year", 256).trim().toIntOrNull()
- profile.setAvatar(webInterface.core.getImage(freenetRequest.httpRequest.getPartAsStringFailsafe("avatarId", 256).trim(), false))
+ if (soneRequest.isPOST) {
+ if (soneRequest.httpRequest.getPartAsStringFailsafe("save-profile", 4) == "true") {
+ profile.firstName = soneRequest.httpRequest.getPartAsStringFailsafe("first-name", 256).trim()
+ profile.middleName = soneRequest.httpRequest.getPartAsStringFailsafe("middle-name", 256).trim()
+ profile.lastName = soneRequest.httpRequest.getPartAsStringFailsafe("last-name", 256).trim()
+ profile.birthDay = soneRequest.httpRequest.getPartAsStringFailsafe("birth-day", 256).trim().toIntOrNull()
+ profile.birthMonth = soneRequest.httpRequest.getPartAsStringFailsafe("birth-month", 256).trim().toIntOrNull()
+ profile.birthYear = soneRequest.httpRequest.getPartAsStringFailsafe("birth-year", 256).trim().toIntOrNull()
+ profile.setAvatar(soneRequest.core.getImage(soneRequest.httpRequest.getPartAsStringFailsafe("avatarId", 256).trim(), false))
profile.fields.forEach { field ->
- field.value = TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), freenetRequest.httpRequest.getPartAsStringFailsafe("field-${field.id}", 400).trim())
+ field.value = TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), soneRequest.httpRequest.getPartAsStringFailsafe("field-${field.id}", 400).trim())
}
currentSone.profile = profile
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
throw RedirectException("editProfile.html")
- } else if (freenetRequest.httpRequest.getPartAsStringFailsafe("add-field", 4) == "true") {
- val fieldName = freenetRequest.httpRequest.getPartAsStringFailsafe("field-name", 100)
+ } else if (soneRequest.httpRequest.getPartAsStringFailsafe("add-field", 4) == "true") {
+ val fieldName = soneRequest.httpRequest.getPartAsStringFailsafe("field-name", 100)
try {
profile.addField(fieldName)
currentSone.profile = profile
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
throw RedirectException("editProfile.html#profile-fields")
} catch (e: DuplicateField) {
templateContext["fieldName"] = fieldName
templateContext["duplicateFieldName"] = true
}
} else profile.fields.forEach { field ->
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("delete-field-${field.id}", 4) == "true") {
+ if (soneRequest.httpRequest.getPartAsStringFailsafe("delete-field-${field.id}", 4) == "true") {
throw RedirectException("deleteProfileField.html?field=${field.id}")
- } else if (freenetRequest.httpRequest.getPartAsStringFailsafe("edit-field-${field.id}", 4) == "true") {
+ } else if (soneRequest.httpRequest.getPartAsStringFailsafe("edit-field-${field.id}", 4) == "true") {
throw RedirectException("editProfileField.html?field=${field.id}")
- } else if (freenetRequest.httpRequest.getPartAsStringFailsafe("move-down-field-${field.id}", 4) == "true") {
+ } else if (soneRequest.httpRequest.getPartAsStringFailsafe("move-down-field-${field.id}", 4) == "true") {
profile.moveFieldDown(field)
currentSone.profile = profile
throw RedirectException("editProfile.html#profile-fields")
- } else if (freenetRequest.httpRequest.getPartAsStringFailsafe("move-up-field-${field.id}", 4) == "true") {
+ } else if (soneRequest.httpRequest.getPartAsStringFailsafe("move-up-field-${field.id}", 4) == "true") {
profile.moveFieldUp(field)
currentSone.profile = profile
throw RedirectException("editProfile.html#profile-fields")
--- /dev/null
+package net.pterodactylus.sone.web.pages
+
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+
+@TemplatePath("/templates/invalid.html")
+@ToadletPath("invalid.html")
+class InvalidPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : SoneTemplatePage(webInterface, loaders, templateRenderer, "Page.Invalid.Title")
+
+@TemplatePath("/templates/noPermission.html")
+@ToadletPath("noPermission.html")
+class NoPermissionPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : SoneTemplatePage(webInterface, loaders, templateRenderer, "Page.NoPermission.Title")
+
+@TemplatePath("/templates/emptyImageTitle.html")
+@ToadletPath("emptyImageTitle.html")
+class EmptyImageTitlePage(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : SoneTemplatePage(webInterface, loaders, templateRenderer, "Page.EmptyImageTitle.Title")
+
+@TemplatePath("/templates/emptyAlbumTitle.html")
+@ToadletPath("emptyAlbumTitle.html")
+class EmptyAlbumTitlePage(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : SoneTemplatePage(webInterface, loaders, templateRenderer, "Page.EmptyAlbumTitle.Title")
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user follow another Sone.
*/
-class FollowSonePage(template: Template, webInterface: WebInterface):
- LoggedInPage("followSone.html", template, "Page.FollowSone.Title", webInterface) {
+@ToadletPath("followSone.html")
+class FollowSonePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.FollowSone.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- freenetRequest.httpRequest.getPartAsStringFailsafe("sone", 1200).split(Regex("[ ,]+"))
- .map { it to webInterface.core.getSone(it) }
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ soneRequest.httpRequest.getPartAsStringFailsafe("sone", 1200).split(Regex("[ ,]+"))
+ .map { it to soneRequest.core.getSone(it) }
.filterNot { it.second == null }
.forEach { sone ->
- webInterface.core.followSone(currentSone, sone.first)
- webInterface.core.markSoneKnown(sone.second)
+ soneRequest.core.followSone(currentSone, sone.first)
+ soneRequest.core.markSoneKnown(sone.second)
}
- throw RedirectException(freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256))
+ throw RedirectException(soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256))
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetPage
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.web.Response
-import java.net.URI
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.*
+import java.net.*
+import javax.inject.*
/**
* Page that delivers a {@link TemporaryImage} to the browser.
*/
-class GetImagePage(webInterface: WebInterface): FreenetPage {
+class GetImagePage @Inject constructor(webInterface: WebInterface) : FreenetPage {
private val core = webInterface.core
}
}
- override fun isLinkExcepted(link: URI?): Boolean {
+ override fun isLinkExcepted(link: URI): Boolean {
return false
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import java.net.URI
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import java.net.*
+import javax.inject.*
/**
* The image browser page is the entry page for the image management.
*/
-class ImageBrowserPage(template: Template, webInterface: WebInterface):
- LoggedInPage("imageBrowser.html", template, "Page.ImageBrowser.Title", webInterface) {
+@MenuName("ImageBrowser")
+@TemplatePath("/templates/imageBrowser.html")
+@ToadletPath("imageBrowser.html")
+class ImageBrowserPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.ImageBrowser.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if ("album" in freenetRequest.parameters) {
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if ("album" in soneRequest.parameters) {
templateContext["albumRequested"] = true
- templateContext["album"] = webInterface.core.getAlbum(freenetRequest.parameters["album"]!!)
- templateContext["page"] = freenetRequest.parameters["page"]
- } else if ("image" in freenetRequest.parameters) {
+ templateContext["album"] = soneRequest.core.getAlbum(soneRequest.parameters["album"]!!)
+ templateContext["page"] = soneRequest.parameters["page"]
+ } else if ("image" in soneRequest.parameters) {
templateContext["imageRequested"] = true
- templateContext["image"] = webInterface.core.getImage(freenetRequest.parameters["image"])
- } else if (freenetRequest.parameters["mode"] == "gallery") {
+ templateContext["image"] = soneRequest.core.getImage(soneRequest.parameters["image"])
+ } else if (soneRequest.parameters["mode"] == "gallery") {
templateContext["galleryRequested"] = true
- webInterface.core.sones
+ soneRequest.core.sones
.map(Sone::getRootAlbum)
.flatMap(Album::getAlbums)
.flatMap { Album.FLATTENER.apply(it)!! }
.filterNot(Album::isEmpty)
.sortedBy(Album::getTitle)
.also { albums ->
- Pagination(albums, webInterface.core.preferences.imagesPerPage).apply { page = freenetRequest.parameters["page"]?.toIntOrNull() ?: 0 }.also { pagination ->
- templateContext["albumPagination"] = pagination
- templateContext["albums"] = pagination.items
- }
+ albums.paginate(soneRequest.core.preferences.imagesPerPage)
+ .turnTo(soneRequest.parameters["page"]?.toIntOrNull() ?: 0)
+ .also { pagination ->
+ templateContext["albumPagination"] = pagination
+ templateContext["albums"] = pagination.items
+ }
}
} else {
templateContext["soneRequested"] = true
- templateContext["sone"] = webInterface.core.getSone(freenetRequest.httpRequest.getParam("sone")) ?: currentSone
+ templateContext["sone"] = soneRequest.core.getSone(soneRequest.httpRequest.getParam("sone")) ?: currentSone
}
}
- override fun isLinkExcepted(link: URI?) = true
+ override fun isLinkExcepted(link: URI) = true
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.notify.PostVisibilityFilter
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.notify.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* The index page shows the main page of Sone. This page will contain the posts
* of all friends of the current user.
*/
-class IndexPage(template: Template, webInterface: WebInterface, private val postVisibilityFilter: PostVisibilityFilter):
- LoggedInPage("index.html", template, "Page.Index.Title", webInterface) {
+@MenuName("Index")
+@TemplatePath("/templates/index.html")
+@ToadletPath("index.html")
+class IndexPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer, private val postVisibilityFilter: PostVisibilityFilter) :
+ LoggedInPage("Page.Index.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- (currentSone.posts +
- currentSone.friends
- .mapNotNull(webInterface.core::getSone)
- .flatMap { it.posts } +
- webInterface.core.getDirectedPosts(currentSone.id)
- ).distinct()
- .filter { postVisibilityFilter.isVisible(currentSone).apply(it) }
- .sortedByDescending { it.time }
- .let { posts ->
- Pagination(posts, webInterface.core.preferences.postsPerPage).apply {
- page = freenetRequest.parameters["page"]?.toIntOrNull() ?: 0
- }.let { pagination ->
- templateContext["pagination"] = pagination
- templateContext["posts"] = pagination.items
- }
- }
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ (currentSone.posts +
+ currentSone.friends
+ .mapNotNull(soneRequest.core::getSone)
+ .flatMap { it.posts } +
+ soneRequest.core.getDirectedPosts(currentSone.id)
+ ).distinct()
+ .filter { postVisibilityFilter.isVisible(currentSone).apply(it) }
+ .sortedByDescending { it.time }
+ .let { posts ->
+ posts.paginate(soneRequest.core.preferences.postsPerPage)
+ .turnTo(soneRequest.parameters["page"]?.toIntOrNull() ?: 0)
+ .let { pagination ->
+ templateContext["pagination"] = pagination
+ templateContext["posts"] = pagination.items
+ }
+ }
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page shows all known Sones.
*/
-class KnownSonesPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("knownSones.html", template, "Page.KnownSones.Title", webInterface, false) {
+@MenuName("KnownSones")
+@TemplatePath("/templates/knownSones.html")
+@ToadletPath("knownSones.html")
+class KnownSonesPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.KnownSones.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- getCurrentSone(freenetRequest.toadletContext).let { currentSone ->
- Pagination(webInterface.core.sones
- .filterNot { freenetRequest.parameters["filter"] == "followed" && currentSone != null && !currentSone.hasFriend(it.id) }
- .filterNot { freenetRequest.parameters["filter"] == "not-followed" && currentSone != null && currentSone.hasFriend(it.id) }
- .filterNot { freenetRequest.parameters["filter"] == "new" && it.isKnown }
- .filterNot { freenetRequest.parameters["filter"] == "not-new" && !it.isKnown }
- .filterNot { freenetRequest.parameters["filter"] == "own" && !it.isLocal }
- .filterNot { freenetRequest.parameters["filter"] == "not-own" && it.isLocal }
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ getCurrentSone(soneRequest.toadletContext).let { currentSone ->
+ soneRequest.core.sones.asSequence()
+ .filterNot { soneRequest.parameters["filter"] == "followed" && currentSone != null && !currentSone.hasFriend(it.id) }
+ .filterNot { soneRequest.parameters["filter"] == "not-followed" && currentSone != null && currentSone.hasFriend(it.id) }
+ .filterNot { soneRequest.parameters["filter"] == "new" && it.isKnown }
+ .filterNot { soneRequest.parameters["filter"] == "not-new" && !it.isKnown }
+ .filterNot { soneRequest.parameters["filter"] == "own" && !it.isLocal }
+ .filterNot { soneRequest.parameters["filter"] == "not-own" && it.isLocal }
.sortedWith(
- when (freenetRequest.parameters["sort"]) {
+ when (soneRequest.parameters["sort"]) {
"images" -> Sone.IMAGE_COUNT_COMPARATOR
"name" -> Sone.NICE_NAME_COMPARATOR.reversed()
"posts" -> Sone.POST_COUNT_COMPARATOR
else -> Sone.LAST_ACTIVITY_COMPARATOR
}.let { comparator ->
- when (freenetRequest.parameters["order"]) {
+ when (soneRequest.parameters["order"]) {
"asc" -> comparator.reversed()
else -> comparator
}
}
- ), 25).apply { page = freenetRequest.parameters["page"]?.toIntOrNull() ?: 0 }
+ ).paginate(25)
+ .turnTo(soneRequest.parameters["page"]?.toIntOrNull() ?: 0)
.let { pagination ->
templateContext["pagination"] = pagination
templateContext["knownSones"] = pagination.items
}
- templateContext["sort"] = freenetRequest.parameters["sort"].let { sort -> if (sort in listOf("images", "name", "posts")) sort else "activity" }
- templateContext["order"] = freenetRequest.parameters["order"].let { order -> if (order == "asc") "asc" else "desc" }
- templateContext["filter"] = freenetRequest.parameters["filter"]
+ templateContext["sort"] = soneRequest.parameters["sort"].let { sort -> if (sort in listOf("images", "name", "posts")) sort else "activity" }
+ templateContext["order"] = soneRequest.parameters["order"].let { order -> if (order == "asc") "asc" else "desc" }
+ templateContext["filter"] = soneRequest.parameters["filter"]
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user like [net.pterodactylus.sone.data.Post]s and [net.pterodactylus.sone.data.Reply]s.
*/
-class LikePage(template: Template, webInterface: WebInterface) :
- LoggedInPage("like.html", template, "Page.Like.Title", webInterface) {
+@ToadletPath("like.html")
+class LikePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.Like.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- freenetRequest.parameters["type", 16]?.also { type ->
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ soneRequest.parameters["type", 16]?.also { type ->
when (type) {
- "post" -> currentSone.addLikedPostId(freenetRequest.parameters["post", 36]!!)
- "reply" -> currentSone.addLikedReplyId(freenetRequest.parameters["reply", 36]!!)
+ "post" -> currentSone.addLikedPostId(soneRequest.parameters["post", 36]!!)
+ "reply" -> currentSone.addLikedReplyId(soneRequest.parameters["reply", 36]!!)
}
}
- throw RedirectException(freenetRequest.parameters["returnPage", 256]!!)
+ throw RedirectException(soneRequest.parameters["returnPage", 256]!!)
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user lock a [net.pterodactylus.sone.data.Sone] to prevent it from being inserted.
*/
-class LockSonePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("lockSone.html", template, "Page.LockSone.Title", webInterface, false) {
+@ToadletPath("lockSone.html")
+class LockSonePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.LockSone.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- freenetRequest.parameters["returnPage", 256]!!.let { returnPage ->
- freenetRequest.parameters["sone", 44]!!
- .let { webInterface.core.getLocalSone(it) }
- ?.let { webInterface.core.lockSone(it) }
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ soneRequest.parameters["returnPage", 256]!!.let { returnPage ->
+ soneRequest.parameters["sone", 44]!!
+ .let { soneRequest.core.getLocalSone(it) }
+ ?.let { soneRequest.core.lockSone(it) }
throw RedirectException(returnPage)
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
/**
* Base class for [SoneTemplatePage] implementations that require a logged in user.
*/
-abstract class LoggedInPage(path: String, template: Template, pageTitleKey: String, webInterface: WebInterface) :
- SoneTemplatePage(path, template, pageTitleKey, webInterface, true) {
+abstract class LoggedInPage(pageTitleKey: String, webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = pageTitleKey, requiresLogin = true) {
- final override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- handleRequest(freenetRequest, getCurrentSone(freenetRequest.toadletContext, false)!!, templateContext)
+ final override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ handleRequest(soneRequest, getCurrentSone(soneRequest.toadletContext, false)!!, templateContext)
}
- protected abstract fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext)
+ protected abstract fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext)
}
package net.pterodactylus.sone.web.pages
-import freenet.clients.http.ToadletContext
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* The login page lets the user log in.
*/
-class LoginPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("login.html", template, "Page.Login.Title", webInterface) {
+@MenuName("Login")
+@TemplatePath("/templates/login.html")
+@ToadletPath("login.html")
+class LoginPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.Login.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val soneId = freenetRequest.httpRequest.getPartAsStringFailsafe("sone-id", 43)
- webInterface.core.getLocalSone(soneId)?.let { sone ->
- setCurrentSone(freenetRequest.toadletContext, sone)
- val target = freenetRequest.httpRequest.getParam("target").emptyToNull ?: "index.html"
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val soneId = soneRequest.httpRequest.getPartAsStringFailsafe("sone-id", 43)
+ soneRequest.core.getLocalSone(soneId)?.let { sone ->
+ setCurrentSone(soneRequest.toadletContext, sone)
+ val target = soneRequest.httpRequest.getParam("target").emptyToNull ?: "index.html"
throw RedirectException(target)
}
}
- templateContext["sones"] = webInterface.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR)
- templateContext["identitiesWithoutSone"] = webInterface.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}" }
+ templateContext["sones"] = soneRequest.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR)
+ templateContext["identitiesWithoutSone"] = soneRequest.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}" }
}
- override public fun getRedirectTarget(freenetRequest: FreenetRequest) =
- getCurrentSone(freenetRequest.toadletContext)?.let { "index.html" }
+ override fun getRedirectTarget(request: FreenetRequest) =
+ getCurrentSone(request.toadletContext)?.let { "index.html" }
- override fun isEnabled(toadletContext: ToadletContext) = when {
- webInterface.core.preferences.isRequireFullAccess && !toadletContext.isAllowedFullAccess -> false
- else -> getCurrentSone(toadletContext, false) == null
+ override fun isEnabled(soneRequest: SoneRequest) = when {
+ soneRequest.core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess -> false
+ else -> getCurrentSone(soneRequest.toadletContext, false) == null
}
}
package net.pterodactylus.sone.web.pages
-import freenet.clients.http.ToadletContext
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Logs a user out.
*/
-class LogoutPage(template: Template, webInterface: WebInterface):
- LoggedInPage("logout.html", template, "Page.Logout.Title", webInterface) {
+@MenuName("Logout")
+@ToadletPath("logout.html")
+class LogoutPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.Logout.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- setCurrentSone(freenetRequest.toadletContext, null)
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ setCurrentSone(soneRequest.toadletContext, null)
throw RedirectException("index.html")
}
- override fun isEnabled(toadletContext: ToadletContext): Boolean =
- if (webInterface.core.preferences.isRequireFullAccess && !toadletContext.isAllowedFullAccess) {
+ override fun isEnabled(soneRequest: SoneRequest): Boolean =
+ if (soneRequest.core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess) {
false
} else
- getCurrentSone(toadletContext) != null && webInterface.core.localSones.size != 1
+ getCurrentSone(soneRequest.toadletContext) != null && soneRequest.core.localSones.size != 1
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.utils.mapPresent
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user mark a number of [net.pterodactylus.sone.data.Sone]s, [Post]s, or
* [Replie][net.pterodactylus.sone.data.Reply]s as known.
*/
-class MarkAsKnownPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("markAsKnown.html", template, "Page.MarkAsKnown.Title", webInterface, false) {
+@ToadletPath("markAsKnown.html")
+class MarkAsKnownPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.MarkAsKnown.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- val ids = freenetRequest.parameters["id", 65536]!!.split(" ")
- when (freenetRequest.parameters["type", 5]) {
- "sone" -> ids.mapNotNull(webInterface.core::getSone).forEach(webInterface.core::markSoneKnown)
- "post" -> ids.mapNotNull(webInterface.core::getPost).forEach(webInterface.core::markPostKnown)
- "reply" -> ids.mapNotNull(webInterface.core::getPostReply).forEach(webInterface.core::markReplyKnown)
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ val ids = soneRequest.parameters["id", 65536]!!.split(" ")
+ when (soneRequest.parameters["type", 5]) {
+ "sone" -> ids.mapNotNull(soneRequest.core::getSone).forEach(soneRequest.core::markSoneKnown)
+ "post" -> ids.mapNotNull(soneRequest.core::getPost).forEach(soneRequest.core::markPostKnown)
+ "reply" -> ids.mapNotNull(soneRequest.core::getPostReply).forEach(soneRequest.core::markReplyKnown)
else -> throw RedirectException("invalid.html")
}
- throw RedirectException(freenetRequest.parameters["returnPage", 256]!!)
+ throw RedirectException(soneRequest.parameters["returnPage", 256]!!)
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.mapPresent
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that displays all new posts and replies. The posts are filtered using
* [PostVisibilityFilter.isPostVisible(Sone, Post)] and sorted by time.
*/
-class NewPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("new.html", template, "Page.New.Title", webInterface, false) {
+@MenuName("New")
+@TemplatePath("/templates/new.html")
+@ToadletPath("new.html")
+class NewPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.New.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) =
- getCurrentSone(freenetRequest.toadletContext).let { currentSone ->
- (webInterface.getNewPosts(currentSone) + webInterface.getNewReplies(currentSone).mapPresent { it.post })
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) =
+ getCurrentSone(soneRequest.toadletContext).let { currentSone ->
+ (soneRequest.webInterface.getNewPosts(currentSone) + soneRequest.webInterface.getNewReplies(currentSone).mapPresent { it.post })
.distinct()
.sortedByDescending { it.time }
.let { posts ->
- Pagination(posts, webInterface.core.preferences.postsPerPage).apply {
- page = freenetRequest.parameters["page"]?.toIntOrNull() ?: 0
- }.let { pagination ->
- templateContext["pagination"] = pagination
- templateContext["posts"] = pagination.items
- }
+ posts.paginate(soneRequest.core.preferences.postsPerPage)
+ .turnTo(soneRequest.parameters["page"]?.toIntOrNull() ?: 0)
+ .let { pagination ->
+ templateContext["pagination"] = pagination
+ templateContext["posts"] = pagination.items
+ }
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.core.Preferences
-import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent
-import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.SoneOptions.*
+import net.pterodactylus.sone.fcp.FcpInterface.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user edit the options of the Sone plugin.
*/
-class OptionsPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("options.html", template, "Page.Options.Title", webInterface, false) {
+@MenuName("Options")
+@TemplatePath("/templates/options.html")
+@ToadletPath("options.html")
+class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.Options.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
val fieldsWithErrors = mutableListOf<String>()
- getCurrentSone(freenetRequest.toadletContext)?.options?.let { options ->
- val autoFollow = "auto-follow" in freenetRequest.parameters
- val loadLinkedImages = freenetRequest.parameters["load-linked-images"].emptyToNull
- val showCustomAvatars = freenetRequest.parameters["show-custom-avatars"].emptyToNull
- val enableSoneInsertNotification = "enable-sone-insert-notifications" in freenetRequest.parameters
- val showNewSoneNotification = "show-notification-new-sones" in freenetRequest.parameters
- val showNewPostNotification = "show-notification-new-posts" in freenetRequest.parameters
- val showNewReplyNotification = "show-notification-new-replies" in freenetRequest.parameters
+ getCurrentSone(soneRequest.toadletContext)?.options?.let { options ->
+ val autoFollow = "auto-follow" in soneRequest.parameters
+ val loadLinkedImages = soneRequest.parameters["load-linked-images"].emptyToNull
+ val showCustomAvatars = soneRequest.parameters["show-custom-avatars"].emptyToNull
+ val enableSoneInsertNotification = "enable-sone-insert-notifications" in soneRequest.parameters
+ val showNewSoneNotification = "show-notification-new-sones" in soneRequest.parameters
+ val showNewPostNotification = "show-notification-new-posts" in soneRequest.parameters
+ val showNewReplyNotification = "show-notification-new-replies" in soneRequest.parameters
options.isAutoFollow = autoFollow
options.isSoneInsertNotificationEnabled = enableSoneInsertNotification
loadLinkedImages?.also { if (cantSetOption { options.loadLinkedImages = LoadExternalContent.valueOf(loadLinkedImages) }) fieldsWithErrors += "load-linked-images" }
showCustomAvatars?.also { if (cantSetOption { options.showCustomAvatars = LoadExternalContent.valueOf(showCustomAvatars) }) fieldsWithErrors += "show-custom-avatars" }
}
- val fullAccessRequired = "require-full-access" in freenetRequest.parameters
- val fcpInterfaceActive = "fcp-interface-active" in freenetRequest.parameters
+ val fullAccessRequired = "require-full-access" in soneRequest.parameters
+ val fcpInterfaceActive = "fcp-interface-active" in soneRequest.parameters
- webInterface.core.preferences.isRequireFullAccess = fullAccessRequired
- webInterface.core.preferences.isFcpInterfaceActive = fcpInterfaceActive
+ soneRequest.core.preferences.newRequireFullAccess = fullAccessRequired
+ soneRequest.core.preferences.newFcpInterfaceActive = fcpInterfaceActive
- val postsPerPage = freenetRequest.parameters["posts-per-page"]?.toIntOrNull()
- val charactersPerPost = freenetRequest.parameters["characters-per-post"]?.toIntOrNull()
- val postCutOffLength = freenetRequest.parameters["post-cut-off-length"]?.toIntOrNull()
- val imagesPerPage = freenetRequest.parameters["images-per-page"]?.toIntOrNull()
- val insertionDelay = freenetRequest.parameters["insertion-delay"]?.toIntOrNull()
- val fcpFullAccessRequired = freenetRequest.parameters["fcp-full-access-required"]?.toIntOrNull()
- val negativeTrust = freenetRequest.parameters["negative-trust"]?.toIntOrNull()
- val positiveTrust = freenetRequest.parameters["positive-trust"]?.toIntOrNull()
- val trustComment = freenetRequest.parameters["trust-comment"]?.emptyToNull
+ val postsPerPage = soneRequest.parameters["posts-per-page"]?.toIntOrNull()
+ val charactersPerPost = soneRequest.parameters["characters-per-post"]?.toIntOrNull()
+ val postCutOffLength = soneRequest.parameters["post-cut-off-length"]?.toIntOrNull()
+ val imagesPerPage = soneRequest.parameters["images-per-page"]?.toIntOrNull()
+ val insertionDelay = soneRequest.parameters["insertion-delay"]?.toIntOrNull()
+ val fcpFullAccessRequired = soneRequest.parameters["fcp-full-access-required"]?.toIntOrNull()
+ val negativeTrust = soneRequest.parameters["negative-trust"]?.toIntOrNull()
+ val positiveTrust = soneRequest.parameters["positive-trust"]?.toIntOrNull()
+ val trustComment = soneRequest.parameters["trust-comment"]?.emptyToNull
- if (cantSetOption { it.setPostsPerPage(postsPerPage) }) fieldsWithErrors += "posts-per-page"
- if (cantSetOption { it.setCharactersPerPost(charactersPerPost) }) fieldsWithErrors += "characters-per-post"
- if (cantSetOption { it.setPostCutOffLength(postCutOffLength) }) fieldsWithErrors += "post-cut-off-length"
- if (cantSetOption { it.setImagesPerPage(imagesPerPage) }) fieldsWithErrors += "images-per-page"
- if (cantSetOption { it.setInsertionDelay(insertionDelay) }) fieldsWithErrors += "insertion-delay"
- fcpFullAccessRequired?.also { if (cantSetOption { it.fcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequired] }) fieldsWithErrors += "fcp-full-access-required" }
- if (cantSetOption { it.setNegativeTrust(negativeTrust) }) fieldsWithErrors += "negative-trust"
- if (cantSetOption { it.setPositiveTrust(positiveTrust) }) fieldsWithErrors += "positive-trust"
- if (cantSetOption { it.trustComment = trustComment }) fieldsWithErrors += "trust-comment"
+ if (cantSetOption { soneRequest.core.preferences.newPostsPerPage = postsPerPage }) fieldsWithErrors += "posts-per-page"
+ if (cantSetOption { soneRequest.core.preferences.newCharactersPerPost = charactersPerPost }) fieldsWithErrors += "characters-per-post"
+ if (cantSetOption { soneRequest.core.preferences.newPostCutOffLength = postCutOffLength }) fieldsWithErrors += "post-cut-off-length"
+ if (cantSetOption { soneRequest.core.preferences.newImagesPerPage = imagesPerPage }) fieldsWithErrors += "images-per-page"
+ if (cantSetOption { soneRequest.core.preferences.newInsertionDelay = insertionDelay }) fieldsWithErrors += "insertion-delay"
+ fcpFullAccessRequired?.also { if (cantSetOption { soneRequest.core.preferences.newFcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequired] }) fieldsWithErrors += "fcp-full-access-required" }
+ if (cantSetOption { soneRequest.core.preferences.newNegativeTrust = negativeTrust }) fieldsWithErrors += "negative-trust"
+ if (cantSetOption { soneRequest.core.preferences.newPositiveTrust = positiveTrust }) fieldsWithErrors += "positive-trust"
+ if (cantSetOption { soneRequest.core.preferences.newTrustComment = trustComment }) fieldsWithErrors += "trust-comment"
if (fieldsWithErrors.isEmpty()) {
- webInterface.core.touchConfiguration()
+ soneRequest.core.touchConfiguration()
throw RedirectException("options.html")
}
templateContext["fieldErrors"] = fieldsWithErrors
}
- getCurrentSone(freenetRequest.toadletContext)?.options?.let { options ->
+ getCurrentSone(soneRequest.toadletContext)?.options?.let { options ->
templateContext["auto-follow"] = options.isAutoFollow
templateContext["show-notification-new-sones"] = options.isShowNewSoneNotifications
templateContext["show-notification-new-posts"] = options.isShowNewPostNotifications
templateContext["load-linked-images"] = options.loadLinkedImages.toString()
templateContext["show-custom-avatars"] = options.showCustomAvatars.toString()
}
- webInterface.core.preferences.let { preferences ->
+ soneRequest.core.preferences.let { preferences ->
templateContext["insertion-delay"] = preferences.insertionDelay
templateContext["characters-per-post"] = preferences.charactersPerPost
templateContext["fcp-full-access-required"] = preferences.fcpFullAccessRequired.ordinal
templateContext["images-per-page"] = preferences.imagesPerPage
- templateContext["fcp-interface-active"] = preferences.isFcpInterfaceActive
- templateContext["require-full-access"] = preferences.isRequireFullAccess
+ templateContext["fcp-interface-active"] = preferences.fcpInterfaceActive
+ templateContext["require-full-access"] = preferences.requireFullAccess
templateContext["negative-trust"] = preferences.negativeTrust
templateContext["positive-trust"] = preferences.positiveTrust
templateContext["post-cut-off-length"] = preferences.postCutOffLength
}
}
- private fun cantSetOption(setter: (Preferences) -> Unit) =
+ private fun cantSetOption(setter: () -> Unit) =
try {
- setter(webInterface.core.preferences)
+ setter()
false
} catch (iae: IllegalArgumentException) {
true
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.util.web.Page
-import net.pterodactylus.util.web.Request
-import net.pterodactylus.util.web.Response
-import java.io.File
+import net.pterodactylus.util.web.*
+import java.io.*
+import javax.inject.*
/**
* [Page] implementation that delivers static files from the filesystem.
*/
-class ReloadingPage<R: Request>(private val prefix: String, private val path: String, private val mimeType: String): Page<R> {
+class ReloadingPage<R : Request> @Inject constructor(private val prefix: String, private val path: String, private val mimeType: String) : Page<R> {
override fun isPrefixPage() = true
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user control the rescue mode for a Sone.
*/
-class RescuePage(template: Template, webInterface: WebInterface):
- LoggedInPage("rescue.html", template, "Page.Rescue.Title", webInterface) {
+@MenuName("Rescue")
+@TemplatePath("/templates/rescue.html")
+@ToadletPath("rescue.html")
+class RescuePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.Rescue.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- val soneRescuer = webInterface.core.getSoneRescuer(currentSone)
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ val soneRescuer = soneRequest.core.getSoneRescuer(currentSone)
templateContext["soneRescuer"] = soneRescuer
- if (freenetRequest.isPOST) {
- freenetRequest.parameters["edition", 9]?.toIntOrNull()?.also {
+ if (soneRequest.isPOST) {
+ soneRequest.parameters["edition", 9]?.toIntOrNull()?.also {
if (it > -1) {
soneRescuer.setEdition(it.toLong())
}
}
- if (freenetRequest.parameters["fetch", 8] == "true") {
+ if (soneRequest.parameters["fetch", 8] == "true") {
soneRescuer.startNextFetch()
}
throw RedirectException("rescue.html")
package net.pterodactylus.sone.web.pages
import com.google.common.base.Ticker
-import com.google.common.cache.Cache
-import com.google.common.cache.CacheBuilder
-import freenet.support.Logger
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.utils.memoize
-import net.pterodactylus.sone.utils.paginate
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.sone.web.pages.SearchPage.Optionality.FORBIDDEN
-import net.pterodactylus.sone.web.pages.SearchPage.Optionality.OPTIONAL
-import net.pterodactylus.sone.web.pages.SearchPage.Optionality.REQUIRED
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import net.pterodactylus.util.text.StringEscaper
-import net.pterodactylus.util.text.TextException
-import java.util.concurrent.TimeUnit.MINUTES
+import com.google.common.cache.*
+import freenet.support.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.sone.web.pages.SearchPage.Optionality.*
+import net.pterodactylus.util.template.*
+import net.pterodactylus.util.text.*
+import java.util.concurrent.TimeUnit.*
+import javax.inject.*
/**
* This page lets the user search for posts and replies that contain certain
* words.
*/
-class SearchPage @JvmOverloads constructor(template: Template, webInterface: WebInterface, ticker: Ticker = Ticker.systemTicker()):
- SoneTemplatePage("search.html", template, "Page.Search.Title", webInterface, false) {
+@TemplatePath("/templates/search.html")
+@ToadletPath("search.html")
+class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer, ticker: Ticker = Ticker.systemTicker()) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.Search.Title") {
+
+ @Inject
+ constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ this(webInterface, loaders, templateRenderer, Ticker.systemTicker())
private val cache: Cache<Iterable<Phrase>, Pagination<Post>> = CacheBuilder.newBuilder().ticker(ticker).expireAfterAccess(5, MINUTES).build()
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
val startTime = System.currentTimeMillis()
val phrases = try {
- freenetRequest.parameters["query"].emptyToNull?.parse()
+ soneRequest.parameters["query"].emptyToNull?.parse()
} catch (te: TextException) {
redirect("index.html")
}
0 -> redirect("index.html")
1 -> phrases.first().phrase.also { word ->
when {
- word.removePrefix("sone://").let(webInterface.core::getSone) != null -> redirect("viewSone.html?sone=${word.removePrefix("sone://")}")
- word.removePrefix("post://").let(webInterface.core::getPost) != null -> redirect("viewPost.html?post=${word.removePrefix("post://")}")
- word.removePrefix("reply://").let(webInterface.core::getPostReply) != null -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(webInterface.core::getPostReply)?.postId}")
- word.removePrefix("album://").let(webInterface.core::getAlbum) != null -> redirect("imageBrowser.html?album=${word.removePrefix("album://")}")
- word.removePrefix("image://").let { webInterface.core.getImage(it, false) } != null -> redirect("imageBrowser.html?image=${word.removePrefix("image://")}")
+ word.removePrefix("sone://").let(soneRequest.core::getSone) != null -> redirect("viewSone.html?sone=${word.removePrefix("sone://")}")
+ word.removePrefix("post://").let(soneRequest.core::getPost) != null -> redirect("viewPost.html?post=${word.removePrefix("post://")}")
+ word.removePrefix("reply://").let(soneRequest.core::getPostReply) != null -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(soneRequest.core::getPostReply)?.postId}")
+ word.removePrefix("album://").let(soneRequest.core::getAlbum) != null -> redirect("imageBrowser.html?album=${word.removePrefix("album://")}")
+ word.removePrefix("image://").let { soneRequest.core.getImage(it, false) } != null -> redirect("imageBrowser.html?image=${word.removePrefix("image://")}")
}
}
}
val soneNameCache = { sone: Sone -> sone.names() }.memoize()
- val sonePagination = webInterface.core.sones
- .scoreAndPaginate(phrases) { it.allText(soneNameCache) }
- .apply { page = freenetRequest.parameters["sonePage"].emptyToNull?.toIntOrNull() ?: 0 }
+ val sonePagination = soneRequest.core.sones
+ .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache) }
+ .apply { page = soneRequest.parameters["sonePage"].emptyToNull?.toIntOrNull() ?: 0 }
val postPagination = cache.get(phrases) {
- webInterface.core.sones
+ soneRequest.core.sones
.flatMap(Sone::getPosts)
.filter { Post.FUTURE_POSTS_FILTER.apply(it) }
- .scoreAndPaginate(phrases) { it.allText(soneNameCache) }
- }.apply { page = freenetRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 }
+ .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache, soneRequest.core::getReplies) }
+ }.apply { page = soneRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 }
- Logger.normal(SearchPage::class.java, "Finished search for “${freenetRequest.parameters["query"]}” in ${System.currentTimeMillis() - startTime}ms.")
+ Logger.normal(SearchPage::class.java, "Finished search for “${soneRequest.parameters["query"]}” in ${System.currentTimeMillis() - startTime}ms.")
templateContext["sonePagination"] = sonePagination
templateContext["soneHits"] = sonePagination.items
templateContext["postPagination"] = postPagination
templateContext["postHits"] = postPagination.items
}
- private fun <T> Iterable<T>.scoreAndPaginate(phrases: Iterable<Phrase>, texter: (T) -> String) =
+ private fun <T> Iterable<T>.scoreAndPaginate(phrases: Iterable<Phrase>, postsPerPage: Int, texter: (T) -> String) =
map { it to score(texter(it), phrases) }
.filter { it.second > 0 }
.sortedByDescending { it.second }
.map { it.first }
- .paginate(webInterface.core.preferences.postsPerPage)
+ .paginate(postsPerPage)
private fun Sone.names() =
with(profile) {
private fun Sone.allText(soneNameCache: (Sone) -> String) =
(soneNameCache(this) + profile.fields.map { "${it.name} ${it.value}" }.joinToString(" ", " ")).toLowerCase()
- private fun Post.allText(soneNameCache: (Sone) -> String) =
- (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + webInterface.core.getReplies(id)
+ private fun Post.allText(soneNameCache: (Sone) -> String, getReplies: (String) -> Collection<PostReply>) =
+ (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + getReplies(id)
.filter { PostReply.FUTURE_REPLY_FILTER.apply(it) }
.map { "${soneNameCache(it.sone)} ${it.text}" }.joinToString(" ", " ")).toLowerCase()
+ private fun Iterable<Phrase>.indicesFor(text: String, predicate: (Phrase) -> Boolean) =
+ filter(predicate).map(Phrase::phrase).map(String::toLowerCase).flatMap { text.findAll(it) }
+
private fun score(text: String, phrases: Iterable<Phrase>): Double {
val requiredPhrases = phrases.count { it.required }
- val requiredHits = phrases.filter(Phrase::required)
- .map(Phrase::phrase)
- .flatMap { text.findAll(it) }
+ val requiredHits = phrases.indicesFor(text, Phrase::required)
.map { Math.pow(1 - it / text.length.toDouble(), 2.0) }
.sum()
- val optionalHits = phrases.filter(Phrase::optional)
- .map(Phrase::phrase)
- .flatMap { text.findAll(it) }
+ val optionalHits = phrases.indicesFor(text, Phrase::optional)
.map { Math.pow(1 - it / text.length.toDouble(), 2.0) }
.sum()
- val forbiddenHits = phrases.filter(Phrase::forbidden)
- .map(Phrase::phrase)
- .map { text.findAll(it).size }
- .sum()
+ val forbiddenHits = phrases.indicesFor(text, Phrase::forbidden)
+ .count()
return requiredHits * 3 + optionalHits + (requiredHits - requiredPhrases) * 5 - (forbiddenHits * 2)
}
private fun String.parse() =
StringEscaper.parseLine(this)
- .map(String::toLowerCase)
.map {
when {
it == "+" || it == "-" -> Phrase(it, OPTIONAL)
package net.pterodactylus.sone.web.pages
-import freenet.clients.http.ToadletContext
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.main.SonePlugin
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.web.SessionProvider
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.sone.web.page.FreenetTemplatePage
-import net.pterodactylus.util.notify.Notification
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import java.net.URLEncoder
+import freenet.clients.http.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.notify.*
+import net.pterodactylus.util.template.*
+import net.pterodactylus.util.web.*
+import java.net.*
/**
* Base page for the Sone web interface.
*/
open class SoneTemplatePage(
- path: String,
- protected val webInterface: WebInterface,
- template: Template,
+ private val webInterface: WebInterface,
+ loaders: Loaders,
+ templateRenderer: TemplateRenderer,
private val pageTitleKey: String? = null,
- private val requiresLogin: Boolean = true
-) : FreenetTemplatePage(path, webInterface.templateContextFactory, template, "noPermission.html") {
-
- @JvmOverloads
- constructor(path: String, template: Template, pageTitleKey: String?, webInterface: WebInterface, requireLogin: Boolean = false) :
- this(path, webInterface, template, pageTitleKey, requireLogin)
-
- constructor(path: String, template: Template, webInterface: WebInterface, requireLogin: Boolean = true) :
- this(path, webInterface, template, null, requireLogin)
+ private val requiresLogin: Boolean = false,
+ private val pageTitle: (FreenetRequest) -> String = { pageTitleKey?.let(webInterface.l10n::getString) ?: "" }
+) : FreenetTemplatePage(templateRenderer, loaders, "noPermission.html") {
private val core = webInterface.core
- protected val sessionProvider: SessionProvider = webInterface
+ private val sessionProvider: SessionProvider = webInterface
protected fun getCurrentSone(toadletContext: ToadletContext, createSession: Boolean = true) =
sessionProvider.getCurrentSone(toadletContext, createSession)
fun requiresLogin() = requiresLogin
- override public fun getPageTitle(freenetRequest: FreenetRequest) =
- pageTitleKey?.let(webInterface.l10n::getString) ?: ""
+ override fun getPageTitle(request: FreenetRequest) = getPageTitle(request.toSoneRequest(core, webInterface))
+
+ open fun getPageTitle(soneRequest: SoneRequest) = pageTitle(soneRequest)
- override public fun getStyleSheets() =
- listOf("css/sone.css")
+ override val styleSheets = listOf("css/sone.css")
- override public fun getShortcutIcon() = "images/icon.png"
+ override val shortcutIcon = "images/icon.png"
- override public fun getAdditionalLinkNodes(request: FreenetRequest) =
+ override fun getAdditionalLinkNodes(request: FreenetRequest) =
listOf(mapOf(
"rel" to "search",
"type" to "application/opensearchdescription+xml",
"href" to "http://${request.httpRequest.getHeader("host")}/Sone/OpenSearch.xml"
))
- final override public fun processTemplate(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- super.processTemplate(freenetRequest, templateContext)
+ final override fun processTemplate(request: FreenetRequest, templateContext: TemplateContext) {
+ super.processTemplate(request, templateContext)
templateContext["preferences"] = core.preferences
- templateContext["currentSone"] = getCurrentSone(freenetRequest.toadletContext)
+ templateContext["currentSone"] = getCurrentSone(request.toadletContext)
templateContext["localSones"] = core.localSones
- templateContext["request"] = freenetRequest
+ templateContext["request"] = request
templateContext["currentVersion"] = SonePlugin.getPluginVersion()
templateContext["hasLatestVersion"] = core.updateChecker.hasLatestVersion()
templateContext["latestEdition"] = core.updateChecker.latestEdition
templateContext["latestVersion"] = core.updateChecker.latestVersion
templateContext["latestVersionTime"] = core.updateChecker.latestVersionDate
- webInterface.getNotifications(getCurrentSone(freenetRequest.toadletContext)).sortedBy(Notification::getCreatedTime).run {
+ webInterface.getNotifications(getCurrentSone(request.toadletContext)).sortedBy(Notification::getCreatedTime).run {
templateContext["notifications"] = this
templateContext["notificationHash"] = this.hashCode()
}
- handleRequest(freenetRequest, templateContext)
+ handleRequest(request, templateContext)
}
- internal open fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ open fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ handleRequest(freenetRequest.toSoneRequest(core, webInterface), templateContext)
}
- override public fun getRedirectTarget(freenetRequest: FreenetRequest): String? {
- if (requiresLogin && getCurrentSone(freenetRequest.toadletContext) == null) {
- val parameters = freenetRequest.httpRequest.parameterNames
- .flatMap { name -> freenetRequest.httpRequest.getMultipleParam(name).map { name to it } }
+ open fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ }
+
+ override fun getRedirectTarget(request: FreenetRequest): String? {
+ if (requiresLogin && getCurrentSone(request.toadletContext) == null) {
+ val parameters = request.httpRequest.parameterNames
+ .flatMap { name -> request.httpRequest.getMultipleParam(name).map { name to it } }
.joinToString("&") { "${it.first.urlEncode}=${it.second.urlEncode}" }
.emptyToNull
- return "login.html?target=${freenetRequest.httpRequest.path}${parameters?.let { ("?" + it).urlEncode } ?: ""}"
+ return "login.html?target=${request.httpRequest.path}${parameters?.let { ("?" + it).urlEncode } ?: ""}"
}
return null
}
private val String.urlEncode: String get() = URLEncoder.encode(this, "UTF-8")
- override fun isEnabled(toadletContext: ToadletContext) = when {
- requiresLogin && getCurrentSone(toadletContext) == null -> false
- core.preferences.isRequireFullAccess && !toadletContext.isAllowedFullAccess -> false
+ override fun isEnabled(toadletContext: ToadletContext) =
+ isEnabled(SoneRequest(toadletContext.uri, Method.GET, HTTPRequestImpl(toadletContext.uri, "GET"), toadletContext, webInterface.l10n, webInterface.sessionManager, core, webInterface))
+
+ open fun isEnabled(soneRequest: SoneRequest) = when {
+ requiresLogin && getCurrentSone(soneRequest.toadletContext) == null -> false
+ core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess -> false
else -> true
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user trust another Sone. This will assign a configurable
* amount of trust to an identity.
*/
-class TrustPage(template: Template, webInterface: WebInterface) :
- LoggedInPage("trust.html", template, "Page.Trust.Title", webInterface) {
+@ToadletPath("trust.html")
+class TrustPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.Trust.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- webInterface.core.getSone(freenetRequest.parameters["sone"]!!)?.let { sone ->
- webInterface.core.trustSone(currentSone, sone)
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ soneRequest.core.getSone(soneRequest.parameters["sone"]!!)?.let { sone ->
+ soneRequest.core.trustSone(currentSone, sone)
}
- throw RedirectException(freenetRequest.parameters["returnPage", 256])
+ throw RedirectException(soneRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.utils.also
-import net.pterodactylus.sone.utils.isGET
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user unbookmark a post.
*/
-class UnbookmarkPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("unbookmark.html", template, "Page.Unbookmark.Title", webInterface, false) {
+@ToadletPath("unbookmark.html")
+class UnbookmarkPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.Unbookmark.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
when {
- freenetRequest.isGET && (freenetRequest.parameters["post"] == "allNotLoaded") -> {
- webInterface.core.bookmarkedPosts
+ soneRequest.isGET && (soneRequest.parameters["post"] == "allNotLoaded") -> {
+ soneRequest.core.bookmarkedPosts
.filterNot(Post::isLoaded)
- .forEach(webInterface.core::unbookmarkPost)
+ .forEach(soneRequest.core::unbookmarkPost)
throw RedirectException("bookmarks.html")
}
- freenetRequest.isPOST -> {
- freenetRequest.parameters["post", 36]
- ?.let(webInterface.core::getPost)
- ?.also(webInterface.core::unbookmarkPost)
- throw RedirectException(freenetRequest.parameters["returnPage", 256])
+ soneRequest.isPOST -> {
+ soneRequest.parameters["post", 36]
+ ?.let(soneRequest.core::getPost)
+ ?.also(soneRequest.core::unbookmarkPost)
+ throw RedirectException(soneRequest.parameters["returnPage", 256])
}
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user unfollow another Sone.
*/
-class UnfollowSonePage(template: Template, webInterface: WebInterface) :
- LoggedInPage("unfollowSone.html", template, "Page.UnfollowSone.Title", webInterface) {
+@ToadletPath("unfollowSone.html")
+class UnfollowSonePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.UnfollowSone.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- freenetRequest.parameters["sone"]!!.split(Regex("[ ,]+"))
- .forEach { webInterface.core.unfollowSone(currentSone, it) }
- throw RedirectException(freenetRequest.parameters["returnPage", 256])
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ soneRequest.parameters["sone"]!!.split(Regex("[ ,]+"))
+ .forEach { soneRequest.core.unfollowSone(currentSone, it) }
+ throw RedirectException(soneRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user unlike a [net.pterodactylus.sone.data.Post] or [net.pterodactylus.sone.data.Reply].
*/
-class UnlikePage(template: Template, webInterface: WebInterface):
- LoggedInPage("unlike.html", template, "Page.Unlike.Title", webInterface) {
+@ToadletPath("unlike.html")
+class UnlikePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.Unlike.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- when (freenetRequest.parameters["type"]) {
- "post" -> currentSone.removeLikedPostId(freenetRequest.parameters["post"]!!)
- "reply" -> currentSone.removeLikedReplyId(freenetRequest.parameters["reply"]!!)
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ when (soneRequest.parameters["type"]) {
+ "post" -> currentSone.removeLikedPostId(soneRequest.parameters["post"]!!)
+ "reply" -> currentSone.removeLikedReplyId(soneRequest.parameters["reply"]!!)
}
- throw RedirectException(freenetRequest.parameters["returnPage", 256])
+ throw RedirectException(soneRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* This page lets the user unlock a [net.pterodactylus.sone.data.Sone] to allow its insertion.
*/
-class UnlockSonePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("unlockSone.html", template, "Page.UnlockSone.Title", webInterface, false) {
+@ToadletPath("unlockSone.html")
+class UnlockSonePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.UnlockSone.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- freenetRequest.parameters["sone", 44]
- .let(webInterface.core::getLocalSone)
- ?.also(webInterface.core::unlockSone)
- throw RedirectException(freenetRequest.parameters["returnPage", 256])
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ soneRequest.parameters["sone", 44]
+ .let(soneRequest.core::getLocalSone)
+ ?.also(soneRequest.core::unlockSone)
+ throw RedirectException(soneRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import javax.inject.*
/**
* Page that lets the user untrust another Sone. This will remove all trust
* assignments for an identity.
*/
-class UntrustPage(template: Template, webInterface: WebInterface) :
- LoggedInPage("untrust.html", template, "Page.Untrust.Title", webInterface) {
+@ToadletPath("untrust.html")
+class UntrustPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.Untrust.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- freenetRequest.parameters["sone", 44]!!
- .let(webInterface.core::getSone)
- ?.also { webInterface.core.untrustSone(currentSone, it) }
- throw RedirectException(freenetRequest.parameters["returnPage", 256])
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ soneRequest.parameters["sone", 44]!!
+ .let(soneRequest.core::getSone)
+ ?.also { soneRequest.core.untrustSone(currentSone, it) }
+ throw RedirectException(soneRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
-import freenet.support.api.Bucket
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.text.TextFilter
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.utils.headers
-import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.utils.use
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import java.awt.image.BufferedImage
-import java.io.ByteArrayInputStream
-import java.io.ByteArrayOutputStream
-import javax.imageio.ImageIO
+import freenet.support.api.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import java.awt.image.*
+import java.io.*
+import javax.imageio.*
+import javax.inject.*
/**
* Page implementation that lets the user upload an image.
*/
-class UploadImagePage(template: Template, webInterface: WebInterface):
- LoggedInPage("uploadImage.html", template, "Page.UploadImage.Title", webInterface) {
+@TemplatePath("/templates/invalid.html")
+@ToadletPath("uploadImage.html")
+class UploadImagePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ LoggedInPage("Page.UploadImage.Title", webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
- if (freenetRequest.isPOST) {
- val parentAlbum = freenetRequest.parameters["parent"]!!.let(webInterface.core::getAlbum) ?: throw RedirectException("noPermission.html")
+ override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) {
+ if (soneRequest.isPOST) {
+ val parentAlbum = soneRequest.parameters["parent"]!!.let(soneRequest.core::getAlbum) ?: throw RedirectException("noPermission.html")
if (parentAlbum.sone != currentSone) {
throw RedirectException("noPermission.html")
}
- val title = freenetRequest.parameters["title", 200].emptyToNull ?: throw RedirectException("emptyImageTitle.html")
+ val title = soneRequest.parameters["title", 200].emptyToNull ?: throw RedirectException("emptyImageTitle.html")
- val uploadedFile = freenetRequest.httpRequest.getUploadedFile("image")
+ val uploadedFile = soneRequest.httpRequest.getUploadedFile("image")
val bytes = uploadedFile.data.use { it.toByteArray() }
val bufferedImage = bytes.toImage()
if (bufferedImage == null) {
- templateContext["messages"] = webInterface.l10n.getString("Page.UploadImage.Error.InvalidImage")
+ templateContext["messages"] = soneRequest.l10n.getString("Page.UploadImage.Error.InvalidImage")
return
}
- val temporaryImage = webInterface.core.createTemporaryImage(bytes.mimeType, bytes)
- webInterface.core.createImage(currentSone, parentAlbum, temporaryImage).modify().apply {
+ val temporaryImage = soneRequest.core.createTemporaryImage(bytes.mimeType, bytes)
+ soneRequest.core.createImage(currentSone, parentAlbum, temporaryImage).modify().apply {
setWidth(bufferedImage.width)
setHeight(bufferedImage.height)
setTitle(title)
- setDescription(TextFilter.filter(freenetRequest.headers["Host"], freenetRequest.parameters["description", 4000]))
+ setDescription(TextFilter.filter(soneRequest.headers["Host"], soneRequest.parameters["description", 4000]))
}.update()
throw RedirectException("imageBrowser.html?album=${parentAlbum.id}")
}
ImageIO.read(it)
}
- private val ByteArray.mimeType get() = ByteArrayInputStream(this).use {
- ImageIO.createImageInputStream(it).use {
- ImageIO.getImageReaders(it).asSequence()
- .firstOrNull()?.originatingProvider?.mimeTypes?.firstOrNull()
- ?: UNKNOWN_MIME_TYPE
+ private val ByteArray.mimeType
+ get() = ByteArrayInputStream(this).use {
+ ImageIO.createImageInputStream(it).use {
+ ImageIO.getImageReaders(it).asSequence()
+ .firstOrNull()?.originatingProvider?.mimeTypes?.firstOrNull()
+ ?: UNKNOWN_MIME_TYPE
+ }
}
- }
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.template.SoneAccessor
-import net.pterodactylus.sone.utils.let
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import java.net.URI
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.template.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import java.net.*
+import javax.inject.*
/**
* This page lets the user view a post and all its replies.
*/
-class ViewPostPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("viewPost.html", template, "Page.ViewPost.Title", webInterface, false) {
+@TemplatePath("/templates/viewPost.html")
+@ToadletPath("viewPost.html")
+class ViewPostPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.ViewPost.Title") {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- templateContext["post"] = freenetRequest.parameters["post"]?.let(webInterface.core::getPost)
- templateContext["raw"] = freenetRequest.parameters["raw"] == "true"
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ templateContext["post"] = soneRequest.parameters["post"]?.let(soneRequest.core::getPost)
+ templateContext["raw"] = soneRequest.parameters["raw"] == "true"
}
- override fun isLinkExcepted(link: URI?) = true
+ override fun isLinkExcepted(link: URI) = true
- public override fun getPageTitle(freenetRequest: FreenetRequest) =
- (freenetRequest.parameters["post"]?.let(webInterface.core::getPost)?.let {
+ override fun getPageTitle(soneRequest: SoneRequest) =
+ (soneRequest.parameters["post"]?.let(soneRequest.core::getPost)?.let {
if (it.text.length > 20) {
it.text.substring(0..19) + "…"
} else {
it.text
} + " - ${SoneAccessor.getNiceName(it.sone)} - "
- } ?: "") + super.getPageTitle(freenetRequest)
+ } ?: "") + super.getPageTitle(soneRequest)
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.template.SoneAccessor
-import net.pterodactylus.sone.utils.mapPresent
-import net.pterodactylus.sone.utils.paginate
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import java.net.URI
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.template.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import java.net.*
+import javax.inject.*
/**
* Lets the user browser another Sone.
*/
-class ViewSonePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("viewSone.html", template, webInterface, false) {
+@TemplatePath("/templates/viewSone.html")
+@ToadletPath("viewSone.html")
+class ViewSonePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- templateContext["soneId"] = freenetRequest.parameters["sone"]
- freenetRequest.parameters["sone"]!!.let(webInterface.core::getSone)?.let { sone ->
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ templateContext["soneId"] = soneRequest.parameters["sone"]
+ soneRequest.parameters["sone"]!!.let(soneRequest.core::getSone)?.let { sone ->
templateContext["sone"] = sone
val sonePosts = sone.posts
- val directedPosts = webInterface.core.getDirectedPosts(sone.id)
+ val directedPosts = soneRequest.core.getDirectedPosts(sone.id)
(sonePosts + directedPosts)
.sortedByDescending(Post::getTime)
- .paginate(webInterface.core.preferences.postsPerPage)
- .apply { page = freenetRequest.parameters["postPage"]?.toIntOrNull() ?: 0 }
+ .paginate(soneRequest.core.preferences.postsPerPage)
+ .apply { page = soneRequest.parameters["postPage"]?.toIntOrNull() ?: 0 }
.also {
templateContext["postPagination"] = it
templateContext["posts"] = it.items
.distinct()
.minus(sonePosts)
.minus(directedPosts)
- .sortedByDescending { webInterface.core.getReplies(it.id).first().time }
- .paginate(webInterface.core.preferences.postsPerPage)
- .apply { page = freenetRequest.parameters["repliedPostPage"]?.toIntOrNull() ?: 0 }
+ .sortedByDescending { soneRequest.core.getReplies(it.id).first().time }
+ .paginate(soneRequest.core.preferences.postsPerPage)
+ .apply { page = soneRequest.parameters["repliedPostPage"]?.toIntOrNull() ?: 0 }
.also {
templateContext["repliedPostPagination"] = it
templateContext["repliedPosts"] = it.items
}
}
- override fun isLinkExcepted(link: URI?) = true
+ override fun isLinkExcepted(link: URI) = true
- public override fun getPageTitle(freenetRequest: FreenetRequest): String =
- freenetRequest.parameters["sone"]!!.let(webInterface.core::getSone)?.let { sone ->
- "${SoneAccessor.getNiceName(sone)} - ${webInterface.l10n.getString("Page.ViewSone.Title")}"
- } ?: webInterface.l10n.getString("Page.ViewSone.Page.TitleWithoutSone")
+ override fun getPageTitle(soneRequest: SoneRequest): String =
+ soneRequest.parameters["sone"]!!.let(soneRequest.core::getSone)?.let { sone ->
+ "${SoneAccessor.getNiceName(sone)} - ${soneRequest.l10n.getString("Page.ViewSone.Title")}"
+ } ?: soneRequest.l10n.getString("Page.ViewSone.Page.TitleWithoutSone")
}
Page.UnfollowSone.Title=Sone entfolgen - Sone
-Page.ImageBrowser.Title=Bildergallerie - Sone
+Page.ImageBrowser.Title=Bildergalerie - Sone
Page.ImageBrowser.Album.Title=Album “{album}”
Page.ImageBrowser.Album.Error.NotFound.Text=Das gewünschte Album konnte nicht gefunden werden. Es ist möglich, dass es noch nicht herunter geladen oder bereits gelöscht wurde.
Page.ImageBrowser.Sone.Title=Alben von {sone}
/**
* Unit test for {@link ConfigurationSoneParser}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ConfigurationSoneParserTest {
/**
* Unit test for {@link Core} and its subclasses.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class CoreTest {
import net.pterodactylus.sone.core.FreenetInterface.BackgroundFetchCallback;
import net.pterodactylus.sone.core.FreenetInterface.Callback;
-import net.pterodactylus.sone.core.FreenetInterface.Fetched;
import net.pterodactylus.sone.core.FreenetInterface.InsertToken;
import net.pterodactylus.sone.core.FreenetInterface.InsertTokenSupplier;
import net.pterodactylus.sone.core.event.ImageInsertAbortedEvent;
/**
* Unit test for {@link FreenetInterface}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FreenetInterfaceTest {
@Test
public void insertingADirectory() throws InsertException, SoneException {
FreenetURI freenetUri = mock(FreenetURI.class);
- HashMap<String, Object> manifestEntries = new HashMap<String, Object>();
+ HashMap<String, Object> manifestEntries = new HashMap<>();
String defaultFile = "index.html";
FreenetURI resultingUri = mock(FreenetURI.class);
when(highLevelSimpleClient.insertManifest(eq(freenetUri), eq(manifestEntries), eq(defaultFile))).thenReturn(resultingUri);
FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI().uskForSSK();
Callback callback = mock(Callback.class);
freenetInterface.registerUsk(freenetUri, callback);
- verify(uskManager).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), eq((RequestClient) highLevelSimpleClient));
+ verify(uskManager).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class));
}
@Test
FreenetURI freenetUri = new FreenetURI("KSK@GPLv3.txt");
Callback callback = mock(Callback.class);
freenetInterface.registerUsk(freenetUri, callback);
- verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), eq((RequestClient) highLevelSimpleClient));
+ verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class));
}
@Test
throws MalformedURLException {
FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI();
freenetInterface.registerActiveUsk(freenetUri, null);
- verify(uskManager, never()).subscribe(any(USK.class),
- any(USKCallback.class), anyBoolean(),
- eq((RequestClient) highLevelSimpleClient));
+ verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class));
}
@Test
throws MalformedURLException {
FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI();
freenetInterface.registerPassiveUsk(freenetUri, null);
- verify(uskManager, never()).subscribe(any(USK.class),
- any(USKCallback.class), anyBoolean(),
- eq((RequestClient) highLevelSimpleClient));
+ verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class));
}
@Test
@Test
public void insertTokenSupplierSuppliesInsertTokens() {
- InsertTokenSupplier insertTokenSupplier = freenetInterface.new InsertTokenSupplier();
+ InsertTokenSupplier insertTokenSupplier = new InsertTokenSupplier(freenetInterface);
assertThat(insertTokenSupplier.apply(image), notNullValue());
}
+++ /dev/null
-package net.pterodactylus.sone.core;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import net.pterodactylus.sone.core.FreenetInterface.InsertToken;
-import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.sone.data.TemporaryImage;
-
-import com.google.common.base.Function;
-import org.junit.Test;
-
-/**
- * Unit test for {@link ImageInserter}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class ImageInserterTest {
-
- private final TemporaryImage temporaryImage = when(mock(TemporaryImage.class).getId()).thenReturn("image-id").getMock();
- private final Image image = when(mock(Image.class).getId()).thenReturn("image-id").getMock();
- private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
- private final InsertToken insertToken = mock(InsertToken.class);
- private final Function<Image, InsertToken> insertTokenSupplier = when(mock(Function.class).apply(any(Image.class))).thenReturn(insertToken).getMock();
- private final ImageInserter imageInserter = new ImageInserter(freenetInterface, insertTokenSupplier);
-
- @Test
- public void inserterInsertsImage() throws SoneException {
- imageInserter.insertImage(temporaryImage, image);
- verify(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken.class));
- }
-
- @Test
- public void exceptionWhenInsertingImageIsIgnored() throws SoneException {
- doThrow(SoneException.class).when(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken.class));
- imageInserter.insertImage(temporaryImage, image);
- verify(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken.class));
- }
-
- @Test
- public void cancellingImageInsertThatIsNotRunningDoesNothing() {
- imageInserter.cancelImageInsert(image);
- verify(insertToken, never()).cancel();
- }
-
- @Test
- public void cancellingImage() {
- imageInserter.insertImage(temporaryImage, image);
- imageInserter.cancelImageInsert(image);
- verify(insertToken).cancel();
- }
-
-}
/**
* Unit test for {@link Options}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class OptionsTest {
/**
* Unit test for {@link PreferencesLoader}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PreferencesLoaderTest {
assertThat(preferences.getImagesPerPage(), is(12));
assertThat(preferences.getCharactersPerPost(), is(150));
assertThat(preferences.getPostCutOffLength(), is(300));
- assertThat(preferences.isRequireFullAccess(), is(true));
+ assertThat(preferences.getRequireFullAccess(), is(true));
assertThat(preferences.getPositiveTrust(), is(50));
assertThat(preferences.getNegativeTrust(), is(-50));
assertThat(preferences.getTrustComment(), is("Trusted"));
- assertThat(preferences.isFcpInterfaceActive(), is(true));
+ assertThat(preferences.getFcpInterfaceActive(), is(true));
assertThat(preferences.getFcpFullAccessRequired(), is(WRITING));
}
+++ /dev/null
-package net.pterodactylus.sone.core;
-
-import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS;
-import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO;
-import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
-import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
-import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent;
-import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent;
-import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged;
-
-import com.google.common.eventbus.EventBus;
-import org.junit.After;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-/**
- * Unit test for {@link Preferences}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class PreferencesTest {
-
- private final EventBus eventBus = mock(EventBus.class);
- private final Preferences preferences = new Preferences(eventBus);
-
- @After
- public void tearDown() {
- verifyNoMoreInteractions(eventBus);
- }
-
- @Test
- public void preferencesRetainInsertionDelay() {
- preferences.setInsertionDelay(15);
- assertThat(preferences.getInsertionDelay(), is(15));
- verify(eventBus).post(any(InsertionDelayChangedEvent.class));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidInsertionDelayIsRejected() {
- preferences.setInsertionDelay(-15);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenInsertionDelayIsSetToNull() {
- preferences.setInsertionDelay(null);
- assertThat(preferences.getInsertionDelay(), is(60));
- verify(eventBus).post(any(InsertionDelayChangedEvent.class));
- }
-
- @Test
- public void preferencesStartWithInsertionDelayDefaultValue() {
- assertThat(preferences.getInsertionDelay(), is(60));
- }
-
- @Test
- public void preferencesRetainPostsPerPage() {
- preferences.setPostsPerPage(15);
- assertThat(preferences.getPostsPerPage(), is(15));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidPostsPerPageIsRejected() {
- preferences.setPostsPerPage(-15);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenPostsPerPageIsSetToNull() {
- preferences.setPostsPerPage(null);
- assertThat(preferences.getPostsPerPage(), is(10));
- }
-
- @Test
- public void preferencesStartWithPostsPerPageDefaultValue() {
- assertThat(preferences.getPostsPerPage(), is(10));
- }
-
- @Test
- public void preferencesRetainImagesPerPage() {
- preferences.setImagesPerPage(15);
- assertThat(preferences.getImagesPerPage(), is(15));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidImagesPerPageIsRejected() {
- preferences.setImagesPerPage(-15);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenImagesPerPageIsSetToNull() {
- preferences.setImagesPerPage(null);
- assertThat(preferences.getImagesPerPage(), is(9));
- }
-
- @Test
- public void preferencesStartWithImagesPerPageDefaultValue() {
- assertThat(preferences.getImagesPerPage(), is(9));
- }
-
- @Test
- public void preferencesRetainCharactersPerPost() {
- preferences.setCharactersPerPost(150);
- assertThat(preferences.getCharactersPerPost(), is(150));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidCharactersPerPostIsRejected() {
- preferences.setCharactersPerPost(-15);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenCharactersPerPostIsSetToNull() {
- preferences.setCharactersPerPost(null);
- assertThat(preferences.getCharactersPerPost(), is(400));
- }
-
- @Test
- public void preferencesStartWithCharactersPerPostDefaultValue() {
- assertThat(preferences.getCharactersPerPost(), is(400));
- }
-
- @Test
- public void preferencesRetainPostCutOffLength() {
- preferences.setPostCutOffLength(150);
- assertThat(preferences.getPostCutOffLength(), is(150));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidPostCutOffLengthIsRejected() {
- preferences.setPostCutOffLength(-15);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void cutOffLengthOfMinusOneIsNotAllowed() {
- preferences.setPostCutOffLength(-1);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenPostCutOffLengthIsSetToNull() {
- preferences.setPostCutOffLength(null);
- assertThat(preferences.getPostCutOffLength(), is(200));
- }
-
- @Test
- public void preferencesStartWithPostCutOffLengthDefaultValue() {
- assertThat(preferences.getPostCutOffLength(), is(200));
- }
-
- @Test
- public void preferencesRetainRequireFullAccessOfTrue() {
- preferences.setRequireFullAccess(true);
- assertThat(preferences.isRequireFullAccess(), is(true));
- }
-
- @Test
- public void preferencesRetainRequireFullAccessOfFalse() {
- preferences.setRequireFullAccess(false);
- assertThat(preferences.isRequireFullAccess(), is(false));
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenRequireFullAccessIsSetToNull() {
- preferences.setRequireFullAccess(null);
- assertThat(preferences.isRequireFullAccess(), is(false));
- }
-
- @Test
- public void preferencesStartWithRequireFullAccessDefaultValue() {
- assertThat(preferences.isRequireFullAccess(), is(false));
- }
-
- @Test
- public void preferencesRetainPositiveTrust() {
- preferences.setPositiveTrust(15);
- assertThat(preferences.getPositiveTrust(), is(15));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidPositiveTrustIsRejected() {
- preferences.setPositiveTrust(-15);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenPositiveTrustIsSetToNull() {
- preferences.setPositiveTrust(null);
- assertThat(preferences.getPositiveTrust(), is(75));
- }
-
- @Test
- public void preferencesStartWithPositiveTrustDefaultValue() {
- assertThat(preferences.getPositiveTrust(), is(75));
- }
-
- @Test
- public void preferencesRetainNegativeTrust() {
- preferences.setNegativeTrust(-15);
- assertThat(preferences.getNegativeTrust(), is(-15));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidNegativeTrustIsRejected() {
- preferences.setNegativeTrust(150);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenNegativeTrustIsSetToNull() {
- preferences.setNegativeTrust(null);
- assertThat(preferences.getNegativeTrust(), is(-25));
- }
-
- @Test
- public void preferencesStartWithNegativeTrustDefaultValue() {
- assertThat(preferences.getNegativeTrust(), is(-25));
- }
-
- @Test
- public void preferencesRetainTrustComment() {
- preferences.setTrustComment("Trust");
- assertThat(preferences.getTrustComment(), is("Trust"));
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenTrustCommentIsSetToNull() {
- preferences.setTrustComment(null);
- assertThat(preferences.getTrustComment(),
- is("Set from Sone Web Interface"));
- }
-
- @Test
- public void preferencesStartWithTrustCommentDefaultValue() {
- assertThat(preferences.getTrustComment(),
- is("Set from Sone Web Interface"));
- }
-
- @Test
- public void preferencesRetainFcpInterfaceActiveOfTrue() {
- preferences.setFcpInterfaceActive(true);
- assertThat(preferences.isFcpInterfaceActive(), is(true));
- verify(eventBus).post(any(FcpInterfaceActivatedEvent.class));
- }
-
- @Test
- public void preferencesRetainFcpInterfaceActiveOfFalse() {
- preferences.setFcpInterfaceActive(false);
- assertThat(preferences.isFcpInterfaceActive(), is(false));
- verify(eventBus).post(any(FcpInterfaceDeactivatedEvent.class));
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenFcpInterfaceActiveIsSetToNull() {
- preferences.setFcpInterfaceActive(null);
- assertThat(preferences.isFcpInterfaceActive(), is(false));
- verify(eventBus).post(any(FcpInterfaceDeactivatedEvent.class));
- }
-
- @Test
- public void preferencesStartWithFcpInterfaceActiveDefaultValue() {
- assertThat(preferences.isFcpInterfaceActive(), is(false));
- }
-
- @Test
- public void preferencesRetainFcpFullAccessRequiredOfNo() {
- preferences.setFcpFullAccessRequired(NO);
- assertThat(preferences.getFcpFullAccessRequired(), is(NO));
- verifyFullAccessRequiredChangedEvent(NO);
- }
-
- private void verifyFullAccessRequiredChangedEvent(
- FullAccessRequired fullAccessRequired) {
- ArgumentCaptor<FullAccessRequiredChanged> fullAccessRequiredCaptor =
- ArgumentCaptor.forClass(FullAccessRequiredChanged.class);
- verify(eventBus).post(fullAccessRequiredCaptor.capture());
- assertThat(
- fullAccessRequiredCaptor.getValue().getFullAccessRequired(),
- is(fullAccessRequired));
- }
-
- @Test
- public void preferencesRetainFcpFullAccessRequiredOfWriting() {
- preferences.setFcpFullAccessRequired(WRITING);
- assertThat(preferences.getFcpFullAccessRequired(), is(WRITING));
- verifyFullAccessRequiredChangedEvent(WRITING);
- }
-
- @Test
- public void preferencesRetainFcpFullAccessRequiredOfAlways() {
- preferences.setFcpFullAccessRequired(ALWAYS);
- assertThat(preferences.getFcpFullAccessRequired(), is(ALWAYS));
- verifyFullAccessRequiredChangedEvent(ALWAYS);
- }
-
- @Test
- public void preferencesReturnDefaultValueWhenFcpFullAccessRequiredIsSetToNull() {
- preferences.setFcpFullAccessRequired(null);
- assertThat(preferences.getFcpFullAccessRequired(), is(ALWAYS));
- verifyFullAccessRequiredChangedEvent(ALWAYS);
- }
-
- @Test
- public void preferencesStartWithFcpFullAccessRequiredDefaultValue() {
- assertThat(preferences.getFcpFullAccessRequired(), is(ALWAYS));
- }
-
-}
+++ /dev/null
-package net.pterodactylus.sone.core;
-
-import static java.util.Arrays.asList;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.HashSet;
-
-import net.pterodactylus.sone.core.SoneChangeDetector.PostProcessor;
-import net.pterodactylus.sone.core.SoneChangeDetector.PostReplyProcessor;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Unit test for {@link SoneChangeDetector}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class SoneChangeDetectorTest {
-
- private final Sone oldSone = mock(Sone.class);
- private final Sone newSone = mock(Sone.class);
- private final SoneChangeDetector soneChangeDetector =
- new SoneChangeDetector(oldSone);
- private final Post oldPost = mock(Post.class);
- private final Post removedPost = mock(Post.class);
- private final Post newPost = mock(Post.class);
- private final PostProcessor newPostProcessor = mock(PostProcessor.class);
- private final PostProcessor removedPostProcessor =
- mock(PostProcessor.class);
- private final PostReply oldPostReply = mock(PostReply.class);
- private final PostReply removedPostReply = mock(PostReply.class);
- private final PostReply newPostReply = mock(PostReply.class);
- private final PostReplyProcessor newPostReplyProcessor =
- mock(PostReplyProcessor.class);
- private final PostReplyProcessor removedPostReplyProcessor =
- mock(PostReplyProcessor.class);
-
- @Before
- public void setupPosts() {
- when(oldSone.getPosts()).thenReturn(asList(oldPost, removedPost));
- when(newSone.getPosts()).thenReturn(asList(oldPost, newPost));
- }
-
- @Before
- public void setupPostProcessors() {
- soneChangeDetector.onNewPosts(newPostProcessor);
- soneChangeDetector.onRemovedPosts(removedPostProcessor);
- }
-
- @Before
- public void setupPostReplies() {
- when(oldSone.getReplies()).thenReturn(
- new HashSet<PostReply>(
- asList(oldPostReply, removedPostReply)));
- when(newSone.getReplies()).thenReturn(
- new HashSet<PostReply>(asList(oldPostReply, newPostReply)));
- }
-
- @Before
- public void setupPostReplyProcessors() {
- soneChangeDetector.onNewPostReplies(newPostReplyProcessor);
- soneChangeDetector.onRemovedPostReplies(removedPostReplyProcessor);
- }
-
- @Test
- public void changeDetectorDetectsChanges() {
- soneChangeDetector.detectChanges(newSone);
-
- verify(newPostProcessor).processPost(newPost);
- verify(newPostProcessor, never()).processPost(oldPost);
- verify(newPostProcessor, never()).processPost(removedPost);
- verify(removedPostProcessor).processPost(removedPost);
- verify(removedPostProcessor, never()).processPost(oldPost);
- verify(removedPostProcessor, never()).processPost(newPost);
-
- verify(newPostReplyProcessor).processPostReply(newPostReply);
- verify(newPostReplyProcessor, never()).processPostReply(oldPostReply);
- verify(newPostReplyProcessor, never()).processPostReply(
- removedPostReply);
- verify(removedPostReplyProcessor).processPostReply(removedPostReply);
- verify(removedPostReplyProcessor, never()).processPostReply(
- oldPostReply);
- verify(removedPostReplyProcessor, never()).processPostReply(
- newPostReply);
- }
-
- @Test
- public void changeDetectorDoesNotNotifyAnyProcessorIfProcessorsUnset() {
- soneChangeDetector.onNewPosts(null);
- soneChangeDetector.onRemovedPosts(null);
- soneChangeDetector.onNewPostReplies(null);
- soneChangeDetector.onRemovedPostReplies(null);
- soneChangeDetector.detectChanges(newSone);
- verify(newPostProcessor, never()).processPost(any(Post.class));
- verify(removedPostProcessor, never()).processPost(any(Post.class));
- verify(newPostReplyProcessor, never()).processPostReply(any(PostReply.class));
- verify(removedPostReplyProcessor, never()).processPostReply(any(PostReply.class));
- }
-
-}
import static net.pterodactylus.sone.data.Sone.SoneStatus.downloading;
import static net.pterodactylus.sone.data.Sone.SoneStatus.idle;
import static net.pterodactylus.sone.data.Sone.SoneStatus.unknown;
+import static net.pterodactylus.sone.web.AllPagesTestKt.getBaseInjector;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.ArgumentCaptor.forClass;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import java.io.IOException;
import java.io.InputStream;
-import net.pterodactylus.sone.core.FreenetInterface.Fetched;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.data.Sone.SoneStatus;
import net.pterodactylus.sone.freenet.wot.Identity;
+import net.pterodactylus.sone.test.GuiceKt;
import freenet.client.ClientMetadata;
import freenet.client.FetchResult;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
/**
* Unit test for {@link SoneDownloaderImpl} and its subclasses.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneDownloaderTest {
- private final Core core = mock(Core.class);
private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
private final SoneParser soneParser = mock(SoneParser.class);
- private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface, soneParser);
- private FreenetURI requestUri = mock(FreenetURI.class);
- private Sone sone = mock(Sone.class);
+ private final UpdatedSoneProcessor updatedSoneProcessor = mock(UpdatedSoneProcessor.class);
+ private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(updatedSoneProcessor, freenetInterface, soneParser);
+ private final InsertableClientSSK clientSSK = createRandom(new DummyRandomSource(), "WoT");
+ private final FreenetURI requestUri = clientSSK.getURI().setKeyType("USK").setDocName("Sone");
+ private final FreenetURI finalRequestUri = requestUri.setMetaString(new String[] { "sone.xml" });
+ private final Sone sone = mock(Sone.class);
+ private final Sone parsedSone = mock(Sone.class);
@Before
public void setupSone() {
- Sone sone = SoneDownloaderTest.this.sone;
Identity identity = mock(Identity.class);
- InsertableClientSSK clientSSK = createRandom(new DummyRandomSource(), "WoT");
when(identity.getRequestUri()).thenReturn(clientSSK.getURI().toString());
when(identity.getId()).thenReturn("identity");
when(sone.getId()).thenReturn("identity");
when(sone.getIdentity()).thenReturn(identity);
- requestUri = clientSSK.getURI().setKeyType("USK").setDocName("Sone");
- when(sone.getRequestUri()).thenAnswer(new Answer<FreenetURI>() {
- @Override
- public FreenetURI answer(InvocationOnMock invocation)
- throws Throwable {
- return requestUri;
- }
- });
+ when(sone.getRequestUri()).thenReturn(requestUri);
when(sone.getTime()).thenReturn(currentTimeMillis() - DAYS.toMillis(1));
}
@Test
public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
- FreenetURI finalRequestUri = requestUri.sskForUSK()
- .setMetaString(new String[] { "sone.xml" });
setupSoneAsUnknown();
- soneDownloader.fetchSoneAction(sone).run();
- verify(freenetInterface).fetchUri(finalRequestUri);
+ soneDownloader.fetchSoneAsSskAction(sone).run();
+ verify(freenetInterface).fetchUri(finalRequestUri.sskForUSK());
verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
- verify(core, never()).updateSone(any(Sone.class));
+ verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
}
private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
@Test
public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
- FreenetURI finalRequestUri = requestUri.sskForUSK()
- .setMetaString(new String[] { "sone.xml" });
- soneDownloader.fetchSoneAction(sone).run();
- verify(freenetInterface).fetchUri(finalRequestUri);
+ soneDownloader.fetchSoneAsSskAction(sone).run();
+ verify(freenetInterface).fetchUri(finalRequestUri.sskForUSK());
verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
- verify(core, never()).updateSone(any(Sone.class));
+ verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
}
@Test(expected = NullPointerException.class)
- public void exceptionWhileFetchingAnUnknownSoneDoesNotUpdateCore() {
- FreenetURI finalRequestUri = requestUri.sskForUSK()
- .setMetaString(new String[] { "sone.xml" });
- setupSoneAsUnknown();
- when(freenetInterface.fetchUri(finalRequestUri)).thenThrow(NullPointerException.class);
- try {
- soneDownloader.fetchSoneAction(sone).run();
- } finally {
- verify(freenetInterface).fetchUri(finalRequestUri);
- verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
- verify(core, never()).updateSone(any(Sone.class));
- }
- }
-
- @Test(expected = NullPointerException.class)
- public void exceptionWhileFetchingAKnownSoneDoesNotUpdateCore() {
- FreenetURI finalRequestUri = requestUri.sskForUSK()
- .setMetaString(new String[] { "sone.xml" });
- when(freenetInterface.fetchUri(finalRequestUri)).thenThrow( NullPointerException.class);
+ public void exceptionWhileFetchingSoneDoesNotProcessUpdatedSone() {
+ when(freenetInterface.fetchUri(any(FreenetURI.class))).thenThrow(NullPointerException.class);
try {
- soneDownloader.fetchSoneAction(sone).run();
+ soneDownloader.fetchSoneAsSskAction(sone).run();
} finally {
- verify(freenetInterface).fetchUri(finalRequestUri);
- verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
- verify(core, never()).updateSone(any(Sone.class));
+ verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
}
}
@Test
- public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
- final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
- when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
- soneDownloader.fetchSoneAction(sone).run();
- verify(core, never()).updateSone(any(Sone.class));
- }
-
- @Test
- public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException {
- final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
- when(core.soneBuilder()).thenReturn(null);
- when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
- soneDownloader.fetchSoneAction(sone).run();
- verify(core, never()).updateSone(any(Sone.class));
+ public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException, SoneException {
+ setupParsedSone();
+ soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
+ verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
+ verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
}
@Test
- public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException {
- final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
- when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
- soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
- verify(core, never()).updateSone(any(Sone.class));
+ public void fetchingACompleteSoneNotifiesTheUpdatedSoneProcessor() throws IOException, SoneException {
+ setupParsedSone();
+ soneDownloader.fetchSone(sone, sone.getRequestUri(), false);
+ verify(updatedSoneProcessor).updateSone(parsedSone);
verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
}
- private Fetched createFetchResult(FreenetURI uri, InputStream inputStream) throws IOException {
+ private void setupParsedSone() throws IOException, SoneException {
+ InputStream inputStream = mock(InputStream.class);
ClientMetadata clientMetadata = new ClientMetadata("application/xml");
Bucket bucket = mock(Bucket.class);
when(bucket.getInputStream()).thenReturn(inputStream);
FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
- return new Fetched(uri, fetchResult);
+ Fetched fetched = new Fetched(finalRequestUri, fetchResult);
+ when(freenetInterface.fetchUri(eq(finalRequestUri))).thenReturn(fetched);
+ when(soneParser.parseSone(sone, inputStream)).thenReturn(parsedSone);
+ }
+
+ @Test
+ public void soneDownloaderCanBeCreatedByDependencyInjection() {
+ assertThat(getBaseInjector().createChildInjector(
+ GuiceKt.supply(UpdatedSoneProcessor.class).byInstance(mock(UpdatedSoneProcessor.class)),
+ GuiceKt.supply(SoneParser.class).byInstance(mock(SoneParser.class))
+ ).getInstance(SoneDownloader.class), notNullValue());
}
}
package net.pterodactylus.sone.core;
import static com.google.common.io.ByteStreams.toByteArray;
-import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static java.lang.System.currentTimeMillis;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
/**
* Unit test for {@link SoneInserter} and its subclasses.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneInserterTest {
@Test
public void insertionDelayIsForwardedToSoneInserter() {
- EventBus eventBus = new AsyncEventBus(sameThreadExecutor());
+ EventBus eventBus = new AsyncEventBus(directExecutor());
eventBus.register(new SoneInserter(core, eventBus, freenetInterface, "SoneId"));
eventBus.post(new InsertionDelayChangedEvent(15));
assertThat(SoneInserter.getInsertionDelay().get(), is(15));
@Test
public void templateIsRenderedCorrectlyForManifestElement()
throws IOException {
- Map<String, Object> soneProperties = new HashMap<String, Object>();
+ Map<String, Object> soneProperties = new HashMap<>();
soneProperties.put("id", "SoneId");
ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
long now = currentTimeMillis();
@Test
public void invalidTemplateReturnsANullManifestElement() {
- Map<String, Object> soneProperties = new HashMap<String, Object>();
+ Map<String, Object> soneProperties = new HashMap<>();
ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
assertThat(manifestCreator.createManifestElement("test.txt",
"plain/text; charset=utf-8",
@Test
public void errorWhileRenderingTemplateReturnsANullManifestElement() {
- Map<String, Object> soneProperties = new HashMap<String, Object>();
+ Map<String, Object> soneProperties = new HashMap<>();
ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
when(core.toString()).thenThrow(NullPointerException.class);
assertThat(manifestCreator.createManifestElement("test.txt",
/**
* Unit test for {@link SoneModificationDetector}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneModificationDetectorTest {
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.database.AlbumBuilder;
+import net.pterodactylus.sone.database.Database;
import net.pterodactylus.sone.database.ImageBuilder;
import net.pterodactylus.sone.database.PostBuilder;
import net.pterodactylus.sone.database.PostReplyBuilder;
/**
* Unit test for {@link SoneParser}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneParserTest {
- private final Core core = mock(Core.class);
- private final SoneParser soneParser = new SoneParser(core);
+ private final Database database = mock(Database.class);
+ private final SoneParser soneParser = new SoneParser(database);
private final Sone sone = mock(Sone.class);
private FreenetURI requestUri = mock(FreenetURI.class);
private final PostBuilder postBuilder = mock(PostBuilder.class);
- private final List<Post> createdPosts = new ArrayList<Post>();
+ private final List<Post> createdPosts = new ArrayList<>();
private Post post = mock(Post.class);
private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
- private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
+ private final Set<PostReply> createdPostReplies = new HashSet<>();
private PostReply postReply = mock(PostReply.class);
private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
private final ListMultimap<Album, Album>
nestedAlbums = ArrayListMultimap.create();
private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
private Album album = mock(Album.class);
- private final Map<String, Album> albums = new HashMap<String, Album>();
+ private final Map<String, Album> albums = new HashMap<>();
private final ImageBuilder imageBuilder = mock(ImageBuilder.class);
private Image image = mock(Image.class);
- private final Map<String, Image> images = new HashMap<String, Image>();
+ private final Map<String, Image> images = new HashMap<>();
@Before
public void setupSone() {
@Before
public void setupSoneBuilder() {
- when(core.soneBuilder()).thenAnswer(new Answer<SoneBuilder>() {
+ when(database.newSoneBuilder()).thenAnswer(new Answer<SoneBuilder>() {
@Override
public SoneBuilder answer(InvocationOnMock invocation) {
return new MemorySoneBuilder(null);
return post;
}
});
- when(core.postBuilder()).thenReturn(postBuilder);
+ when(database.newPostBuilder()).thenReturn(postBuilder);
}
@Before
return postReply;
}
});
- when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
+ when(database.newPostReplyBuilder()).thenReturn(postReplyBuilder);
}
@Before
return album;
}
});
- when(core.albumBuilder()).thenReturn(albumBuilder);
+ when(database.newAlbumBuilder()).thenReturn(albumBuilder);
}
@Before
public void setupAlbums() {
- when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
+ when(database.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
@Override
public Album answer(InvocationOnMock invocation)
throws Throwable {
return image;
}
});
- when(core.imageBuilder()).thenReturn(imageBuilder);
+ when(database.newImageBuilder()).thenReturn(imageBuilder);
}
@Before
public void setupImages() {
- when(core.getImage(anyString())).thenAnswer(new Answer<Image>() {
+ when(database.getImage(anyString())).thenAnswer(new Answer<Image>() {
@Override
public Image answer(InvocationOnMock invocation)
throws Throwable {
/**
* Unit test for {@link SoneRescuer}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneRescuerTest {
/**
* Unit test for {@link SoneUri}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class SoneUriTest {
import java.io.InputStream;
import net.pterodactylus.sone.core.FreenetInterface.Callback;
-import net.pterodactylus.sone.core.FreenetInterface.Fetched;
import net.pterodactylus.sone.core.event.UpdateFoundEvent;
+import net.pterodactylus.sone.main.PluginHomepage;
import net.pterodactylus.util.version.Version;
import freenet.client.ClientMetadata;
/**
* Unit test for {@link UpdateChecker}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class UpdateCheckerTest {
private final EventBus eventBus = mock(EventBus.class);
private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
private final Version currentVersion = new Version(1, 0, 0);
- private final UpdateChecker updateChecker = new UpdateChecker(eventBus, freenetInterface, currentVersion);
+ private final PluginHomepage pluginHomepage = new PluginHomepage("KSK@homepage");
+ private final UpdateChecker updateChecker = new UpdateChecker(eventBus, freenetInterface, currentVersion, pluginHomepage);
@Before
public void startUpdateChecker() {
/**
* Unit test for {@link WebOfTrustUpdaterImpl} and its subclasses.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class WebOfTrustUpdaterTest {
+++ /dev/null
-package net.pterodactylus.sone.data;
-
-import net.pterodactylus.sone.data.Profile.Field;
-
-import org.hamcrest.MatcherAssert;
-import org.hamcrest.Matchers;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-/**
- * Unit test for {@link Profile}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class ProfileTest {
-
- private final Sone sone = Mockito.mock(Sone.class);
- private final Profile profile = new Profile(sone);
-
- @Test
- public void newFieldsAreInitializedWithAnEmptyString() {
- Field newField = profile.addField("testField");
- MatcherAssert.assertThat(newField.getValue(), Matchers.is(""));
- }
-
-}
/**
* Unit test for {@link AbstractSoneBuilder}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class AbstractSoneBuilderTest {
/**
* Unit test for {@link ImageImpl}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageImplTest {
+++ /dev/null
-package net.pterodactylus.sone.database.memory;
-
-import static java.util.Arrays.asList;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import net.pterodactylus.sone.test.TestValue;
-import net.pterodactylus.util.config.Configuration;
-import net.pterodactylus.util.config.ConfigurationException;
-import net.pterodactylus.util.config.Value;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link ConfigurationLoader}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class ConfigurationLoaderTest {
-
- private final Configuration configuration = mock(Configuration.class);
- private final ConfigurationLoader configurationLoader =
- new ConfigurationLoader(configuration);
-
- @Test
- public void loaderCanLoadKnownPosts() {
- when(configuration.getStringValue("KnownPosts/0/ID"))
- .thenReturn(TestValue.from("Post2"));
- when(configuration.getStringValue("KnownPosts/1/ID"))
- .thenReturn(TestValue.from("Post1"));
- when(configuration.getStringValue("KnownPosts/2/ID"))
- .thenReturn(TestValue.<String>from(null));
- Set<String> knownPosts = configurationLoader.loadKnownPosts();
- assertThat(knownPosts, containsInAnyOrder("Post1", "Post2"));
- }
-
- @Test
- public void loaderCanLoadKnownPostReplies() {
- when(configuration.getStringValue("KnownReplies/0/ID"))
- .thenReturn(TestValue.from("PostReply2"));
- when(configuration.getStringValue("KnownReplies/1/ID"))
- .thenReturn(TestValue.from("PostReply1"));
- when(configuration.getStringValue("KnownReplies/2/ID"))
- .thenReturn(TestValue.<String>from(null));
- Set<String> knownPosts = configurationLoader.loadKnownPostReplies();
- assertThat(knownPosts,
- containsInAnyOrder("PostReply1", "PostReply2"));
- }
-
- @Test
- public void loaderCanLoadBookmarkedPosts() {
- when(configuration.getStringValue("Bookmarks/Post/0/ID"))
- .thenReturn(TestValue.from("Post2"));
- when(configuration.getStringValue("Bookmarks/Post/1/ID"))
- .thenReturn(TestValue.from("Post1"));
- when(configuration.getStringValue("Bookmarks/Post/2/ID"))
- .thenReturn(TestValue.<String>from(null));
- Set<String> knownPosts = configurationLoader.loadBookmarkedPosts();
- assertThat(knownPosts, containsInAnyOrder("Post1", "Post2"));
- }
-
- @Test
- public void loaderCanSaveBookmarkedPosts() throws ConfigurationException {
- final Value<String> post1 = TestValue.<String>from(null);
- final Value<String> post2 = TestValue.<String>from(null);
- final Value<String> post3 = TestValue.<String>from(null);
- when(configuration.getStringValue("Bookmarks/Post/0/ID")).thenReturn(post1);
- when(configuration.getStringValue("Bookmarks/Post/1/ID")).thenReturn(post2);
- when(configuration.getStringValue("Bookmarks/Post/2/ID")).thenReturn(post3);
- HashSet<String> originalPosts = new HashSet<String>(asList("Post1", "Post2"));
- configurationLoader.saveBookmarkedPosts(originalPosts);
- HashSet<String> extractedPosts =
- new HashSet<String>(asList(post1.getValue(), post2.getValue()));
- assertThat(extractedPosts, containsInAnyOrder("Post1", "Post2"));
- assertThat(post3.getValue(), nullValue());
- }
-
-}
/**
* Unit test for {@link MemoryBookmarkDatabase}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class MemoryBookmarkDatabaseTest {
mock(ConfigurationLoader.class);
private final MemoryBookmarkDatabase bookmarkDatabase =
new MemoryBookmarkDatabase(memoryDatabase, configurationLoader);
- private final Map<String, Post> posts = new HashMap<String, Post>();
+ private final Map<String, Post> posts = new HashMap<>();
@Before
public void setupMemoryDatabase() {
@Test
public void bookmarkDatabaseRetainsBookmarkedPosts() {
- Set<Post> allPosts = new HashSet<Post>(posts.values());
+ Set<Post> allPosts = new HashSet<>(posts.values());
for (Post post : allPosts) {
bookmarkDatabase.bookmarkPost(post);
}
@Test
public void removingABookmarkRemovesTheCorrectBookmark() {
- Set<Post> allPosts = new HashSet<Post>(posts.values());
+ Set<Post> allPosts = new HashSet<>(posts.values());
for (Post post : allPosts) {
bookmarkDatabase.bookmarkPost(post);
}
+++ /dev/null
-/*
- * Sone - MemoryDatabaseTest.java - Copyright © 2013–2016 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.database.memory;
-
-import static com.google.common.base.Optional.of;
-import static java.util.Arrays.asList;
-import static java.util.UUID.randomUUID;
-import static net.pterodactylus.sone.test.Matchers.isAlbum;
-import static net.pterodactylus.sone.test.Matchers.isImage;
-import static net.pterodactylus.sone.test.Matchers.isPost;
-import static net.pterodactylus.sone.test.Matchers.isPostReply;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.emptyIterable;
-import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.data.impl.AlbumImpl;
-import net.pterodactylus.sone.test.TestAlbumBuilder;
-import net.pterodactylus.sone.test.TestImageBuilder;
-import net.pterodactylus.sone.test.TestPostBuilder;
-import net.pterodactylus.sone.test.TestPostReplyBuilder;
-import net.pterodactylus.sone.test.TestValue;
-import net.pterodactylus.util.config.Configuration;
-import net.pterodactylus.util.config.Value;
-
-import com.google.common.base.Optional;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-/**
- * Tests for {@link MemoryDatabase}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class MemoryDatabaseTest {
-
- private static final String SONE_ID = "sone";
- private static final String RECIPIENT_ID = "recipient";
- private final Configuration configuration = mock(Configuration.class);
- private final MemoryDatabase memoryDatabase = new MemoryDatabase(null, configuration);
- private final Sone sone = mock(Sone.class);
-
- @Before
- public void setupSone() {
- when(sone.getId()).thenReturn(SONE_ID);
- }
-
- @Test
- public void storedSoneIsMadeAvailable() {
- Post firstPost = new TestPostBuilder().withId("post1")
- .from(SONE_ID)
- .withTime(1000L)
- .withText("post1")
- .build();
- Post secondPost = new TestPostBuilder().withId("post2")
- .from(SONE_ID)
- .withTime(2000L)
- .withText("post2")
- .to(RECIPIENT_ID)
- .build();
- List<Post> posts = asList(firstPost, secondPost);
- when(sone.getPosts()).thenReturn(posts);
- PostReply firstPostFirstReply =
- new TestPostReplyBuilder().withId("reply1")
- .from(SONE_ID)
- .to(firstPost.getId())
- .withTime(3000L)
- .withText("reply1")
- .build();
- PostReply firstPostSecondReply =
- new TestPostReplyBuilder().withId("reply3")
- .from(RECIPIENT_ID)
- .to(firstPost.getId())
- .withTime(5000L)
- .withText("reply3")
- .build();
- PostReply secondPostReply =
- new TestPostReplyBuilder().withId("reply2")
- .from(SONE_ID)
- .to(secondPost.getId())
- .withTime(4000L)
- .withText("reply2")
- .build();
- Set<PostReply> postReplies = new HashSet<PostReply>(
- asList(firstPostFirstReply, firstPostSecondReply,
- secondPostReply));
- when(sone.getReplies()).thenReturn(postReplies);
- Album firstAlbum = new TestAlbumBuilder().withId("album1")
- .by(sone)
- .build()
- .modify()
- .setTitle("album1")
- .setDescription("album-description1")
- .update();
- Album secondAlbum = new TestAlbumBuilder().withId("album2").by(
- sone).build().modify().setTitle("album2").setDescription(
- "album-description2").update();
- Album thirdAlbum = new TestAlbumBuilder().withId("album3").by(
- sone).build().modify().setTitle("album3").setDescription(
- "album-description3").update();
- firstAlbum.addAlbum(thirdAlbum);
- Album rootAlbum = mock(Album.class);
- when(rootAlbum.getAlbums()).thenReturn(
- asList(firstAlbum, secondAlbum));
- when(sone.getRootAlbum()).thenReturn(rootAlbum);
- Image firstImage = new TestImageBuilder().withId("image1")
- .build()
- .modify()
- .setSone(sone)
- .setCreationTime(1000L)
- .setKey("KSK@image1")
- .setTitle("image1")
- .setDescription("image-description1")
- .setWidth(16)
- .setHeight(9)
- .update();
- Image secondImage = new TestImageBuilder().withId("image2")
- .build()
- .modify()
- .setSone(sone)
- .setCreationTime(2000L)
- .setKey("KSK@image2")
- .setTitle("image2")
- .setDescription("image-description2")
- .setWidth(32)
- .setHeight(18)
- .update();
- Image thirdImage = new TestImageBuilder().withId("image3")
- .build()
- .modify()
- .setSone(sone)
- .setCreationTime(3000L)
- .setKey("KSK@image3")
- .setTitle("image3")
- .setDescription("image-description3")
- .setWidth(48)
- .setHeight(27)
- .update();
- firstAlbum.addImage(firstImage);
- firstAlbum.addImage(thirdImage);
- secondAlbum.addImage(secondImage);
- memoryDatabase.storeSone(sone);
- assertThat(memoryDatabase.getPost("post1"),
- isPost(firstPost.getId(), 1000L, "post1",
- Optional.<String>absent()));
- assertThat(memoryDatabase.getPost("post2"),
- isPost(secondPost.getId(), 2000L, "post2", of(RECIPIENT_ID)));
- assertThat(memoryDatabase.getPost("post3"), nullValue());
- assertThat(memoryDatabase.getPostReply("reply1"),
- isPostReply("reply1", "post1", 3000L, "reply1"));
- assertThat(memoryDatabase.getPostReply("reply2"),
- isPostReply("reply2", "post2", 4000L, "reply2"));
- assertThat(memoryDatabase.getPostReply("reply3"),
- isPostReply("reply3", "post1", 5000L, "reply3"));
- assertThat(memoryDatabase.getPostReply("reply4"), nullValue());
- assertThat(memoryDatabase.getAlbum("album1"),
- isAlbum("album1", null, "album1", "album-description1"));
- assertThat(memoryDatabase.getAlbum("album2"),
- isAlbum("album2", null, "album2", "album-description2"));
- assertThat(memoryDatabase.getAlbum("album3"),
- isAlbum("album3", "album1", "album3", "album-description3"));
- assertThat(memoryDatabase.getAlbum("album4"), nullValue());
- assertThat(memoryDatabase.getImage("image1"),
- isImage("image1", 1000L, "KSK@image1", "image1",
- "image-description1", 16, 9));
- assertThat(memoryDatabase.getImage("image2"),
- isImage("image2", 2000L, "KSK@image2", "image2",
- "image-description2", 32, 18));
- assertThat(memoryDatabase.getImage("image3"),
- isImage("image3", 3000L, "KSK@image3", "image3",
- "image-description3", 48, 27));
- assertThat(memoryDatabase.getImage("image4"), nullValue());
- }
-
- @Test
- public void storedAndRemovedSoneIsNotAvailable() {
- storedSoneIsMadeAvailable();
- memoryDatabase.removeSone(sone);
- assertThat(memoryDatabase.getSones(), empty());
- }
-
- @Test
- public void postRecipientsAreDetectedCorrectly() {
- Post postWithRecipient = createPost(of(RECIPIENT_ID));
- memoryDatabase.storePost(postWithRecipient);
- Post postWithoutRecipient = createPost(Optional.<String>absent());
- memoryDatabase.storePost(postWithoutRecipient);
- assertThat(memoryDatabase.getDirectedPosts(RECIPIENT_ID),
- contains(postWithRecipient));
- }
-
- private Post createPost(Optional<String> recipient) {
- Post postWithRecipient = mock(Post.class);
- when(postWithRecipient.getId()).thenReturn(randomUUID().toString());
- when(postWithRecipient.getSone()).thenReturn(sone);
- when(postWithRecipient.getRecipientId()).thenReturn(recipient);
- return postWithRecipient;
- }
-
- @Test
- public void postRepliesAreManagedCorrectly() {
- Post firstPost = createPost(Optional.<String>absent());
- PostReply firstPostFirstReply = createPostReply(firstPost, 1000L);
- Post secondPost = createPost(Optional.<String>absent());
- PostReply secondPostFirstReply = createPostReply(secondPost, 1000L);
- PostReply secondPostSecondReply = createPostReply(secondPost, 2000L);
- memoryDatabase.storePost(firstPost);
- memoryDatabase.storePost(secondPost);
- memoryDatabase.storePostReply(firstPostFirstReply);
- memoryDatabase.storePostReply(secondPostFirstReply);
- memoryDatabase.storePostReply(secondPostSecondReply);
- assertThat(memoryDatabase.getReplies(firstPost.getId()),
- contains(firstPostFirstReply));
- assertThat(memoryDatabase.getReplies(secondPost.getId()),
- contains(secondPostFirstReply, secondPostSecondReply));
- }
-
- private PostReply createPostReply(Post post, long time) {
- PostReply postReply = mock(PostReply.class);
- when(postReply.getId()).thenReturn(randomUUID().toString());
- when(postReply.getTime()).thenReturn(time);
- when(postReply.getPost()).thenReturn(of(post));
- final String postId = post.getId();
- when(postReply.getPostId()).thenReturn(postId);
- return postReply;
- }
-
- @Test
- public void testBasicAlbumFunctionality() {
- Album newAlbum = new AlbumImpl(mock(Sone.class));
- assertThat(memoryDatabase.getAlbum(newAlbum.getId()), nullValue());
- memoryDatabase.storeAlbum(newAlbum);
- assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(newAlbum));
- memoryDatabase.removeAlbum(newAlbum);
- assertThat(memoryDatabase.getAlbum(newAlbum.getId()), nullValue());
- }
-
- private void initializeFriends() {
- when(configuration.getStringValue("Sone/" + SONE_ID + "/Friends/0/ID")).thenReturn(
- TestValue.from("Friend1"));
- when(configuration.getStringValue("Sone/" + SONE_ID + "/Friends/1/ID")).thenReturn(
- TestValue.from("Friend2"));
- when(configuration.getStringValue("Sone/" + SONE_ID + "/Friends/2/ID")).thenReturn(
- TestValue.<String>from(null));
- }
-
- @Test
- public void friendsAreReturnedCorrectly() {
- initializeFriends();
- when(sone.isLocal()).thenReturn(true);
- Collection<String> friends = memoryDatabase.getFriends(sone);
- assertThat(friends, containsInAnyOrder("Friend1", "Friend2"));
- }
-
- @Test
- public void friendsAreOnlyLoadedOnceFromConfiguration() {
- friendsAreReturnedCorrectly();
- memoryDatabase.getFriends(sone);
- verify(configuration).getStringValue("Sone/" + SONE_ID + "/Friends/0/ID");
- }
-
- @Test
- public void friendsAreOnlyReturnedForLocalSones() {
- Collection<String> friends = memoryDatabase.getFriends(sone);
- assertThat(friends, emptyIterable());
- verify(configuration, never()).getStringValue("Sone/" + SONE_ID + "/Friends/0/ID");
- }
-
- @Test
- public void checkingForAFriendReturnsTrue() {
- initializeFriends();
- when(sone.isLocal()).thenReturn(true);
- assertThat(memoryDatabase.isFriend(sone, "Friend1"), is(true));
- }
-
- @Test
- public void checkingForAFriendThatIsNotAFriendReturnsFalse() {
- initializeFriends();
- when(sone.isLocal()).thenReturn(true);
- assertThat(memoryDatabase.isFriend(sone, "FriendX"), is(false));
- }
-
- @Test
- public void checkingForAFriendOfRemoteSoneReturnsFalse() {
- initializeFriends();
- assertThat(memoryDatabase.isFriend(sone, "Friend1"), is(false));
- }
-
- private Map<String, Value<String>> prepareConfigurationValues() {
- final Map<String, Value<String>> configurationValues = new HashMap<String, Value<String>>();
- when(configuration.getStringValue(anyString())).thenAnswer(new Answer<Value<String>>() {
- @Override
- public Value<String> answer(InvocationOnMock invocation) throws Throwable {
- Value<String> stringValue = TestValue.from(null);
- configurationValues.put((String) invocation.getArguments()[0], stringValue);
- return stringValue;
- }
- });
- return configurationValues;
- }
-
- @Test
- public void friendIsAddedCorrectlyToLocalSone() {
- Map<String, Value<String>> configurationValues = prepareConfigurationValues();
- when(sone.isLocal()).thenReturn(true);
- memoryDatabase.addFriend(sone, "Friend1");
- assertThat(configurationValues.get("Sone/" + SONE_ID + "/Friends/0/ID"),
- is(TestValue.from("Friend1")));
- assertThat(configurationValues.get("Sone/" + SONE_ID + "/Friends/1/ID"),
- is(TestValue.<String>from(null)));
- }
-
- @Test
- public void friendIsNotAddedToRemoteSone() {
- memoryDatabase.addFriend(sone, "Friend1");
- verify(configuration, never()).getStringValue(anyString());
- }
-
- @Test
- public void configurationIsWrittenOnceIfFriendIsAddedTwice() {
- prepareConfigurationValues();
- when(sone.isLocal()).thenReturn(true);
- memoryDatabase.addFriend(sone, "Friend1");
- memoryDatabase.addFriend(sone, "Friend1");
- verify(configuration, times(3)).getStringValue(anyString());
- }
-
- @Test
- public void friendIsRemovedCorrectlyFromLocalSone() {
- Map<String, Value<String>> configurationValues = prepareConfigurationValues();
- when(sone.isLocal()).thenReturn(true);
- memoryDatabase.addFriend(sone, "Friend1");
- memoryDatabase.removeFriend(sone, "Friend1");
- assertThat(configurationValues.get("Sone/" + SONE_ID + "/Friends/0/ID"),
- is(TestValue.<String>from(null)));
- assertThat(configurationValues.get("Sone/" + SONE_ID + "/Friends/1/ID"),
- is(TestValue.<String>from(null)));
- }
-
- @Test
- public void configurationIsNotWrittenWhenANonFriendIsRemoved() {
- prepareConfigurationValues();
- when(sone.isLocal()).thenReturn(true);
- memoryDatabase.removeFriend(sone, "Friend1");
- verify(configuration).getStringValue(anyString());
- }
-
-}
/**
* Unit test for {@link Key}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class KeyTest {
/*
- * Sone - DefaultIdentityTest.java - Copyright © 2013–2016 David Roden
+ * Sone - DefaultIdentityTest.java - Copyright © 2013–2019 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
/**
* Unit test for {@link DefaultIdentity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultIdentityTest {
/*
- * Sone - DefaultOwnIdentityTest.java - Copyright © 2013–2016 David Roden
+ * Sone - DefaultOwnIdentityTest.java - Copyright © 2013–2019 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
/**
* Unit test for {@link DefaultOwnIdentity}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultOwnIdentityTest extends DefaultIdentityTest {
/*
- * Sone - Identities.java - Copyright © 2013–2016 David Roden
+ * Sone - Identities.java - Copyright © 2013–2019 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
/**
* Creates {@link Identity}s and {@link OwnIdentity}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Identities {
/*
- * Sone - IdentityChangeDetectorTest.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityChangeDetectorTest.java - Copyright © 2013–2019 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
/**
* Unit test for {@link IdentityChangeDetector}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityChangeDetectorTest {
/*
- * Sone - IdentityChangeEventSenderTest.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityChangeEventSenderTest.java - Copyright © 2013–2019 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
/**
* Unit test for {@link IdentityChangeEventSender}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityChangeEventSenderTest {
}
private Map<OwnIdentity, Collection<Identity>> createNewIdentities() {
- Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<OwnIdentity, Collection<Identity>>();
+ Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<>();
oldIdentities.put(ownIdentities.get(1), asList(identities.get(3), identities.get(2)));
oldIdentities.put(ownIdentities.get(2), asList(identities.get(1), identities.get(2)));
return oldIdentities;
}
private Map<OwnIdentity, Collection<Identity>> createOldIdentities() {
- Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<OwnIdentity, Collection<Identity>>();
+ Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<>();
oldIdentities.put(ownIdentities.get(0), asList(identities.get(0), identities.get(1)));
oldIdentities.put(ownIdentities.get(1), asList(identities.get(0), identities.get(1)));
return oldIdentities;
/*
- * Sone - IdentityLoaderTest.java - Copyright © 2013–2016 David Roden
+ * Sone - IdentityLoaderTest.java - Copyright © 2013–2019 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
/**
* Unit test for {@link IdentityLoader}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityLoaderTest {
/**
* Unit test for {@link IdentityManagerImpl}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityManagerTest {
/**
* Unit test for {@link IdentityEvent}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityEventTest {
/**
* Unit test for {@link OwnIdentityEvent}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class OwnIdentityEventTest {
import net.pterodactylus.util.web.Page;
import net.pterodactylus.util.web.Response;
+import freenet.clients.http.SessionManager;
import freenet.clients.http.ToadletContext;
+import freenet.l10n.BaseL10n;
import freenet.support.api.HTTPRequest;
import com.google.common.base.Charsets;
/**
* Unit test for {@link DebugLoaders}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DebugLoadersTest {
@Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+ private final BaseL10n l10n = mock(BaseL10n.class);
private final StringWriter stringWriter = new StringWriter();
private final TemplateContext templateContext = new TemplateContext();
private Loaders loaders;
Method method = Method.GET;
HTTPRequest httpRequest = mock(HTTPRequest.class);
ToadletContext toadletContext = mock(ToadletContext.class);
- FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext);
+ SessionManager sessionManager = mock(SessionManager.class);
+ FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager);
OutputStream outputStream = new ByteArrayOutputStream();
Response response = new Response(outputStream);
page.handleRequest(request, response);
import net.pterodactylus.util.web.Page;
import net.pterodactylus.util.web.Response;
+import freenet.clients.http.SessionManager;
import freenet.clients.http.ToadletContext;
+import freenet.l10n.BaseL10n;
import freenet.support.api.HTTPRequest;
import org.junit.Test;
/**
* Unit test for {@link DefaultLoaders}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultLoadersTest {
+ private final BaseL10n l10n = mock(BaseL10n.class);
private final Loaders loaders = new DefaultLoaders();
private final StringWriter stringWriter = new StringWriter();
private final TemplateContext templateContext = new TemplateContext();
Method method = Method.GET;
HTTPRequest httpRequest = mock(HTTPRequest.class);
ToadletContext toadletContext = mock(ToadletContext.class);
- FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext);
+ SessionManager sessionManager = mock(SessionManager.class);
+ FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager);
OutputStream outputStream = new ByteArrayOutputStream();
Response response = new Response(outputStream);
staticPage.handleRequest(request, response);
/**
* Unit test for {@link ReparseFilter}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ReparseFilterTest {
/**
* Unit test for {@link ListNotificationFilterTest}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ListNotificationFilterTest {
/**
* Unit test for {@link ListNotification}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ListNotificationTest {
public ListNotificationTest() {
when(template.getInitialContext()).thenReturn(templateInitialContext);
- listNotification = new ListNotification<Object>(ID, KEY, template);
+ listNotification = new ListNotification<>(ID, KEY, template);
}
@Test
/**
* Unit test for {@link PostVisibilityFilterTest}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PostVisibilityFilterTest {
/**
* Unit test for {@link ReplyVisibilityFilterTest}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ReplyVisibilityFilterTest {
/**
* Unit test for {@link AlbumAccessor}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class AlbumAccessorTest {
/**
* Unit test for {@link CollectionAccessor}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class CollectionAccessorTest {
private final CollectionAccessor accessor = new CollectionAccessor();
- private final Collection<Object> collection = new ArrayList<Object>();
+ private final Collection<Object> collection = new ArrayList<>();
@Before
public void setupCollection() {
/**
* Unit test for {@link CssClassNameFilter}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class CssClassNameFilterTest {
/**
* Unit test for {@link FilesystemTemplate}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FilesystemTemplateTest {
private final File tempFile;
private final FilesystemTemplate filesystemTemplate;
- private final AtomicReference<StringWriter> stringWriter = new AtomicReference<StringWriter>(new StringWriter());
+ private final AtomicReference<StringWriter> stringWriter = new AtomicReference<>(new StringWriter());
private final TemplateContext templateContext = new TemplateContext();
public FilesystemTemplateTest() throws IOException {
/**
* Unit test for {@link GetPagePlugin}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class GetPagePluginTest {
private final TemplateContext context = mock(TemplateContext.class);
private final FreenetRequest request = mock(FreenetRequest.class);
private final Map<String, String> parameters =
- new HashMap<String, String>();
+ new HashMap<>();
private HTTPRequest httpRequest = mock(HTTPRequest.class);
@Before
/**
* Unit test for {@link HttpRequestAccessor}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class HttpRequestAccessorTest {
/**
* Unit test for {@link IdentityAccessor}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IdentityAccessorTest {
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.util.template.HtmlFilter;
-import net.pterodactylus.util.template.TemplateContext;
-import net.pterodactylus.util.template.TemplateContextFactory;
import com.google.common.collect.ImmutableMap;
import org.hamcrest.Matchers;
/**
* Unit test for {@link ImageLinkFilterTest}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class ImageLinkFilterTest {
private final Core core = mock(Core.class);
- private final TemplateContextFactory templateContextFactory = new TemplateContextFactory();
- private final ImageLinkFilter imageLinkFilter = new ImageLinkFilter(core, templateContextFactory);
- private final TemplateContext templateContext = null;
+ private final ImageLinkFilter imageLinkFilter = new ImageLinkFilter(core);
private final Image image = mock(Image.class);
@Before
- public void setupTemplateContextFactory() {
- templateContextFactory.addFilter("html", new HtmlFilter());
- }
-
- @Before
public void setupCore() {
when(core.getImage("image-id", false)).thenReturn(image);
}
@Test
public void imageLinkIsGeneratedCorrectlyForNotInsertedImages() {
when(image.isInserted()).thenReturn(false);
- String result = String.valueOf(imageLinkFilter.format(templateContext, image, ImmutableMap.<String, Object>of()));
+ String result = String.valueOf(imageLinkFilter.format(null, image, ImmutableMap.<String, Object>of()));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("class"), is(""));
assertThat(imageElement.attr("src"), is("getImage.html?image=image-id"));
@Test
public void imageLinkIsGeneratedCorrectlyForInsertedImages() {
- String result = String.valueOf(imageLinkFilter.format(templateContext, image, ImmutableMap.<String, Object>of()));
+ String result = String.valueOf(imageLinkFilter.format(null, image, ImmutableMap.<String, Object>of()));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("class"), is(""));
assertThat(imageElement.attr("src"), is("/image-key"));
@Test
public void imageTitleAndDescriptionAreOverriddenCorrectly() {
- String result = String.valueOf(imageLinkFilter.format(templateContext, image, ImmutableMap.<String, Object>of("title", "Test Title")));
+ String result = String.valueOf(imageLinkFilter.format(null, image, ImmutableMap.<String, Object>of("title", "Test Title")));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("title"), is("Test Title"));
assertThat(imageElement.attr("alt"), is("Test Title"));
@Test
public void imageIsScaledByWidthCorrectly() {
- String result = String.valueOf(imageLinkFilter.format(templateContext, image, ImmutableMap.<String, Object>of("max-width", "320")));
+ String result = String.valueOf(imageLinkFilter.format(null, image, ImmutableMap.<String, Object>of("max-width", "320")));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("width"), is("320"));
assertThat(imageElement.attr("height"), is("135"));
@Test
public void imageIsScaledByHeightCorrectly() {
- String result = String.valueOf(imageLinkFilter.format(templateContext, image, ImmutableMap.<String, Object>of("max-height", "135")));
+ String result = String.valueOf(imageLinkFilter.format(null, image, ImmutableMap.<String, Object>of("max-height", "135")));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("width"), is("320"));
assertThat(imageElement.attr("height"), is("135"));
@Test
public void wideImageIsEnlargedCorrectly() {
- String result = String.valueOf(imageLinkFilter.format(templateContext, image,
+ String result = String.valueOf(imageLinkFilter.format(null, image,
ImmutableMap.<String, Object>of("mode", "enlarge", "max-width", "100", "max-height", "100")));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("width"), is("237"));
public void highImageIsEnlargedCorrectly() {
when(image.getWidth()).thenReturn(270);
when(image.getHeight()).thenReturn(640);
- String result = String.valueOf(imageLinkFilter.format(templateContext, image,
+ String result = String.valueOf(imageLinkFilter.format(null, image,
ImmutableMap.<String, Object>of("mode", "enlarge", "max-width", "100", "max-height", "100")));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("width"), is("100"));
@Test
public void nullImageIsReturnedAsNull() {
- assertThat(imageLinkFilter.format(templateContext, null, null), nullValue());
+ assertThat(imageLinkFilter.format(null, null, null), nullValue());
}
@Test
public void stringIsUsedToLoadImageFromCore() {
- String result = String.valueOf(imageLinkFilter.format(templateContext, "image-id", ImmutableMap.<String, Object>of()));
+ String result = String.valueOf(imageLinkFilter.format(null, "image-id", ImmutableMap.<String, Object>of()));
Element imageElement = getSingleElement(result);
assertThat(imageElement.attr("class"), is(""));
assertThat(imageElement.attr("src"), is("/image-key"));
/**
* Unit test for {@link JavascriptFilter}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class JavascriptFilterTest {
/**
* Unit test for {@link PostAccessor}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PostAccessorTest {
package net.pterodactylus.sone.test;
import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- * This annotation marks test methods that are somehow not good test methods.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ * This annotation marks tests or test methods that are dirty,
+ * i.e. written in a way no test should ever be written in.
*/
@Retention(SOURCE)
-@Target(METHOD)
+@Target(value = { TYPE, METHOD })
public @interface Dirty {
String value() default "";
/*
- * Sone - Matchers.java - Copyright © 2013–2016 David Roden
+ * Sone - Matchers.java - Copyright © 2013–2019 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
/**
* Matchers used throughout the tests.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class Matchers {
/**
* {@link AlbumBuilder} that returns a mocked {@link Album}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TestAlbumBuilder implements AlbumBuilder {
private final Album album = mock(Album.class);
- private final List<Album> albums = new ArrayList<Album>();
- private final List<Image> images = new ArrayList<Image>();
+ private final List<Album> albums = new ArrayList<>();
+ private final List<Image> images = new ArrayList<>();
private Album parentAlbum;
private String title;
private String description;
/**
* {@link ImageBuilder} implementation that returns a mocked {@link Image}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TestImageBuilder implements ImageBuilder {
/**
* {@link PostBuilder} implementation that returns a mocked {@link Post}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TestPostBuilder implements PostBuilder {
/**
* {@link PostReplyBuilder} that returns a mocked {@link PostReply}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TestPostReplyBuilder implements PostReplyBuilder {
package net.pterodactylus.sone.test;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import kotlin.Deprecated;
+import kotlin.ReplaceWith;
+
/**
* Utilities for testing.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TestUtil {
+ @Deprecated(message = "It only checks the given class, not its superclasses.", replaceWith = @ReplaceWith(imports = { "net.pterodactylus.sone.test" }, expression = "setField(`object`, fieldName, value)"))
public static void setFinalField(Object object, String fieldName, Object value) {
try {
Field clientCoreField = object.getClass().getField(fieldName);
modifiersField.setAccessible(true);
modifiersField.setInt(clientCoreField, clientCoreField.getModifiers() & ~Modifier.FINAL);
clientCoreField.set(object, value);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
+ } catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(object);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
+ } catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Method method = object.getClass().getDeclaredMethod(methodName, new Class[0]);
method.setAccessible(true);
return (T) method.invoke(object);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- } catch (InvocationTargetException e) {
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
+ }
+ }
+
+ public static <T> T createObject(Class<T> clazz, Class[] parameterTypes, Object... arguments) {
+ try {
+ Constructor<T> constructor = clazz.getDeclaredConstructor(parameterTypes);
+ constructor.setAccessible(true);
+ return constructor.newInstance(arguments);
+ } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import net.pterodactylus.util.config.ConfigurationException;
import net.pterodactylus.util.config.Value;
/**
* Simple {@link Value} implementation.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TestValue<T> implements Value<T> {
- private final AtomicReference<T> value = new AtomicReference<T>();
+ private final AtomicReference<T> value = new AtomicReference<>();
- public TestValue(T originalValue) {
+ public TestValue(@Nullable T originalValue) {
value.set(originalValue);
}
@Override
+ @Nullable
public T getValue() throws ConfigurationException {
return value.get();
}
@Override
- public T getValue(T defaultValue) {
+ @Nullable
+ public T getValue(@Nullable T defaultValue) {
final T realValue = value.get();
return (realValue != null) ? realValue : defaultValue;
}
@Override
- public void setValue(T newValue) throws ConfigurationException {
+ public void setValue(@Nullable T newValue) throws ConfigurationException {
value.set(newValue);
}
}
@Override
+ @Nonnull
public String toString() {
return String.valueOf(value.get());
}
- public static <T> Value<T> from(T value) {
- return new TestValue<T>(value);
+ @Nonnull
+ public static <T> Value<T> from(@Nullable T value) {
+ return new TestValue<>(value);
}
}
/**
* Unit test for {@link FreemailPart}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class FreemailPartTest {
/**
* Unit test for {@link PostPart}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class PostPartTest {
+++ /dev/null
-/*
- * Sone - SoneTextParserTest.java - Copyright © 2011–2016 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.text;
-
-import static java.lang.String.format;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.isIn;
-import static org.hamcrest.Matchers.notNullValue;
-
-import java.io.IOException;
-import java.util.Collection;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.data.impl.IdOnlySone;
-import net.pterodactylus.sone.database.PostProvider;
-import net.pterodactylus.sone.database.SoneProvider;
-
-import com.google.common.base.Optional;
-import kotlin.jvm.functions.Function1;
-import org.junit.Test;
-
-/**
- * JUnit test case for {@link SoneTextParser}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class SoneTextParserTest {
-
- private final SoneTextParser soneTextParser = new SoneTextParser(null, null);
-
- @SuppressWarnings("static-method")
- @Test
- public void testPlainText() throws IOException {
- /* check basic operation. */
- Iterable<Part> parts = soneTextParser.parse("Test.", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Test."));
-
- /* check empty lines at start and end. */
- parts = soneTextParser.parse("\nTest.\n\n", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Test."));
-
- /* check duplicate empty lines in the text. */
- parts = soneTextParser.parse("\nTest.\n\n\nTest.", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Test.\n\nTest."));
- }
-
- @Test
- public void consecutiveLinesAreSeparatedByLinefeed() {
- Iterable<Part> parts = soneTextParser.parse("Text.\nText", null);
- assertThat("Part Text", convertText(parts), is("Text.\nText"));
- }
-
- @Test
- public void freenetLinksHaveTheFreenetPrefixRemoved() {
- Iterable<Part> parts = soneTextParser.parse("freenet:KSK@gpl.txt", null);
- assertThat("Part Text", convertText(parts), is("[KSK@gpl.txt|KSK@gpl.txt|gpl.txt]"));
- }
-
- @Test
- public void onlyTheFirstItemInALineIsPrefixedWithALineBreak() {
- Iterable<Part> parts = soneTextParser.parse("Text.\nKSK@gpl.txt and KSK@gpl.txt", null);
- assertThat("Part Text", convertText(parts), is("Text.\n[KSK@gpl.txt|KSK@gpl.txt|gpl.txt] and [KSK@gpl.txt|KSK@gpl.txt|gpl.txt]"));
- }
-
- @Test
- public void soneLinkWithTooShortSoneIdIsRenderedAsPlainText() {
- Iterable<Part> parts = soneTextParser.parse("sone://too-short", null);
- assertThat("Part Text", convertText(parts), is("sone://too-short"));
- }
-
- @Test
- public void soneLinkIsRenderedCorrectlyIfSoneIsNotPresent() {
- SoneTextParser parser = new SoneTextParser(new AbsentSoneProvider(), null);
- Iterable<Part> parts = parser.parse("sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU", null);
- assertThat("Part Text", convertText(parts), is("[Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU]"));
- }
-
- @Test
- public void soneAndPostCanBeParsedFromTheSameText() {
- SoneTextParser parser = new SoneTextParser(new TestSoneProvider(), new TestPostProvider());
- Iterable<Part> parts = parser.parse("Text sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU more text post://f3757817-b45a-497a-803f-9c5aafc10dc6 even more text", null);
- assertThat("Part Text", convertText(parts), is("Text [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] more text [Post|f3757817-b45a-497a-803f-9c5aafc10dc6|text] even more text"));
- }
-
- @Test
- public void postLinkIsRenderedAsPlainTextIfPostIdIsTooShort() {
- Iterable<Part> parts = soneTextParser.parse("post://too-short", null);
- assertThat("Part Text", convertText(parts), is("post://too-short"));
- }
-
- @Test
- public void postLinkIsRenderedCorrectlyIfPostIsPresent() {
- SoneTextParser parser = new SoneTextParser(null, new TestPostProvider());
- Iterable<Part> parts = parser.parse("post://f3757817-b45a-497a-803f-9c5aafc10dc6", null);
- assertThat("Part Text", convertText(parts), is("[Post|f3757817-b45a-497a-803f-9c5aafc10dc6|text]"));
- }
-
- @Test
- public void postLinkIsRenderedAsPlainTextIfPostIsAbsent() {
- SoneTextParser parser = new SoneTextParser(null, new AbsentPostProvider());
- Iterable<Part> parts = parser.parse("post://f3757817-b45a-497a-803f-9c5aafc10dc6", null);
- assertThat("Part Text", convertText(parts), is("post://f3757817-b45a-497a-803f-9c5aafc10dc6"));
- }
-
- @Test
- public void nameOfFreenetLinkDoesNotContainUrlParameters() {
- Iterable<Part> parts = soneTextParser.parse("KSK@gpl.txt?max-size=12345", null);
- assertThat("Part Text", convertText(parts), is("[KSK@gpl.txt?max-size=12345|KSK@gpl.txt|gpl.txt]"));
- }
-
- @Test
- public void trailingSlashInFreenetLinkIsRemovedForName() {
- Iterable<Part> parts = soneTextParser.parse("KSK@gpl.txt/", null);
- assertThat("Part Text", convertText(parts), is("[KSK@gpl.txt/|KSK@gpl.txt/|gpl.txt]"));
- }
-
- @Test
- public void lastMetaStringOfFreenetLinkIsUsedAsName() {
- Iterable<Part> parts = soneTextParser.parse("CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING", null);
- assertThat("Part Text", convertText(parts), is("[CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING|CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING|COPYING]"));
- }
-
- @Test
- public void freenetLinkWithoutMetaStringsAndDocNameGetsFirstNineCharactersOfKeyAsName() {
- Iterable<Part> parts = soneTextParser.parse("CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8", null);
- assertThat("Part Text", convertText(parts), is("[CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8|CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8|CHK@qM1nm]"));
- }
-
- @Test
- public void malformedKeyIsRenderedAsPlainText() {
- Iterable<Part> parts = soneTextParser.parse("CHK@qM1nmgU", null);
- assertThat("Part Text", convertText(parts), is("CHK@qM1nmgU"));
- }
-
- @Test
- public void httpsLinkHasItsPathsShortened() {
- Iterable<Part> parts = soneTextParser.parse("https://test.test/some-long-path/file.txt", null);
- assertThat("Part Text", convertText(parts), is("[https://test.test/some-long-path/file.txt|https://test.test/some-long-path/file.txt|test.test/…/file.txt]"));
- }
-
- @Test
- public void httpLinksHaveTheirLastSlashRemoved() {
- Iterable<Part> parts = soneTextParser.parse("http://test.test/test/", null);
- assertThat("Part Text", convertText(parts), is("[http://test.test/test/|http://test.test/test/|test.test/…]"));
- }
-
- @Test
- public void wwwPrefixIsRemovedForHostnameWithTwoDotsAndNoPath() {
- Iterable<Part> parts = soneTextParser.parse("http://www.test.test", null);
- assertThat("Part Text", convertText(parts), is("[http://www.test.test|http://www.test.test|test.test]"));
- }
-
- @Test
- public void wwwPrefixIsRemovedForHostnameWithTwoDotsAndAPath() {
- Iterable<Part> parts = soneTextParser.parse("http://www.test.test/test.html", null);
- assertThat("Part Text", convertText(parts), is("[http://www.test.test/test.html|http://www.test.test/test.html|test.test/test.html]"));
- }
-
- @Test
- public void hostnameIsKeptIntactIfNotBeginningWithWww() {
- Iterable<Part> parts = soneTextParser.parse("http://test.test.test/test.html", null);
- assertThat("Part Text", convertText(parts), is("[http://test.test.test/test.html|http://test.test.test/test.html|test.test.test/test.html]"));
- }
-
- @Test
- public void hostnameWithOneDotButNoSlashIsKeptIntact() {
- Iterable<Part> parts = soneTextParser.parse("http://test.test", null);
- assertThat("Part Text", convertText(parts), is("[http://test.test|http://test.test|test.test]"));
- }
-
- @Test
- public void urlParametersAreRemovedForHttpLinks() {
- Iterable<Part> parts = soneTextParser.parse("http://test.test?foo=bar", null);
- assertThat("Part Text", convertText(parts), is("[http://test.test?foo=bar|http://test.test?foo=bar|test.test]"));
- }
-
- @Test
- public void emptyStringIsParsedCorrectly() {
- Iterable<Part> parts = soneTextParser.parse("", null);
- assertThat("Part Text", convertText(parts), is(""));
- }
-
- @Test
- public void linksAreParsedInCorrectOrder() {
- Iterable<Part> parts = soneTextParser.parse("KSK@ CHK@", null);
- assertThat("Part Text", convertText(parts), is("KSK@ CHK@"));
- }
-
- @Test
- public void invalidSskAndUskLinkIsParsedAsText() {
- Iterable<Part> parts = soneTextParser.parse("SSK@a USK@a", null);
- assertThat("Part Text", convertText(parts), is("SSK@a USK@a"));
- }
-
- @Test
- public void sskLinkWithoutContextIsNotTrusted() {
- Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", null);
- assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"));
- }
-
- @Test
- public void sskLinkWithContextWithoutSoneIsNotTrusted() {
- SoneTextParserContext context = new SoneTextParserContext(null);
- Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context);
- assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"));
- }
-
- @Test
- public void sskLinkWithContextWithDifferentSoneIsNotTrusted() {
- SoneTextParserContext context = new SoneTextParserContext(new IdOnlySone("DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU"));
- Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context);
- assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"));
- }
-
- @Test
- public void sskLinkWithContextWithCorrectSoneIsTrusted() {
- SoneTextParserContext context = new SoneTextParserContext(new IdOnlySone("qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU"));
- Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context);
- assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|trusted|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"));
- }
-
- @Test
- public void uskLinkWithContextWithCorrectSoneIsTrusted() {
- SoneTextParserContext context = new SoneTextParserContext(new IdOnlySone("qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU"));
- Iterable<Part> parts = soneTextParser.parse("USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0", context);
- assertThat("Part Text", convertText(parts), is("[USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|trusted|USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|test]"));
- }
-
- @SuppressWarnings("static-method")
- @Test
- public void testKSKLinks() throws IOException {
- /* check basic links. */
- Iterable<Part> parts = soneTextParser.parse("KSK@gpl.txt", null);
- assertThat("Part Text", convertText(parts, FreenetLinkPart.class), is("[KSK@gpl.txt|KSK@gpl.txt|gpl.txt]"));
-
- /* check embedded links. */
- parts = soneTextParser.parse("Link is KSK@gpl.txt\u200b.", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, FreenetLinkPart.class), is("Link is [KSK@gpl.txt|KSK@gpl.txt|gpl.txt]\u200b."));
-
- /* check embedded links and line breaks. */
- parts = soneTextParser.parse("Link is KSK@gpl.txt\nKSK@test.dat\n", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, FreenetLinkPart.class), is("Link is [KSK@gpl.txt|KSK@gpl.txt|gpl.txt]\n[KSK@test.dat|KSK@test.dat|test.dat]"));
- }
-
- @SuppressWarnings({ "synthetic-access", "static-method" })
- @Test
- public void testEmptyLinesAndSoneLinks() throws IOException {
- SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
-
- /* check basic links. */
- Iterable<Part> parts = soneTextParser.parse("Some text.\n\nLink to sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU and stuff.", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, SonePart.class), is("Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff."));
- }
-
- @SuppressWarnings({ "synthetic-access", "static-method" })
- @Test
- public void testEmpyHttpLinks() throws IOException {
- SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
-
- /* check empty http links. */
- Iterable<Part> parts = soneTextParser.parse("Some text. Empty link: http:// – nice!", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Some text. Empty link: http:// – nice!"));
- }
-
- @Test
- public void httpLinkWithoutParensEndsAtNextClosingParen() {
- Iterable<Part> parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc) – nice!", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text (and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]) – nice!"));
- }
-
- @Test
- public void uskLinkEndsAtFirstNonNumericNonSlashCharacterAfterVersionNumber() {
- Iterable<Part> parts = soneTextParser.parse("Some link (USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0). Nice", null);
- assertThat("Part Text", convertText(parts), is("Some link ([USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|test]). Nice"));
- }
-
- @Test
- public void httpLinkWithOpenedAndClosedParensEndsAtNextClosingParen() {
- Iterable<Part> parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc_(def)) – nice!", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text (and a link: [http://example.sone/abc_(def)|http://example.sone/abc_(def)|example.sone/abc_(def)]) – nice!"));
- }
-
- @Test
- public void punctuationIsIgnoredAtEndOfLinkBeforeWhitespace() {
- Iterable<Part> parts = soneTextParser.parse("Some text and a link: http://example.sone/abc. Nice!", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]. Nice!"));
- }
-
- @Test
- public void multiplePunctuationCharactersAreIgnoredAtEndOfLinkBeforeWhitespace() {
- Iterable<Part> parts = soneTextParser.parse("Some text and a link: http://example.sone/abc... Nice!", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]... Nice!"));
- }
-
- @Test
- public void commasAreIgnoredAtEndOfLinkBeforeWhitespace() {
- Iterable<Part> parts = soneTextParser.parse("Some text and a link: http://example.sone/abc, nice!", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc], nice!"));
- }
-
- @Test
- public void exclamationMarksAreIgnoredAtEndOfLinkBeforeWhitespace() {
- Iterable<Part> parts = soneTextParser.parse("A link: http://example.sone/abc!", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("A link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]!"));
- }
-
- @Test
- public void questionMarksAreIgnoredAtEndOfLinkBeforeWhitespace() {
- Iterable<Part> parts = soneTextParser.parse("A link: http://example.sone/abc?", null);
- assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("A link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]?"));
- }
-
- @Test
- public void correctFreemailAddressIsLinkedToCorrectly() {
- Iterable<Part> parts = soneTextParser.parse("Mail me at sone@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!", null);
- assertThat("Part Text", convertText(parts), is("Mail me at [Freemail|sone|t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra|nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI]!"));
- }
-
- @Test
- public void freemailAddressWithInvalidFreemailIdIsParsedAsText() {
- Iterable<Part> parts = soneTextParser.parse("Mail me at sone@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqr8.freemail!", null);
- assertThat("Part Text", convertText(parts), is("Mail me at sone@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqr8.freemail!"));
- }
-
- @Test
- public void freemailAddressWithInvalidSizedFreemailIdIsParsedAsText() {
- Iterable<Part> parts = soneTextParser.parse("Mail me at sone@4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!", null);
- assertThat("Part Text", convertText(parts), is("Mail me at sone@4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!"));
- }
-
- @Test
- public void freemailAddressWithoutLocalPartIsParsedAsText() {
- Iterable<Part> parts = soneTextParser.parse(" @t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!", null);
- assertThat("Part Text", convertText(parts), is(" @t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!"));
- }
-
- @Test
- public void correctFreemailAddressIsParsedCorrectly() {
- Iterable<Part> parts = soneTextParser.parse("sone@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail", null);
- assertThat("Part Text", convertText(parts), is("[Freemail|sone|t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra|nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI]"));
- }
-
- @Test
- public void localPartOfFreemailAddressCanContainLettersDigitsMinusDotUnderscore() {
- Iterable<Part> parts = soneTextParser.parse("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail", null);
- assertThat("Part Text", convertText(parts), is("[Freemail|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._|t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra|nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI]"));
- }
-
- /**
- * Converts all given {@link Part}s into a string, validating that the
- * part’s classes match only the expected classes.
- *
- * @param parts
- * The parts to convert to text
- * @param validClasses
- * The valid classes; if no classes are given, all classes are
- * valid
- * @return The converted text
- */
- private static String convertText(Iterable<Part> parts, Class<?>... validClasses) {
- StringBuilder text = new StringBuilder();
- for (Part part : parts) {
- assertThat("Part", part, notNullValue());
- if (validClasses.length != 0) {
- assertThat("Part’s class", part.getClass(), isIn(validClasses));
- }
- if (part instanceof PlainTextPart) {
- text.append(((PlainTextPart) part).getText());
- } else if (part instanceof FreenetLinkPart) {
- FreenetLinkPart freenetLinkPart = (FreenetLinkPart) part;
- text.append('[').append(freenetLinkPart.getLink()).append('|').append(freenetLinkPart.getTrusted() ? "trusted|" : "").append(freenetLinkPart.getTitle()).append('|').append(freenetLinkPart.getText()).append(']');
- } else if (part instanceof FreemailPart) {
- FreemailPart freemailPart = (FreemailPart) part;
- text.append(format("[Freemail|%s|%s|%s]", freemailPart.getEmailLocalPart(), freemailPart.getFreemailId(), freemailPart.getIdentityId()));
- } else if (part instanceof LinkPart) {
- LinkPart linkPart = (LinkPart) part;
- text.append('[').append(linkPart.getLink()).append('|').append(linkPart.getTitle()).append('|').append(linkPart.getText()).append(']');
- } else if (part instanceof SonePart) {
- SonePart sonePart = (SonePart) part;
- text.append("[Sone|").append(sonePart.getSone().getId()).append(']');
- } else if (part instanceof PostPart) {
- PostPart postPart = (PostPart) part;
- text.append("[Post|").append(postPart.getPost().getId()).append("|").append(postPart.getPost().getText()).append("]");
- }
- }
- return text.toString();
- }
-
- /**
- * Mock Sone provider.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- private static class TestSoneProvider implements SoneProvider {
-
- @Nonnull
- @Override
- public Function1<String, Sone> getSoneLoader() {
- return new Function1<String, Sone>() {
- @Override
- public Sone invoke(String soneId) {
- return getSone(soneId);
- }
- };
- }
-
- @Nullable
- @Override
- public Sone getSone(final String soneId) {
- return new IdOnlySone(soneId);
- }
-
- /**
- * {@inheritDocs}
- */
- @Override
- public Collection<Sone> getSones() {
- return null;
- }
-
- /**
- * {@inheritDocs}
- */
- @Override
- public Collection<Sone> getLocalSones() {
- return null;
- }
-
- /**
- * {@inheritDocs}
- */
- @Override
- public Collection<Sone> getRemoteSones() {
- return null;
- }
-
- }
-
- private static class AbsentSoneProvider extends TestSoneProvider {
-
- @Override
- public Sone getSone(String soneId) {
- return null;
- }
-
- }
-
- private static class TestPostProvider implements PostProvider {
-
- @Nullable
- @Override
- public Post getPost(@Nonnull final String postId) {
- return new Post() {
- @Override
- public String getId() {
- return postId;
- }
-
- @Override
- public boolean isLoaded() {
- return false;
- }
-
- @Override
- public Sone getSone() {
- return null;
- }
-
- @Override
- public Optional<String> getRecipientId() {
- return null;
- }
-
- @Override
- public Optional<Sone> getRecipient() {
- return null;
- }
-
- @Override
- public long getTime() {
- return 0;
- }
-
- @Override
- public String getText() {
- return "text";
- }
-
- @Override
- public boolean isKnown() {
- return false;
- }
-
- @Override
- public Post setKnown(boolean known) {
- return null;
- }
- };
- }
-
- @Override
- public Collection<Post> getPosts(String soneId) {
- return null;
- }
-
- @Override
- public Collection<Post> getDirectedPosts(String recipientId) {
- return null;
- }
-
- }
-
- private static class AbsentPostProvider extends TestPostProvider {
-
- @Nullable
- @Override
- public Post getPost(@Nonnull String postId) {
- return null;
- }
-
- }
-
-}
/**
* JUnit test for {@link TextFilter}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TextFilterTest {
/**
* Unit test for {@link DefaultOption}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class DefaultOptionTest {
@Test
public void defaultOptionReturnsDefaultValueWhenUnset() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue);
assertThat(defaultOption.get(), is(defaultValue));
}
@Test
public void defaultOptionReturnsNullForRealWhenUnset() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue);
assertThat(defaultOption.getReal(), nullValue());
}
@Test
public void defaultOptionWillReturnSetValue() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue);
Object newValue = new Object();
defaultOption.set(newValue);
assertThat(defaultOption.get(), is(newValue));
@Test
public void defaultOptionWithValidatorAcceptsValidValues() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue, matchesAcceptedValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue);
defaultOption.set(acceptedValue);
assertThat(defaultOption.get(), is(acceptedValue));
}
@Test(expected = IllegalArgumentException.class)
public void defaultOptionWithValidatorRejectsInvalidValues() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue, matchesAcceptedValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue);
defaultOption.set(new Object());
}
@Test
public void defaultOptionValidatesObjectsCorrectly() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue, matchesAcceptedValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue);
assertThat(defaultOption.validate(acceptedValue), is(true));
assertThat(defaultOption.validate(new Object()), is(false));
}
@Test
public void settingToNullWillRestoreDefaultValue() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue);
defaultOption.set(null);
assertThat(defaultOption.get(), is(defaultValue));
}
@Test
public void validateWithoutValidatorWillValidateNull() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue);
assertThat(defaultOption.validate(null), is(true));
}
@Test
public void validateWithValidatorWillValidateNull() {
- DefaultOption<Object> defaultOption = new DefaultOption<Object>(defaultValue, matchesAcceptedValue);
+ DefaultOption<Object> defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue);
assertThat(defaultOption.validate(null), is(true));
}
/**
* Unit test for {@link IntegerRangePredicate}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class IntegerRangePredicateTest {
/**
* Unit test for {@link NumberParsers}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class NumberParsersTest {
/**
* Utilities for testing the <code>web</code> package.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class WebTestUtils {
+++ /dev/null
-package net.pterodactylus.sone.web.page;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.Mockito.mock;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import net.pterodactylus.util.web.Method;
-
-import freenet.clients.http.ToadletContext;
-import freenet.support.api.HTTPRequest;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link FreenetRequest}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class FreenetRequestTest {
-
- private final URI uri = new URI(".");
- private final Method method = Method.GET;
- private final HTTPRequest httpRequest = mock(HTTPRequest.class);
- private final ToadletContext toadletContext = mock(ToadletContext.class);
- private final FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext);
-
- @SuppressWarnings("unused")
- public FreenetRequestTest() throws URISyntaxException {
- }
-
- @Test
- public void uriIsRetainedCorrectly() {
- assertThat(request.getUri(), is(uri));
- }
-
- @Test
- public void methodIsRetainedCorrectly() {
- assertThat(request.getMethod(), is(method));
- }
-
- @Test
- public void httpRequestIsRetainedCorrectly() {
- assertThat(request.getHttpRequest(), is(httpRequest));
- }
-
- @Test
- public void toadletContextIsRetainedCorrectly() {
- assertThat(request.getToadletContext(), is(toadletContext));
- }
-
-}
@Test
fun `default image loader can be loaded by guice`() {
val injector = createInjector(bindMock<FreenetInterface>())
- assertThat(injector.getInstance(ElementLoader::class.java), notNullValue());
+ assertThat(injector.getInstance(ElementLoader::class.java), notNullValue())
}
}
--- /dev/null
+package net.pterodactylus.sone.core
+
+import net.pterodactylus.sone.core.FreenetInterface.InsertToken
+import net.pterodactylus.sone.core.FreenetInterface.InsertTokenSupplier
+import net.pterodactylus.sone.data.Image
+import net.pterodactylus.sone.data.TemporaryImage
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.notNullValue
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.doThrow
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/**
+ * Unit test for [ImageInserter].
+ */
+class ImageInserterTest {
+
+ private val temporaryImage = mock<TemporaryImage>().apply { whenever(id).thenReturn("image-id") }
+ private val image = mock<Image>().apply { whenever(id).thenReturn("image-id") }
+ private val freenetInterface = mock<FreenetInterface>()
+ private val insertToken = mock<InsertToken>()
+ private val insertTokenSupplier: InsertTokenSupplier = mock<InsertTokenSupplier>().apply { whenever(apply(any())).thenReturn(insertToken) }
+ private val imageInserter = ImageInserter(freenetInterface, insertTokenSupplier)
+
+ @Test
+ fun `inserter inserts image`() {
+ imageInserter.insertImage(temporaryImage, image)
+ verify(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken::class.java))
+ }
+
+ @Test
+ fun `exception when inserting image is ignored`() {
+ doThrow(SoneException::class.java).`when`(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken::class.java))
+ imageInserter.insertImage(temporaryImage, image)
+ verify(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken::class.java))
+ }
+
+ @Test
+ fun `cancelling image insert that is not running does nothing`() {
+ imageInserter.cancelImageInsert(image)
+ verify(insertToken, never()).cancel()
+ }
+
+ @Test
+ fun `cancelling image cancels the insert token`() {
+ imageInserter.insertImage(temporaryImage, image)
+ imageInserter.cancelImageInsert(image)
+ verify(insertToken).cancel()
+ }
+
+ @Test
+ fun `image inserter can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<ImageInserter>(), notNullValue())
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.core
+
+import com.google.common.eventbus.*
+import net.pterodactylus.sone.core.event.*
+import net.pterodactylus.sone.fcp.FcpInterface.*
+import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.*
+import net.pterodactylus.sone.fcp.event.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/**
+ * Unit test for [Preferences].
+ */
+class PreferencesTest {
+
+ private val eventBus = mock<EventBus>()
+ private val preferences = Preferences(eventBus)
+ private val eventsCaptor = capture<Any>()
+
+ @Test
+ fun `preferences retain insertion delay`() {
+ preferences.newInsertionDelay = 15
+ assertThat(preferences.insertionDelay, equalTo(15))
+ }
+
+ @Test
+ fun `preferences sends event on setting insertion delay`() {
+ preferences.newInsertionDelay = 15
+ verify(eventBus, atLeastOnce()).post(eventsCaptor.capture())
+ assertThat(eventsCaptor.allValues, hasItem(InsertionDelayChangedEvent(15)))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `invalid insertion delay is rejected`() {
+ preferences.newInsertionDelay = -15
+ }
+
+ @Test
+ fun `no event is sent when invalid insertion delay is set`() {
+ try {
+ preferences.newInsertionDelay = -15
+ } catch (iae: IllegalArgumentException) {
+ /* ignore. */
+ }
+
+ verify(eventBus, never()).post(any())
+ }
+
+ @Test
+ fun `preferences return default value when insertion delay is set to null`() {
+ preferences.newInsertionDelay = null
+ assertThat(preferences.insertionDelay, equalTo(60))
+ }
+
+ @Test
+ fun `preferences start with insertion delay default value`() {
+ assertThat(preferences.insertionDelay, equalTo(60))
+ }
+
+ @Test
+ fun `preferences retain posts per page`() {
+ preferences.newPostsPerPage = 15
+ assertThat(preferences.postsPerPage, equalTo(15))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `invalid posts per page is rejected`() {
+ preferences.newPostsPerPage = -15
+ }
+
+ @Test
+ fun `preferences return default value when posts per page is set to null`() {
+ preferences.newPostsPerPage = null
+ assertThat(preferences.postsPerPage, equalTo(10))
+ }
+
+ @Test
+ fun `preferences start with posts per page default value`() {
+ assertThat(preferences.postsPerPage, equalTo(10))
+ }
+
+ @Test
+ fun `preferences retain images per page`() {
+ preferences.newImagesPerPage = 15
+ assertThat(preferences.imagesPerPage, equalTo(15))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `invalid images per page is rejected`() {
+ preferences.newImagesPerPage = -15
+ }
+
+ @Test
+ fun `preferences return default value when images per page is set to null`() {
+ preferences.newImagesPerPage = null
+ assertThat(preferences.imagesPerPage, equalTo(9))
+ }
+
+ @Test
+ fun `preferences start with images per page default value`() {
+ assertThat(preferences.imagesPerPage, equalTo(9))
+ }
+
+ @Test
+ fun `preferences retain characters per post`() {
+ preferences.newCharactersPerPost = 150
+ assertThat(preferences.charactersPerPost, equalTo(150))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `invalid characters per post is rejected`() {
+ preferences.newCharactersPerPost = -15
+ }
+
+ @Test
+ fun `preferences return default value when characters per post is set to null`() {
+ preferences.newCharactersPerPost = null
+ assertThat(preferences.charactersPerPost, equalTo(400))
+ }
+
+ @Test
+ fun `preferences start with characters per post default value`() {
+ assertThat(preferences.charactersPerPost, equalTo(400))
+ }
+
+ @Test
+ fun `preferences retain post cut off length`() {
+ preferences.newPostCutOffLength = 150
+ assertThat(preferences.postCutOffLength, equalTo(150))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `invalid post cut off length is rejected`() {
+ preferences.newPostCutOffLength = -15
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `cut off length of minus one is not allowed`() {
+ preferences.newPostCutOffLength = -1
+ }
+
+ @Test
+ fun `preferences return default value when post cut off length is set to null`() {
+ preferences.newPostCutOffLength = null
+ assertThat(preferences.postCutOffLength, equalTo(200))
+ }
+
+ @Test
+ fun `preferences start with post cut off length default value`() {
+ assertThat(preferences.postCutOffLength, equalTo(200))
+ }
+
+ @Test
+ fun `preferences retain require full access of true`() {
+ preferences.newRequireFullAccess = true
+ assertThat(preferences.requireFullAccess, equalTo(true))
+ }
+
+ @Test
+ fun `preferences retain require full access of false`() {
+ preferences.newRequireFullAccess = false
+ assertThat(preferences.requireFullAccess, equalTo(false))
+ }
+
+ @Test
+ fun `preferences return default value when require full access is set to null`() {
+ preferences.newRequireFullAccess = null
+ assertThat(preferences.requireFullAccess, equalTo(false))
+ }
+
+ @Test
+ fun `preferences start with require full access default value`() {
+ assertThat(preferences.requireFullAccess, equalTo(false))
+ }
+
+ @Test
+ fun `preferences retain positive trust`() {
+ preferences.newPositiveTrust = 15
+ assertThat(preferences.positiveTrust, equalTo(15))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `invalid positive trust is rejected`() {
+ preferences.newPositiveTrust = -15
+ }
+
+ @Test
+ fun `preferences return default value when positive trust is set to null`() {
+ preferences.newPositiveTrust = null
+ assertThat(preferences.positiveTrust, equalTo(75))
+ }
+
+ @Test
+ fun `preferences start with positive trust default value`() {
+ assertThat(preferences.positiveTrust, equalTo(75))
+ }
+
+ @Test
+ fun `preferences retain negative trust`() {
+ preferences.newNegativeTrust = -15
+ assertThat(preferences.negativeTrust, equalTo(-15))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `invalid negative trust is rejected`() {
+ preferences.newNegativeTrust = 150
+ }
+
+ @Test
+ fun `preferences return default value when negative trust is set to null`() {
+ preferences.newNegativeTrust = null
+ assertThat(preferences.negativeTrust, equalTo(-25))
+ }
+
+ @Test
+ fun `preferences start with negative trust default value`() {
+ assertThat(preferences.negativeTrust, equalTo(-25))
+ }
+
+ @Test
+ fun `preferences retain trust comment`() {
+ preferences.newTrustComment = "Trust"
+ assertThat(preferences.trustComment, equalTo("Trust"))
+ }
+
+ @Test
+ fun `preferences return default value when trust comment is set to null`() {
+ preferences.newTrustComment = null
+ assertThat(preferences.trustComment,
+ equalTo("Set from Sone Web Interface"))
+ }
+
+ @Test
+ fun `preferences start with trust comment default value`() {
+ assertThat(preferences.trustComment,
+ equalTo("Set from Sone Web Interface"))
+ }
+
+ @Test
+ fun `preferences retain fcp interface active of true`() {
+ preferences.newFcpInterfaceActive = true
+ assertThat(preferences.fcpInterfaceActive, equalTo(true))
+ verify(eventBus).post(any(FcpInterfaceActivatedEvent::class.java))
+ }
+
+ @Test
+ fun `preferences retain fcp interface active of false`() {
+ preferences.newFcpInterfaceActive = false
+ assertThat(preferences.fcpInterfaceActive, equalTo(false))
+ verify(eventBus).post(any(FcpInterfaceDeactivatedEvent::class.java))
+ }
+
+ @Test
+ fun `preferences return default value when fcp interface active is set to null`() {
+ preferences.newFcpInterfaceActive = null
+ assertThat(preferences.fcpInterfaceActive, equalTo(false))
+ verify(eventBus).post(any(FcpInterfaceDeactivatedEvent::class.java))
+ }
+
+ @Test
+ fun `preferences start with fcp interface active default value`() {
+ assertThat(preferences.fcpInterfaceActive, equalTo(false))
+ }
+
+ @Test
+ fun `preferences retain fcp full access required of no`() {
+ preferences.newFcpFullAccessRequired = NO
+ assertThat(preferences.fcpFullAccessRequired, equalTo(NO))
+ verifyFullAccessRequiredChangedEvent(NO)
+ }
+
+ private fun verifyFullAccessRequiredChangedEvent(
+ fullAccessRequired: FullAccessRequired) {
+ verify(eventBus).post(eventsCaptor.capture())
+ assertThat(eventsCaptor.value, instanceOf(FullAccessRequiredChanged::class.java))
+ assertThat((eventsCaptor.value as FullAccessRequiredChanged).fullAccessRequired,
+ equalTo(fullAccessRequired))
+ }
+
+ @Test
+ fun `preferences retain fcp full access required of writing`() {
+ preferences.newFcpFullAccessRequired = WRITING
+ assertThat(preferences.fcpFullAccessRequired, equalTo(WRITING))
+ verifyFullAccessRequiredChangedEvent(WRITING)
+ }
+
+ @Test
+ fun `preferences retain fcp full access required of always`() {
+ preferences.newFcpFullAccessRequired = ALWAYS
+ assertThat(preferences.fcpFullAccessRequired, equalTo(ALWAYS))
+ verifyFullAccessRequiredChangedEvent(ALWAYS)
+ }
+
+ @Test
+ fun `preferences return default value when fcp full access required is set to null`() {
+ preferences.newFcpFullAccessRequired = null
+ assertThat(preferences.fcpFullAccessRequired, equalTo(ALWAYS))
+ verifyFullAccessRequiredChangedEvent(ALWAYS)
+ }
+
+ @Test
+ fun `preferences start with fcp full access required default value`() {
+ assertThat(preferences.fcpFullAccessRequired, equalTo(ALWAYS))
+ }
+
+ @Test
+ fun `setting insertion delay to valid value sends change event`() {
+ testPreferencesChangedEvent("InsertionDelay", { preferences.newInsertionDelay = it }, 30)
+ }
+
+ @Test
+ fun `setting posts per page to valid value sends change event`() {
+ testPreferencesChangedEvent("PostsPerPage", { preferences.newPostsPerPage = it }, 31)
+ }
+
+ private fun <T : Any> testPreferencesChangedEvent(name: String, setter: (T) -> Unit, value: T) {
+ setter(value)
+ verify(eventBus, atLeastOnce()).post(eventsCaptor.capture())
+ assertThat(eventsCaptor.allValues, hasItem(PreferenceChangedEvent(name, value)))
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.core
+
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+class SoneComparsisonTest {
+
+ private val oldSone = mock<Sone>()
+ private val newSone = mock<Sone>()
+
+ private val oldPost = mock<Post>()
+ private val removedPost = mock<Post>()
+ private val newPost = mock<Post>()
+ private val oldPostReply = mock<PostReply>()
+ private val removedPostReply = mock<PostReply>()
+ private val newPostReply = mock<PostReply>()
+
+ init {
+ whenever(oldSone.posts).thenReturn(listOf(oldPost, removedPost))
+ whenever(newSone.posts).thenReturn(listOf(oldPost, newPost))
+ whenever(oldSone.replies).thenReturn(setOf(oldPostReply, removedPostReply))
+ whenever(newSone.replies).thenReturn(setOf(oldPostReply, newPostReply))
+ }
+
+ private val soneComparison = SoneComparison(oldSone, newSone)
+
+ @Test
+ fun `new posts are identified correctly`() {
+ assertThat(soneComparison.newPosts, contains(newPost))
+ }
+
+ @Test
+ fun `removed posts are identified correctly`() {
+ assertThat(soneComparison.removedPosts, contains(removedPost))
+ }
+
+ @Test
+ fun `new post replies are identified correctly`() {
+ assertThat(soneComparison.newPostReplies, contains(newPostReply))
+ }
+
+ @Test
+ fun `removed post replies are identified correctly`() {
+ assertThat(soneComparison.removedPostReplies, contains(removedPostReply))
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.core
+
+import com.google.common.eventbus.EventBus
+import net.pterodactylus.sone.core.event.NewPostFoundEvent
+import net.pterodactylus.sone.core.event.NewPostReplyFoundEvent
+import net.pterodactylus.sone.core.event.PostRemovedEvent
+import net.pterodactylus.sone.core.event.PostReplyRemovedEvent
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.database.Database
+import net.pterodactylus.sone.test.argumentCaptor
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedByMock
+import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.contains
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.notNullValue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.any
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+/**
+ * Unit test for all [UpdatedSoneProcessor] implementations.
+ */
+class UpdatedSoneProcessorTest {
+
+ private val database = mock<Database>()
+ private val eventBus = mock<EventBus>()
+ private val updatedSoneProcessor = DefaultUpdateSoneProcessor(database, eventBus)
+ private val storedSone = mock<Sone>()
+ private val newSone = mock<Sone>()
+ private val posts = listOf(mock<Post>(), mock(), mock())
+ private val postReplies = listOf(mock<PostReply>(), mock(), mock())
+
+ private val events = argumentCaptor<Any>()
+
+ @Before
+ fun setupPostsAndReplies() {
+ posts.forEachIndexed { index, post -> whenever(post.time).thenReturn((index + 1) * 1000L + 100) }
+ postReplies.forEachIndexed { index, postReply -> whenever(postReply.time).thenReturn((index + 1) * 1000L + 200) }
+ }
+
+ @Before
+ fun setupSones() {
+ whenever(storedSone.time).thenReturn(1000L)
+ whenever(storedSone.posts).thenReturn(posts.slice(0..1))
+ whenever(storedSone.replies).thenReturn(postReplies.slice(0..1).toSet())
+ whenever(newSone.id).thenReturn("sone")
+ whenever(newSone.time).thenReturn(2000L)
+ whenever(newSone.posts).thenReturn(posts.slice(1..2))
+ whenever(newSone.replies).thenReturn(postReplies.slice(1..2).toSet())
+ }
+
+ @Before
+ fun setupDatabase() {
+ whenever(database.getSone("sone")).thenReturn(storedSone)
+ whenever(database.getFollowingTime("sone")).thenReturn(500L)
+ }
+
+ @Test
+ fun `updated Sone processor emits no event if no stored sone exists`() {
+ whenever(database.getSone("sone")).thenReturn(null)
+ updatedSoneProcessor.updateSone(newSone)
+ verify(eventBus, never()).post(any())
+ }
+
+ @Test
+ fun `updated Sone processor emits no event if new Sone is older than stored Sone`() {
+ whenever(newSone.time).thenReturn(500L)
+ updatedSoneProcessor.updateSone(newSone)
+ verify(eventBus, never()).post(any())
+ }
+
+ @Test
+ fun `updated Sone processor emits correct events when new Sone is newer`() {
+ updatedSoneProcessor.updateSone(newSone)
+ verify(eventBus, times(4)).post(events.capture())
+ assertThat(events.allValues, containsInAnyOrder(
+ NewPostFoundEvent(posts[2]),
+ PostRemovedEvent(posts[0]),
+ NewPostReplyFoundEvent(postReplies[2]),
+ PostReplyRemovedEvent(postReplies[0])
+ ))
+ }
+
+ @Test
+ fun `updated Sone processor does not mark new post as known if sone was not followed after post`() {
+ updatedSoneProcessor.updateSone(newSone)
+ verify(posts[2], never()).isKnown = true
+ }
+
+ @Test
+ fun `updated Sone processor does not mark new posts as known if Sone is not followed`() {
+ whenever(database.getFollowingTime("sone")).thenReturn(null)
+ updatedSoneProcessor.updateSone(newSone)
+ posts.forEach { verify(it, never()).isKnown = true }
+ }
+
+ @Test
+ fun `updated Sone processor marks new post as known if sone was followed after post`() {
+ whenever(database.getFollowingTime("sone")).thenReturn(3500L)
+ updatedSoneProcessor.updateSone(newSone)
+ verify(posts[2]).isKnown = true
+ }
+
+ @Test
+ fun `updated Sone processor does not emit event for post if it is already known`() {
+ whenever(posts[2].isKnown).thenReturn(true)
+ updatedSoneProcessor.updateSone(newSone)
+ verify(eventBus, atLeastOnce()).post(events.capture())
+ assertThat(events.allValues, not(contains<Any>(NewPostFoundEvent(posts[2]))))
+ }
+
+ @Test
+ fun `updated Sone processor does not mark new reply as known if sone was not followed after reply`() {
+ updatedSoneProcessor.updateSone(newSone)
+ verify(postReplies[2], never()).isKnown = true
+ }
+
+ @Test
+ fun `updated Sone processor marks new reply as known if sone was followed after reply`() {
+ whenever(database.getFollowingTime("sone")).thenReturn(3500L)
+ updatedSoneProcessor.updateSone(newSone)
+ verify(postReplies[2]).isKnown = true
+ }
+
+ @Test
+ fun `updated Sone processor does not emit event for reply if it is already known`() {
+ whenever(postReplies[2].isKnown).thenReturn(true)
+ updatedSoneProcessor.updateSone(newSone)
+ verify(eventBus, atLeastOnce()).post(events.capture())
+ assertThat(events.allValues, not(contains<Any>(NewPostReplyFoundEvent(postReplies[2]))))
+ }
+
+ @Test
+ fun `updated sone processor stores sone in database`() {
+ updatedSoneProcessor.updateSone(newSone)
+ verify(database).storeSone(newSone)
+ }
+
+ @Test
+ fun `default updated Sone processor can be created by dependency injection`() {
+ assertThat(baseInjector.createChildInjector(
+ Database::class.isProvidedByMock()
+ ).getInstance<UpdatedSoneProcessor>(), notNullValue())
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.data
+
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+/**
+ * Unit test for [Profile].
+ */
+class ProfileTest {
+
+ private val sone = mock<Sone>()
+ private val profile = Profile(sone)
+
+ @Test
+ fun `first name is initialized with null`() {
+ assertThat(profile.firstName, nullValue())
+ }
+
+ @Test
+ fun `setting first name to value will set it to value`() {
+ profile.firstName = "first name"
+ assertThat(profile.firstName, equalTo("first name"))
+ }
+
+ @Test
+ fun `setting first name to null will set it to null`() {
+ profile.firstName = null
+ assertThat(profile.firstName, nullValue())
+ }
+
+ @Test
+ fun `setting first name to empty string will set it to null`() {
+ profile.firstName = ""
+ assertThat(profile.firstName, nullValue())
+ }
+
+ @Test
+ fun `middle name is initialized with null`() {
+ assertThat(profile.middleName, nullValue())
+ }
+
+ @Test
+ fun `setting middle name to value will set it to value`() {
+ profile.middleName = "middle name"
+ assertThat(profile.middleName, equalTo("middle name"))
+ }
+
+ @Test
+ fun `setting middle name to null will set it to null`() {
+ profile.middleName = null
+ assertThat(profile.middleName, nullValue())
+ }
+
+ @Test
+ fun `setting middle name to empty string will set it to null`() {
+ profile.middleName = ""
+ assertThat(profile.middleName, nullValue())
+ }
+
+ @Test
+ fun `last name is initialized with null`() {
+ assertThat(profile.lastName, nullValue())
+ }
+
+ @Test
+ fun `setting last name to value will set it to value`() {
+ profile.lastName = "last name"
+ assertThat(profile.lastName, equalTo("last name"))
+ }
+
+ @Test
+ fun `setting last name to null will set it to null`() {
+ profile.lastName = null
+ assertThat(profile.lastName, nullValue())
+ }
+
+ @Test
+ fun `setting last name to empty string will set it to null`() {
+ profile.lastName = ""
+ assertThat(profile.lastName, nullValue())
+ }
+
+ @Test
+ fun `new fields are initialized with an empty string`() {
+ val newField = profile.addField("testField")
+ assertThat(newField.value, equalTo(""))
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.database.memory
+
+import net.pterodactylus.sone.test.TestValue.from
+import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.util.config.Configuration
+import net.pterodactylus.util.config.Value
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.nullValue
+import org.junit.Test
+
+/**
+ * Unit test for [ConfigurationLoader].
+ */
+class ConfigurationLoaderTest {
+
+ private val configuration = mock<Configuration>()
+ private val configurationLoader = ConfigurationLoader(configuration)
+
+ private fun setupStringValue(attribute: String, value: String? = null): Value<String?> =
+ from(value).apply {
+ whenever(configuration.getStringValue(attribute)).thenReturn(this)
+ }
+
+ private fun setupLongValue(attribute: String, value: Long? = null): Value<Long?> =
+ from(value).apply {
+ whenever(configuration.getLongValue(attribute)).thenReturn(this)
+ }
+
+ @Test
+ fun `loader can load known posts`() {
+ setupStringValue("KnownPosts/0/ID", "Post2")
+ setupStringValue("KnownPosts/1/ID", "Post1")
+ setupStringValue("KnownPosts/2/ID")
+ val knownPosts = configurationLoader.loadKnownPosts()
+ assertThat(knownPosts, containsInAnyOrder("Post1", "Post2"))
+ }
+
+ @Test
+ fun `loader can load known post replies`() {
+ setupStringValue("KnownReplies/0/ID", "PostReply2")
+ setupStringValue("KnownReplies/1/ID", "PostReply1")
+ setupStringValue("KnownReplies/2/ID")
+ val knownPosts = configurationLoader.loadKnownPostReplies()
+ assertThat(knownPosts, containsInAnyOrder("PostReply1", "PostReply2"))
+ }
+
+ @Test
+ fun `loader can load bookmarked posts`() {
+ setupStringValue("Bookmarks/Post/0/ID", "Post2")
+ setupStringValue("Bookmarks/Post/1/ID", "Post1")
+ setupStringValue("Bookmarks/Post/2/ID")
+ val knownPosts = configurationLoader.loadBookmarkedPosts()
+ assertThat(knownPosts, containsInAnyOrder("Post1", "Post2"))
+ }
+
+ @Test
+ fun `loader can save bookmarked posts`() {
+ val post1 = setupStringValue("Bookmarks/Post/0/ID")
+ val post2 = setupStringValue("Bookmarks/Post/1/ID")
+ val post3 = setupStringValue("Bookmarks/Post/2/ID")
+ val originalPosts = setOf("Post1", "Post2")
+ configurationLoader.saveBookmarkedPosts(originalPosts)
+ val extractedPosts = setOf(post1.value, post2.value)
+ assertThat(extractedPosts, containsInAnyOrder("Post1", "Post2"))
+ assertThat(post3.value, nullValue())
+ }
+
+ @Test
+ fun `loader can load Sone following times`() {
+ setupStringValue("SoneFollowingTimes/0/Sone", "Sone1")
+ setupLongValue("SoneFollowingTimes/0/Time", 1000L)
+ setupStringValue("SoneFollowingTimes/1/Sone", "Sone2")
+ setupLongValue("SoneFollowingTimes/1/Time", 2000L)
+ setupStringValue("SoneFollowingTimes/2/Sone")
+ assertThat(configurationLoader.getSoneFollowingTime("Sone1"), equalTo(1000L))
+ assertThat(configurationLoader.getSoneFollowingTime("Sone2"), equalTo(2000L))
+ assertThat(configurationLoader.getSoneFollowingTime("Sone3"), nullValue())
+ }
+
+ @Test
+ fun `loader can overwrite existing Sone following time`() {
+ val sone1Id = setupStringValue("SoneFollowingTimes/0/Sone", "Sone1")
+ val sone1Time = setupLongValue("SoneFollowingTimes/0/Time", 1000L)
+ val sone2Id = setupStringValue("SoneFollowingTimes/1/Sone", "Sone2")
+ val sone2Time = setupLongValue("SoneFollowingTimes/1/Time", 2000L)
+ setupStringValue("SoneFollowingTimes/2/Sone")
+ configurationLoader.setSoneFollowingTime("Sone1", 3000L)
+ assertThat(listOf(sone1Id.value to sone1Time.value, sone2Id.value to sone2Time.value), containsInAnyOrder<Pair<String?, Long?>>(
+ "Sone1" to 3000L,
+ "Sone2" to 2000L
+ ))
+ }
+
+ @Test
+ fun `loader can remove Sone following time`() {
+ val sone1Id = setupStringValue("SoneFollowingTimes/0/Sone", "Sone1")
+ val sone1Time = setupLongValue("SoneFollowingTimes/0/Time", 1000L)
+ val sone2Id = setupStringValue("SoneFollowingTimes/1/Sone", "Sone2")
+ val sone2Time = setupLongValue("SoneFollowingTimes/1/Time", 2000L)
+ setupStringValue("SoneFollowingTimes/2/Sone")
+ configurationLoader.removeSoneFollowingTime("Sone1")
+ assertThat(sone1Id.value, equalTo("Sone2"))
+ assertThat(sone1Time.value, equalTo(2000L))
+ assertThat(sone2Id.value, nullValue())
+ }
+
+ @Test
+ fun `sone with missing following time is not loaded`() {
+ setupStringValue("SoneFollowingTimes/0/Sone", "Sone1")
+ setupLongValue("SoneFollowingTimes/0/Time", 1000L)
+ setupStringValue("SoneFollowingTimes/1/Sone", "Sone2")
+ setupLongValue("SoneFollowingTimes/1/Time")
+ setupStringValue("SoneFollowingTimes/2/Sone")
+ assertThat(configurationLoader.getSoneFollowingTime("Sone1"), equalTo(1000L))
+ assertThat(configurationLoader.getSoneFollowingTime("Sone2"), nullValue())
+ assertThat(configurationLoader.getSoneFollowingTime("Sone3"), nullValue())
+ }
+
+}
--- /dev/null
+/*
+ * Sone - MemoryDatabaseTest.kt - Copyright © 2013–2019 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.database.memory
+
+import com.google.common.base.*
+import com.google.common.base.Optional.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.impl.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.test.Matchers.*
+import net.pterodactylus.util.config.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.*
+import org.mockito.invocation.*
+import java.util.Arrays.*
+import java.util.UUID.*
+import kotlin.test.*
+
+/**
+ * Tests for [MemoryDatabase].
+ */
+class MemoryDatabaseTest {
+
+ private val configuration = mock<Configuration>()
+ private val memoryDatabase = MemoryDatabase(configuration)
+ private val sone = mock<Sone>()
+
+ @BeforeTest
+ fun setupSone() {
+ whenever(sone.id).thenReturn(SONE_ID)
+ }
+
+ @Test
+ fun `stored sone is made available`() {
+ storeSone()
+ assertThat(memoryDatabase.getPost("post1"), isPost("post1", 1000L, "post1", absent()))
+ assertThat(memoryDatabase.getPost("post2"), isPost("post2", 2000L, "post2", of(RECIPIENT_ID)))
+ assertThat(memoryDatabase.getPost("post3"), nullValue())
+ assertThat(memoryDatabase.getPostReply("reply1"), isPostReply("reply1", "post1", 3000L, "reply1"))
+ assertThat(memoryDatabase.getPostReply("reply2"), isPostReply("reply2", "post2", 4000L, "reply2"))
+ assertThat(memoryDatabase.getPostReply("reply3"), isPostReply("reply3", "post1", 5000L, "reply3"))
+ assertThat(memoryDatabase.getPostReply("reply4"), nullValue())
+ assertThat(memoryDatabase.getAlbum("album1"), isAlbum("album1", null, "album1", "album-description1"))
+ assertThat(memoryDatabase.getAlbum("album2"), isAlbum("album2", null, "album2", "album-description2"))
+ assertThat(memoryDatabase.getAlbum("album3"), isAlbum("album3", "album1", "album3", "album-description3"))
+ assertThat(memoryDatabase.getAlbum("album4"), nullValue())
+ assertThat(memoryDatabase.getImage("image1"), isImage("image1", 1000L, "KSK@image1", "image1", "image-description1", 16, 9))
+ assertThat(memoryDatabase.getImage("image2"), isImage("image2", 2000L, "KSK@image2", "image2", "image-description2", 32, 18))
+ assertThat(memoryDatabase.getImage("image3"), isImage("image3", 3000L, "KSK@image3", "image3", "image-description3", 48, 27))
+ assertThat(memoryDatabase.getImage("image4"), nullValue())
+ }
+
+ private fun storeSone() {
+ val firstPost = TestPostBuilder().withId("post1")
+ .from(SONE_ID)
+ .withTime(1000L)
+ .withText("post1")
+ .build()
+ val secondPost = TestPostBuilder().withId("post2")
+ .from(SONE_ID)
+ .withTime(2000L)
+ .withText("post2")
+ .to(RECIPIENT_ID)
+ .build()
+ val posts = asList(firstPost, secondPost)
+ whenever(sone.posts).thenReturn(posts)
+ val firstPostFirstReply = TestPostReplyBuilder().withId("reply1")
+ .from(SONE_ID)
+ .to(firstPost.id)
+ .withTime(3000L)
+ .withText("reply1")
+ .build()
+ val firstPostSecondReply = TestPostReplyBuilder().withId("reply3")
+ .from(RECIPIENT_ID)
+ .to(firstPost.id)
+ .withTime(5000L)
+ .withText("reply3")
+ .build()
+ val secondPostReply = TestPostReplyBuilder().withId("reply2")
+ .from(SONE_ID)
+ .to(secondPost.id)
+ .withTime(4000L)
+ .withText("reply2")
+ .build()
+ val postReplies = setOf(firstPostFirstReply, firstPostSecondReply, secondPostReply)
+ whenever(sone.replies).thenReturn(postReplies)
+ val firstAlbum = TestAlbumBuilder().withId("album1")
+ .by(sone)
+ .build()
+ .modify()
+ .setTitle("album1")
+ .setDescription("album-description1")
+ .update()
+ val secondAlbum = TestAlbumBuilder().withId("album2")
+ .by(sone)
+ .build()
+ .modify()
+ .setTitle("album2")
+ .setDescription("album-description2")
+ .update()
+ val thirdAlbum = TestAlbumBuilder().withId("album3")
+ .by(sone)
+ .build()
+ .modify()
+ .setTitle("album3")
+ .setDescription("album-description3")
+ .update()
+ firstAlbum.addAlbum(thirdAlbum)
+ val rootAlbum = mock<Album>()
+ whenever(rootAlbum.id).thenReturn("root")
+ whenever(rootAlbum.albums).thenReturn(listOf(firstAlbum, secondAlbum))
+ whenever(sone.rootAlbum).thenReturn(rootAlbum)
+ val firstImage = TestImageBuilder().withId("image1")
+ .build()
+ .modify()
+ .setSone(sone)
+ .setCreationTime(1000L)
+ .setKey("KSK@image1")
+ .setTitle("image1")
+ .setDescription("image-description1")
+ .setWidth(16)
+ .setHeight(9)
+ .update()
+ val secondImage = TestImageBuilder().withId("image2")
+ .build()
+ .modify()
+ .setSone(sone)
+ .setCreationTime(2000L)
+ .setKey("KSK@image2")
+ .setTitle("image2")
+ .setDescription("image-description2")
+ .setWidth(32)
+ .setHeight(18)
+ .update()
+ val thirdImage = TestImageBuilder().withId("image3")
+ .build()
+ .modify()
+ .setSone(sone)
+ .setCreationTime(3000L)
+ .setKey("KSK@image3")
+ .setTitle("image3")
+ .setDescription("image-description3")
+ .setWidth(48)
+ .setHeight(27)
+ .update()
+ firstAlbum.addImage(firstImage)
+ firstAlbum.addImage(thirdImage)
+ secondAlbum.addImage(secondImage)
+ memoryDatabase.storeSone(sone)
+ }
+
+ @Test
+ fun `stored and removed sone is not available`() {
+ storeSone()
+ memoryDatabase.removeSone(sone)
+ assertThat(memoryDatabase.sones, empty())
+ }
+
+ @Test
+ fun `post recipients are detected correctly`() {
+ val postWithRecipient = createPost(of(RECIPIENT_ID))
+ memoryDatabase.storePost(postWithRecipient)
+ val postWithoutRecipient = createPost(absent())
+ memoryDatabase.storePost(postWithoutRecipient)
+ assertThat(memoryDatabase.getDirectedPosts(RECIPIENT_ID), contains(postWithRecipient))
+ }
+
+ private fun createPost(recipient: Optional<String>): Post {
+ val postWithRecipient = mock<Post>()
+ whenever(postWithRecipient.id).thenReturn(randomUUID().toString())
+ whenever(postWithRecipient.sone).thenReturn(sone)
+ whenever(postWithRecipient.recipientId).thenReturn(recipient)
+ return postWithRecipient
+ }
+
+ @Test
+ fun `post replies are managed correctly`() {
+ val firstPost = createPost(absent())
+ val firstPostFirstReply = createPostReply(firstPost, 1000L)
+ val secondPost = createPost(absent())
+ val secondPostFirstReply = createPostReply(secondPost, 1000L)
+ val secondPostSecondReply = createPostReply(secondPost, 2000L)
+ memoryDatabase.storePost(firstPost)
+ memoryDatabase.storePost(secondPost)
+ memoryDatabase.storePostReply(firstPostFirstReply)
+ memoryDatabase.storePostReply(secondPostFirstReply)
+ memoryDatabase.storePostReply(secondPostSecondReply)
+ assertThat(memoryDatabase.getReplies(firstPost.id), contains(firstPostFirstReply))
+ assertThat(memoryDatabase.getReplies(secondPost.id), contains(secondPostFirstReply, secondPostSecondReply))
+ }
+
+ private fun createPostReply(post: Post, time: Long): PostReply {
+ val postReply = mock<PostReply>()
+ whenever(postReply.id).thenReturn(randomUUID().toString())
+ whenever(postReply.time).thenReturn(time)
+ whenever(postReply.post).thenReturn(of(post))
+ val postId = post.id
+ whenever(postReply.postId).thenReturn(postId)
+ return postReply
+ }
+
+ @Test
+ fun `test basic album functionality`() {
+ val newAlbum = AlbumImpl(mock())
+ assertThat(memoryDatabase.getAlbum(newAlbum.id), nullValue())
+ memoryDatabase.storeAlbum(newAlbum)
+ assertThat(memoryDatabase.getAlbum(newAlbum.id), equalTo<Album>(newAlbum))
+ memoryDatabase.removeAlbum(newAlbum)
+ assertThat(memoryDatabase.getAlbum(newAlbum.id), nullValue())
+ }
+
+ private fun initializeFriends() {
+ whenever(configuration.getStringValue("Sone/$SONE_ID/Friends/0/ID")).thenReturn(TestValue.from("Friend1"))
+ whenever(configuration.getStringValue("Sone/$SONE_ID/Friends/1/ID")).thenReturn(TestValue.from("Friend2"))
+ whenever(configuration.getStringValue("Sone/$SONE_ID/Friends/2/ID")).thenReturn(TestValue.from(null))
+ }
+
+ @Test
+ fun `friends are returned correctly`() {
+ initializeFriends()
+ whenever(sone.isLocal).thenReturn(true)
+ val friends = memoryDatabase.getFriends(sone)
+ assertThat(friends, containsInAnyOrder("Friend1", "Friend2"))
+ }
+
+ @Test
+ fun `friends are only loaded once from configuration`() {
+ initializeFriends()
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.getFriends(sone)
+ memoryDatabase.getFriends(sone)
+ verify(configuration, times(1)).getStringValue("Sone/$SONE_ID/Friends/0/ID")
+ }
+
+ @Test
+ fun `friends are only returned for local sones`() {
+ val friends = memoryDatabase.getFriends(sone)
+ assertThat(friends, emptyIterable<Any>())
+ verify(configuration, never()).getStringValue("Sone/$SONE_ID/Friends/0/ID")
+ }
+
+ @Test
+ fun `checking for a friend returns true`() {
+ initializeFriends()
+ whenever(sone.isLocal).thenReturn(true)
+ assertThat(memoryDatabase.isFriend(sone, "Friend1"), equalTo(true))
+ }
+
+ @Test
+ fun `checking for a friend that is not a friend returns false`() {
+ initializeFriends()
+ whenever(sone.isLocal).thenReturn(true)
+ assertThat(memoryDatabase.isFriend(sone, "FriendX"), equalTo(false))
+ }
+
+ @Test
+ fun `checking for a friend of remote sone returns false`() {
+ initializeFriends()
+ assertThat(memoryDatabase.isFriend(sone, "Friend1"), equalTo(false))
+ }
+
+ private fun prepareConfigurationValues(): Map<String, Value<*>> =
+ mutableMapOf<String, Value<*>>().also { configurationValues ->
+ whenever(configuration.getStringValue(anyString())).thenAnswer(createAndCacheValue(configurationValues))
+ whenever(configuration.getLongValue(anyString())).thenAnswer(createAndCacheValue(configurationValues))
+ }
+
+ private fun createAndCacheValue(configurationValues: MutableMap<String, Value<*>>) =
+ { invocation: InvocationOnMock ->
+ configurationValues[invocation[0]]
+ ?: TestValue.from(null).also {
+ configurationValues[invocation[0]] = it
+ }
+ }
+
+ @Test
+ fun `friend is added correctly to local sone`() {
+ val configurationValues = prepareConfigurationValues()
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.addFriend(sone, "Friend1")
+ assertThat(configurationValues["Sone/$SONE_ID/Friends/0/ID"], equalTo<Value<*>>(TestValue.from("Friend1")))
+ assertThat(configurationValues["Sone/$SONE_ID/Friends/1/ID"], equalTo<Value<*>>(TestValue.from(null)))
+ }
+
+ @Test
+ fun `friend is not added to remote sone`() {
+ memoryDatabase.addFriend(sone, "Friend1")
+ verify(configuration, never()).getStringValue(anyString())
+ }
+
+ @Test
+ fun `friend is removed correctly from local sone`() {
+ val configurationValues = prepareConfigurationValues()
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.addFriend(sone, "Friend1")
+ memoryDatabase.removeFriend(sone, "Friend1")
+ assertThat(configurationValues["Sone/$SONE_ID/Friends/0/ID"], equalTo<Value<*>>(TestValue.from(null)))
+ assertThat(configurationValues["Sone/$SONE_ID/Friends/1/ID"], equalTo<Value<*>>(TestValue.from(null)))
+ }
+
+ @Test
+ fun `configuration is not written when a non-friend is removed`() {
+ prepareConfigurationValues()
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.removeFriend(sone, "Friend1")
+ verify(configuration).getStringValue(anyString())
+ }
+
+ @Test
+ fun `sone following time is returned correctly`() {
+ prepareConfigurationValues()
+ configuration.getStringValue("SoneFollowingTimes/0/Sone").value = "sone"
+ configuration.getLongValue("SoneFollowingTimes/0/Time").value = 1000L
+ assertThat(memoryDatabase.getFollowingTime("sone"), equalTo(1000L))
+ }
+
+ @Test
+ fun `null is returned when sone is not followed`() {
+ prepareConfigurationValues()
+ configuration.getStringValue("SoneFollowingTimes/0/Sone").value = "otherSone"
+ configuration.getLongValue("SoneFollowingTimes/0/Time").value = 1000L
+ assertThat(memoryDatabase.getFollowingTime("sone"), nullValue())
+ }
+
+ @Test
+ fun `time is stored in configuration when a sone is followed`() {
+ prepareConfigurationValues()
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.addFriend(sone, "Friend")
+ assertThat(configuration.getStringValue("SoneFollowingTimes/0/Sone").value, equalTo("Friend"))
+ assertThat(System.currentTimeMillis() - configuration.getLongValue("SoneFollowingTimes/0/Time").value, lessThan(1000L))
+ assertThat(configuration.getStringValue("SoneFollowingTimes/1/Sone").value, nullValue())
+ }
+
+ @Test
+ fun `existing time is not overwritten when a sone is followed`() {
+ prepareConfigurationValues()
+ configuration.getStringValue("SoneFollowingTimes/0/Sone").value = "Friend"
+ configuration.getLongValue("SoneFollowingTimes/0/Time").value = 1000L
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.addFriend(sone, "Friend")
+ assertThat(configuration.getStringValue("SoneFollowingTimes/0/Sone").value, equalTo("Friend"))
+ assertThat(configuration.getLongValue("SoneFollowingTimes/0/Time").value, equalTo(1000L))
+ assertThat(configuration.getStringValue("SoneFollowingTimes/1/Sone").value, nullValue())
+ }
+
+ @Test
+ fun `unfollowing a sone removes the following time`() {
+ prepareConfigurationValues()
+ configuration.getStringValue("Sone/sone/Friends/0/ID").value = "Friend"
+ configuration.getStringValue("SoneFollowingTimes/0/Sone").value = "Friend"
+ configuration.getLongValue("SoneFollowingTimes/0/Time").value = 1000L
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.removeFriend(sone, "Friend")
+ assertThat(configuration.getStringValue("SoneFollowingTimes/0/Sone").value, nullValue())
+ }
+
+ @Test
+ fun `unfollowing a sone does not remove the following time if another local sone follows it`() {
+ prepareConfigurationValues()
+ configuration.getStringValue("Sone/sone/Friends/0/ID").value = "Friend"
+ configuration.getStringValue("Sone/other-sone/Friends/0/ID").value = "Friend"
+ configuration.getStringValue("SoneFollowingTimes/0/Sone").value = "Friend"
+ configuration.getLongValue("SoneFollowingTimes/0/Time").value = 1000L
+ val otherSone = mock<Sone>()
+ whenever(otherSone.isLocal).thenReturn(true)
+ whenever(otherSone.id).thenReturn("other-sone")
+ memoryDatabase.getFriends(otherSone)
+ whenever(sone.isLocal).thenReturn(true)
+ memoryDatabase.removeFriend(sone, "Friend")
+ assertThat(configuration.getStringValue("SoneFollowingTimes/0/Sone").value, equalTo("Friend"))
+ assertThat(configuration.getLongValue("SoneFollowingTimes/0/Time").value, equalTo(1000L))
+ }
+
+ @Test
+ fun `marking a post as known saves configuration`() {
+ prepareConfigurationValues()
+ val post = mock<Post>()
+ whenever(post.id).thenReturn("post-id")
+ memoryDatabase.setPostKnown(post, true)
+ assertThat(configuration.getStringValue("KnownPosts/0/ID").value, equalTo("post-id"))
+ assertThat(configuration.getStringValue("KnownPosts/1/ID").value, equalTo<Any>(null))
+ }
+
+ @Test
+ fun `marking a post reply as known saves configuration`() {
+ prepareConfigurationValues()
+ val postReply = mock<PostReply>()
+ whenever(postReply.id).thenReturn("post-reply-id")
+ memoryDatabase.setPostReplyKnown(postReply, true)
+ assertThat(configuration.getStringValue("KnownReplies/0/ID").value, equalTo("post-reply-id"))
+ assertThat(configuration.getStringValue("KnownReplies/1/ID").value, equalTo<Any>(null))
+ }
+
+}
+
+private const val SONE_ID = "sone"
+private const val RECIPIENT_ID = "recipient"
--- /dev/null
+package net.pterodactylus.sone.database.memory
+
+import com.google.common.base.Optional.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.util.*
+
+class MemoryPostTest {
+
+ private val memoryDatabase = mock<MemoryDatabase>()
+
+ @Test
+ fun `memory post returns empty optional for post without recipient`() {
+ val memoryPost = MemoryPost(memoryDatabase, memoryDatabase, UUID.randomUUID().toString(), "soneId", null, 123, "text")
+ assertThat(memoryPost.recipient, equalTo(absent()))
+ }
+
+ @Test
+ fun `empty optional is returned if recipient is set but non-existent`() {
+ val memoryPost = MemoryPost(memoryDatabase, memoryDatabase, UUID.randomUUID().toString(), "soneId", "recipientId", 123, "text")
+ assertThat(memoryPost.recipient, equalTo(absent()))
+ }
+
+}
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
package net.pterodactylus.sone.fcp
-import com.google.common.base.Optional.of
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
/**
* Unit test for [CreateReplyCommand].
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
package net.pterodactylus.sone.fcp
-import com.google.common.base.Optional.of
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeletePostCommand].
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
package net.pterodactylus.sone.fcp
-import com.google.common.base.Optional.of
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeleteReplyCommand].
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
@Test
fun `command does not require write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(false))
+ assertThat(command.requiresWriteAccess, equalTo(false))
}
@Test
@Test
fun `command does not require write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(false))
+ assertThat(command.requiresWriteAccess, equalTo(false))
}
@Test
@Test
fun `command does not require write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(false))
+ assertThat(command.requiresWriteAccess, equalTo(false))
}
@Test
@Test
fun `command does not require write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(false))
+ assertThat(command.requiresWriteAccess, equalTo(false))
}
@Test
@Test
fun `command does not require write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(false))
+ assertThat(command.requiresWriteAccess, equalTo(false))
}
@Test
@Test
fun `command does not require write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(false))
+ assertThat(command.requiresWriteAccess, equalTo(false))
}
@Test
package net.pterodactylus.sone.fcp
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.freenet.fcp.FcpException
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.freenet.fcp.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [LikePostCommand].
*/
class LikePostCommandTest : SoneCommandTest() {
- private val post = createPost("PostId", mock<Sone>(), null, 1000, "Text")
+ private val post = createPost("PostId", mock(), null, 1000, "Text")
override fun createCommand(core: Core) = LikePostCommand(core)
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
@Test
fun `request with valid parameters adds post to liked posts for sone`() {
- whenever(core.getLikes(post)).thenReturn(setOf(mock<Sone>(), mock<Sone>(), mock<Sone>()))
+ whenever(core.getLikes(post)).thenReturn(setOf(mock(), mock(), mock()))
parameters += "Post" to "PostId"
parameters += "Sone" to "LocalSoneId"
val replyParameters = command.execute(parameters).replyParameters
package net.pterodactylus.sone.fcp
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.freenet.fcp.FcpException
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.freenet.fcp.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [LikeReplyCommand].
*/
class LikeReplyCommandTest : SoneCommandTest() {
- private val reply = createReply("ReplyId", mock<Sone>(), mock<Post>(), 1000, "Text")
+ private val reply = createReply("ReplyId", mock(), mock(), 1000, "Text")
override fun createCommand(core: Core) = LikeReplyCommand(core)
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
@Test
fun `request with local sone adds reply id to sone`() {
- whenever(core.getLikes(reply)).thenReturn(setOf(mock<Sone>(), mock<Sone>(), mock<Sone>()))
+ whenever(core.getLikes(reply)).thenReturn(setOf(mock(), mock(), mock()))
parameters += "Reply" to "ReplyId"
parameters += "Sone" to "LocalSoneId"
val replyParameters = command.execute(parameters).replyParameters
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
package net.pterodactylus.sone.fcp
-import com.google.common.base.Optional.absent
-import freenet.support.SimpleFieldSet
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.freenet.fcp.FcpException
-import net.pterodactylus.sone.template.SoneAccessor
-import net.pterodactylus.sone.test.OneByOneMatcher
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
-import org.junit.Before
-import org.junit.Rule
-import org.junit.rules.ExpectedException
-import org.mockito.ArgumentMatchers.anyString
+import freenet.support.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.freenet.fcp.*
+import net.pterodactylus.sone.template.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import org.junit.*
+import org.junit.rules.*
+import org.mockito.ArgumentMatchers.*
/**
* Base class for Sone FCP command tests.
@Test
fun `command requires write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(true))
+ assertThat(command.requiresWriteAccess, equalTo(true))
}
@Test
@Test
fun `command does not require write access`() {
- assertThat(command.requiresWriteAccess(), equalTo(false))
+ assertThat(command.requiresWriteAccess, equalTo(false))
}
@Test
import freenet.l10n.BaseL10n.LANGUAGE.ENGLISH
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.util.template.TemplateContext
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
*/
class L10nFilterTest {
- private val webInterface = mock<WebInterface>()
- private val filter = L10nFilter(webInterface)
- private val templateContext = mock<TemplateContext>()
private val l10n = mock<BaseL10n>()
+ private val filter = L10nFilter(l10n)
+ private val templateContext = mock<TemplateContext>()
private val translations = mutableMapOf<String, String>()
@Before
- fun setupWebInterface() {
- whenever(webInterface.l10n).thenReturn(l10n)
- }
-
- @Before
fun setupL10n() {
whenever(l10n.selectedLanguage).thenReturn(ENGLISH)
whenever(l10n.getString(anyString())).then { translations[it.arguments[0]] }
@Test
fun `filter does not replace values if there are no parameters`() {
translations["data"] = "{link}"
- assertThat(filter.format(templateContext, "data", emptyMap()), equalTo("{link}"));
+ assertThat(filter.format(templateContext, "data", emptyMap()), equalTo("{link}"))
}
}
package net.pterodactylus.sone.main
-import com.google.inject.Guice
-import freenet.client.HighLevelSimpleClient
-import freenet.clients.http.SessionManager
-import freenet.node.Node
-import freenet.pluginmanager.PluginRespirator
-import net.pterodactylus.sone.test.deepMock
-import net.pterodactylus.sone.test.getInstance
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.sameInstance
-import org.junit.Test
-import org.mockito.Mockito.verify
+import com.google.inject.*
+import freenet.client.*
+import freenet.clients.http.*
+import freenet.node.*
+import freenet.pluginmanager.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [FreenetModule].
}
private val node = pluginRespirator.node!!
private val highLevelSimpleClient = pluginRespirator.hlSimpleClient!!
+ private val toadletContainer: ToadletContainer = pluginRespirator.toadletContainer
+ private val pageMaker: PageMaker = pluginRespirator.pageMaker
private val module = FreenetModule(pluginRespirator)
private val injector = Guice.createInjector(module)
- private inline fun <reified T: Any> verifySingletonInstance() {
+ private inline fun <reified T : Any> verifySingletonInstance() {
val firstInstance = injector.getInstance<T>()
val secondInstance = injector.getInstance<T>()
assertThat(firstInstance, sameInstance(secondInstance))
@Test
fun `plugin respirator is returned correctly`() {
- assertThat(injector.getInstance<PluginRespirator>(), sameInstance(pluginRespirator))
+ assertThat(injector.getInstance(), sameInstance(pluginRespirator))
}
@Test
@Test
fun `node is returned correctly`() {
- assertThat(injector.getInstance<Node>(), sameInstance(node))
+ assertThat(injector.getInstance(), sameInstance(node))
}
@Test
@Test
fun `high level simply client is returned correctly`() {
- assertThat(injector.getInstance<HighLevelSimpleClient>(), sameInstance(highLevelSimpleClient))
+ assertThat(injector.getInstance(), sameInstance(highLevelSimpleClient))
}
@Test
@Test
fun `session manager is returned correctly`() {
- assertThat(injector.getInstance<SessionManager>(), sameInstance(sessionManager))
+ assertThat(injector.getInstance(), sameInstance(sessionManager))
}
@Test
verify(pluginRespirator).getSessionManager("Sone")
}
+ @Test
+ fun `toadlet container is returned correctly`() {
+ assertThat(injector.getInstance(), sameInstance(toadletContainer))
+ }
+
+ @Test
+ fun `toadlet container is returned as singleten`() {
+ verifySingletonInstance<ToadletContainer>()
+ }
+
+ @Test
+ fun `page maker is returned correctly`() {
+ assertThat(injector.getInstance(), sameInstance(pageMaker))
+ }
+
+ @Test
+ fun `page maker is returned as singleten`() {
+ verifySingletonInstance<PageMaker>()
+ }
+
}
--- /dev/null
+package net.pterodactylus.sone.main
+
+import com.google.common.base.*
+import com.google.common.eventbus.*
+import com.google.inject.*
+import com.google.inject.name.Names.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.database.memory.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.util.config.*
+import net.pterodactylus.util.version.Version
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.io.*
+import java.util.concurrent.atomic.*
+
+class SoneModuleCreatorTest {
+
+ private val currentDir: File = File(".")
+ private val pluginVersion = Version("", 0, 1, 2)
+ private val pluginYear = 2019
+ private val pluginHomepage = "home://page"
+ private val sonePlugin = mock<SonePlugin>().apply {
+ whenever(version).thenReturn(pluginVersion.toString())
+ whenever(year).thenReturn(pluginYear)
+ whenever(homepage).thenReturn(pluginHomepage)
+ }
+
+ @After
+ fun removePropertiesFromCurrentDirectory() {
+ File(currentDir, "sone.properties").delete()
+ }
+
+ @Test
+ fun `creator binds configuration when no file is present`() {
+ File(currentDir, "sone.properties").delete()
+ assertThat(getInstance<Configuration>(), notNullValue())
+ }
+
+ @Test
+ fun `creator binds first start to true when no file is present`() {
+ File(currentDir, "sone.properties").delete()
+ assertThat(getInstance(named("FirstStart")), equalTo(true))
+ }
+
+ @Test
+ fun `config file is created in current directory if not present`() {
+ File(currentDir, "sone.properties").delete()
+ val configuration = getInstance<Configuration>()
+ configuration.save()
+ assertThat(File(currentDir, "sone.properties").exists(), equalTo(true))
+ }
+
+ @Test
+ fun `creator binds configuration when file is present`() {
+ File(currentDir, "sone.properties").writeText("Option=old")
+ assertThat(getInstance<Configuration>().getStringValue("Option").value, equalTo("old"))
+ }
+
+ @Test
+ fun `creator binds first start to false when file is present`() {
+ File(currentDir, "sone.properties").writeText("Option=old")
+ assertThat(getInstance(named("FirstStart")), equalTo(false))
+ }
+
+ @Test
+ fun `invalid config file leads to new config being created`() {
+ File(currentDir, "sone.properties").writeText("Option=old\nbroken")
+ val configuration = getInstance<Configuration>()
+ assertThat(configuration.getStringValue("Option").getValue(null), nullValue())
+ }
+
+ @Test
+ fun `invalid config file leads to new config being set to true`() {
+ File(currentDir, "sone.properties").writeText("Option=old\nbroken")
+ assertThat(getInstance(named("NewConfig")), equalTo(true))
+ }
+
+ @Test
+ fun `valid config file leads to new config being set to false`() {
+ File(currentDir, "sone.properties").writeText("Option=old")
+ assertThat(getInstance(named("NewConfig")), equalTo(false))
+ }
+
+ @Test
+ fun `event bus is bound`() {
+ assertThat(getInstance<EventBus>(), notNullValue())
+ }
+
+ @Test
+ fun `context is bound`() {
+ assertThat(getInstance<Context>().context, equalTo("Sone"))
+ }
+
+ @Test
+ fun `optional context is bound`() {
+ assertThat(getInstance<Optional<Context>>().get().context, equalTo("Sone"))
+ }
+
+ @Test
+ fun `sone plugin is bound`() {
+ assertThat(getInstance(), sameInstance(sonePlugin))
+ }
+
+ @Test
+ fun `version is bound`() {
+ assertThat(getInstance(), equalTo(pluginVersion))
+ }
+
+ @Test
+ fun `plugin version is bound`() {
+ assertThat(getInstance(), equalTo(PluginVersion(pluginVersion.toString())))
+ }
+
+ @Test
+ fun `plugin year is bound`() {
+ assertThat(getInstance(), equalTo(PluginYear(pluginYear)))
+ }
+
+ @Test
+ fun `plugin homepage in bound`() {
+ assertThat(getInstance(), equalTo(PluginHomepage(pluginHomepage)))
+ }
+
+ @Test
+ fun `database is bound correctly`() {
+ assertThat(getInstance<Database>(), instanceOf(MemoryDatabase::class.java))
+ }
+
+ @Test
+ fun `default loader is used without dev options`() {
+ assertThat(getInstance<Loaders>(), instanceOf(DefaultLoaders::class.java))
+ }
+
+ @Test
+ fun `default loaders are used if no path is given`() {
+ File(currentDir, "sone.properties").writeText("Developer.LoadFromFilesystem=true")
+ assertThat(getInstance<Loaders>(), instanceOf(DefaultLoaders::class.java))
+ }
+
+ @Test
+ fun `debug loaders are used if path is given`() {
+ File(currentDir, "sone.properties").writeText("Developer.LoadFromFilesystem=true\nDeveloper.FilesystemPath=/tmp")
+ assertThat(getInstance<Loaders>(), instanceOf(DebugLoaders::class.java))
+ }
+
+ class TestObject {
+ val ref: AtomicReference<Any?> = AtomicReference()
+ @Subscribe
+ fun testEvent(event: Any?) {
+ ref.set(event)
+ }
+ }
+
+ @Test
+ fun `created objects are registered with event bus`() {
+ val injector = createInjector()
+ val eventBus: EventBus = getInstance(injector = injector)
+ val testObject = getInstance<TestObject>(injector = injector)
+ val event = Any()
+ eventBus.post(event)
+ assertThat(testObject.ref.get(), sameInstance(event))
+ }
+
+ private fun createInjector(): Injector = SoneModuleCreator()
+ .createModule(sonePlugin)
+ .let { Guice.createInjector(it) }
+
+ private inline fun <reified R : Any> getInstance(annotation: Annotation? = null, injector: Injector = createInjector()): R =
+ annotation
+ ?.let { injector.getInstance(Key.get(object : TypeLiteral<R>() {}, it)) }
+ ?: injector.getInstance(Key.get(object : TypeLiteral<R>() {}))
+
+}
--- /dev/null
+package net.pterodactylus.sone.main
+
+import freenet.client.async.USKManager
+import freenet.l10n.BaseL10n.LANGUAGE.ENGLISH
+import freenet.node.Node
+import freenet.node.NodeClientCore
+import freenet.pluginmanager.PluginRespirator
+import net.pterodactylus.sone.test.*
+import org.junit.Test
+
+/**
+ * Unit test for [SonePlugin].
+ */
+class SonePluginTest {
+
+ private val sonePlugin = SonePlugin()
+ private val pluginRespirator = deepMock<PluginRespirator>()
+ private val node = deepMock<Node>()
+ private val clientCore = deepMock<NodeClientCore>()
+ private val uskManager = deepMock<USKManager>()
+
+ init {
+ setField(node, "clientCore", clientCore)
+ whenever(pluginRespirator.node).thenReturn(node)
+ setField(clientCore, "uskManager", uskManager)
+ }
+
+ @Test
+ fun `sone plugin can be started`() {
+ sonePlugin.setLanguage(ENGLISH)
+ sonePlugin.runPlugin(pluginRespirator)
+ }
+
+}
private val accessor = ImageAccessor()
private val album = mock<Album>()
- private val images = listOf(mock<Image>(), mock<Image>())
+ private val images = listOf(mock<Image>(), mock())
@Before
fun setupImages() {
package net.pterodactylus.sone.template
-import com.google.inject.Guice
-import net.pterodactylus.sone.core.LinkedElement
-import net.pterodactylus.sone.test.getInstance
-import net.pterodactylus.sone.test.isProvidedByMock
-import net.pterodactylus.util.template.ClassPathTemplateProvider
-import net.pterodactylus.util.template.HtmlFilter
-import net.pterodactylus.util.template.TemplateContextFactory
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.`is`
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.notNullValue
-import org.hamcrest.Matchers.nullValue
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Element
-import org.junit.Test
+import com.google.inject.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.util.template.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.jsoup.*
+import org.jsoup.nodes.*
+import org.junit.*
/**
* Unit test for [LinkedElementRenderFilter].
*/
class LinkedElementRenderFilterTest {
- private val templateContextFactory = TemplateContextFactory()
-
- init {
- templateContextFactory.addFilter("html", HtmlFilter())
- templateContextFactory.addProvider(ClassPathTemplateProvider(LinkedElementRenderFilter::class.java, "/templates/"))
- }
-
- private val filter = LinkedElementRenderFilter(templateContextFactory)
+ private val filter = LinkedElementRenderFilter()
@Test
fun `filter returns null for objects that are not linked elements`() {
@Test
fun `filter renders empty span for not loaded elements`() {
- val html = filter.format(null, LinkedElement("KSK@gpl.png", loading = true), emptyMap<String, Any?>()) as String
+ val html = filter.format(null, LinkedElement("KSK@gpl.png", loading = true), emptyMap()) as String
val spanNode = Jsoup.parseBodyFragment(html).body().child(0)
- assertThat(spanNode.nodeName(), `is`("span"))
- assertThat(spanNode.attr("class"), `is`("linked-element not-loaded"))
- assertThat(spanNode.attr("title"), `is`("KSK@gpl.png"))
- assertThat(spanNode.hasAttr("style"), `is`(false))
- assertThat(spanNode.children().isEmpty(), `is`(true))
+ assertThat(spanNode.nodeName(), equalTo("span"))
+ assertThat(spanNode.attr("class"), equalTo("linked-element not-loaded"))
+ assertThat(spanNode.attr("title"), equalTo("KSK@gpl.png"))
+ assertThat(spanNode.hasAttr("style"), equalTo(false))
+ assertThat(spanNode.children().isEmpty(), equalTo(true))
}
@Test
fun `filter can render linked images`() {
- val html = filter.format(null, LinkedElement("KSK@gpl.png", properties = mapOf("type" to "image")), emptyMap<String, Any?>()) as String
+ val html = filter.format(null, LinkedElement("KSK@gpl.png", properties = mapOf("type" to "image")), emptyMap()) as String
val outerSpanNode = Jsoup.parseBodyFragment(html).body().child(0)
- assertThat(outerSpanNode.nodeName(), `is`("span"))
- assertThat(outerSpanNode.attr("class"), `is`("linked-element loaded"))
- assertThat(outerSpanNode.attr("title"), `is`("KSK@gpl.png"))
+ assertThat(outerSpanNode.nodeName(), equalTo("span"))
+ assertThat(outerSpanNode.attr("class"), equalTo("linked-element loaded"))
+ assertThat(outerSpanNode.attr("title"), equalTo("KSK@gpl.png"))
val linkNode = outerSpanNode.child(0)
- assertThat(linkNode.nodeName(), `is`("a"))
- assertThat(linkNode.attr("href"), `is`("/KSK@gpl.png"))
+ assertThat(linkNode.nodeName(), equalTo("a"))
+ assertThat(linkNode.attr("href"), equalTo("/KSK@gpl.png"))
val innerSpanNode = linkNode.child(0)
- assertThat(innerSpanNode.attr("style"), `is`("background-image: url('/KSK@gpl.png')"))
+ assertThat(innerSpanNode.attr("style"), equalTo("background-image: url('/KSK@gpl.png')"))
}
@Test
fun `filter can render HTML pages`() {
- val html = filter.format(null, LinkedElement("KSK@gpl.html", properties = mapOf("type" to "html", "title" to "Page Title", "description" to "This is the description.")), emptyMap<String, Any?>()) as String
+ val html = filter.format(null, LinkedElement("KSK@gpl.html", properties = mapOf("type" to "html", "title" to "Page Title", "description" to "This is the description.")), emptyMap()) as String
val outerSpanNode = Jsoup.parseBodyFragment(html).body().child(0)
assertThat(outerSpanNode.nodeName(), equalTo("span"))
- assertThat(outerSpanNode.attr("class"), `is`("linked-element loaded"))
- assertThat(outerSpanNode.attr("title"), `is`("KSK@gpl.html"))
+ assertThat(outerSpanNode.attr("class"), equalTo("linked-element loaded"))
+ assertThat(outerSpanNode.attr("title"), equalTo("KSK@gpl.html"))
val linkNode = outerSpanNode.child(0)
assertThat(linkNode.nodeName(), equalTo("a"))
assertThat(linkNode.attr("href"), equalTo("/KSK@gpl.html"))
@Before
fun setupCore() {
- whenever(core.getImage(eq("avatar-id"), anyBoolean())).thenReturn(mock<Image>())
+ whenever(core.getImage(eq("avatar-id"), anyBoolean())).thenReturn(mock())
}
package net.pterodactylus.sone.template
-import net.pterodactylus.sone.core.Core
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.text.FreemailPart
-import net.pterodactylus.sone.text.FreenetLinkPart
-import net.pterodactylus.sone.text.LinkPart
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.text.*
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.util.template.HtmlFilter
-import net.pterodactylus.util.template.TemplateContext
-import net.pterodactylus.util.template.TemplateContextFactory
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.`is`
-import org.hamcrest.Matchers.containsInAnyOrder
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Attribute
-import org.jsoup.nodes.Element
-import org.junit.Test
-import org.mockito.Mockito.`when`
-import java.net.URLEncoder
+import net.pterodactylus.util.template.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.jsoup.*
+import org.jsoup.nodes.*
+import org.junit.*
+import org.mockito.*
+import java.net.*
/**
* Unit test for [RenderFilter].
private const val POST_ID = "37a06250-6775-4b94-86ff-257ba690953c"
}
- private val core = mock<Core>()
- private val templateContextFactory = TemplateContextFactory()
- private val templateContext: TemplateContext
+ private val soneProvider = mock<SoneProvider>()
+ private val soneTextParser = mock<SoneTextParser>()
+ private val htmlFilter = HtmlFilter()
private val sone = setupSone(SONE_IDENTITY, "Sone", "First")
private val parameters = mutableMapOf<String, Any?>()
- init {
- templateContextFactory.addFilter("html", HtmlFilter())
- templateContext = templateContextFactory.createTemplateContext()
- }
-
- private val filter = RenderFilter(core, templateContextFactory)
+ private val filter = RenderFilter(soneProvider, soneTextParser, htmlFilter)
+ private val templateContext = TemplateContext()
@Test
fun `plain text part is rendered correctly`() {
- assertThat(renderParts(PlainTextPart("plain text")), `is`("plain text"))
+ assertThat(renderParts(PlainTextPart("plain text")), equalTo("plain text"))
}
private fun renderParts(vararg part: Part) = filter.format(templateContext, listOf(*part), parameters) as String
}
private fun verifyLink(linkNode: Element, url: String, cssClass: String, tooltip: String, text: String) {
- assertThat(linkNode.nodeName(), `is`("a"))
+ assertThat(linkNode.nodeName(), equalTo("a"))
assertThat<List<Attribute>>(linkNode.attributes().asList(), containsInAnyOrder(
Attribute("href", url),
Attribute("class", cssClass),
Attribute("title", tooltip)
))
- assertThat(linkNode.text(), `is`(text))
+ assertThat(linkNode.text(), equalTo(text))
}
@Test
private fun setupSone(identity: String, name: String?, firstName: String): Sone {
val sone = mock<Sone>()
- `when`(sone.id).thenReturn(identity)
- `when`(sone.profile).thenReturn(Profile(sone))
- `when`(sone.name).thenReturn(name)
+ whenever(sone.id).thenReturn(identity)
+ whenever(sone.profile).thenReturn(Profile(sone))
+ whenever(sone.name).thenReturn(name)
sone.profile.firstName = firstName
- `when`(core.getSone(identity)).thenReturn(sone)
+ whenever(soneProvider.getSone(identity)).thenReturn(sone)
return sone
}
@Test
fun `post part is cut off correctly when there are spaces`() {
val post = setupPost(sone, "1234 678901 345 789012 45678 01.")
+ whenever(soneTextParser.parse(eq("1234 678901 345 789012 45678 01."), ArgumentMatchers.any()))
+ .thenReturn(listOf(PlainTextPart("1234 678901 345 789012 45678 01.")))
val linkNode = renderParts(PostPart(post)).toLinkNode()
verifyLink(linkNode, "viewPost.html?post=$POST_ID", "in-sone", "First", "1234 678901 345…")
}
- private fun setupPost(sone: Sone, value: String): Post {
- val post = mock<Post>()
- `when`(post.id).thenReturn(POST_ID)
- `when`(post.sone).thenReturn(sone)
- `when`(post.text).thenReturn(value)
- return post
- }
+ private fun setupPost(sone: Sone, value: String) =
+ mock<Post>().apply {
+ whenever(id).thenReturn(POST_ID)
+ whenever(this.sone).thenReturn(this@RenderFilterTest.sone)
+ whenever(text).thenReturn(value)
+ }
@Test
fun `post part is cut off correctly when there are no spaces`() {
val post = setupPost(sone, "1234567890123456789012345678901.")
+ whenever(soneTextParser.parse(eq("1234567890123456789012345678901."), ArgumentMatchers.any()))
+ .thenReturn(listOf(PlainTextPart("1234567890123456789012345678901.")))
val linkNode = renderParts(PostPart(post)).toLinkNode()
verifyLink(linkNode, "viewPost.html?post=$POST_ID", "in-sone", "First", "12345678901234567890…")
}
@Test
fun `post part shorter than 21 chars is not cut off`() {
val post = setupPost(sone, "12345678901234567890")
+ whenever(soneTextParser.parse(eq("12345678901234567890"), ArgumentMatchers.any()))
+ .thenReturn(listOf(PlainTextPart("12345678901234567890")))
val linkNode = renderParts(PostPart(post)).toLinkNode()
verifyLink(linkNode, "viewPost.html?post=$POST_ID", "in-sone", "First", "12345678901234567890")
}
@Test
fun `multiple parts are rendered correctly`() {
val parts = arrayOf(PlainTextPart("te"), PlainTextPart("xt"))
- assertThat(renderParts(*parts), `is`("text"))
+ assertThat(renderParts(*parts), equalTo("text"))
}
@Test
@Test
fun `returns the likes correctly`() {
- val sones = setOf(mock<Sone>(), mock<Sone>(), mock<Sone>())
+ val sones = setOf(mock<Sone>(), mock(), mock())
whenever(core.getLikes(reply)).thenReturn(sones)
assertThat(accessor.get(templateContext, reply, "likes"), equalTo<Any>(sones))
}
@Test
fun `return that a reply is loaded if its sone is not null`() {
- whenever(reply.sone).thenReturn(mock<Sone>())
+ whenever(reply.sone).thenReturn(mock())
assertThat(accessor.get(templateContext, reply, "loaded"), equalTo<Any>(true))
}
@Before
fun setupReplies() {
- (0..4).forEach {
+ repeat(5) {
sones += mock<Sone>()
}
(0..7).forEach {
@Test
fun `accessor returns that the sone’s status is not unknown if it is not unknown`() {
- whenever(sone.status).thenReturn(mock<SoneStatus>())
+ whenever(sone.status).thenReturn(mock())
assertAccessorReturnValue("unknown", false)
}
@Test
fun `accessor returns that the sone’s status is not idle if it is not idle`() {
- whenever(sone.status).thenReturn(mock<SoneStatus>())
+ whenever(sone.status).thenReturn(mock())
assertAccessorReturnValue("idle", false)
}
@Test
fun `accessor returns that the sone’s status is not inserting if it is not inserting`() {
- whenever(sone.status).thenReturn(mock<SoneStatus>())
+ whenever(sone.status).thenReturn(mock())
assertAccessorReturnValue("inserting", false)
}
@Test
fun `accessor returns that the sone’s status is not downloading if it is not downloading`() {
- whenever(sone.status).thenReturn(mock<SoneStatus>())
+ whenever(sone.status).thenReturn(mock())
assertAccessorReturnValue("downloading", false)
}
@Test
fun `accessor returns all images in the correct order`() {
- val images = listOf(mock<Image>(), mock<Image>(), mock<Image>(), mock<Image>(), mock<Image>())
+ val images = listOf(mock<Image>(), mock(), mock(), mock(), mock())
val firstAlbum = createAlbum(listOf(), listOf(images[0], images[3]))
val secondAlbum = createAlbum(listOf(), listOf(images[1], images[4], images[2]))
val rootAlbum = createAlbum(listOf(firstAlbum, secondAlbum), listOf())
@Test
fun `accessor returns all albums in the correct order`() {
- val albums = listOf(mock<Album>(), mock<Album>(), mock<Album>(), mock<Album>(), mock<Album>())
+ val albums = listOf(mock<Album>(), mock(), mock(), mock(), mock())
val rootAlbum = createAlbum(albums, listOf())
whenever(sone.rootAlbum).thenReturn(rootAlbum)
assertAccessorReturnValueMatches("albums", contains(*albums.toTypedArray()))
import com.google.inject.Injector
import com.google.inject.Module
+import com.google.inject.name.*
+import org.mockito.*
import javax.inject.Provider
import kotlin.reflect.KClass
-fun <T : Any> KClass<T>.isProvidedBy(instance: T) = Module { it.bind(this.java).toProvider { instance } }
+fun <T : Any> KClass<T>.isProvidedBy(instance: T) = Module { it.bind(this.java).toProvider(Provider<T> { instance }) }
+fun <T : Any> KClass<T>.withNameIsProvidedBy(instance: T, name: String) = Module { it.bind(this.java).annotatedWith(Names.named(name)).toProvider(Provider<T> { instance }) }
fun <T : Any> KClass<T>.isProvidedBy(provider: com.google.inject.Provider<T>) = Module { it.bind(this.java).toProvider(provider) }
fun <T : Any> KClass<T>.isProvidedBy(provider: KClass<out Provider<T>>) = Module { it.bind(this.java).toProvider(provider.java) }
-inline fun <reified T : Any> KClass<T>.isProvidedByMock() = Module { it.bind(this.java).toProvider { mock<T>() } }
+inline fun <reified T : Any> KClass<T>.isProvidedByMock() = Module { it.bind(this.java).toProvider(Provider<T> { mock() }) }
+inline fun <reified T : Any> KClass<T>.isProvidedByDeepMock() = Module { it.bind(this.java).toProvider(Provider<T> { deepMock() }) }
inline fun <reified T : Any> Injector.getInstance() = getInstance(T::class.java)!!
fun <T : Any> supply(javaClass: Class<T>): Source<T> = object : Source<T> {
override fun fromInstance(instance: T) = Module { it.bind(javaClass).toInstance(instance) }
- override fun byInstance(instance: T) = Module { it.bind(javaClass).toProvider { instance } }
+ override fun byInstance(instance: T) = Module { it.bind(javaClass).toProvider(Provider<T> { instance }) }
override fun byProvider(provider: com.google.inject.Provider<T>) = Module { it.bind(javaClass).toProvider(provider) }
override fun byProvider(provider: Class<Provider<T>>) = Module { it.bind(javaClass).toProvider(provider) }
+ override fun byMock() = Module { it.bind(javaClass).toInstance(Mockito.mock(javaClass)) }
}
interface Source<T : Any> {
fun byInstance(instance: T): Module
fun byProvider(provider: com.google.inject.Provider<T>): Module
fun byProvider(provider: Class<Provider<T>>): Module
+ fun byMock(): Module
}
--- /dev/null
+package net.pterodactylus.sone.test
+
+import net.pterodactylus.util.web.*
+import org.hamcrest.*
+
+fun hasHeader(name: String, value: String) = object : TypeSafeDiagnosingMatcher<Header>() {
+ override fun matchesSafely(item: Header, mismatchDescription: Description) =
+ compare(item.name, { it.equals(name, ignoreCase = true) }) { mismatchDescription.appendText("name is ").appendValue(it) }
+ ?: compare(item.hasValue(value), { it }) { mismatchDescription.appendText("does not have value ").appendValue(value) }
+ ?: true
+
+ override fun describeTo(description: Description) {
+ description.appendText("name is ").appendValue(name)
+ .appendText(", value is ").appendValue(value)
+ }
+}
+
+fun <T : Any> compare(value: T, comparison: (T) -> Boolean, onError: (T) -> Unit) =
+ false.takeUnless { comparison(value) }
+ ?.also { onError(value) }
+
package net.pterodactylus.sone.test
import com.google.inject.Module
-import org.mockito.ArgumentCaptor
-import org.mockito.Mockito
+import org.mockito.*
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.OngoingStubbing
Module { it!!.bind(T::class.java).toInstance(implementation) }
inline fun <reified T : Any> bindMock(): Module =
- Module { it!!.bind(T::class.java).toInstance(mock<T>()) }
+ Module { it!!.bind(T::class.java).toInstance(mock()) }
inline fun <reified T: Any?> whenever(methodCall: T) = Mockito.`when`(methodCall)!!
-inline fun <reified T : Any> OngoingStubbing<T>.thenReturnMock(): OngoingStubbing<T> = this.thenReturn(mock<T>())
+inline fun <reified T : Any> OngoingStubbing<T>.thenReturnMock(): OngoingStubbing<T> = this.thenReturn(mock())
operator fun <T> InvocationOnMock.get(index: Int): T = getArgument(index)
inline fun <reified T> argumentCaptor(): ArgumentCaptor<T> = ArgumentCaptor.forClass<T, T>(T::class.java)!!
+
+fun <T> eq(t: T): T {
+ ArgumentMatchers.eq(t)
+ return null as T
+}
private val matchers = mutableListOf<Matcher<A, *>>()
fun <V> expect(description: String, expected: V, actual: (A) -> V) {
- matchers += Matcher<A, V>(expected, actual, description)
+ matchers += Matcher(expected, actual, description)
}
override fun describeTo(description: Description) {
--- /dev/null
+package net.pterodactylus.sone.test
+
+import java.lang.reflect.*
+
+private val modifiers = Field::class.java.getDeclaredField("modifiers").apply {
+ isAccessible = true
+}
+
+fun setField(instance: Any, name: String, value: Any?) {
+ generateSequence<Class<*>>(instance.javaClass) { it.superclass }
+ .flatMap { it.declaredFields.asSequence() }
+ .filter { it.name == name }
+ .toList()
+ .forEach { field ->
+ field.isAccessible = true
+ modifiers.setInt(field, field.modifiers and Modifier.FINAL.inv())
+ field.set(instance, value)
+ }
+}
private val sone = mock<Sone>()
init {
- `when`(sone.profile).thenReturn(mock<Profile>())
+ `when`(sone.profile).thenReturn(mock())
`when`(sone.name).thenReturn("sone")
}
--- /dev/null
+/*
+ * Sone - SoneTextParserTest.kt - Copyright © 2011–2019 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.text
+
+import com.google.inject.Guice.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.impl.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import kotlin.test.*
+
+/**
+ * JUnit test case for [SoneTextParser].
+ */
+class SoneTextParserTest {
+
+ private val soneTextParser = SoneTextParser(null, null)
+
+ @Test
+ fun `basic operation`() {
+ val parts = soneTextParser.parse("Test.", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java), equalTo("Test."))
+ }
+
+ @Test
+ fun `empty lines at start and end are stripped`() {
+ val parts = soneTextParser.parse("\nTest.\n\n", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java), equalTo("Test."))
+ }
+
+ @Test
+ fun `duplicate empty lines in the text are stripped`() {
+ val parts = soneTextParser.parse("\nTest.\n\n\nTest.", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java), equalTo("Test.\n\nTest."))
+ }
+
+ @Test
+ fun `consecutive lines are separated by linefeed`() {
+ val parts = soneTextParser.parse("Text.\nText", null)
+ assertThat("Part Text", convertText(parts), equalTo("Text.\nText"))
+ }
+
+ @Test
+ fun `freenet links have the freenet prefix removed`() {
+ val parts = soneTextParser.parse("freenet:KSK@gpl.txt", null)
+ assertThat("Part Text", convertText(parts), equalTo("[KSK@gpl.txt|KSK@gpl.txt|gpl.txt]"))
+ }
+
+ @Test
+ fun `only the first item in a line is prefixed with a line break`() {
+ val parts = soneTextParser.parse("Text.\nKSK@gpl.txt and KSK@gpl.txt", null)
+ assertThat("Part Text", convertText(parts), equalTo("Text.\n[KSK@gpl.txt|KSK@gpl.txt|gpl.txt] and [KSK@gpl.txt|KSK@gpl.txt|gpl.txt]"))
+ }
+
+ @Test
+ fun `sone link with too short sone ID is rendered as plain text`() {
+ val parts = soneTextParser.parse("sone://too-short", null)
+ assertThat("Part Text", convertText(parts), equalTo("sone://too-short"))
+ }
+
+ @Test
+ fun `sone link is rendered correctly if sone is not present`() {
+ val parser = SoneTextParser(AbsentSoneProvider(), null)
+ val parts = parser.parse("sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU", null)
+ assertThat("Part Text", convertText(parts), equalTo("[Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU]"))
+ }
+
+ @Test
+ fun `sone and post can be parsed from the same text`() {
+ val parser = SoneTextParser(TestSoneProvider(), TestPostProvider())
+ val parts = parser.parse("Text sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU more text post://f3757817-b45a-497a-803f-9c5aafc10dc6 even more text", null)
+ assertThat("Part Text", convertText(parts), equalTo("Text [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] more text [Post|f3757817-b45a-497a-803f-9c5aafc10dc6|text] even more text"))
+ }
+
+ @Test
+ fun `post link is rendered as plain text if post ID is too short`() {
+ val parts = soneTextParser.parse("post://too-short", null)
+ assertThat("Part Text", convertText(parts), equalTo("post://too-short"))
+ }
+
+ @Test
+ fun `post link is rendered correctly if post is present`() {
+ val parser = SoneTextParser(null, TestPostProvider())
+ val parts = parser.parse("post://f3757817-b45a-497a-803f-9c5aafc10dc6", null)
+ assertThat("Part Text", convertText(parts), equalTo("[Post|f3757817-b45a-497a-803f-9c5aafc10dc6|text]"))
+ }
+
+ @Test
+ fun `post link is rendered as plain text if post is absent`() {
+ val parser = SoneTextParser(null, AbsentPostProvider())
+ val parts = parser.parse("post://f3757817-b45a-497a-803f-9c5aafc10dc6", null)
+ assertThat("Part Text", convertText(parts), equalTo("post://f3757817-b45a-497a-803f-9c5aafc10dc6"))
+ }
+
+ @Test
+ fun `name of freenet link does not contain url parameters`() {
+ val parts = soneTextParser.parse("KSK@gpl.txt?max-size=12345", null)
+ assertThat("Part Text", convertText(parts), equalTo("[KSK@gpl.txt?max-size=12345|KSK@gpl.txt|gpl.txt]"))
+ }
+
+ @Test
+ fun `trailing slash in freenet link is removed for name`() {
+ val parts = soneTextParser.parse("KSK@gpl.txt/", null)
+ assertThat("Part Text", convertText(parts), equalTo("[KSK@gpl.txt/|KSK@gpl.txt/|gpl.txt]"))
+ }
+
+ @Test
+ fun `last meta string of freenet link is used as name`() {
+ val parts = soneTextParser.parse("CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING", null)
+ assertThat("Part Text", convertText(parts), equalTo("[CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING|CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING|COPYING]"))
+ }
+
+ @Test
+ fun `freenet link without meta strings and doc name gets first nine characters of key as name`() {
+ val parts = soneTextParser.parse("CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8", null)
+ assertThat("Part Text", convertText(parts), equalTo("[CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8|CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8|CHK@qM1nm]"))
+ }
+
+ @Test
+ fun `malformed key is rendered as plain text`() {
+ val parts = soneTextParser.parse("CHK@qM1nmgU", null)
+ assertThat("Part Text", convertText(parts), equalTo("CHK@qM1nmgU"))
+ }
+
+ @Test
+ fun `https link has its paths shortened`() {
+ val parts = soneTextParser.parse("https://test.test/some-long-path/file.txt", null)
+ assertThat("Part Text", convertText(parts), equalTo("[https://test.test/some-long-path/file.txt|https://test.test/some-long-path/file.txt|test.test/…/file.txt]"))
+ }
+
+ @Test
+ fun `http links have their last slash removed`() {
+ val parts = soneTextParser.parse("http://test.test/test/", null)
+ assertThat("Part Text", convertText(parts), equalTo("[http://test.test/test/|http://test.test/test/|test.test/…]"))
+ }
+
+ @Test
+ fun `www prefix is removed for hostname with two dots and no path`() {
+ val parts = soneTextParser.parse("http://www.test.test", null)
+ assertThat("Part Text", convertText(parts), equalTo("[http://www.test.test|http://www.test.test|test.test]"))
+ }
+
+ @Test
+ fun `www prefix is removed for hostname with two dots and a path`() {
+ val parts = soneTextParser.parse("http://www.test.test/test.html", null)
+ assertThat("Part Text", convertText(parts), equalTo("[http://www.test.test/test.html|http://www.test.test/test.html|test.test/test.html]"))
+ }
+
+ @Test
+ fun `hostname is kept intact if not beginning with www`() {
+ val parts = soneTextParser.parse("http://test.test.test/test.html", null)
+ assertThat("Part Text", convertText(parts), equalTo("[http://test.test.test/test.html|http://test.test.test/test.html|test.test.test/test.html]"))
+ }
+
+ @Test
+ fun `hostname with one dot but no slash is kept intact`() {
+ val parts = soneTextParser.parse("http://test.test", null)
+ assertThat("Part Text", convertText(parts), equalTo("[http://test.test|http://test.test|test.test]"))
+ }
+
+ @Test
+ fun `url parameters are removed for http links`() {
+ val parts = soneTextParser.parse("http://test.test?foo=bar", null)
+ assertThat("Part Text", convertText(parts), equalTo("[http://test.test?foo=bar|http://test.test?foo=bar|test.test]"))
+ }
+
+ @Test
+ fun `empty string is parsed correctly`() {
+ val parts = soneTextParser.parse("", null)
+ assertThat("Part Text", convertText(parts), equalTo(""))
+ }
+
+ @Test
+ fun `links are parsed in correct order`() {
+ val parts = soneTextParser.parse("KSK@ CHK@", null)
+ assertThat("Part Text", convertText(parts), equalTo("KSK@ CHK@"))
+ }
+
+ @Test
+ fun `invalid ssk and usk link is parsed as text`() {
+ val parts = soneTextParser.parse("SSK@a USK@a", null)
+ assertThat("Part Text", convertText(parts), equalTo("SSK@a USK@a"))
+ }
+
+ @Test
+ fun `ssk without document name is parsed correctly`() {
+ val parts = soneTextParser.parse(
+ "SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8", null)
+ assertThat("Part Text", convertText(parts),
+ equalTo("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8|"
+ + "SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8|"
+ + "SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU]"))
+ }
+
+ @Test
+ fun `ssk link without context is not trusted`() {
+ val parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", null)
+ assertThat("Part Text", convertText(parts), equalTo("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"))
+ }
+
+ @Test
+ fun `ssk link with context without sone is not trusted`() {
+ val context = SoneTextParserContext(null)
+ val parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context)
+ assertThat("Part Text", convertText(parts), equalTo("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"))
+ }
+
+ @Test
+ fun `ssk link with context with different sone is not trusted`() {
+ val context = SoneTextParserContext(IdOnlySone("DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU"))
+ val parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context)
+ assertThat("Part Text", convertText(parts), equalTo("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"))
+ }
+
+ @Test
+ fun `ssk link with context with correct sone is trusted`() {
+ val context = SoneTextParserContext(IdOnlySone("qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU"))
+ val parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context)
+ assertThat("Part Text", convertText(parts), equalTo("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|trusted|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"))
+ }
+
+ @Test
+ fun `usk link with context with correct sone is trusted`() {
+ val context = SoneTextParserContext(IdOnlySone("qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU"))
+ val parts = soneTextParser.parse("USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0", context)
+ assertThat("Part Text", convertText(parts), equalTo("[USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|trusted|USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|test]"))
+ }
+
+ @Test
+ fun `usk links with backlinks is parsed correctly`() {
+ val context = SoneTextParserContext(IdOnlySone("qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU"))
+ val parts = soneTextParser.parse("USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0/../../../USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/78/", context)
+ assertThat("Part Text", convertText(parts), equalTo("[USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|trusted|USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|test]"))
+ }
+
+ @Test
+ fun `test basic ksk links`() {
+ val parts: Iterable<Part> = soneTextParser.parse("KSK@gpl.txt", null)
+ assertThat("Part Text", convertText(parts, FreenetLinkPart::class.java), equalTo("[KSK@gpl.txt|KSK@gpl.txt|gpl.txt]"))
+ }
+
+ @Test
+ fun `embedded ksk links are parsed correctly`() {
+ val parts = soneTextParser.parse("Link is KSK@gpl.txt\u200b.", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, FreenetLinkPart::class.java), equalTo("Link is [KSK@gpl.txt|KSK@gpl.txt|gpl.txt]\u200b."))
+ }
+
+ @Test
+ fun `embedded ksk links and line breaks are parsed correctly`() {
+ val parts = soneTextParser.parse("Link is KSK@gpl.txt\nKSK@test.dat\n", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, FreenetLinkPart::class.java), equalTo("Link is [KSK@gpl.txt|KSK@gpl.txt|gpl.txt]\n[KSK@test.dat|KSK@test.dat|test.dat]"))
+ }
+
+ @Test
+ fun `ksk links with backlinks are parsed correctly`() {
+ val parts = soneTextParser.parse("KSK@gallery/../Sone/imageBrowser.html?album=30c930ee-97cd-11e9-bd44-f3e595768b77", null)
+ assertThat("Part Text", convertText(parts, FreenetLinkPart::class.java), equalTo("[KSK@gallery|KSK@gallery|gallery]"))
+ }
+
+ @Test
+ fun `test empty lines and sone links`() {
+ val soneTextParser = SoneTextParser(TestSoneProvider(), null)
+
+ /* check basic links. */
+ val parts = soneTextParser.parse("Some text.\n\nLink to sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU and stuff.", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, SonePart::class.java), equalTo("Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff."))
+ }
+
+ @Test
+ fun `test empy http links`() {
+ val soneTextParser = SoneTextParser(TestSoneProvider(), null)
+
+ /* check empty http links. */
+ val parts = soneTextParser.parse("Some text. Empty link: http:// – nice!", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java), equalTo("Some text. Empty link: http:// – nice!"))
+ }
+
+ @Test
+ fun `http link without parens ends at next closing paren`() {
+ val parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc) – nice!", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, LinkPart::class.java), equalTo("Some text (and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]) – nice!"))
+ }
+
+ @Test
+ fun `usk link ends at first non numeric non slash character after version number`() {
+ val parts = soneTextParser.parse("Some link (USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0). Nice", null)
+ assertThat("Part Text", convertText(parts), equalTo("Some link ([USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|test]). Nice"))
+ }
+
+ @Test
+ fun `usk link with filename shows the filename`() {
+ val parts = soneTextParser.parse("Some link (USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0/images/image.jpg). Nice", null)
+ assertThat("Part Text", convertText(parts), equalTo("Some link ([USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0/images/image.jpg|USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0/images/image.jpg|image.jpg]). Nice"))
+ }
+
+ @Test
+ fun `usk link without filename but ending in slash shows the path`() {
+ val parts = soneTextParser.parse("Some link (USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0/). Nice", null)
+ assertThat("Part Text", convertText(parts), equalTo("Some link ([USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|test]). Nice"))
+ }
+
+ @Test
+ fun `http link with opened and closed parens ends at next closing paren`() {
+ val parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc_(def)) – nice!", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, LinkPart::class.java), equalTo("Some text (and a link: [http://example.sone/abc_(def)|http://example.sone/abc_(def)|example.sone/abc_(def)]) – nice!"))
+ }
+
+ @Test
+ fun `punctuation is ignored at end of link before whitespace`() {
+ val parts = soneTextParser.parse("Some text and a link: http://example.sone/abc. Nice!", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, LinkPart::class.java), equalTo("Some text and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]. Nice!"))
+ }
+
+ @Test
+ fun `multiple punctuation characters are ignored at end of link before whitespace`() {
+ val parts = soneTextParser.parse("Some text and a link: http://example.sone/abc... Nice!", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, LinkPart::class.java), equalTo("Some text and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]... Nice!"))
+ }
+
+ @Test
+ fun `commas are ignored at end of link before whitespace`() {
+ val parts = soneTextParser.parse("Some text and a link: http://example.sone/abc, nice!", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, LinkPart::class.java), equalTo("Some text and a link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc], nice!"))
+ }
+
+ @Test
+ fun `exclamation marks are ignored at end of link before whitespace`() {
+ val parts = soneTextParser.parse("A link: http://example.sone/abc!", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, LinkPart::class.java), equalTo("A link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]!"))
+ }
+
+ @Test
+ fun `question marks are ignored at end of link before whitespace`() {
+ val parts = soneTextParser.parse("A link: http://example.sone/abc?", null)
+ assertThat("Part Text", convertText(parts, PlainTextPart::class.java, LinkPart::class.java), equalTo("A link: [http://example.sone/abc|http://example.sone/abc|example.sone/abc]?"))
+ }
+
+ @Test
+ fun `correct freemail address is linked to correctly`() {
+ val parts = soneTextParser.parse("Mail me at sone@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!", null)
+ assertThat("Part Text", convertText(parts), equalTo("Mail me at [Freemail|sone|t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra|nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI]!"))
+ }
+
+ @Test
+ fun `freemail address with invalid freemail id is parsed as text`() {
+ val parts = soneTextParser.parse("Mail me at sone@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqr8.freemail!", null)
+ assertThat("Part Text", convertText(parts), equalTo("Mail me at sone@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqr8.freemail!"))
+ }
+
+ @Test
+ fun `freemail address with invalid sized freemail id is parsed as text`() {
+ val parts = soneTextParser.parse("Mail me at sone@4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!", null)
+ assertThat("Part Text", convertText(parts), equalTo("Mail me at sone@4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!"))
+ }
+
+ @Test
+ fun `freemail address without local part is parsed as text`() {
+ val parts = soneTextParser.parse(" @t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!", null)
+ assertThat("Part Text", convertText(parts), equalTo(" @t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail!"))
+ }
+
+ @Test
+ fun `local part of freemail address can contain letters digits minus dot underscore`() {
+ val parts = soneTextParser.parse("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._@t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra.freemail", null)
+ assertThat("Part Text", convertText(parts), equalTo("[Freemail|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._|t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra|nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI]"))
+ }
+
+ private fun convertText(parts: Iterable<Part>, vararg validClasses: Class<*>): String {
+ if (validClasses.isNotEmpty()) {
+ assertThat(parts.map { it.javaClass }.distinct() - validClasses.distinct(), empty())
+ }
+ return parts.joinToString("") { part ->
+ when (part) {
+ is PlainTextPart -> part.text
+ is FreenetLinkPart -> "[${part.link}|${if (part.trusted) "trusted|" else ""}${part.title}|${part.text}]"
+ is FreemailPart -> "[Freemail|${part.emailLocalPart}|${part.freemailId}|${part.identityId}]"
+ is LinkPart -> "[${part.link}|${part.title}|${part.text}]"
+ is SonePart -> "[Sone|${part.sone.id}]"
+ is PostPart -> "[Post|${part.post.id}|${part.post.text}]"
+ else -> throw NoSuchElementException()
+ }
+ }
+ }
+
+ @Test
+ fun `parser can be created by guice`() {
+ val injector = createInjector(
+ SoneProvider::class.isProvidedByMock(),
+ PostProvider::class.isProvidedByMock()
+ )
+ assertThat(injector.getInstance<SoneTextParser>(), notNullValue())
+ }
+
+ /**
+ * Mock Sone provider.
+ */
+ private open class TestSoneProvider : SoneProvider {
+
+ override val soneLoader = this::getSone
+ override val sones: Collection<Sone> = emptySet()
+ override val localSones: Collection<Sone> = emptySet()
+ override val remoteSones: Collection<Sone> = emptySet()
+
+ override fun getSone(soneId: String): Sone? = IdOnlySone(soneId)
+
+ }
+
+ private class AbsentSoneProvider : TestSoneProvider() {
+
+ override fun getSone(soneId: String): Sone? = null
+
+ }
+
+ private open class TestPostProvider : PostProvider {
+
+ override fun getPost(postId: String): Post? {
+ return object : Post {
+ override val id = postId
+ override fun isLoaded() = false
+ override fun getSone() = null
+ override fun getRecipientId() = null
+ override fun getRecipient() = null
+ override fun getTime() = 0L
+ override fun getText() = "text"
+ override fun isKnown() = false
+ override fun setKnown(known: Boolean) = null
+ }
+ }
+
+ override fun getPosts(soneId: String) = emptySet<Post>()
+ override fun getDirectedPosts(recipientId: String) = emptySet<Post>()
+
+ }
+
+ private class AbsentPostProvider : TestPostProvider() {
+
+ override fun getPost(postId: String): Post? = null
+
+ }
+
+}
package net.pterodactylus.sone.utils
import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.empty
+import org.hamcrest.Matchers.*
import org.junit.Test
/**
assertThat(null.asList(), empty())
}
+ @Test(expected = IllegalArgumentException::class)
+ fun `exception is thrown for null and true condition`() {
+ null.throwOnNullIf(true) { IllegalArgumentException() }
+ }
+
+ @Test
+ fun `exception is not thrown for null and false condition`() {
+ assertThat(null.throwOnNullIf(false) { IllegalArgumentException() }, nullValue())
+ }
+
+ @Test
+ fun `exception is not thrown for any and true condition`() {
+ val any = Any()
+ assertThat(any.throwOnNullIf(true) { IllegalArgumentException() }, equalTo(any))
+ }
+
+ @Test
+ fun `exception is not thrown for any and false condition`() {
+ val any = Any()
+ assertThat(any.throwOnNullIf(false) { IllegalArgumentException() }, equalTo(any))
+ }
+
}
class PaginationTest {
private val items = listOf(1, 2, 3, 4, 5)
- private val pagination = Pagination<Int>(items, 2)
+ private val pagination = Pagination(items, 2)
@Test
fun `pagination can be created from iterable`() {
--- /dev/null
+package net.pterodactylus.sone.web
+
+import com.google.inject.Guice.createInjector
+import net.pterodactylus.sone.core.Core
+import net.pterodactylus.sone.core.FreenetInterface
+import net.pterodactylus.sone.main.PluginHomepage
+import net.pterodactylus.sone.main.PluginVersion
+import net.pterodactylus.sone.main.PluginYear
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.sone.web.pages.*
+import net.pterodactylus.util.template.Template
+import org.hamcrest.Matcher
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers
+import org.junit.Test
+
+/**
+ * Test for [AllPages].
+ */
+class AllPagesTest {
+
+ private val injector by lazy {
+ baseInjector.createChildInjector(
+ PluginVersion::class.isProvidedByMock(),
+ PluginYear::class.isProvidedByMock(),
+ PluginHomepage::class.isProvidedByMock()
+ )!!
+ }
+ private val allPages by lazy { injector.getInstance<AllPages>() }
+
+ private inline fun <reified T> instanceOf(): Matcher<T> = Matchers.instanceOf<T>(T::class.java)
+
+ @Test
+ fun `about page can be injected`() {
+ assertThat(allPages.aboutPage, instanceOf<AboutPage>())
+ }
+
+ @Test
+ fun `bookmark page can be injected`() {
+ assertThat(allPages.bookmarkPage, instanceOf<BookmarkPage>())
+ }
+
+ @Test
+ fun `bookmarks page can be injected`() {
+ assertThat(allPages.bookmarksPage, instanceOf<BookmarksPage>())
+ }
+
+ @Test
+ fun `create album page can be injected`() {
+ assertThat(allPages.createAlbumPage, instanceOf<CreateAlbumPage>())
+ }
+
+ @Test
+ fun `create post page can be injected`() {
+ assertThat(allPages.createPostPage, instanceOf<CreatePostPage>())
+ }
+
+ @Test
+ fun `create reply page can be injected`() {
+ assertThat(allPages.createReplyPage, instanceOf<CreateReplyPage>())
+ }
+
+ @Test
+ fun `create sone page can be injected`() {
+ assertThat(allPages.createSonePage, instanceOf<CreateSonePage>())
+ }
+
+ @Test
+ fun `delete album page can be injected`() {
+ assertThat(allPages.deleteAlbumPage, instanceOf<DeleteAlbumPage>())
+ }
+
+ @Test
+ fun `delete image page can be injected`() {
+ assertThat(allPages.deleteImagePage, instanceOf<DeleteImagePage>())
+ }
+
+ @Test
+ fun `delete post page can be injected`() {
+ assertThat(allPages.deletePostPage, instanceOf<DeletePostPage>())
+ }
+
+ @Test
+ fun `delete profile field page can be injected`() {
+ assertThat(allPages.deleteProfileFieldPage, instanceOf<DeleteProfileFieldPage>())
+ }
+
+ @Test
+ fun `delete reply page can be injected`() {
+ assertThat(allPages.deleteReplyPage, instanceOf<DeleteReplyPage>())
+ }
+
+ @Test
+ fun `delete sone page can be injected`() {
+ assertThat(allPages.deleteSonePage, instanceOf<DeleteSonePage>())
+ }
+
+ @Test
+ fun `dismiss notification page can be injected`() {
+ assertThat(allPages.dismissNotificationPage, instanceOf<DismissNotificationPage>())
+ }
+
+ @Test
+ fun `distrust page can be injected`() {
+ assertThat(allPages.distrustPage, instanceOf<DistrustPage>())
+ }
+
+ @Test
+ fun `edit album page can be injected`() {
+ assertThat(allPages.editAlbumPage, instanceOf<EditAlbumPage>())
+ }
+
+ @Test
+ fun `edit image page can be injected`() {
+ assertThat(allPages.editImagePage, instanceOf<EditImagePage>())
+ }
+
+ @Test
+ fun `edit profile field page can be injected`() {
+ assertThat(allPages.editProfileFieldPage, instanceOf<EditProfileFieldPage>())
+ }
+
+ @Test
+ fun `edit profile page can be injected`() {
+ assertThat(allPages.editProfilePage, instanceOf<EditProfilePage>())
+ }
+
+ @Test
+ fun `follow sone page can be injected`() {
+ assertThat(allPages.followSonePage, instanceOf<FollowSonePage>())
+ }
+
+ @Test
+ fun `get image page can be injected`() {
+ assertThat(allPages.getImagePage, instanceOf<GetImagePage>())
+ }
+
+ @Test
+ fun `image browser page can be injected`() {
+ assertThat(allPages.imageBrowserPage, instanceOf<ImageBrowserPage>())
+ }
+
+ @Test
+ fun `index page can be injected`() {
+ assertThat(allPages.indexPage, instanceOf<IndexPage>())
+ }
+
+ @Test
+ fun `known sones page can be injected`() {
+ assertThat(allPages.knownSonesPage, instanceOf<KnownSonesPage>())
+ }
+
+ @Test
+ fun `like page can be injected`() {
+ assertThat(allPages.likePage, instanceOf<LikePage>())
+ }
+
+ @Test
+ fun `lock sone page can be injected`() {
+ assertThat(allPages.lockSonePage, instanceOf<LockSonePage>())
+ }
+
+ @Test
+ fun `login page can be injected`() {
+ assertThat(allPages.loginPage, instanceOf<LoginPage>())
+ }
+
+ @Test
+ fun `logout page can be injected`() {
+ assertThat(allPages.logoutPage, instanceOf<LogoutPage>())
+ }
+
+ @Test
+ fun `mark as known page can be injected`() {
+ assertThat(allPages.markAsKnownPage, instanceOf<MarkAsKnownPage>())
+ }
+
+ @Test
+ fun `new page can be injected`() {
+ assertThat(allPages.newPage, instanceOf<NewPage>())
+ }
+
+ @Test
+ fun `options page can be injected`() {
+ assertThat(allPages.optionsPage, instanceOf<OptionsPage>())
+ }
+
+ @Test
+ fun `rescue page can be injected`() {
+ assertThat(allPages.rescuePage, instanceOf<RescuePage>())
+ }
+
+ @Test
+ fun `search page can be injected`() {
+ assertThat(allPages.searchPage, instanceOf<SearchPage>())
+ }
+
+ @Test
+ fun `trust page can be injected`() {
+ assertThat(allPages.trustPage, instanceOf<TrustPage>())
+ }
+
+ @Test
+ fun `unbookmark page can be injected`() {
+ assertThat(allPages.unbookmarkPage, instanceOf<UnbookmarkPage>())
+ }
+
+ @Test
+ fun `unfollow sone page can be injected`() {
+ assertThat(allPages.unfollowSonePage, instanceOf<UnfollowSonePage>())
+ }
+
+ @Test
+ fun `unlike page can be injected`() {
+ assertThat(allPages.unlikePage, instanceOf<UnlikePage>())
+ }
+
+ @Test
+ fun `unlock sone page can be injected`() {
+ assertThat(allPages.unlockSonePage, instanceOf<UnlockSonePage>())
+ }
+
+ @Test
+ fun `untrust page can be injected`() {
+ assertThat(allPages.untrustPage, instanceOf<UntrustPage>())
+ }
+
+ @Test
+ fun `upload image page can be injected`() {
+ assertThat(allPages.uploadImagePage, instanceOf<UploadImagePage>())
+ }
+
+ @Test
+ fun `view post page can be injected`() {
+ assertThat(allPages.viewPostPage, instanceOf<ViewPostPage>())
+ }
+
+ @Test
+ fun `view sone page can be injected`() {
+ assertThat(allPages.viewSonePage, instanceOf<ViewSonePage>())
+ }
+}
+
+val baseInjector by lazy {
+ createInjector(
+ Core::class.isProvidedByMock(),
+ FreenetInterface::class.isProvidedByMock(),
+ Template::class.isProvidedByMock(),
+ WebInterface::class.isProvidedByDeepMock(),
+ TemplateRenderer::class.isProvidedByMock()
+ )!!
+}
--- /dev/null
+package net.pterodactylus.sone.web
+
+import com.google.inject.*
+import freenet.clients.http.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.*
+import org.junit.*
+import org.junit.rules.*
+import org.mockito.Mockito.*
+
+class PageToadletRegistryTest {
+
+ private val pageMaker = mock<PageMaker>()
+ private val toadletContainer = mock<ToadletContainer>()
+ private val pageToadletFactory = mock<PageToadletFactory>()
+ private val sonePlugin = mock<SonePlugin>()
+
+ private val injector = Guice.createInjector(
+ PageMaker::class.isProvidedBy(pageMaker),
+ ToadletContainer::class.isProvidedBy(toadletContainer),
+ PageToadletFactory::class.isProvidedBy(pageToadletFactory),
+ SonePlugin::class.isProvidedBy(sonePlugin)
+ )
+
+ @JvmField
+ @Rule
+ val expectedException: ExpectedException = ExpectedException.none()
+ private val pageToadletRegistry = injector.getInstance<PageToadletRegistry>()
+
+ @Test
+ fun `registry adds navigation category to page maker`() {
+ pageToadletRegistry.registerToadlets()
+ verify(pageMaker).addNavigationCategory("/Sone/index.html", "Navigation.Menu.Sone.Name", "Navigation.Menu.Sone.Tooltip", sonePlugin)
+ }
+
+ private val page = TestPage()
+
+ @Test
+ fun `adding a page without menuname will add it correctly`() {
+ val toadletWithoutMenuname = createPageToadlet()
+ whenever(pageToadletFactory.createPageToadlet(page)).thenReturn(toadletWithoutMenuname)
+ pageToadletRegistry.addPage(page)
+ pageToadletRegistry.registerToadlets()
+ verify(toadletContainer).register(toadletWithoutMenuname, null, "/Sone/", true, false)
+ }
+
+ @Test
+ fun `adding a page with menuname will add it correctly`() {
+ val toadletWithMenuname = createPageToadlet("Test")
+ whenever(pageToadletFactory.createPageToadlet(page)).thenReturn(toadletWithMenuname)
+ pageToadletRegistry.addPage(page)
+ pageToadletRegistry.registerToadlets()
+ verify(toadletContainer).register(toadletWithMenuname, "Navigation.Menu.Sone.Name", "/Sone/", true, "Navigation.Menu.Sone.Item.Test.Name", "Navigation.Menu.Sone.Item.Test.Tooltip", false, toadletWithMenuname)
+ }
+
+ @Test
+ fun `adding a page after registering will throw an exception`() {
+ val toadletWithMenuname = createPageToadlet("Test")
+ whenever(pageToadletFactory.createPageToadlet(page)).thenReturn(toadletWithMenuname)
+ pageToadletRegistry.registerToadlets()
+ expectedException.expect(IllegalStateException::class.java)
+ pageToadletRegistry.addPage(page)
+ }
+
+ @Test
+ fun `unregistering toadlets will remove category link`() {
+ pageToadletRegistry.unregisterToadlets()
+ verify(pageMaker).removeNavigationCategory("Navigation.Menu.Sone.Name")
+ }
+
+ @Test
+ fun `unregistering toadlets will unregister them from the container`() {
+ val toadletWithMenuname = createPageToadlet("Test")
+ whenever(pageToadletFactory.createPageToadlet(page)).thenReturn(toadletWithMenuname)
+ pageToadletRegistry.addPage(page)
+ pageToadletRegistry.registerToadlets()
+ pageToadletRegistry.unregisterToadlets()
+ verify(toadletContainer).unregister(toadletWithMenuname)
+ }
+
+ private fun createPageToadlet(menuName: String? = null) =
+ mock<PageToadlet>().apply {
+ whenever(this.path()).thenReturn("/Sone/")
+ whenever(this.menuName).thenReturn(menuName)
+ }
+
+ private class TestPage : Page<FreenetRequest> {
+ override fun getPath() = ""
+ override fun isPrefixPage() = false
+ override fun handleRequest(freenetRequest: FreenetRequest, response: Response) = response
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.web
+
+import com.google.inject.Guice.*
+import freenet.client.*
+import freenet.clients.http.*
+import freenet.l10n.*
+import freenet.support.api.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.template.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.text.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.template.*
+import net.pterodactylus.util.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+class WebInterfaceModuleTest {
+
+ private val webInterfaceModule = WebInterfaceModule()
+ private val l10n = mock<BaseL10n>()
+ private val loaders = mock<Loaders>()
+ private val additionalModules = arrayOf(
+ Core::class.isProvidedByMock(),
+ SoneProvider::class.isProvidedByMock(),
+ BaseL10n::class.isProvidedBy(l10n),
+ SoneTextParser::class.isProvidedByMock(),
+ ElementLoader::class.isProvidedByMock(),
+ Loaders::class.isProvidedBy(loaders),
+ HighLevelSimpleClient::class.isProvidedByMock(),
+ SessionManager::class.isProvidedByMock()
+ )
+ private val injector = createInjector(webInterfaceModule, *additionalModules)!!
+ private val templateContext by lazy { injector.getInstance<TemplateContextFactory>().createTemplateContext()!! }
+
+ @Test
+ fun `template context factory creates template with reflection accessor for objects`() {
+ verifyAccessor<Any, ReflectionAccessor>()
+ }
+
+ @Test
+ fun `template context factory creates template with collection accessor for collections`() {
+ verifyAccessor<Collection<*>, CollectionAccessor>()
+ }
+
+ @Test
+ fun `template context contains sone accessor for sones`() {
+ verifyAccessor<Sone, SoneAccessor>()
+ }
+
+ @Test
+ fun `template context contains post accessor for posts`() {
+ verifyAccessor<Post, PostAccessor>()
+ }
+
+ @Test
+ fun `template context contains reply accessor for replies`() {
+ verifyAccessor<Reply<*>, ReplyAccessor>()
+ }
+
+ @Test
+ fun `template context contains album accessor for albums`() {
+ verifyAccessor<Album, AlbumAccessor>()
+ }
+
+ @Test
+ fun `template context contains image accessor for images`() {
+ verifyAccessor<Image, ImageAccessor>()
+ }
+
+ @Test
+ fun `template context contains identity accessor for identities`() {
+ verifyAccessor<Identity, IdentityAccessor>()
+ }
+
+ @Test
+ fun `template context contains trust accessor for trusts`() {
+ verifyAccessor<Trust, TrustAccessor>()
+ }
+
+ @Test
+ fun `template context contains http request accessor for http requests`() {
+ verifyAccessor<HTTPRequest, HttpRequestAccessor>()
+ }
+
+ @Test
+ fun `template context contains profile accessor for profiles`() {
+ verifyAccessor<Profile, ProfileAccessor>()
+ }
+
+ private inline fun <reified O, reified A : Accessor> verifyAccessor() {
+ assertThat(templateContext.getAccessor(O::class.java), instanceOf(A::class.java))
+ }
+
+ @Test
+ fun `template context contains date filter`() {
+ verifyFilter<DateFilter>("date")
+ }
+
+ @Test
+ fun `template context contains html filter`() {
+ verifyFilter<HtmlFilter>("html")
+ }
+
+ @Test
+ fun `template context contains replace filter`() {
+ verifyFilter<ReplaceFilter>("replace")
+ }
+
+ @Test
+ fun `template context contains store filter`() {
+ verifyFilter<StoreFilter>("store")
+ }
+
+ @Test
+ fun `template context contains l10n filter`() {
+ verifyFilter<L10nFilter>("l10n")
+ }
+
+ @Test
+ fun `template context contains substring filter`() {
+ verifyFilter<SubstringFilter>("substring")
+ }
+
+ @Test
+ fun `template context contains xml filter`() {
+ verifyFilter<XmlFilter>("xml")
+ }
+
+ @Test
+ fun `template context contains change filter`() {
+ verifyFilter<RequestChangeFilter>("change")
+ }
+
+ @Test
+ fun `template context contains match filter`() {
+ verifyFilter<MatchFilter>("match")
+ }
+
+ @Test
+ fun `template context contains css filter`() {
+ verifyFilter<CssClassNameFilter>("css")
+ }
+
+ @Test
+ fun `template context contains js filter`() {
+ verifyFilter<JavascriptFilter>("js")
+ }
+
+ @Test
+ fun `template context contains parser filter`() {
+ verifyFilter<ParserFilter>("parse")
+ }
+
+ @Test
+ fun `template context contains shorten filter`() {
+ verifyFilter<ShortenFilter>("shorten")
+ }
+
+ @Test
+ fun `template context contains render filter`() {
+ verifyFilter<RenderFilter>("render")
+ }
+
+ @Test
+ fun `template context contains linked elements filter`() {
+ verifyFilter<LinkedElementsFilter>("linked-elements")
+ }
+
+ @Test
+ fun `template context contains linked elements render filter`() {
+ verifyFilter<LinkedElementRenderFilter>("render-linked-element")
+ }
+
+ @Test
+ fun `template context contains reparse filter`() {
+ verifyFilter<ReparseFilter>("reparse")
+ }
+
+ @Test
+ fun `template context contains unknown date filter`() {
+ verifyFilter<UnknownDateFilter>("unknown")
+ }
+
+ @Test
+ fun `unknown date filter uses correct l10n key`() {
+ whenever(l10n.getString("View.Sone.Text.UnknownDate")).thenReturn("unknown")
+ assertThat(getFilter("unknown")!!.format(null, 0L, emptyMap()), equalTo<Any>("unknown"))
+ }
+
+ @Test
+ fun `template context contains format filter`() {
+ verifyFilter<FormatFilter>("format")
+ }
+
+ @Test
+ fun `template context contains collection sort filter`() {
+ verifyFilter<CollectionSortFilter>("sort")
+ }
+
+ @Test
+ fun `template context contains image link filter`() {
+ verifyFilter<ImageLinkFilter>("image-link")
+ }
+
+ @Test
+ fun `template context contains reply group filter`() {
+ verifyFilter<ReplyGroupFilter>("replyGroup")
+ }
+
+ @Test
+ fun `template context contains contains filter`() {
+ verifyFilter<ContainsFilter>("in")
+ }
+
+ @Test
+ fun `template context unique elements filter`() {
+ verifyFilter<UniqueElementFilter>("unique")
+ }
+
+ @Test
+ fun `template context mod filter`() {
+ verifyFilter<ModFilter>("mod")
+ }
+
+ @Test
+ fun `template context pagination filter`() {
+ verifyFilter<PaginationFilter>("paginate")
+ }
+
+ private inline fun <reified F : Filter> verifyFilter(name: String) {
+ assertThat(getFilter(name), instanceOf(F::class.java))
+ }
+
+ private fun getFilter(name: String): Filter? = templateContext.getFilter(name)
+
+ @Test
+ fun `template context factory is created as singleton`() {
+ val factory1 = injector.getInstance<TemplateContextFactory>()
+ val factory2 = injector.getInstance<TemplateContextFactory>()
+ assertThat(factory1, sameInstance(factory2))
+ }
+
+ @Test
+ fun `template from classpath is returned`() {
+ val template = Template()
+ templateContext["testTemplate"] = template
+ assertThat(templateContext.getTemplate("testTemplate"), sameInstance(template))
+ }
+
+ @Test
+ fun `template from loaders’ provider is returned`() {
+ val template = Template()
+ whenever(loaders.templateProvider).thenReturn(TemplateProvider { _, templateName ->
+ template.takeIf { templateName == "testTemplate" }
+ })
+ assertThat(templateContext.getTemplate("testTemplate"), sameInstance(template))
+ }
+
+ @Test
+ fun `page toadlet factory is created with correct prefix`() {
+ val page = mock<Page<FreenetRequest>>()
+ assertThat(injector.getInstance<PageToadletFactory>().createPageToadlet(page).path(), startsWith("/Sone/"))
+ }
+
+}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Post
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
verify(core).bookmarkPost(post)
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<BookmarkAjaxPage>(), notNullValue())
+ }
+
}
import com.google.common.base.Optional
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.sone.utils.asOptional
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.hamcrest.Matchers.nullValue
import org.junit.Test
whenever(this.recipientId).thenReturn(recipientId.asOptional())
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<CreatePostAjaxPage>(), notNullValue())
+ }
+
}
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.PostReply
import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
/**
assertThat(json["sone"]?.asText(), equalTo("local-sone"))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<CreateReplyAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeletePostAjaxPage].
verify(core).deletePost(post)
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeletePostAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeleteProfileFieldAjaxPage].
verify(core).touchConfiguration()
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeleteProfileFieldAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeleteReplyAjaxPage].
verify(core).deleteReply(reply)
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeleteReplyAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.notify.Notification
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.notify.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DismissNotificationAjaxPage].
verify(notification).dismiss()
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DismissNotificationAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.Mockito.verify
@Test
fun `request with valid sone results in correct trust value being sent back`() {
- core.preferences.negativeTrust = -33
+ core.preferences.newNegativeTrust = -33
val sone = mock<Sone>()
addSone(sone, "sone-id")
addRequestParameter("sone", "sone-id")
assertThat(json["trustValue"]?.asInt(), equalTo(-33))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DistrustAjaxPage>(), notNullValue())
+ }
+
}
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.data.impl.AlbumImpl
import net.pterodactylus.sone.test.deepMock
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
/**
assertThat(json["description"]?.asText(), equalTo("foo KSK@foo.html link"))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<EditAlbumAjaxPage>(), notNullValue())
+ }
+
}
import net.pterodactylus.sone.template.RenderFilter
import net.pterodactylus.sone.template.ShortenFilter
import net.pterodactylus.sone.test.argumentCaptor
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedByMock
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
assertThat(parameterCaptor.value["sone"], equalTo<Any>(sone))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.createChildInjector(
+ ParserFilter::class.isProvidedByMock(),
+ ShortenFilter::class.isProvidedByMock(),
+ RenderFilter::class.isProvidedByMock()
+ ).getInstance<EditImageAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.Mockito.verify
verify(currentSone).profile = profile
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<EditProfileFieldAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [FollowSoneAjaxPage].
verify(core).markSoneKnown(sone)
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<FollowSoneAjaxPage>(), notNullValue())
+ }
+
}
import net.pterodactylus.sone.data.PostReply
import net.pterodactylus.sone.data.Profile
import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.contains
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
/**
assertThatJsonFailed("invalid-type")
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<GetLikesAjaxPage>(), notNullValue())
+ }
+
}
private fun createSone(index: Int) = mock<Sone>().apply {
package net.pterodactylus.sone.web.ajax
import com.fasterxml.jackson.databind.JsonNode
+import net.pterodactylus.sone.core.ElementLoader
import net.pterodactylus.sone.core.LinkedElement
import net.pterodactylus.sone.template.LinkedElementRenderFilter
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedByMock
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.sone.utils.jsonArray
+import net.pterodactylus.sone.web.baseInjector
import net.pterodactylus.util.template.TemplateContext
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers
-import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.ArgumentMatchers
))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.createChildInjector(
+ ElementLoader::class.isProvidedByMock(),
+ LinkedElementRenderFilter::class.isProvidedByMock()
+ ).getInstance<GetLinkedElementAjaxPage>(), notNullValue())
+ }
+
private fun JsonNode.toMap() = fields().asSequence().map { it.key!! to if (it.value.isNull) null else it.value.asText()!! }.toMap()
}
import net.pterodactylus.sone.main.SonePlugin
import net.pterodactylus.sone.test.argumentCaptor
import net.pterodactylus.sone.test.get
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
import net.pterodactylus.util.notify.Notification
import net.pterodactylus.util.notify.TemplateNotification
import net.pterodactylus.util.template.TemplateContext
import org.hamcrest.Matchers.containsInAnyOrder
import org.hamcrest.Matchers.empty
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.verify
assertThat(templateContext.value["notification"], equalTo<Any>(templateNotification))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<GetNotificationsAjaxPage>(), notNullValue())
+ }
+
}
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.sone.utils.asOptional
import net.pterodactylus.sone.utils.asTemplate
+import net.pterodactylus.sone.web.baseInjector
import net.pterodactylus.util.template.ReflectionAccessor
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
/**
).joinToString("\n")))
}
+ @Test
+ fun `page can be created dependency injection`() {
+ assertThat(baseInjector.getInstance<GetPostAjaxPage>(), notNullValue())
+ }
+
}
import net.pterodactylus.sone.data.PostReply
import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.sone.utils.asTemplate
+import net.pterodactylus.sone.web.baseInjector
import net.pterodactylus.util.template.ReflectionAccessor
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
/**
).joinToString("\n")))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<GetReplyAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
import com.fasterxml.jackson.databind.JsonNode
+import net.pterodactylus.sone.core.ElementLoader
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.data.Sone.SoneStatus.downloading
import net.pterodactylus.sone.data.Sone.SoneStatus.inserting
import net.pterodactylus.sone.freenet.L10nFilter
import net.pterodactylus.sone.freenet.L10nText
import net.pterodactylus.sone.test.deepMock
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedByMock
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.sone.text.TimeText
import net.pterodactylus.sone.text.TimeTextConverter
import net.pterodactylus.sone.utils.jsonArray
+import net.pterodactylus.sone.web.baseInjector
import net.pterodactylus.util.notify.Notification
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.emptyIterable
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.hasEntry
+import org.hamcrest.Matchers.notNullValue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.any
))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.createChildInjector(
+ ElementLoader::class.isProvidedByMock(),
+ TimeTextConverter::class.isProvidedByMock(),
+ L10nFilter::class.isProvidedByMock()
+ ).getInstance<GetStatusAjaxPage>(), notNullValue())
+ }
+
private fun JsonNode.toMap() = fields().asSequence().map { it.key!! to if (it.value.isNull) null else it.value.asText()!! }.toMap()
}
import net.pterodactylus.sone.freenet.L10nFilter
import net.pterodactylus.sone.freenet.L10nText
import net.pterodactylus.sone.test.get
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedByMock
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.sone.text.TimeText
import net.pterodactylus.sone.text.TimeTextConverter
import net.pterodactylus.sone.utils.jsonObject
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.containsInAnyOrder
import org.hamcrest.Matchers.emptyIterable
-import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyLong
+import java.util.TimeZone
import java.util.TimeZone.getTimeZone
/**
))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.createChildInjector(
+ TimeTextConverter::class.isProvidedByMock(),
+ L10nFilter::class.isProvidedByMock(),
+ TimeZone::class.isProvidedByMock()
+ ).getInstance<GetTimesAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
/**
assertThat(json["value"]?.asText(), equalTo("bar"))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<GetTranslationAjaxPage>(), notNullValue())
+ }
+
}
private val outputStream = ByteArrayOutputStream()
private val response = Response(outputStream)
- private val page = object : JsonPage("path.html", webInterface) {
+ private val page = object : JsonPage(webInterface) {
override val needsFormPassword get() = this@JsonPageBaseTest.needsFormPassword
@Test
fun `page returns 403 is full access is required but request is not full access`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
page.handleRequest(freenetRequest, response)
assertThat(response.statusCode, equalTo(403))
assertThat(response.statusText, equalTo("Forbidden"))
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
verify(core, never()).touchConfiguration()
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<LikeAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [LockSoneAjaxPage].
verify(core).lockSone(sone)
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<LockSoneAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+
/**
* Unit test for [LoggedInJsonPageTest].
*/
-class LoggedInJsonPageTest : JsonPageTest("path", requiresLogin = true, pageSupplier = { webInterface -> LoggedInJsonPage("path", webInterface) })
+class LoggedInJsonPageTest : JsonPageTest("path", requiresLogin = true, pageSupplier = ::TestPage)
+
+@ToadletPath("path")
+class TestPage(webInterface: WebInterface) : LoggedInJsonPage(webInterface)
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.any
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [MarkAsKnownAjaxPage].
verify(core).markReplyKnown(reply2)
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<MarkAsKnownAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.Mockito.verify
verify(currentSone).profile = profile
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<MoveProfileFieldAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.Mockito.verify
fun `request with valid sone returns positive trust value`() {
addSone(sone, "sone-id")
addRequestParameter("sone", "sone-id")
- core.preferences.positiveTrust = 31
+ core.preferences.newPositiveTrust = 31
assertThatJsonIsSuccessful()
assertThat(json["trustValue"]?.asInt(), equalTo(31))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<TrustAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
verify(core).unbookmarkPost(eq(post))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnbookmarkAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [UnfollowSoneAjaxPage].
verify(core).unfollowSone(currentSone, "sone-id")
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnfollowSoneAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
import org.mockito.Mockito.verify
verify(core).touchConfiguration()
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnlikeAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [UnlockSoneAjaxPage].
verify(core).unlockSone(sone)
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnlockSoneAjaxPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [UntrustAjaxPage].
assertThat(json["trustValue"], nullValue())
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UntrustAjaxPage>(), notNullValue())
+ }
+
}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import freenet.clients.http.*
+import freenet.clients.http.SessionManager.*
+import freenet.l10n.*
+import freenet.support.api.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.util.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.*
+import org.mockito.Mockito.*
+import java.net.*
+
+class FreenetRequestTest {
+
+ private val uri = URI(".")
+ private val method = Method.GET
+ private val httpRequest = mock(HTTPRequest::class.java)
+ private val toadletContext = mock(ToadletContext::class.java)
+ private val l10n = mock<BaseL10n>()
+ private val sessionManager = mock<SessionManager>()
+ private val request = FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager)
+
+ @Test
+ fun `uri is retained correctly`() {
+ assertThat(request.uri, equalTo(uri))
+ }
+
+ @Test
+ fun `method is retained correctly`() {
+ assertThat(request.method, equalTo(method))
+ }
+
+ @Test
+ fun `http request is retained correctly`() {
+ assertThat(request.httpRequest, equalTo(httpRequest))
+ }
+
+ @Test
+ fun `toadlet context is retained correctly`() {
+ assertThat(request.toadletContext, equalTo(toadletContext))
+ }
+
+ @Test
+ fun `l10n is retained correctly`() {
+ assertThat(request.l10n, equalTo(l10n))
+ }
+
+ @Test
+ fun `null is returned if no session exists`() {
+ assertThat(request.existingSession, nullValue())
+ }
+
+ @Test
+ fun `existing session can be retrieved`() {
+ val session = mock<Session>()
+ whenever(sessionManager.useSession(toadletContext)).thenReturn(session)
+ assertThat(request.existingSession, sameInstance(session))
+ }
+
+ @Test
+ fun `existing session is returned if it exists`() {
+ val session = mock<Session>()
+ whenever(sessionManager.useSession(toadletContext)).thenReturn(session)
+ assertThat(request.session, sameInstance(session))
+ }
+
+ @Test
+ fun `new session is returned if none exists`() {
+ val session = mock<Session>()
+ whenever(sessionManager.createSession(anyString(), ArgumentMatchers.eq(toadletContext))).thenReturn(session)
+ assertThat(request.session, sameInstance(session))
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.util.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
+import java.io.*
+import java.nio.charset.StandardCharsets.*
+
+class FreenetTemplatePageTest {
+
+ private val templateRenderer = deepMock<TemplateRenderer>()
+ private val loaders = mock<Loaders>()
+ private val page = TestPage(templateRenderer, loaders)
+
+ @Test
+ fun `path is exposed correctly`() {
+ assertThat(page.path, equalTo("/test/path"))
+ }
+
+ @Test
+ fun `getPageTitle() default implementation returns empty string`() {
+ assertThat(page.getPageTitle(mock()), equalTo(""))
+ }
+
+ @Test
+ fun `isPrefixPage() default implementation returns false`() {
+ assertThat(page.isPrefixPage, equalTo(false))
+ }
+
+ @Test
+ fun `getStylesheets() default implementation returns empty collection`() {
+ assertThat(page.styleSheets, empty())
+ }
+
+ @Test
+ fun `getShortcutIcon() default implementation returns null`() {
+ assertThat(page.shortcutIcon, nullValue())
+ }
+
+ @Test
+ fun `getRedirectTarget() default implementation returns null`() {
+ assertThat(page.getRedirectTarget(mock()), nullValue())
+ }
+
+ @Test
+ fun `getAdditionalLinkNodes() default implementation returns empty collection`() {
+ assertThat(page.getAdditionalLinkNodes(mock()), empty())
+ }
+
+ @Test
+ fun `isFullAccessOnly() default implementation returns false`() {
+ assertThat(page.isFullAccessOnly, equalTo(false))
+ }
+
+ @Test
+ fun `isLinkExcepted() default implementation returns false`() {
+ assertThat(page.isLinkExcepted(mock()), equalTo(false))
+ }
+
+ @Test
+ fun `isEnabled() returns true if full access only is false`() {
+ assertThat(page.isEnabled(mock()), equalTo(true))
+ }
+
+ @Test
+ fun `isEnabled() returns false if full access only is true`() {
+ val page = object : TestPage(templateRenderer, loaders) {
+ override val isFullAccessOnly = true
+ }
+ assertThat(page.isEnabled(mock()), equalTo(false))
+ }
+
+ @Test
+ fun `page with redirect target throws redirect exception on handleRequest`() {
+ val page = object : TestPage(templateRenderer, loaders) {
+ override fun getRedirectTarget(request: FreenetRequest) = "foo"
+ }
+ val request = mock<FreenetRequest>()
+ val response = mock<Response>()
+ val pageResponse = page.handleRequest(request, response)
+ assertThat(pageResponse.statusCode, anyOf(equalTo(302), equalTo(307)))
+ assertThat(pageResponse.headers, contains(hasHeader("location", "foo")))
+ }
+
+ @Test
+ fun `page with full access only returns unauthorized on handleRequest with non-full access request`() {
+ val page = object : TestPage(templateRenderer, loaders) {
+ override val isFullAccessOnly = true
+ }
+ val request = deepMock<FreenetRequest>()
+ val response = Response(null)
+ val pageResponse = page.handleRequest(request, response)
+ assertThat(pageResponse.statusCode, equalTo(401))
+ }
+
+ @Test
+ fun `page redirects on POST without form password`() {
+ val request = deepMock<FreenetRequest>().apply {
+ whenever(httpRequest.getPartAsStringFailsafe(any(), anyInt())).thenReturn("")
+ whenever(method).thenReturn(POST)
+ }
+ val response = Response(null)
+ val pageResponse = page.handleRequest(request, response)
+ assertThat(pageResponse.statusCode, anyOf(equalTo(302), equalTo(307)))
+ assertThat(pageResponse.headers, contains(hasHeader("location", "invalid-form-password")))
+ }
+
+ @Test
+ fun `page redirects on POST with invalid password`() {
+ val request = deepMock<FreenetRequest>().apply {
+ whenever(httpRequest.getPartAsStringFailsafe(any(), anyInt())).thenReturn("invalid")
+ whenever(method).thenReturn(POST)
+ }
+ val response = Response(null)
+ val pageResponse = page.handleRequest(request, response)
+ assertThat(pageResponse.statusCode, anyOf(equalTo(302), equalTo(307)))
+ assertThat(pageResponse.headers, contains(hasHeader("location", "invalid-form-password")))
+ }
+
+ @Test
+ @Dirty
+ fun `freenet template page creates page with correct title`() {
+ val page = object : TestPage(templateRenderer, loaders) {
+ override fun getPageTitle(request: FreenetRequest) = "page title"
+ }
+ val request = deepMock<FreenetRequest>()
+ val pageMakerInteractionFactory = deepMock<PageMakerInteractionFactory>()
+ whenever(pageMakerInteractionFactory.createPageMaker(request.toadletContext, "page title").renderPage()).thenReturn("<page>")
+ setField(page, "pageMakerInteractionFactory", pageMakerInteractionFactory)
+ val response = page.handleRequest(request, Response(ByteArrayOutputStream()))
+ assertThat(response.statusCode, equalTo(200))
+ assertThat((response.content as ByteArrayOutputStream).toString(UTF_8.name()), equalTo("<page>"))
+ }
+
+ @Test
+ fun `template from annotation is loaded`() {
+ verify(loaders).loadTemplate("template-path")
+ }
+
+ @TemplatePath("template-path")
+ @ToadletPath("/test/path")
+ private open class TestPage(templateRenderer: TemplateRenderer, loaders: Loaders) : FreenetTemplatePage(templateRenderer, loaders, "invalid-form-password")
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import com.google.inject.*
+import freenet.clients.http.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
+
+class PageMakerInteractionFactoryTest {
+
+ private val pageMakerInteractionFactory: PageMakerInteractionFactory = DefaultPageMakerInteractionFactory()
+
+ @Test
+ fun `page maker interaction factory can be created by guice`() {
+ val injector = Guice.createInjector()
+ assertThat(injector.getInstance<PageMakerInteractionFactory>(), notNullValue())
+ }
+
+ @Test
+ fun `page maker interaction sets page title correctly`() {
+ val toadletContext = deepMock<ToadletContext>()
+ pageMakerInteractionFactory.createPageMaker(toadletContext, "page title")
+ verify(toadletContext.pageMaker).getPageNode("page title", toadletContext)
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import freenet.clients.http.*
+import freenet.support.*
+import freenet.support.HTMLNode.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.test.TestUtil.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+@Dirty
+class PageMakerInteractionTest {
+
+ private val toadletContext = deepMock<ToadletContext>()
+ private val pageMaker: PageMaker = toadletContext.pageMaker
+ private val outerNode = HTMLDoctype("html", "-//W3C//DTD XHTML 1.1//EN")
+ private val htmlNode: HTMLNode = outerNode.addChild("html")
+ private val headNode: HTMLNode = htmlNode.addChild("head")
+ private val contentNode: HTMLNode = htmlNode.addChild("body").addChild("div")
+ private val pageNode: PageNode = createObject(PageNode::class.java, arrayOf(HTMLNode::class.java, HTMLNode::class.java, HTMLNode::class.java), outerNode, headNode, contentNode)
+
+ init {
+ whenever(pageMaker.getPageNode("page title", toadletContext)).thenReturn(pageNode)
+ }
+
+ private val pageMakerInteractions = PageMakerInteraction(toadletContext, "page title")
+
+ @Test
+ fun `interactions can add style sheet`() {
+ pageMakerInteractions.addStyleSheet("style.sheet")
+ assertThat(headNode.children.filter { it.name == "link" }.map { it.attributes }, contains(
+ mapOf("rel" to "stylesheet", "href" to "style.sheet", "type" to "text/css", "media" to "screen")
+ ))
+ }
+
+ @Test
+ fun `link nodes can be added`() {
+ pageMakerInteractions.addLinkNode(mapOf("foo" to "bar"))
+ assertThat(headNode.children.filter { it.name == "link" }.map { it.attributes }, contains(
+ mapOf("foo" to "bar")
+ ))
+ }
+
+ @Test
+ fun `shortcut icon can be added`() {
+ pageMakerInteractions.addShortcutIcon("shortcut.icon")
+ assertThat(headNode.children.filter { it.name == "link" }.map { it.attributes }, contains(
+ mapOf("rel" to "icon", "href" to "shortcut.icon")
+ ))
+ }
+
+ @Test
+ fun `content can be set`() {
+ pageMakerInteractions.setContent("foo<bar")
+ assertThat(contentNode.generate(), containsString("foo<bar"))
+ }
+
+ @Test
+ fun `whole page can be rendered`() {
+ pageMakerInteractions.setContent("foo<bar")
+ assertThat(pageMakerInteractions.renderPage(), containsString("foo<bar"))
+ }
+
+ private val HTMLNode.name: String get() = firstTag
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import com.google.inject.*
+import freenet.client.*
+import freenet.clients.http.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+private val highLevelSimpleClient = mock<HighLevelSimpleClient>()
+private val sessionManager = mock<SessionManager>()
+private const val pathPrefix = "/some/prefix/"
+
+class PageToadletFactoryTest {
+
+ private val pageToadletFactory = PageToadletFactory(highLevelSimpleClient, sessionManager, pathPrefix)
+
+ @Test
+ fun `page toadlet without menu name is created without menu name`() {
+ val page = mock<Page<FreenetRequest>>()
+ val pageToadlet = pageToadletFactory.createPageToadlet(page)
+ assertThat(pageToadlet.menuName, nullValue())
+ }
+
+ @Test
+ fun `page toadlet with menu name is created with menu name`() {
+ val page = mock<Page<FreenetRequest>>()
+ val pageToadlet = pageToadletFactory.createPageToadlet(page, "testName")
+ assertThat(pageToadlet.menuName, equalTo("testName"))
+ }
+
+ @Test
+ fun `path prefix is handed down correctly`() {
+ val page = mock<Page<FreenetRequest>>().apply {
+ whenever(path).thenReturn("path")
+ }
+ val pageToadlet = pageToadletFactory.createPageToadlet(page)
+ assertThat(pageToadlet.path(), equalTo("/some/prefix/path"))
+ }
+
+ @Test
+ fun `menu name is added from annotation when no menu name is given`() {
+ val page = TestPageWithMenuName()
+ val pageToadlet = pageToadletFactory.createPageToadlet(page)
+ assertThat(pageToadlet.menuName, equalTo("testName"))
+ }
+
+ @Test
+ fun `menu name from annotation is ignored when menu name is given`() {
+ val page = TestPageWithMenuName()
+ val pageToadlet = pageToadletFactory.createPageToadlet(page, "foo")
+ assertThat(pageToadlet.menuName, equalTo("foo"))
+ }
+
+ @Test
+ fun `page toadlet factory can be created by guice`() {
+ val injector = Guice.createInjector(
+ HighLevelSimpleClient::class.isProvidedBy(highLevelSimpleClient),
+ SessionManager::class.isProvidedBy(sessionManager),
+ String::class.withNameIsProvidedBy("/Sone/", "toadletPathPrefix")
+ )
+ assertThat(injector.getInstance<PageToadletFactory>(), notNullValue())
+ }
+
+}
+
+@MenuName("testName")
+private class TestPageWithMenuName : Page<FreenetRequest> {
+
+ override fun getPath() = ""
+ override fun isPrefixPage() = false
+ override fun handleRequest(request: FreenetRequest, response: Response) = response
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import freenet.clients.http.*
+import freenet.l10n.*
+import freenet.support.api.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.*
+import java.net.*
+
+class SoneRequestTest {
+
+ private val uri = URI(".")
+ private val method = Method.GET
+ private val httpRequest = Mockito.mock(HTTPRequest::class.java)
+ private val toadletContext = Mockito.mock(ToadletContext::class.java)
+ private val l10n = mock<BaseL10n>()
+ private val sessionManager = mock<SessionManager>()
+ private val core = mock<Core>()
+ private val webInterface = mock<WebInterface>()
+ private val soneRequest = SoneRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager, core, webInterface)
+
+ @Test
+ fun `freenet request properties are retained correctly`() {
+ assertThat(soneRequest.uri, equalTo(uri))
+ assertThat(soneRequest.method, equalTo(method))
+ assertThat(soneRequest.httpRequest, equalTo(httpRequest))
+ assertThat(soneRequest.toadletContext, equalTo(toadletContext))
+ assertThat(soneRequest.l10n, equalTo(l10n))
+ assertThat(soneRequest.sessionManager, equalTo(sessionManager))
+ }
+
+ @Test
+ fun `core is retained correctly`() {
+ assertThat(soneRequest.core, sameInstance(core))
+ }
+
+ @Test
+ fun `web interface is retained correctly`() {
+ assertThat(soneRequest.webInterface, sameInstance(webInterface))
+ }
+
+ @Test
+ fun `freenet request is wrapped correctly`() {
+ val freenetRequest = FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager)
+ val wrappedSoneRequest = freenetRequest.toSoneRequest(core, webInterface)
+ assertThat(wrappedSoneRequest.uri, equalTo(uri))
+ assertThat(wrappedSoneRequest.method, equalTo(method))
+ assertThat(wrappedSoneRequest.httpRequest, equalTo(httpRequest))
+ assertThat(wrappedSoneRequest.toadletContext, equalTo(toadletContext))
+ assertThat(wrappedSoneRequest.l10n, equalTo(l10n))
+ assertThat(wrappedSoneRequest.sessionManager, equalTo(sessionManager))
+ assertThat(wrappedSoneRequest.core, sameInstance(core))
+ assertThat(wrappedSoneRequest.webInterface, sameInstance(webInterface))
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.web.page
+
+import com.google.inject.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.util.template.*
+import net.pterodactylus.util.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.junit.rules.*
+
+class TemplateRendererTest {
+
+ @Rule
+ @JvmField
+ val expectedException: ExpectedException = ExpectedException.none()
+ private val templateContextFactory = TemplateContextFactory()
+ private val templateRenderer = TemplateRenderer(templateContextFactory)
+
+ @Test
+ fun `renderer can render template`() {
+ val template = "foo".asTemplate()
+ val rendered = templateRenderer.render(template)
+ assertThat(rendered, equalTo("foo"))
+ }
+
+ @Test
+ fun `renderer merges template contexts from template and context factory`() {
+ templateContextFactory.addTemplateObject("a", 1)
+ val template = "<%a><%b>".asTemplate()
+ template.initialContext.set("b", 2)
+ val rendered = templateRenderer.render(template)
+ assertThat(rendered, equalTo("12"))
+ }
+
+ @Test
+ fun `template context can be processed`() {
+ templateContextFactory.addTemplateObject("a", 1)
+ val template = "<%a><%b><%c>".asTemplate()
+ template.initialContext.set("b", 2)
+ val rendered = templateRenderer.render(template) { templateContext -> templateContext.set("c", 3) }
+ assertThat(rendered, equalTo("123"))
+ }
+
+ @Test
+ fun `redirect exceptions are thrown`() {
+ expectedException.expect(RedirectException::class.java)
+ templateRenderer.render(Template()) { _ -> throw RedirectException("foo") }
+ }
+
+ @Test
+ fun `template renderer can be created by guice`() {
+ val injector = Guice.createInjector(
+ TemplateContextFactory::class.isProvidedByMock()
+ )
+ assertThat(injector.getInstance<TemplateRenderer>(), notNullValue())
+ }
+
+}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.main.SonePlugin.PluginHomepage
-import net.pterodactylus.sone.main.SonePlugin.PluginVersion
-import net.pterodactylus.sone.main.SonePlugin.PluginYear
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
/**
* Unit test for [AboutPage].
*/
-class AboutPageTest: WebPageTest({ template, webInterface -> AboutPage(template, webInterface, PluginVersion(version), PluginYear(year), PluginHomepage(homepage)) }) {
+class AboutPageTest : WebPageTest({ webInterface, loaders, templateRenderer -> AboutPage(webInterface, loaders, templateRenderer, PluginVersion(version), PluginYear(year), PluginHomepage(homepage)) }) {
companion object {
private const val version = "0.1.2"
assertThat(templateContext["year"], equalTo<Any>(year))
}
+ @Test
+ fun `about page can be created by dependency injection`() {
+ val injector = baseInjector.createChildInjector(
+ PluginVersion::class.isProvidedByMock(),
+ PluginYear::class.isProvidedByMock(),
+ PluginHomepage::class.isProvidedByMock()
+ )
+ assertThat(injector.getInstance<AboutPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("About"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/about.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
/**
* Unit test for [BookmarkPage].
*/
-class BookmarkPageTest: WebPageTest(::BookmarkPage) {
+class BookmarkPageTest : WebPageTest(::BookmarkPage) {
@Test
fun `path is set correctly`() {
}
}
+ @Test
+ fun `bookmark page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<BookmarkPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.Pagination
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
/**
* Unit test for [BookmarksPage].
*/
-class BookmarksPageTest: WebPageTest(::BookmarksPage) {
+class BookmarksPageTest : WebPageTest(::BookmarksPage) {
private val post1 = createLoadedPost(1000)
private val post2 = createLoadedPost(3000)
@Before
fun setupBookmarkedPostsAndPagination() {
whenever(core.bookmarkedPosts).thenReturn(setOf(post1, post2, post3))
- core.preferences.postsPerPage = 5
+ core.preferences.newPostsPerPage = 5
}
@Test
}
}
+ @Test
+ fun `bookmarks page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<BookmarksPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("Bookmarks"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/bookmarks.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Album.Modifier
-import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty
-import net.pterodactylus.sone.test.deepMock
-import net.pterodactylus.sone.test.selfMock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Album.*
+import net.pterodactylus.sone.data.Album.Modifier.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [CreateAlbumPage].
*/
-class CreateAlbumPageTest: WebPageTest(::CreateAlbumPage) {
+class CreateAlbumPageTest : WebPageTest(::CreateAlbumPage) {
private val parentAlbum = createAlbum("parent-id")
private val newAlbum = createAlbum("album-id")
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<CreateAlbumPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/createAlbum.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import com.google.common.base.Optional.absent
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.utils.asOptional
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import com.google.common.base.Optional.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [CreatePostPage].
*/
-class CreatePostPageTest: WebPageTest(::CreatePostPage) {
+class CreatePostPageTest : WebPageTest(::CreatePostPage) {
@Test
fun `page returns correct path`() {
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<CreatePostPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/createPost.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [CreateReplyPage].
*/
-class CreateReplyPageTest: WebPageTest(::CreateReplyPage) {
+class CreateReplyPageTest : WebPageTest(::CreateReplyPage) {
@Test
fun `page returns correct path`() {
verifyRedirect("noPermission.html")
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<CreateReplyPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/createReply.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.freenet.wot.OwnIdentity
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.verify
/**
* Unit test for [CreateSonePage].
*/
-class CreateSonePageTest: WebPageTest(::CreateSonePage) {
+class CreateSonePageTest : WebPageTest(::CreateSonePage) {
private val localSones_ = listOf(
createSone("local-sone1"),
@Test
fun `create sone is not shown in menu if full access is required but client doesn’t have full access`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
assertThat(page.isEnabled(toadletContext), equalTo(false))
}
@Test
fun `create sone is shown in menu if no sone is logged in and client has full access`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
unsetCurrentSone()
assertThat(page.isEnabled(toadletContext), equalTo(true))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<CreateSonePage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with the correct menuname`() {
+ assertThat(page.menuName, equalTo("CreateSone"))
+ }
+
+ @Test
+ fun `page is annotated with the correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/createSone.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.verify
/**
* Unit test for [DeleteAlbumPage].
*/
-class DeleteAlbumPageTest: WebPageTest(::DeleteAlbumPage) {
+class DeleteAlbumPageTest : WebPageTest(::DeleteAlbumPage) {
private val sone = mock<Sone>()
private val album = mock<Album>()
fun `album is deleted and page redirects to album if parent album is not root album`() {
setMethod(POST)
whenever(parentAlbum.isRoot).thenReturn(false)
- whenever(sone.rootAlbum).thenReturn(mock<Album>())
+ whenever(sone.rootAlbum).thenReturn(mock())
addAlbum("album-id", album)
addHttpRequestPart("album", "album-id")
verifyRedirect("imageBrowser.html?album=parent-id") {
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeleteAlbumPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/deleteAlbum.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeleteImagePage].
*/
-class DeleteImagePageTest: WebPageTest(::DeleteImagePage) {
+class DeleteImagePageTest : WebPageTest(::DeleteImagePage) {
private val image = mock<Image>()
private val sone = mock<Sone>()
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeleteImagePage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/deleteImage.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeletePostPage].
*/
-class DeletePostPageTest: WebPageTest(::DeletePostPage) {
+class DeletePostPageTest : WebPageTest(::DeletePostPage) {
private val post = mock<Post>()
private val sone = mock<Sone>()
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("deletePost.html"))
+ assertThat(page.path, equalTo("deletePost.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
assertThat(templateContext["returnPage"], equalTo<Any>("return.html"))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeletePostPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/deletePost.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.any
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeleteProfileFieldPage].
*/
-class DeleteProfileFieldPageTest: WebPageTest(::DeleteProfileFieldPage) {
+class DeleteProfileFieldPageTest : WebPageTest(::DeleteProfileFieldPage) {
private val profile = Profile(currentSone)
private val field = profile.addField("name")
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeleteProfileFieldPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/deleteProfileField.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeleteReplyPage].
*/
-class DeleteReplyPageTest: WebPageTest(::DeleteReplyPage) {
+class DeleteReplyPageTest : WebPageTest(::DeleteReplyPage) {
private val sone = mock<Sone>()
private val reply = mock<PostReply>()
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("deleteReply.html"))
+ assertThat(page.path, equalTo("deleteReply.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeleteReplyPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/deleteReply.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.any
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DeleteSonePage].
*/
-class DeleteSonePageTest: WebPageTest(::DeleteSonePage) {
+class DeleteSonePageTest : WebPageTest(::DeleteSonePage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("deleteSone.html"))
+ assertThat(page.path, equalTo("deleteSone.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.DeleteSone.Title")).thenReturn("delete sone page")
- assertThat(page.getPageTitle(freenetRequest), equalTo("delete sone page"))
+ whenever(l10n.getString("Page.DeleteSone.Title")).thenReturn("delete sone page")
+ assertThat(page.getPageTitle(soneRequest), equalTo("delete sone page"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DeleteSonePage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("DeleteSone"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/deleteSone.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.notify.Notification
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.notify.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DismissNotificationPage].
*/
-class DismissNotificationPageTest: WebPageTest(::DismissNotificationPage) {
+class DismissNotificationPageTest : WebPageTest(::DismissNotificationPage) {
private val notification = mock<Notification>()
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.DismissNotification.Title")).thenReturn("dismiss notification page")
- assertThat(page.getPageTitle(freenetRequest), equalTo("dismiss notification page"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("dismiss notification page"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DismissNotificationPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [DistrustPage].
*/
-class DistrustPageTest: WebPageTest(::DistrustPage) {
+class DistrustPageTest : WebPageTest(::DistrustPage) {
@Test
fun `page returns correct path`() {
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.Distrust.Title")).thenReturn("distrust page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("distrust page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("distrust page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<DistrustPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Album.Modifier
-import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.mockBuilder
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Album.*
+import net.pterodactylus.sone.data.Album.Modifier.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [EditAlbumPage].
*/
-class EditAlbumPageTest: WebPageTest(::EditAlbumPage) {
+class EditAlbumPageTest : WebPageTest(::EditAlbumPage) {
private val album = mock<Album>()
private val parentAlbum = mock<Album>()
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.EditAlbum.Title")).thenReturn("edit album page")
- assertThat(page.getPageTitle(freenetRequest), equalTo("edit album page"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("edit album page"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<EditAlbumPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Image.Modifier
-import net.pterodactylus.sone.data.Image.Modifier.ImageTitleMustNotBeEmpty
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.doThrow
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.mockBuilder
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Image.*
+import net.pterodactylus.sone.data.Image.Modifier.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [EditImagePage].
*/
-class EditImagePageTest: WebPageTest(::EditImagePage) {
+class EditImagePageTest : WebPageTest(::EditImagePage) {
private val image = mock<Image>()
private val modifier = mockBuilder<Modifier>()
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.EditImage.Title")).thenReturn("edit image page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("edit image page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("edit image page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<EditImagePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [EditProfileFieldPage].
*/
-class EditProfileFieldPageTest: WebPageTest(::EditProfileFieldPage) {
+class EditProfileFieldPageTest : WebPageTest(::EditProfileFieldPage) {
private val profile = Profile(currentSone)
private val field = profile.addField("Name")
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("editProfileField.html"))
+ assertThat(page.path, equalTo("editProfileField.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.EditProfileField.Title")).thenReturn("edit profile field title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("edit profile field title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("edit profile field title"))
}
@Test
assertThat(templateContext["duplicateFieldName"], equalTo<Any>(true))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<EditProfileFieldPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/editProfileField.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.notNullValue
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [EditProfilePage].
*/
-class EditProfilePageTest: WebPageTest(::EditProfilePage) {
+class EditProfilePageTest : WebPageTest(::EditProfilePage) {
private val profile = Profile(currentSone)
private val firstField = profile.addField("First Field")
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("editProfile.html"))
+ assertThat(page.path, equalTo("editProfile.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.EditProfile.Title")).thenReturn("edit profile page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("edit profile page title"))
+ whenever(l10n.getString("Page.EditProfile.Title")).thenReturn("edit profile page title")
+ assertThat(page.getPageTitle(soneRequest), equalTo("edit profile page title"))
}
@Test
verifyRedirect("editProfileField.html?field=${firstField.id}")
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<EditProfilePage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("EditProfile"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/editProfile.html"))
+ }
+
}
--- /dev/null
+package net.pterodactylus.sone.web.pages
+
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+class ErrorPagesTest {
+
+ private val webPageTest = WebPageTest()
+
+ private fun testPage(page: (WebInterface, Loaders, TemplateRenderer) -> FreenetTemplatePage, test: (FreenetTemplatePage) -> Unit) =
+ with(webPageTest) {
+ test(page(webInterface, loaders, templateRenderer))
+ }
+
+ @Test
+ fun `invalid page returns correct path`() {
+ testPage(::InvalidPage) { page ->
+ assertThat(page.path, equalTo("invalid.html"))
+ }
+ }
+
+ @Test
+ fun `invalid page returns correct title`() {
+ testPage(::InvalidPage) { page ->
+ assertThat(page.getPageTitle(webPageTest.freenetRequest), equalTo("Page.Invalid.Title"))
+ }
+ }
+
+ @Test
+ fun `invalid page is annotated with correct template path`() {
+ testPage(::InvalidPage) { page ->
+ assertThat(page.templatePath, equalTo("/templates/invalid.html"))
+ }
+ }
+
+ @Test
+ fun `no permission page returns correct path`() {
+ testPage(::NoPermissionPage) { page ->
+ assertThat(page.path, equalTo("noPermission.html"))
+ }
+ }
+
+ @Test
+ fun `no permission page returns correct title`() {
+ testPage(::NoPermissionPage) { page ->
+ assertThat(page.getPageTitle(webPageTest.freenetRequest), equalTo("Page.NoPermission.Title"))
+ }
+ }
+
+ @Test
+ fun `no permission page is annotated with correct template path`() {
+ testPage(::NoPermissionPage) { page ->
+ assertThat(page.templatePath, equalTo("/templates/noPermission.html"))
+ }
+ }
+
+ @Test
+ fun `empty image title page returns correct path`() {
+ testPage(::EmptyImageTitlePage) { page ->
+ assertThat(page.path, equalTo("emptyImageTitle.html"))
+ }
+ }
+
+ @Test
+ fun `empty image title page returns correct page title`() {
+ testPage(::EmptyImageTitlePage) { page ->
+ assertThat(page.getPageTitle(webPageTest.freenetRequest), equalTo("Page.EmptyImageTitle.Title"))
+ }
+ }
+
+ @Test
+ fun `empty image title page is annotated with correct template path`() {
+ testPage(::EmptyImageTitlePage) { page ->
+ assertThat(page.templatePath, equalTo("/templates/emptyImageTitle.html"))
+ }
+ }
+
+ @Test
+ fun `empty album title page returns correct path`() {
+ testPage(::EmptyAlbumTitlePage) { page ->
+ assertThat(page.path, equalTo("emptyAlbumTitle.html"))
+ }
+ }
+
+ @Test
+ fun `empty album title page returns correct page title`() {
+ testPage(::EmptyAlbumTitlePage) { page ->
+ assertThat(page.getPageTitle(webPageTest.freenetRequest), equalTo("Page.EmptyAlbumTitle.Title"))
+ }
+ }
+
+ @Test
+ fun `empty album title page is annotated with correct template path`() {
+ testPage(::EmptyAlbumTitlePage) { page ->
+ assertThat(page.templatePath, equalTo("/templates/emptyAlbumTitle.html"))
+ }
+ }
+
+}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.ArgumentMatchers
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.*
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.never
/**
* Unit test for [FollowSonePage].
*/
-class FollowSonePageTest: WebPageTest(::FollowSonePage) {
+class FollowSonePageTest : WebPageTest(::FollowSonePage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("followSone.html"))
+ assertThat(page.path, equalTo("followSone.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.FollowSone.Title")).thenReturn("follow sone page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("follow sone page title"))
+ whenever(l10n.getString("Page.FollowSone.Title")).thenReturn("follow sone page title")
+ assertThat(page.getPageTitle(soneRequest), equalTo("follow sone page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<FollowSonePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.TemporaryImage
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.net.*
/**
* Unit test for [GetImagePage].
@Test
fun `page is not link-excepted`() {
- assertThat(page.isLinkExcepted(null), equalTo(false))
+ assertThat(page.isLinkExcepted(URI("")), equalTo(false))
}
@Test
))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<GetImagePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.net.*
/**
* Unit test for [ImageBrowserPage].
*/
-class ImageBrowserPageTest: WebPageTest(::ImageBrowserPage) {
+class ImageBrowserPageTest : WebPageTest(::ImageBrowserPage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("imageBrowser.html"))
+ assertThat(page.path, equalTo("imageBrowser.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.ImageBrowser.Title")).thenReturn("image browser page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("image browser page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("image browser page title"))
}
@Test
@Test
fun `get request for gallery can show second page`() {
- core.preferences.imagesPerPage = 2
+ core.preferences.newImagesPerPage = 2
val firstSone = createSone("first album", "second album")
addSone("sone1", firstSone)
val secondSone = createSone("third album", "fourth album")
@Test
fun `page is link-excepted`() {
- assertThat(page.isLinkExcepted(null), equalTo(true))
+ assertThat(page.isLinkExcepted(URI("")), equalTo(true))
+ }
+
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<ImageBrowserPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("ImageBrowser"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/imageBrowser.html"))
}
}
package net.pterodactylus.sone.web.pages
-import com.google.common.base.Optional.fromNullable
-import com.google.common.base.Predicate
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.notify.PostVisibilityFilter
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.Pagination
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.emptyIterable
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.ArgumentMatchers
+import com.google.common.base.*
+import com.google.common.base.Optional.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.notify.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.*
/**
* Unit test for [IndexPage].
*/
-class IndexPageTest: WebPageTest({ template, webInterface -> IndexPage(template, webInterface, postVisibilityFilter) }) {
+class IndexPageTest : WebPageTest({ webInterface, loaders, templateRenderer -> IndexPage(webInterface, loaders, templateRenderer, postVisibilityFilter) }) {
companion object {
private val postVisibilityFilter = mock<PostVisibilityFilter>()
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("index.html"))
+ assertThat(page.path, equalTo("index.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.Index.Title")).thenReturn("index page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("index page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("index page title"))
}
@Before
fun `index page sets page correctly`() {
val posts = listOf(createPost(3000), createPost(2000), createPost(1000))
whenever(currentSone.posts).thenReturn(posts)
- core.preferences.postsPerPage = 1
+ core.preferences.newPostsPerPage = 1
addHttpRequestParameter("page", "2")
page.processTemplate(freenetRequest, templateContext)
@Suppress("UNCHECKED_CAST")
@Test
fun `index page without posts sets correct pagination`() {
- core.preferences.postsPerPage = 1
+ core.preferences.newPostsPerPage = 1
page.processTemplate(freenetRequest, templateContext)
@Suppress("UNCHECKED_CAST")
(templateContext["pagination"] as Pagination<Post>).let { pagination ->
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<IndexPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("Index"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/index.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.freenet.wot.Identity
-import net.pterodactylus.sone.freenet.wot.OwnIdentity
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.Pagination
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
/**
* Unit test for [KnownSonesPage].
*/
-class KnownSonesPageTest: WebPageTest(::KnownSonesPage) {
+class KnownSonesPageTest : WebPageTest(::KnownSonesPage) {
private val sones = listOf(
createSone(1000, 4, 7, 2, "sone2", true, true),
whenever(albums).thenReturn(listOf(album))
}
whenever(this.rootAlbum).thenReturn(rootAlbum)
- whenever(this.profile).thenReturn(mock<Profile>())
+ whenever(this.profile).thenReturn(mock())
whenever(id).thenReturn(name.toLowerCase())
whenever(this.name).thenReturn(name)
}
@Test
fun `page returns correct title`() {
whenever(l10n.getString("Page.KnownSones.Title")).thenReturn("known sones page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("known sones page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("known sones page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<KnownSonesPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with the correct menuname`() {
+ assertThat(page.menuName, equalTo("KnownSones"))
+ }
+
+ @Test
+ fun `page is annotated with corrrect template path`() {
+ assertThat(page.templatePath, equalTo("/templates/knownSones.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [LikePage].
*/
-class LikePageTest: WebPageTest(::LikePage) {
+class LikePageTest : WebPageTest(::LikePage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("like.html"))
+ assertThat(page.path, equalTo("like.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
- addTranslation("Page.Like.Title", "like page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("like page title"))
+ addTranslation("Page.Like.Title", "like page title")
+ assertThat(page.getPageTitle(soneRequest), equalTo("like page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<LikePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
/**
* Unit test for [LockSonePage].
*/
-class LockSonePageTest: WebPageTest(::LockSonePage) {
+class LockSonePageTest : WebPageTest(::LockSonePage) {
@Test
fun `page returns correct path`() {
@Test
fun `page does not require login`() {
- assertThat(page.requiresLogin(), equalTo(false))
+ assertThat(page.requiresLogin(), equalTo(false))
}
@Test
fun `page returns correct title`() {
- addTranslation("Page.LockSone.Title", "lock Sone page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("lock Sone page title"))
+ addTranslation("Page.LockSone.Title", "lock Sone page title")
+ assertThat(page.getPageTitle(soneRequest), equalTo("lock Sone page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<LockSonePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.freenet.wot.Identity
-import net.pterodactylus.sone.freenet.wot.OwnIdentity
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.thenReturnMock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.containsInAnyOrder
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [LoginPage].
*/
-class LoginPageTest: WebPageTest(::LoginPage) {
+class LoginPageTest : WebPageTest(::LoginPage) {
private val sones = listOf(createSone("Sone", "Test"), createSone("Test"), createSone("Sone"))
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("login.html"))
+ assertThat(page.path, equalTo("login.html"))
}
@Test
fun `page does not require login`() {
- assertThat(page.requiresLogin(), equalTo(false))
+ assertThat(page.requiresLogin(), equalTo(false))
}
@Test
@Test
fun `page is not enabled if full access required and request is not full access`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
assertThat(page.isEnabled(toadletContext), equalTo(false))
}
@Test
fun `page is enabled if full access required and request is full access and there is no current sone`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
unsetCurrentSone()
whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
assertThat(page.isEnabled(toadletContext), equalTo(true))
@Test
fun `page is not enabled if full access required and request is full access but there is a current sone`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
assertThat(page.isEnabled(toadletContext), equalTo(false))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<LoginPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("Login"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/login.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [LogoutPage].
*/
-class LogoutPageTest: WebPageTest(::LogoutPage) {
+class LogoutPageTest : WebPageTest(::LogoutPage) {
@Test
fun `page returns correct path`() {
@Test
fun `page returns correct title`() {
addTranslation("Page.Logout.Title", "logout page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("logout page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("logout page title"))
}
@Test
@Test
fun `page is not enabled if sone requires full access and request does not have full access`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
assertThat(page.isEnabled(toadletContext), equalTo(false))
}
@Test
fun `page is enabled if full access is required and present and sone is logged in and there is more than one sone`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
whenever(core.localSones).thenReturn(listOf(currentSone, currentSone))
assertThat(page.isEnabled(toadletContext), equalTo(true))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<LogoutPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("Logout"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [MarkAsKnownPage].
*/
-class MarkAsKnownPageTest: WebPageTest(::MarkAsKnownPage) {
+class MarkAsKnownPageTest : WebPageTest(::MarkAsKnownPage) {
@Test
fun `page returns correct path`() {
@Test
fun `page returns correct title`() {
addTranslation("Page.MarkAsKnown.Title", "mark as known page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("mark as known page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("mark as known page title"))
}
@Test
addHttpRequestPart("returnPage", "return.html")
addHttpRequestPart("type", "post")
addHttpRequestPart("id", "post1 post2 post3")
- val posts = listOf(mock<Post>(), mock<Post>())
+ val posts = listOf(mock<Post>(), mock())
addPost("post1", posts[0])
addPost("post3", posts[1])
verifyRedirect("return.html") {
addHttpRequestPart("returnPage", "return.html")
addHttpRequestPart("type", "reply")
addHttpRequestPart("id", "reply1 reply2 reply3")
- val replies = listOf(mock<PostReply>(), mock<PostReply>())
+ val replies = listOf(mock<PostReply>(), mock())
addPostReply("reply1", replies[0])
addPostReply("reply3", replies[1])
verifyRedirect("return.html") {
addHttpRequestPart("returnPage", "return.html")
addHttpRequestPart("type", "sone")
addHttpRequestPart("id", "sone1 sone2 sone3")
- val sones = listOf(mock<Sone>(), mock<Sone>())
+ val sones = listOf(mock<Sone>(), mock())
addSone("sone1", sones[0])
addSone("sone3", sones[1])
verifyRedirect("return.html") {
verifyRedirect("invalid.html")
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<MarkAsKnownPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.asOptional
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.containsInAnyOrder
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import java.util.Arrays.asList
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.util.Arrays.*
/**
* Unit test for [NewPage].
*/
-class NewPageTest: WebPageTest(::NewPage) {
+class NewPageTest : WebPageTest(::NewPage) {
@Before
fun setupNumberOfPostsPerPage() {
- webInterface.core.preferences.postsPerPage = 5
+ webInterface.core.preferences.newPostsPerPage = 5
}
@Test
@Test
fun `page returns correct title`() {
addTranslation("Page.New.Title", "new page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("new page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("new page title"))
}
@Test
fun `posts are not duplicated when they come from both new posts and new replies notifications`() {
val extraPost = mock<Post>().withTime(2000)
val posts = asList(mock<Post>().withTime(1000), mock<Post>().withTime(3000))
- val postReplies = asList(mock<PostReply>(), mock<PostReply>())
+ val postReplies = asList(mock<PostReply>(), mock())
whenever(postReplies[0].post).thenReturn(posts[0].asOptional())
whenever(postReplies[1].post).thenReturn(extraPost.asOptional())
whenever(webInterface.getNewPosts(currentSone)).thenReturn(posts)
@Test
@Suppress("UNCHECKED_CAST")
fun `posts are paginated properly`() {
- webInterface.core.preferences.postsPerPage = 2
+ webInterface.core.preferences.newPostsPerPage = 2
val posts = listOf(mock<Post>().withTime(2000), mock<Post>().withTime(3000), mock<Post>().withTime(1000))
whenever(webInterface.getNewPosts(currentSone)).thenReturn(posts)
verifyNoRedirect {
@Test
@Suppress("UNCHECKED_CAST")
fun `posts are paginated properly on second page`() {
- webInterface.core.preferences.postsPerPage = 2
+ webInterface.core.preferences.newPostsPerPage = 2
addHttpRequestParameter("page", "1")
val posts = listOf(mock<Post>().withTime(2000), mock<Post>().withTime(3000), mock<Post>().withTime(1000))
whenever(webInterface.getNewPosts(currentSone)).thenReturn(posts)
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<NewPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with the correct menuname`() {
+ assertThat(page.menuName, equalTo("New"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/new.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions
-import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.FOLLOWED
-import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.TRUSTED
+import net.pterodactylus.sone.data.SoneOptions.*
+import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.*
+import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.*
import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS
-import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO
-import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.hasItem
-import org.hamcrest.Matchers.nullValue
-import org.junit.Before
-import org.junit.Test
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
/**
* Unit test for [OptionsPage].
*/
-class OptionsPageTest: WebPageTest(::OptionsPage) {
+class OptionsPageTest : WebPageTest(::OptionsPage) {
@Before
fun setupPreferences() {
- core.preferences.insertionDelay = 1
- core.preferences.charactersPerPost = 50
- core.preferences.fcpFullAccessRequired = WRITING
- core.preferences.imagesPerPage = 4
- core.preferences.isFcpInterfaceActive = true
- core.preferences.isRequireFullAccess = true
- core.preferences.negativeTrust = 7
- core.preferences.positiveTrust = 8
- core.preferences.postCutOffLength = 51
- core.preferences.postsPerPage = 10
- core.preferences.trustComment = "11"
+ core.preferences.newInsertionDelay = 1
+ core.preferences.newCharactersPerPost = 50
+ core.preferences.newFcpFullAccessRequired = WRITING
+ core.preferences.newImagesPerPage = 4
+ core.preferences.newFcpInterfaceActive = true
+ core.preferences.newRequireFullAccess = true
+ core.preferences.newNegativeTrust = 7
+ core.preferences.newPositiveTrust = 8
+ core.preferences.newPostCutOffLength = 51
+ core.preferences.newPostsPerPage = 10
+ core.preferences.newTrustComment = "11"
}
@Before
@Test
fun `page returns correct title`() {
addTranslation("Page.Options.Title", "options page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("options page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("options page title"))
}
@Test
@Test
fun `fcp interface can be set to true`() {
- verifyThatPreferencesCanBeSet("fcp-interface-active", "checked", true) { core.preferences.isFcpInterfaceActive }
+ verifyThatPreferencesCanBeSet("fcp-interface-active", "checked", true) { core.preferences.fcpInterfaceActive }
}
@Test
fun `fcp interface can be set to false`() {
- verifyThatPreferencesCanBeSet("fcp-interface-active", null, false) { core.preferences.isFcpInterfaceActive }
+ verifyThatPreferencesCanBeSet("fcp-interface-active", null, false) { core.preferences.fcpInterfaceActive }
}
@Test
fun `require full access can be set to true`() {
- verifyThatPreferencesCanBeSet("require-full-access", "checked", true) { core.preferences.isRequireFullAccess }
+ verifyThatPreferencesCanBeSet("require-full-access", "checked", true) { core.preferences.requireFullAccess }
}
@Test
fun `require full access can be set to false`() {
- verifyThatPreferencesCanBeSet("require-full-access", null, false) { core.preferences.isRequireFullAccess }
+ verifyThatPreferencesCanBeSet("require-full-access", null, false) { core.preferences.requireFullAccess }
}
@Test
verifyThatPreferencesCanBeSet("trust-comment", "", "Set from Sone Web Interface") { core.preferences.trustComment }
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<OptionsPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("Options"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/options.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.web.page.FreenetRequest
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
-import java.nio.file.Files
-import java.nio.file.Paths
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.junit.rules.*
+import java.nio.file.*
import kotlin.text.Charsets.UTF_8
/**
*/
class ReloadingPageTest {
- @Rule @JvmField val tempFolder = TemporaryFolder()
+ @Rule
+ @JvmField
+ val tempFolder = TemporaryFolder()
private val folder by lazy { tempFolder.newFolder()!! }
private val page by lazy { ReloadingPage<FreenetRequest>("/prefix/", folder.path, "text/plain") }
private val webPageTest = WebPageTest()
assertThat(responseBytes.toByteArray(), equalTo("Hello\nWorld\n".toByteArray()))
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<ReloadingPage<*>>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.core.SoneRescuer
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
/**
* Unit test for [RescuePage].
*/
-class RescuePageTest: WebPageTest(::RescuePage) {
+class RescuePageTest : WebPageTest(::RescuePage) {
private val soneRescuer = mock<SoneRescuer>()
@Test
fun `page returns correct title`() {
addTranslation("Page.Rescue.Title", "rescue page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("rescue page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("rescue page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<RescuePage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct menuname`() {
+ assertThat(page.menuName, equalTo("Rescue"))
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/rescue.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import com.google.common.base.Optional.absent
-import com.google.common.base.Ticker
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.isOnPage
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicInteger
+import com.google.common.base.*
+import com.google.common.base.Optional.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.util.concurrent.*
+import java.util.concurrent.atomic.*
/**
* Unit test for [SearchPage].
*/
-class SearchPageTest: WebPageTest({ template, webInterface -> SearchPage(template, webInterface, ticker) }) {
+class SearchPageTest : WebPageTest({ webInterface, loaders, templateRenderer -> SearchPage(webInterface, loaders, templateRenderer, ticker) }) {
companion object {
val ticker = mock<Ticker>()
@Test
fun `page returns correct title`() {
addTranslation("Page.Search.Title", "search page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("search page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("search page title"))
}
@Test
@Test
fun `searching for sone link redirects to view sone page`() {
- addSone("sone-id", mock<Sone>())
- addHttpRequestParameter("query", "sone://sone-id")
- verifyRedirect("viewSone.html?sone=sone-id")
+ addSone("Sone-ID", mock())
+ addHttpRequestParameter("query", "sone://Sone-ID")
+ verifyRedirect("viewSone.html?sone=Sone-ID")
}
@Test
fun `searching for sone link without prefix redirects to view sone page`() {
- addSone("sone-id", mock<Sone>())
+ addSone("sone-id", mock())
addHttpRequestParameter("query", "sone-id")
verifyRedirect("viewSone.html?sone=sone-id")
}
@Test
fun `searching for a post link redirects to post page`() {
- addPost("post-id", mock<Post>())
- addHttpRequestParameter("query", "post://post-id")
- verifyRedirect("viewPost.html?post=post-id")
+ addPost("Post-id", mock())
+ addHttpRequestParameter("query", "post://Post-id")
+ verifyRedirect("viewPost.html?post=Post-id")
}
@Test
fun `searching for a post ID without prefix redirects to post page`() {
- addPost("post-id", mock<Post>())
+ addPost("post-id", mock())
addHttpRequestParameter("query", "post-id")
verifyRedirect("viewPost.html?post=post-id")
}
@Test
fun `searching for a reply link redirects to the post page`() {
val postReply = mock<PostReply>().apply { whenever(postId).thenReturn("post-id") }
- addPostReply("reply-id", postReply)
- addHttpRequestParameter("query", "reply://reply-id")
+ addPostReply("Reply-id", postReply)
+ addHttpRequestParameter("query", "reply://Reply-id")
verifyRedirect("viewPost.html?post=post-id")
}
@Test
fun `searching for an album link redirects to the image browser`() {
- addAlbum("album-id", mock<Album>())
+ addAlbum("album-id", mock())
addHttpRequestParameter("query", "album://album-id")
verifyRedirect("imageBrowser.html?album=album-id")
}
@Test
fun `searching for an album ID redirects to the image browser`() {
- addAlbum("album-id", mock<Album>())
+ addAlbum("album-id", mock())
addHttpRequestParameter("query", "album-id")
verifyRedirect("imageBrowser.html?album=album-id")
}
@Test
fun `searching for an image link redirects to the image browser`() {
- addImage("image-id", mock<Image>())
+ addImage("image-id", mock())
addHttpRequestParameter("query", "image://image-id")
verifyRedirect("imageBrowser.html?image=image-id")
}
@Test
fun `searching for an image ID redirects to the image browser`() {
- addImage("image-id", mock<Image>())
+ addImage("image-id", mock())
addHttpRequestParameter("query", "image-id")
verifyRedirect("imageBrowser.html?image=image-id")
}
@Test
fun `sone hits are paginated correctly`() {
- core.preferences.postsPerPage = 2
+ core.preferences.newPostsPerPage = 2
val sones = listOf(createSone("1Sone"), createSone("Other1"), createSone("22Sone"), createSone("333Sone"), createSone("Other2"))
.onEach { addSone(it.id, it) }
addHttpRequestParameter("query", "sone")
@Test
fun `sone hits page 2 is shown correctly`() {
- core.preferences.postsPerPage = 2
+ core.preferences.newPostsPerPage = 2
val sones = listOf(createSone("1Sone"), createSone("Other1"), createSone("22Sone"), createSone("333Sone"), createSone("Other2"))
.onEach { addSone(it.id, it) }
addHttpRequestParameter("query", "sone")
@Test
fun `post hits are paginated correctly`() {
- core.preferences.postsPerPage = 2
+ core.preferences.newPostsPerPage = 2
val sones = listOf(createSoneWithPost("match1", "1Sone"), createSoneWithPost("no-match1", "Other1"), createSoneWithPost("match2", "22Sone"), createSoneWithPost("match3", "333Sone"), createSoneWithPost("no-match2", "Other2"))
addHttpRequestParameter("query", "sone")
verifyNoRedirect {
@Test
fun `post hits page 2 is shown correctly`() {
- core.preferences.postsPerPage = 2
+ core.preferences.newPostsPerPage = 2
val sones = listOf(createSoneWithPost("match1", "1Sone"), createSoneWithPost("no-match1", "Other1"), createSoneWithPost("match2", "22Sone"), createSoneWithPost("match3", "333Sone"), createSoneWithPost("no-match2", "Other2"))
addHttpRequestParameter("query", "sone")
addHttpRequestParameter("postPage", "1")
@Suppress("UNCHECKED_CAST")
private operator fun <T> get(key: String): T? = templateContext[key] as? T
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<SearchPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/search.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.main.SonePlugin
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.util.notify.Notification
-import net.pterodactylus.util.template.TemplateContext
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.notify.*
+import net.pterodactylus.util.template.*
import net.pterodactylus.util.version.Version
-import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.anyOf
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.containsInAnyOrder
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Test
+import org.hamcrest.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
/**
* Unit test for [SoneTemplatePage].
*/
-class SoneTemplatePageTest : WebPageTest({ template, webInterface -> object : SoneTemplatePage("path.html", template, webInterface, true) {} }) {
+class SoneTemplatePageTest : WebPageTest({ webInterface, loaders, templateRenderer -> object : SoneTemplatePage(webInterface, loaders, templateRenderer, requiresLogin = true) {} }) {
+
+ init {
+ request("index.html")
+ }
@Test
fun `page title is empty string if no page title key was given`() {
- SoneTemplatePage("path.html", template, null, webInterface).let { page ->
- assertThat(page.getPageTitle(freenetRequest), equalTo(""))
+ SoneTemplatePage(webInterface, loaders, templateRenderer, requiresLogin = false).let { page ->
+ assertThat(page.getPageTitle(soneRequest), equalTo(""))
}
}
@Test
fun `page title is retrieved from l10n if page title key is given`() {
- SoneTemplatePage("path.html", template, "page.title", webInterface).let { page ->
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "page.title", requiresLogin = false).let { page ->
whenever(l10n.getString("page.title")).thenReturn("Page Title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("Page Title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("Page Title"))
}
}
@Test
fun `local sones are set in template context`() {
- val localSones = listOf(mock<Sone>(), mock<Sone>())
+ val localSones = listOf(mock<Sone>(), mock())
whenever(core.localSones).thenReturn(localSones)
verifyVariableMatches("localSones", containsInAnyOrder(*localSones.toTypedArray()))
}
@Test
fun `handleRequest method is called`() {
var called = false
- val page = object : SoneTemplatePage("path.html", template, webInterface, true) {
+ val page = object : SoneTemplatePage(webInterface, loaders, templateRenderer, requiresLogin = true) {
override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
called = true
}
@Test
fun `redirect does not happen if login is not required`() {
- val page = SoneTemplatePage("page.html", template, webInterface, false)
+ val page = SoneTemplatePage(webInterface, loaders, templateRenderer, requiresLogin = false)
assertThat(page.getRedirectTarget(freenetRequest), nullValue())
}
@Test
fun `redirect does happen if sone is not logged in`() {
unsetCurrentSone()
- request("index.html")
assertThat(page.getRedirectTarget(freenetRequest), equalTo("login.html?target=index.html"))
}
@Test
fun `redirect does happen with parameters encoded correctly if sone is not logged in`() {
unsetCurrentSone()
- request("index.html")
addHttpRequestParameter("foo", "b=r")
addHttpRequestParameter("baz", "q&o")
assertThat(page.getRedirectTarget(freenetRequest), anyOf(
@Test
fun `page is disabled if full access is required but request does not have full access`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
assertThat(page.isEnabled(toadletContext), equalTo(false))
}
@Test
fun `page is enabled if full access is required and request has full access and login is required and there is a current sone`() {
- core.preferences.isRequireFullAccess = true
+ core.preferences.newRequireFullAccess = true
whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
assertThat(page.isEnabled(toadletContext), equalTo(true))
}
@Test
fun `page is enabled if no full access is required and login is not required`() {
- SoneTemplatePage("path.html", template, webInterface, false).let { page ->
+ SoneTemplatePage(webInterface, loaders, templateRenderer, requiresLogin = false).let { page ->
assertThat(page.isEnabled(toadletContext), equalTo(true))
}
}
+ @Test
+ fun `handle request with sone request is called`() {
+ var called = false
+ val page = object : SoneTemplatePage(webInterface, loaders, templateRenderer) {
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ called = true
+ }
+ }
+ page.processTemplate(freenetRequest, templateContext)
+ assertThat(called, equalTo(true))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.never
/**
* Unit test for [TrustPage].
*/
-class TrustPageTest: WebPageTest(::TrustPage) {
+class TrustPageTest : WebPageTest(::TrustPage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("trust.html"))
+ assertThat(page.path, equalTo("trust.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
- addTranslation("Page.Trust.Title", "title trust page")
- assertThat(page.getPageTitle(freenetRequest), equalTo("title trust page"))
+ addTranslation("Page.Trust.Title", "title trust page")
+ assertThat(page.getPageTitle(soneRequest), equalTo("title trust page"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<TrustPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.test.capture
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.any
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [UnbookmarkPage].
*/
-class UnbookmarkPageTest: WebPageTest(::UnbookmarkPage) {
+class UnbookmarkPageTest : WebPageTest(::UnbookmarkPage) {
@Test
fun `page returns correct path`() {
@Test
fun `page returns correct title`() {
addTranslation("Page.Unbookmark.Title", "unbookmark page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("unbookmark page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("unbookmark page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnbookmarkPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.verify
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
/**
* Unit test for [UnfollowSonePage].
*/
-class UnfollowSonePageTest: WebPageTest(::UnfollowSonePage) {
+class UnfollowSonePageTest : WebPageTest(::UnfollowSonePage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("unfollowSone.html"))
+ assertThat(page.path, equalTo("unfollowSone.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct page title`() {
- addTranslation("Page.UnfollowSone.Title", "unfollow page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("unfollow page title"))
+ addTranslation("Page.UnfollowSone.Title", "unfollow page title")
+ assertThat(page.getPageTitle(soneRequest), equalTo("unfollow page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnfollowSonePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
/**
* Unit test for [UnlikePage].
*/
-class UnlikePageTest: WebPageTest(::UnlikePage) {
+class UnlikePageTest : WebPageTest(::UnlikePage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("unlike.html"))
+ assertThat(page.path, equalTo("unlike.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
addTranslation("Page.Unlike.Title", "unlike page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("unlike page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("unlike page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnlikePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
/**
* Unit test for [UnlockSonePage].
*/
-class UnlockSonePageTest: WebPageTest(::UnlockSonePage) {
+class UnlockSonePageTest : WebPageTest(::UnlockSonePage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("unlockSone.html"))
+ assertThat(page.path, equalTo("unlockSone.html"))
}
@Test
fun `page does not require login`() {
- assertThat(page.requiresLogin(), equalTo(false))
+ assertThat(page.requiresLogin(), equalTo(false))
}
@Test
fun `page returns correct title`() {
addTranslation("Page.UnlockSone.Title", "unlock page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("unlock page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("unlock page title"))
}
@Test
setMethod(POST)
addHttpRequestPart("returnPage", "return.html")
addHttpRequestPart("sone", "remote-sone")
- addSone("remote-sone", mock<Sone>())
+ addSone("remote-sone", mock())
verifyRedirect("return.html") {
verify(core, never()).unlockSone(any())
}
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UnlockSonePage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.never
/**
* Unit test for [UntrustPage].
*/
-class UntrustPageTest: WebPageTest(::UntrustPage) {
+class UntrustPageTest : WebPageTest(::UntrustPage) {
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("untrust.html"))
+ assertThat(page.path, equalTo("untrust.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
addTranslation("Page.Untrust.Title", "untrust page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("untrust page title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("untrust page title"))
}
@Test
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UntrustPage>(), notNullValue())
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Image.Modifier
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.data.TemporaryImage
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Image.*
+import net.pterodactylus.sone.test.getInstance
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.mockBuilder
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.web.Method.POST
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import org.mockito.Mockito.any
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.util.web.Method.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
import org.mockito.Mockito.eq
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
/**
* Unit test for [UploadImagePage].
*/
-class UploadImagePageTest: WebPageTest(::UploadImagePage) {
+class UploadImagePageTest : WebPageTest(::UploadImagePage) {
private val parentAlbum = mock<Album>().apply {
whenever(id).thenReturn("parent-id")
@Test
fun `page returns correct path`() {
- assertThat(page.path, equalTo("uploadImage.html"))
+ assertThat(page.path, equalTo("uploadImage.html"))
}
@Test
fun `page requires login`() {
- assertThat(page.requiresLogin(), equalTo(true))
+ assertThat(page.requiresLogin(), equalTo(true))
}
@Test
fun `page returns correct title`() {
- addTranslation("Page.UploadImage.Title", "upload image page title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("upload image page title"))
+ addTranslation("Page.UploadImage.Title", "upload image page title")
+ assertThat(page.getPageTitle(soneRequest), equalTo("upload image page title"))
}
@Test
fun `post request with parent that is not the current sone results in no permission error page`() {
setMethod(POST)
addHttpRequestPart("parent", "parent-id")
- whenever(parentAlbum.sone).thenReturn(mock<Sone>())
+ whenever(parentAlbum.sone).thenReturn(mock())
addAlbum("parent-id", parentAlbum)
verifyRedirect("noPermission.html")
}
}
}
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<UploadImagePage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/invalid.html"))
+ }
+
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.net.*
/**
* Unit test for [ViewPostPage].
*/
-class ViewPostPageTest: WebPageTest(::ViewPostPage) {
+class ViewPostPageTest : WebPageTest(::ViewPostPage) {
private val post = mock<Post>()
@Test
fun `the view post page is link-excepted`() {
- assertThat(page.isLinkExcepted(null), equalTo(true))
+ assertThat(page.isLinkExcepted(URI("")), equalTo(true))
}
@Test
@Test
fun `page title for request without parameters is default title`() {
addTranslation("Page.ViewPost.Title", "view post title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("view post title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("view post title"))
}
@Test
fun `page title for request with invalid post is default title`() {
addHttpRequestParameter("post", "invalid-post-id")
addTranslation("Page.ViewPost.Title", "view post title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("view post title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("view post title"))
}
@Test
addPost("post-id", post)
addHttpRequestParameter("post", "post-id")
addTranslation("Page.ViewPost.Title", "view post title")
- assertThat(page.getPageTitle(freenetRequest), equalTo("This is a text that … - First M. Last - view post title"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("This is a text that … - First M. Last - view post title"))
+ }
+
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<ViewPostPage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/viewPost.html"))
}
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Profile
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.isOnPage
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.asOptional
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Before
-import org.junit.Test
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.net.*
/**
* Unit test for [ViewSonePage].
*/
-class ViewSonePageTest: WebPageTest(::ViewSonePage) {
+class ViewSonePageTest : WebPageTest(::ViewSonePage) {
init {
whenever(currentSone.id).thenReturn("sone-id")
private val post1 = createPost("post1", "First Post.", 1000, currentSone)
private val post2 = createPost("post2", "Second Post.", 2000, currentSone)
- private val foreignPost1 = createPost("foreign-post1", "First Foreign Post.", 1000, mock<Sone>())
- private val foreignPost2 = createPost("foreign-post2", "Second Foreign Post.", 2000, mock<Sone>())
- private val foreignPost3 = createPost("foreign-post3", "Third Foreign Post.", 3000, mock<Sone>())
- private val directed1 = createPost("post3", "First directed.", 1500, mock<Sone>(), recipient = currentSone)
- private val directed2 = createPost("post4", "Second directed.", 2500, mock<Sone>(), recipient = currentSone)
+ private val foreignPost1 = createPost("foreign-post1", "First Foreign Post.", 1000, mock())
+ private val foreignPost2 = createPost("foreign-post2", "Second Foreign Post.", 2000, mock())
+ private val foreignPost3 = createPost("foreign-post3", "Third Foreign Post.", 3000, mock())
+ private val directed1 = createPost("post3", "First directed.", 1500, mock(), recipient = currentSone)
+ private val directed2 = createPost("post4", "Second directed.", 2500, mock(), recipient = currentSone)
@Before
fun setup() {
whenever(currentSone.posts).thenReturn(mutableListOf(post2, post1))
whenever(core.getDirectedPosts("sone-id")).thenReturn(setOf(directed1, directed2))
- core.preferences.postsPerPage = 2
+ core.preferences.newPostsPerPage = 2
}
@Test
@Test
fun `page title is default for request without parameters`() {
addTranslation("Page.ViewSone.Page.TitleWithoutSone", "view sone page without sone")
- assertThat(page.getPageTitle(freenetRequest), equalTo("view sone page without sone"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("view sone page without sone"))
}
@Test
fun `page title is default for request with invalid sone parameters`() {
addHttpRequestParameter("sone", "invalid-sone-id")
addTranslation("Page.ViewSone.Page.TitleWithoutSone", "view sone page without sone")
- assertThat(page.getPageTitle(freenetRequest), equalTo("view sone page without sone"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("view sone page without sone"))
}
@Test
lastName = "Last"
})
addTranslation("Page.ViewSone.Title", "view sone page")
- assertThat(page.getPageTitle(freenetRequest), equalTo("First M. Last - view sone page"))
+ assertThat(page.getPageTitle(soneRequest), equalTo("First M. Last - view sone page"))
}
@Test
fun `page is link-excepted`() {
- assertThat(page.isLinkExcepted(null), equalTo(true))
+ assertThat(page.isLinkExcepted(URI("")), equalTo(true))
+ }
+
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<ViewSonePage>(), notNullValue())
+ }
+
+ @Test
+ fun `page is annotated with correct template path`() {
+ assertThat(page.templatePath, equalTo("/templates/viewSone.html"))
}
}
package net.pterodactylus.sone.web.pages
-import com.google.common.eventbus.EventBus
-import freenet.clients.http.ToadletContext
-import freenet.support.SimpleReadOnlyArrayBucket
-import freenet.support.api.HTTPRequest
-import freenet.support.api.HTTPUploadedFile
-import net.pterodactylus.sone.core.Preferences
-import net.pterodactylus.sone.data.Album
-import net.pterodactylus.sone.data.Image
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.data.TemporaryImage
-import net.pterodactylus.sone.freenet.wot.OwnIdentity
+import com.google.common.eventbus.*
+import freenet.clients.http.*
+import freenet.support.*
+import freenet.support.api.*
+import net.pterodactylus.sone.core.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.main.*
import net.pterodactylus.sone.test.deepMock
import net.pterodactylus.sone.test.get
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asList
-import net.pterodactylus.sone.utils.asOptional
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
import net.pterodactylus.sone.web.page.FreenetTemplatePage.RedirectException
-import net.pterodactylus.util.notify.Notification
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import net.pterodactylus.util.web.Method
-import net.pterodactylus.util.web.Method.GET
-import net.pterodactylus.util.web.Response
-import org.junit.Assert.fail
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyLong
-import org.mockito.ArgumentMatchers.anyString
+import net.pterodactylus.util.notify.*
+import net.pterodactylus.util.template.*
+import net.pterodactylus.util.web.*
+import net.pterodactylus.util.web.Method.*
+import org.junit.Assert.*
+import org.mockito.ArgumentMatchers.*
import org.mockito.ArgumentMatchers.eq
-import java.io.ByteArrayOutputStream
-import java.net.URI
-import java.nio.charset.Charset
+import java.io.*
+import java.net.*
+import java.nio.charset.*
import kotlin.text.Charsets.UTF_8
/**
* Base class for web page tests.
*/
-open class WebPageTest(pageSupplier: (Template, WebInterface) -> SoneTemplatePage = { _, _ -> mock<SoneTemplatePage>() }) {
+open class WebPageTest(pageSupplier: (WebInterface, Loaders, TemplateRenderer) -> SoneTemplatePage = { _, _, _ -> mock() }) {
val currentSone = mock<Sone>()
- val template = mock<Template>()
+ val loaders = mock<Loaders>()
+ val templateRenderer = mock<TemplateRenderer>()
val webInterface = deepMock<WebInterface>()
val core = webInterface.core
val eventBus = mock<EventBus>()
val preferences = Preferences(eventBus)
val l10n = webInterface.l10n!!
+ val sessionManager = mock<SessionManager>()
+
+ val page by lazy { pageSupplier(webInterface, loaders, templateRenderer) }
- val page by lazy { pageSupplier(template, webInterface) }
val httpRequest = mock<HTTPRequest>()
val freenetRequest = mock<FreenetRequest>()
+
+ init {
+ whenever(freenetRequest.l10n).thenReturn(l10n)
+ whenever(freenetRequest.sessionManager).thenReturn(sessionManager)
+ whenever(freenetRequest.uri).thenReturn(mock())
+ }
+
+ val soneRequest by lazy { freenetRequest.toSoneRequest(core, webInterface) }
val templateContext = TemplateContext()
val toadletContext = deepMock<ToadletContext>()
val responseContent = ByteArrayOutputStream()
whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]] }
whenever(core.getReplies(anyString())).then { perPostReplies[it[0]].asList() }
whenever(core.getAlbum(anyString())).then { allAlbums[it[0]] }
- whenever(core.getImage(anyString())).then { allImages[it[0]]}
- whenever(core.getImage(anyString(), anyBoolean())).then { allImages[it[0]]}
+ whenever(core.getImage(anyString())).then { allImages[it[0]] }
+ whenever(core.getImage(anyString(), anyBoolean())).then { allImages[it[0]] }
whenever(core.getTemporaryImage(anyString())).thenReturn(null)
}
private fun setupWebInterface() {
+ whenever(webInterface.sessionManager).thenReturn(sessionManager)
whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone)
whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone)
whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).then { postRequestParameters[it[0]]?.decode()?.take(it[1]) ?: "" }
whenever(httpRequest.getUploadedFile(anyString())).then {
it.get<String>(0).takeIf { it in uploadedFileNames }
- ?.let { name -> UploadedFile(uploadedFileNames[name]!!, uploadedFileContentTypes[name]!!, uploadedFileResources[name]!!)
- }
+ ?.let { name ->
+ UploadedFile(uploadedFileNames[name]!!, uploadedFileContentTypes[name]!!, uploadedFileResources[name]!!)
+ }
}
}
- private class UploadedFile(private val filename: String, private val contentType: String, private val resourceName: String): HTTPUploadedFile {
+ private class UploadedFile(private val filename: String, private val contentType: String, private val resourceName: String) : HTTPUploadedFile {
override fun getFilename() = filename
override fun getContentType() = contentType
override fun getData() = javaClass.getResourceAsStream(resourceName).readBytes().let(::SimpleReadOnlyArrayBucket)
}
fun addHttpRequestParameter(name: String, value: String) {
- getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf<String>() }.apply { add(value) }
+ getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf() }.apply { add(value) }
}
fun addHttpRequestPart(name: String, value: String) {
import org.ajoberstar.grgit.Grgit
-task(writeVersion) << {
- def grgit = Grgit.open(dir: project.rootDir)
- def version = grgit.resolve.toCommit("HEAD").id
- def niceVersion = grgit.describe()
- grgit.close()
+task(writeVersion) {
+ doLast {
+ def grgit = Grgit.open(dir: project.rootDir)
+ def version = grgit.resolve.toCommit("HEAD").id
+ def niceVersion = grgit.describe()
+ grgit.close()
- new File("src/generated/resources").mkdirs()
- project.file("src/generated/resources/version.yaml").withWriter { out ->
- out.println "id: ${version}"
- out.println "nice: ${niceVersion}"
- }
+ new File("src/generated/resources").mkdirs()
+ project.file("src/generated/resources/version.yaml").withWriter { out ->
+ out.println "id: ${version}"
+ out.println "nice: ${niceVersion}"
+ }
+ }
}
sourceSets {