From: David ‘Bombe’ Roden Date: Fri, 29 Nov 2019 17:28:46 +0000 (+0100) Subject: 🔀 Merge 'feature/sone-locked-notification' into 'next' X-Git-Tag: v81^2~15 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=8c387bf3c1315ee19902a8d15616ef394b486ffe;hp=70714f7e4b50549ec1550e28c8b90e3b14e31398 🔀 Merge 'feature/sone-locked-notification' into 'next' --- diff --git a/build.gradle b/build.gradle index 47113fc..78c3110 100644 --- a/build.gradle +++ b/build.gradle @@ -39,9 +39,9 @@ dependencies { compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8' compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.3.0-RC' - compile group: 'net.pterodactylus', name: 'utils', version: '0.12.4' + compile group: 'net.pterodactylus', name: 'utils', version: '0.13.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.google.guava', name: 'guava', version: '27.0.1-jre' 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' diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 5c1e537..0228951 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -24,6 +24,7 @@ import static com.google.common.primitives.Longs.tryParse; import static java.lang.String.format; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static net.pterodactylus.sone.data.AlbumsKt.getAllImages; import java.util.ArrayList; import java.util.Collection; @@ -634,9 +635,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, loadSone(sone); database.storeSone(sone); sone.setStatus(SoneStatus.idle); - if (sone.getPosts().isEmpty() && sone.getReplies().isEmpty()) { + if (sone.getPosts().isEmpty() && sone.getReplies().isEmpty() && getAllImages(sone.getRootAlbum()).isEmpty()) { // dirty hack lockSone(sone); + eventBus.post(new SoneLockedOnStartup(sone)); } soneInserter.start(); return sone; diff --git a/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.java b/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.java deleted file mode 100644 index 5fa33e4..0000000 --- a/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.java +++ /dev/null @@ -1,82 +0,0 @@ -package net.pterodactylus.sone.core; - -import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; -import net.pterodactylus.util.config.Configuration; -import net.pterodactylus.util.config.ConfigurationException; - -/** - * Loads preferences stored in a {@link Configuration} into a {@link - * Preferences} object. - */ -public class PreferencesLoader { - - private final Preferences preferences; - - public PreferencesLoader(Preferences preferences) { - this.preferences = preferences; - } - - public void loadFrom(Configuration configuration) { - loadInsertionDelay(configuration); - loadPostsPerPage(configuration); - loadImagesPerPage(configuration); - loadCharactersPerPost(configuration); - loadPostCutOffLength(configuration); - loadRequireFullAccess(configuration); - loadFcpInterfaceActive(configuration); - loadFcpFullAccessRequired(configuration); - } - - private void loadInsertionDelay(Configuration configuration) { - preferences.setNewInsertionDelay(configuration.getIntValue( - "Option/InsertionDelay").getValue(null)); - } - - private void loadPostsPerPage(Configuration configuration) { - preferences.setNewPostsPerPage( - configuration.getIntValue("Option/PostsPerPage") - .getValue(null)); - } - - private void loadImagesPerPage(Configuration configuration) { - preferences.setNewImagesPerPage( - configuration.getIntValue("Option/ImagesPerPage") - .getValue(null)); - } - - private void loadCharactersPerPost(Configuration configuration) { - preferences.setNewCharactersPerPost( - configuration.getIntValue("Option/CharactersPerPost") - .getValue(null)); - } - - private void loadPostCutOffLength(Configuration configuration) { - try { - preferences.setNewPostCutOffLength( - configuration.getIntValue("Option/PostCutOffLength") - .getValue(null)); - } catch (IllegalArgumentException iae1) { - /* previous versions allowed -1, ignore and use default. */ - } - } - - private void loadRequireFullAccess(Configuration configuration) { - preferences.setNewRequireFullAccess( - configuration.getBooleanValue("Option/RequireFullAccess") - .getValue(null)); - } - - private void loadFcpInterfaceActive(Configuration configuration) { - preferences.setNewFcpInterfaceActive(configuration.getBooleanValue( - "Option/ActivateFcpInterface").getValue(null)); - } - - private void loadFcpFullAccessRequired(Configuration configuration) { - Integer fullAccessRequiredInteger = configuration - .getIntValue("Option/FcpFullAccessRequired").getValue(null); - preferences.setNewFcpFullAccessRequired( - (fullAccessRequiredInteger == null) ? null : - FullAccessRequired.values()[fullAccessRequiredInteger]); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.kt b/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.kt new file mode 100644 index 0000000..32c35cb --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.kt @@ -0,0 +1,58 @@ +package net.pterodactylus.sone.core + +import net.pterodactylus.sone.fcp.FcpInterface.* +import net.pterodactylus.util.config.* + +/** + * Loads preferences stored in a [Configuration] into a [Preferences] object. + */ +class PreferencesLoader(private val preferences: Preferences) { + + fun loadFrom(configuration: Configuration) { + loadInsertionDelay(configuration) + loadPostsPerPage(configuration) + loadImagesPerPage(configuration) + loadCharactersPerPost(configuration) + loadPostCutOffLength(configuration) + loadRequireFullAccess(configuration) + loadFcpInterfaceActive(configuration) + loadFcpFullAccessRequired(configuration) + } + + private fun loadInsertionDelay(configuration: Configuration) { + preferences.newInsertionDelay = configuration.getIntValue("Option/InsertionDelay").getValue(null) + } + + private fun loadPostsPerPage(configuration: Configuration) { + preferences.newPostsPerPage = configuration.getIntValue("Option/PostsPerPage").getValue(null) + } + + private fun loadImagesPerPage(configuration: Configuration) { + preferences.newImagesPerPage = configuration.getIntValue("Option/ImagesPerPage").getValue(null) + } + + private fun loadCharactersPerPost(configuration: Configuration) { + preferences.newCharactersPerPost = configuration.getIntValue("Option/CharactersPerPost").getValue(null) + } + + private fun loadPostCutOffLength(configuration: Configuration) { + try { + preferences.newPostCutOffLength = configuration.getIntValue("Option/PostCutOffLength").getValue(null) + } catch (iae1: IllegalArgumentException) { /* previous versions allowed -1, ignore and use default. */ + } + } + + private fun loadRequireFullAccess(configuration: Configuration) { + preferences.newRequireFullAccess = configuration.getBooleanValue("Option/RequireFullAccess").getValue(null) + } + + private fun loadFcpInterfaceActive(configuration: Configuration) { + preferences.newFcpInterfaceActive = configuration.getBooleanValue("Option/ActivateFcpInterface").getValue(null) + } + + private fun loadFcpFullAccessRequired(configuration: Configuration) { + val fullAccessRequiredInteger = configuration.getIntValue("Option/FcpFullAccessRequired").getValue(null) + preferences.newFcpFullAccessRequired = fullAccessRequiredInteger?.let { FullAccessRequired.values()[it] } + } + +} diff --git a/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java b/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java index c42b056..2e18347 100644 --- a/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java +++ b/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java @@ -1,6 +1,7 @@ package net.pterodactylus.sone.main; import java.io.File; +import javax.annotation.Nonnull; import net.pterodactylus.sone.template.FilesystemTemplate; import net.pterodactylus.sone.web.pages.ReloadingPage; @@ -21,16 +22,19 @@ public class DebugLoaders implements Loaders { this.filesystemPath = filesystemPath; } + @Nonnull @Override - public Template loadTemplate(String path) { + public Template loadTemplate(@Nonnull String path) { return new FilesystemTemplate(new File(filesystemPath, path).getAbsolutePath()); } + @Nonnull @Override - public Page loadStaticPage(String basePath, String prefix, String mimeType) { + public Page loadStaticPage(@Nonnull String basePath, @Nonnull String prefix, @Nonnull String mimeType) { return new ReloadingPage<>(basePath, new File(filesystemPath, prefix).getAbsolutePath(), mimeType); } + @Nonnull @Override public TemplateProvider getTemplateProvider() { return new FilesystemTemplateProvider(new File(filesystemPath, "/templates/").getAbsolutePath()); diff --git a/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java b/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java index 72d8d19..8e02573 100644 --- a/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java +++ b/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java @@ -6,6 +6,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; +import javax.annotation.Nonnull; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.util.io.Closer; @@ -21,8 +22,9 @@ import net.pterodactylus.util.web.StaticPage; */ public class DefaultLoaders implements Loaders { + @Nonnull @Override - public Template loadTemplate(String path) { + public Template loadTemplate(@Nonnull String path) { InputStream templateInputStream = null; Reader reader = null; try { @@ -37,12 +39,14 @@ public class DefaultLoaders implements Loaders { } } + @Nonnull @Override - public Page loadStaticPage(String pathPrefix, String basePath, String mimeType) { + public Page loadStaticPage(@Nonnull String pathPrefix, @Nonnull String basePath, @Nonnull String mimeType) { return new StaticPage(pathPrefix, basePath, mimeType) { }; } + @Nonnull @Override public TemplateProvider getTemplateProvider() { return new ClassPathTemplateProvider(WebInterface.class, "/templates/"); diff --git a/src/main/java/net/pterodactylus/sone/main/Loaders.java b/src/main/java/net/pterodactylus/sone/main/Loaders.java index 8ee5132..b07118b 100644 --- a/src/main/java/net/pterodactylus/sone/main/Loaders.java +++ b/src/main/java/net/pterodactylus/sone/main/Loaders.java @@ -1,5 +1,7 @@ package net.pterodactylus.sone.main; +import javax.annotation.Nonnull; + import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateProvider; import net.pterodactylus.util.web.Page; @@ -13,8 +15,8 @@ import com.google.inject.ImplementedBy; @ImplementedBy(DefaultLoaders.class) public interface Loaders { - Template loadTemplate(String path); - Page loadStaticPage(String basePath, String prefix, String mimeType); - TemplateProvider getTemplateProvider(); + @Nonnull Template loadTemplate(@Nonnull String path); + @Nonnull Page loadStaticPage(@Nonnull String basePath, @Nonnull String prefix, @Nonnull String mimeType); + @Nonnull TemplateProvider getTemplateProvider(); } diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index 7723db1..a3cb385 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -26,6 +26,7 @@ import net.pterodactylus.sone.core.*; import net.pterodactylus.sone.fcp.*; import net.pterodactylus.sone.freenet.wot.*; import net.pterodactylus.sone.web.*; +import net.pterodactylus.sone.web.notification.NotificationHandler; import freenet.l10n.BaseL10n.*; import freenet.l10n.*; @@ -196,12 +197,16 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /* create the web interface. */ webInterface = injector.getInstance(WebInterface.class); + NotificationHandler notificationHandler = injector.getInstance(NotificationHandler.class); /* start core! */ core.start(); + + /* start the web interface! */ webInterface.start(); webInterface.setFirstStart(injector.getInstance(Key.get(Boolean.class, Names.named("FirstStart")))); webInterface.setNewConfig(injector.getInstance(Key.get(Boolean.class, Names.named("NewConfig")))); + notificationHandler.start(); } @VisibleForTesting diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index e31d9a8..bcc7a11 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -124,7 +124,7 @@ public class WebInterface implements SessionProvider { private final Loaders loaders; /** The notification manager. */ - private final NotificationManager notificationManager = new NotificationManager(); + private final NotificationManager notificationManager; /** The Sone plugin. */ private final SonePlugin sonePlugin; @@ -207,7 +207,8 @@ public class WebInterface implements SessionProvider { ParserFilter parserFilter, ShortenFilter shortenFilter, RenderFilter renderFilter, LinkedElementRenderFilter linkedElementRenderFilter, - PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter) { + PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter, + NotificationManager notificationManager) { this.sonePlugin = sonePlugin; this.loaders = loaders; this.listNotificationFilter = listNotificationFilter; @@ -223,6 +224,7 @@ public class WebInterface implements SessionProvider { this.metricRegistry = metricRegistry; this.l10nFilter = l10nFilter; this.translation = translation; + this.notificationManager = notificationManager; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); soneTextParser = new SoneTextParser(getCore(), getCore()); diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedOnStartup.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedOnStartup.kt new file mode 100644 index 0000000..21c5738 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedOnStartup.kt @@ -0,0 +1,25 @@ +/** + * Sone - SoneLockedOnStartup.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Signals that a Sone was locked on startup because it’s empty. + */ +class SoneLockedOnStartup(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt new file mode 100644 index 0000000..f1c7d3b --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt @@ -0,0 +1,23 @@ +/** + * Sone - Albums.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +/** Returns all images contained in this album and all its albums. */ +val Album.allImages: Collection + get() = + images + albums.flatMap { it.allImages } diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Renderables.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Renderables.kt new file mode 100644 index 0000000..d641bfc --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Renderables.kt @@ -0,0 +1,27 @@ +/** + * Sone - Renderables.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.utils + +import net.pterodactylus.util.io.* +import java.io.* + +/** + * Renders the [Renderable] into a [String]. + */ +fun Renderable.render() = + StringWriter().use { it.also(::render) }.toString() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt b/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt index c996e30..63f3fc6 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt @@ -1,5 +1,6 @@ package net.pterodactylus.sone.web +import com.google.common.eventbus.* import com.google.inject.* import freenet.support.api.* import net.pterodactylus.sone.core.* @@ -10,6 +11,8 @@ import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.main.* import net.pterodactylus.sone.template.* import net.pterodactylus.sone.text.* +import net.pterodactylus.sone.web.notification.* +import net.pterodactylus.util.notify.* import net.pterodactylus.util.template.* import javax.inject.* import javax.inject.Singleton @@ -126,4 +129,14 @@ class WebInterfaceModule : AbstractModule() { @Named("toadletPathPrefix") fun getPathPrefix(): String = "/Sone/" + @Provides + @Singleton + fun getNotificationManager() = + NotificationManager() + + @Provides + @Singleton + fun getNotificationHandler(eventBus: EventBus, loaders: Loaders, notificationManager: NotificationManager) = + NotificationHandler(eventBus, loaders, notificationManager) + } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt index db6e5c5..66c8ed2 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt @@ -3,8 +3,7 @@ package net.pterodactylus.sone.web.ajax import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.SoneOptions import net.pterodactylus.sone.main.SonePlugin -import net.pterodactylus.sone.utils.jsonArray -import net.pterodactylus.sone.utils.jsonObject +import net.pterodactylus.sone.utils.* import net.pterodactylus.sone.web.WebInterface import net.pterodactylus.sone.web.page.* import net.pterodactylus.util.notify.Notification @@ -74,5 +73,3 @@ private val SoneOptions?.asJsonObject "ShowNotification/NewReplies" to options.isShowNewReplyNotifications ) } ?: jsonObject {} - -private fun Notification.render() = StringWriter().use { it.also { render(it) } }.toString() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandler.kt new file mode 100644 index 0000000..7f81f9a --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandler.kt @@ -0,0 +1,36 @@ +/** + * Sone - NotificationHandler.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.main.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for notifications that can create notifications and register them with an event bus. + */ +@Suppress("UnstableApiUsage") +class NotificationHandler @Inject constructor(private val eventBus: EventBus, private val loaders: Loaders, private val notificationManager: NotificationManager) { + + fun start() { + SoneLockedOnStartupHandler(notificationManager, loaders.loadTemplate("/templates/notify/soneLockedOnStartupNotification.html")) + .also(eventBus::register) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandler.kt new file mode 100644 index 0000000..d6ec08f --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandler.kt @@ -0,0 +1,42 @@ +/** + * Sone - SoneLockedOnStartupNotification.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import net.pterodactylus.util.template.* + +/** + * Handler for [SoneLockedOnStartup][net.pterodactylus.sone.core.event.SoneLockedOnStartup] events + * that adds the appropriate notification to the [NotificationManager]. + */ +class SoneLockedOnStartupHandler(private val notificationManager: NotificationManager, template: Template) { + + private val notification = ListNotification("sone-locked-on-startup", "sones", template) + + @Subscribe + @Suppress("UnstableApiUsage") + fun soneLockedOnStartup(soneLockedOnStartup: SoneLockedOnStartup) { + notification.add(soneLockedOnStartup.sone) + notificationManager.addNotification(notification) + } + +} diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties index a2ff8b1..6c91fcd 100644 --- a/src/main/resources/i18n/sone.de.properties +++ b/src/main/resources/i18n/sone.de.properties @@ -461,3 +461,4 @@ Notification.Mention.Text=Sie wurden in diesen Nachrichten erwähnt: Notification.SoneIsInserting.Text=Ihre Sone sone://{0} wird jetzt hoch geladen. Notification.SoneIsInserted.Text=Ihre Sone sone://{0} wurde in {1,number} {1,choice,0#Sekunden|1#Sekunde|1 + <%= Notification.SoneLockedOnStartup.Text|l10n|html> + <%foreach sones sone> + <% sone.niceName|html><%notlast>,<%/notlast><%last>.<%/last> + <%/foreach> + diff --git a/src/test/java/net/pterodactylus/sone/core/PreferencesLoaderTest.java b/src/test/java/net/pterodactylus/sone/core/PreferencesLoaderTest.java deleted file mode 100644 index 72831fc..0000000 --- a/src/test/java/net/pterodactylus/sone/core/PreferencesLoaderTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package net.pterodactylus.sone.core; - -import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import net.pterodactylus.sone.test.TestValue; -import net.pterodactylus.util.config.Configuration; - -import com.google.common.eventbus.EventBus; -import org.junit.Before; -import org.junit.Test; - -/** - * Unit test for {@link PreferencesLoader}. - */ -public class PreferencesLoaderTest { - - private final EventBus eventBus = mock(EventBus.class); - private final Preferences preferences = new Preferences(eventBus); - private final Configuration configuration = mock(Configuration.class); - private final PreferencesLoader preferencesLoader = - new PreferencesLoader(preferences); - - @Before - public void setupConfiguration() { - setupIntValue("InsertionDelay", 15); - setupIntValue("PostsPerPage", 25); - setupIntValue("ImagesPerPage", 12); - setupIntValue("CharactersPerPost", 150); - setupIntValue("PostCutOffLength", 300); - setupBooleanValue("RequireFullAccess", true); - setupBooleanValue("ActivateFcpInterface", true); - setupIntValue("FcpFullAccessRequired", 1); - } - - private void setupIntValue(String optionName, int value) { - when(configuration.getIntValue("Option/" + optionName)).thenReturn( - TestValue.from(value)); - } - - private void setupBooleanValue(String optionName, boolean value) { - when(configuration.getBooleanValue( - "Option/" + optionName)).thenReturn( - TestValue.from(value)); - } - - @Test - public void configurationIsLoadedCorrectly() { - setupConfiguration(); - preferencesLoader.loadFrom(configuration); - assertThat(preferences.getInsertionDelay(), is(15)); - assertThat(preferences.getPostsPerPage(), is(25)); - assertThat(preferences.getImagesPerPage(), is(12)); - assertThat(preferences.getCharactersPerPost(), is(150)); - assertThat(preferences.getPostCutOffLength(), is(300)); - assertThat(preferences.getRequireFullAccess(), is(true)); - assertThat(preferences.getFcpInterfaceActive(), is(true)); - assertThat(preferences.getFcpFullAccessRequired(), is(WRITING)); - } - - @Test - public void configurationIsLoadedCorrectlyWithCutOffLengthMinusOne() { - setupConfiguration(); - setupIntValue("PostCutOffLength", -1); - preferencesLoader.loadFrom(configuration); - assertThat(preferences.getPostCutOffLength(), not(is(-1))); - } - -} diff --git a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt new file mode 100644 index 0000000..0cd7e6d --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt @@ -0,0 +1,61 @@ +package net.pterodactylus.sone.core + +import com.google.common.eventbus.* +import net.pterodactylus.sone.fcp.FcpInterface.* +import net.pterodactylus.util.config.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import org.junit.* + +/** + * Unit test for [PreferencesLoader]. + */ +class PreferencesLoaderTest { + + @Suppress("UnstableApiUsage") + private val eventBus = EventBus() + private val preferences = Preferences(eventBus) + private val configuration = Configuration(MapConfigurationBackend()) + private val preferencesLoader = PreferencesLoader(preferences) + + @Before + fun setupConfiguration() { + setupIntValue("InsertionDelay", 15) + setupIntValue("PostsPerPage", 25) + setupIntValue("ImagesPerPage", 12) + setupIntValue("CharactersPerPost", 150) + setupIntValue("PostCutOffLength", 300) + setupBooleanValue("RequireFullAccess", true) + setupBooleanValue("ActivateFcpInterface", true) + setupIntValue("FcpFullAccessRequired", 1) + } + + private fun setupIntValue(optionName: String, value: Int) { + configuration.getIntValue("Option/$optionName").value = value + } + + private fun setupBooleanValue(optionName: String, value: Boolean) { + configuration.getBooleanValue("Option/$optionName").value = value + } + + @Test + fun `configuration is loaded correctly`() { + preferencesLoader.loadFrom(configuration) + assertThat(preferences.insertionDelay, equalTo(15)) + assertThat(preferences.postsPerPage, equalTo(25)) + assertThat(preferences.imagesPerPage, equalTo(12)) + assertThat(preferences.charactersPerPost, equalTo(150)) + assertThat(preferences.postCutOffLength, equalTo(300)) + assertThat(preferences.requireFullAccess, equalTo(true)) + assertThat(preferences.fcpInterfaceActive, equalTo(true)) + assertThat(preferences.fcpFullAccessRequired, equalTo(FullAccessRequired.WRITING)) + } + + @Test + fun `configuration is loaded correctly with cut off length minus one`() { + setupIntValue("PostCutOffLength", -1) + preferencesLoader.loadFrom(configuration) + assertThat(preferences.postCutOffLength, not(equalTo(-1))) + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt new file mode 100644 index 0000000..9c06a5e --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt @@ -0,0 +1,48 @@ +/** + * Sone - AlbumsTest.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +import net.pterodactylus.sone.data.impl.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import kotlin.test.* + +/** + * Unit test for various helper method in `Albums.kt`. + */ +class AlbumsTest { + + @Test + fun `recursive list of all images for album is returned correctly`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + val firstNestedAlbum = AlbumImpl(sone) + val secondNestedAlbum = AlbumImpl(sone) + firstNestedAlbum.addImage(createImage(sone, "image-1")) + firstNestedAlbum.addImage(createImage(sone, "image-2")) + secondNestedAlbum.addImage(createImage(sone, "image-3")) + album.addImage(createImage(sone, "image-4")) + album.addAlbum(firstNestedAlbum) + album.addAlbum(secondNestedAlbum) + val images = album.allImages + assertThat(images.map(Image::id), containsInAnyOrder("image-1", "image-2", "image-3", "image-4")) + } + + private fun createImage(sone: IdOnlySone, id: String) = ImageImpl(id).modify().setSone(sone).update() + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/main/SonePluginTest.kt b/src/test/kotlin/net/pterodactylus/sone/main/SonePluginTest.kt index 9388cb0..eef312f 100644 --- a/src/test/kotlin/net/pterodactylus/sone/main/SonePluginTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/main/SonePluginTest.kt @@ -10,6 +10,7 @@ import net.pterodactylus.sone.fcp.* import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.web.* +import net.pterodactylus.sone.web.notification.* import org.hamcrest.MatcherAssert.* import org.hamcrest.Matchers.* import org.mockito.Mockito.* @@ -64,6 +65,12 @@ class SonePluginTest { assertThat(injector.getInstance(), notNullValue()) } + @Test + fun `notification handler can be created`() { + val injector: Injector = runSonePluginWithRealInjector() + assertThat(injector.getInstance(), notNullValue()) + } + private fun runSonePluginWithRealInjector(): Injector { lateinit var injector: Injector val sonePlugin = SonePlugin { @@ -83,6 +90,13 @@ class SonePluginTest { verify(core).start() } + @Test + fun `notification handler is being started`() { + sonePlugin.runPlugin(pluginRespirator) + val notificationHandler = injector.getInstance() + verify(notificationHandler).start() + } + } private fun mockInjector() = mock().apply { diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/RenderablesTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/RenderablesTest.kt new file mode 100644 index 0000000..d921270 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/utils/RenderablesTest.kt @@ -0,0 +1,36 @@ +/** + * Sone - RenderablesTest.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.utils + +import net.pterodactylus.util.io.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import kotlin.test.* + +/** + * Unit tests for tools in `Renderables.kt`. + */ +class RenderablesTest { + + @Test + fun `render method renders notification`() { + val notification = Renderable { writer -> writer.use { it.append("Test!\n") } } + assertThat(notification.render(), equalTo("Test!\n")) + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt index 14427ef..2bb31c9 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt @@ -14,7 +14,9 @@ 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.notification.* import net.pterodactylus.sone.web.page.* +import net.pterodactylus.util.notify.* import net.pterodactylus.util.template.* import net.pterodactylus.util.web.* import org.hamcrest.MatcherAssert.* @@ -283,4 +285,23 @@ class WebInterfaceModuleTest { assertThat(injector.getInstance().createPageToadlet(page).path(), startsWith("/Sone/")) } + @Test + fun `notification manager is created as singleton`() { + val firstNotificationManager = injector.getInstance() + val secondNotificationManager = injector.getInstance() + assertThat(firstNotificationManager, sameInstance(secondNotificationManager)) + } + + @Test + fun `notification handler can be created`() { + assertThat(injector.getInstance(), notNullValue()) + } + + @Test + fun `notification handler is created as singleton`() { + val firstNotificationHandler = injector.getInstance() + val secondNotificationHandler = injector.getInstance() + assertThat(firstNotificationHandler, sameInstance(secondNotificationHandler)) + } + } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/notification/NotificationHandlerTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/notification/NotificationHandlerTest.kt new file mode 100644 index 0000000..d145038 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/web/notification/NotificationHandlerTest.kt @@ -0,0 +1,95 @@ +/** + * Sone - NotificationHandlerTest.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import com.google.inject.* +import com.google.inject.Guice.* +import net.pterodactylus.sone.main.* +import net.pterodactylus.sone.test.* +import net.pterodactylus.util.notify.* +import net.pterodactylus.util.template.* +import net.pterodactylus.util.web.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import kotlin.test.* + +/** + * Unit test for [NotificationHandler]. + */ +class NotificationHandlerTest { + + private val eventBus = TestEventBus() + private val loaders = TestLoaders() + private val notificationManager = NotificationManager() + private val handler = NotificationHandler(eventBus, loaders, notificationManager) + + @Test + fun `notification handler can be created by guice`() { + val injector = createInjector( + EventBus::class.isProvidedBy(eventBus), + NotificationManager::class.isProvidedBy(notificationManager), + Loaders::class.isProvidedBy(loaders) + ) + assertThat(injector.getInstance(), notNullValue()) + } + + @Test + fun `notification handler registers handler for sone-locked event`() { + handler.start() + assertThat(eventBus.registeredObjects.any { it.javaClass == SoneLockedOnStartupHandler::class.java }, equalTo(true)) + } + + @Test + fun `notification handler loads sone-locked notification template`() { + handler.start() + assertThat(loaders.requestedTemplatePaths.any { it == "/templates/notify/soneLockedOnStartupNotification.html" }, equalTo(true)) + } + +} + +@Suppress("UnstableApiUsage") +private class TestEventBus : EventBus() { + private val _registeredObjects = mutableListOf() + val registeredObjects: List + get() = _registeredObjects + + override fun register(`object`: Any) { + super.register(`object`) + _registeredObjects += `object` + } + +} + +private class TestLoaders : Loaders { + val requestedTemplatePaths = mutableListOf() + + override fun loadTemplate(path: String) = + Template().also { requestedTemplatePaths += path } + + override fun loadStaticPage(basePath: String, prefix: String, mimeType: String) = object : Page { + + override fun getPath() = "" + override fun isPrefixPage() = false + override fun handleRequest(request: REQ, response: Response) = response + + } + + override fun getTemplateProvider() = TemplateProvider { _, _ -> Template() } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandlerTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandlerTest.kt new file mode 100644 index 0000000..0b9d1e1 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandlerTest.kt @@ -0,0 +1,63 @@ +/** + * Sone - SoneLockedOnStartupNotificationTest.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.impl.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.sone.utils.* +import net.pterodactylus.util.notify.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import kotlin.test.* + +/** + * Unit test for [SoneLockedOnStartupHandler]. + */ +class SoneLockedOnStartupHandlerTest { + + @Suppress("UnstableApiUsage") + private val eventBus = EventBus() + private val manager = NotificationManager() + private val notification by lazy { manager.notifications.single() as ListNotification<*> } + + init { + SoneLockedOnStartupHandler(manager, template).also(eventBus::register) + eventBus.post(SoneLockedOnStartup(sone)) + } + + @Test + fun `notification has correct id`() { + assertThat(notification.id, equalTo("sone-locked-on-startup")) + } + + @Test + fun `handler adds sone to notification when event is posted`() { + assertThat(notification.elements, contains(sone)) + } + + @Test + fun `handler creates notification with correct key`() { + assertThat(notification.render(), equalTo(listOf(sone).toString())) + } + +} + +private val sone = IdOnlySone("sone-id") +private val template = "<% sones>".asTemplate()