From 9fd8201ee2baf94ae7dc0635b7a210b2c34c0b4e Mon Sep 17 00:00:00 2001
From: Arne Babenhauserheide
Date: Sat, 8 Jul 2023 12:42:41 +0200
Subject: [PATCH 1/6] Only accept the 100 most recent posts from the past year
---
.../sone/data/impl/SoneImpl.java | 43 +++++++++++++++++--
.../net/pterodactylus/sone/data/Post.kt | 8 ++++
.../net/pterodactylus/sone/data/Reply.kt | 8 ++++
.../net/pterodactylus/sone/data/SoneTest.kt | 27 ++++++++++++
4 files changed, 83 insertions(+), 3 deletions(-)
diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
index f2f2ea620..9e233be63 100644
--- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
+++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
@@ -21,8 +21,11 @@
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.logging.Logger.getLogger;
+import static java.util.stream.Collectors.toList;
import static net.pterodactylus.sone.data.PostKt.newestPostFirst;
+import static net.pterodactylus.sone.data.PostKt.noOldPost;
import static net.pterodactylus.sone.data.ReplyKt.newestReplyFirst;
+import static net.pterodactylus.sone.data.ReplyKt.noOldReply;
import static net.pterodactylus.sone.data.SoneKt.*;
import java.net.MalformedURLException;
@@ -38,6 +41,8 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
+
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.AlbumKt;
import net.pterodactylus.sone.data.Client;
@@ -367,7 +372,7 @@ public List getPosts() {
sortedPosts = new ArrayList<>(posts);
}
sortedPosts.sort(newestPostFirst());
- return sortedPosts;
+ return sortedPosts;
}
/**
@@ -379,13 +384,29 @@ public List getPosts() {
*/
@Nonnull
public Sone setPosts(@Nonnull Collection posts) {
+ List sortedPosts;
+ synchronized (this) {
+ sortedPosts = new ArrayList<>(posts);
+ }
+ sortedPosts.sort(newestPostFirst());
+ List limitedPosts = this.local
+ ? sortedPosts
+ : filterRemotePosts(sortedPosts);
synchronized (this) {
this.posts.clear();
- this.posts.addAll(posts);
+ this.posts.addAll(limitedPosts);
}
return this;
}
+ @NotNull
+ public static List filterRemotePosts(List sortedPosts) {
+ return sortedPosts.stream()
+ .filter(noOldPost()::invoke)
+ .limit(100)
+ .collect(toList());
+ }
+
/**
* Adds the given post to this Sone. The post will not be added if its {@link
* Post#getSone() Sone} is not this Sone.
@@ -430,11 +451,27 @@ public Set getReplies() {
*/
@Nonnull
public Sone setReplies(@Nonnull Collection replies) {
+ List sortedReplies;
+ synchronized (this) {
+ sortedReplies = new ArrayList<>(replies);
+ }
+ sortedReplies.sort(newestReplyFirst());
+ List limitedReplies = this.local
+ ? sortedReplies
+ : filterRemoteReplies(sortedReplies);
this.replies.clear();
- this.replies.addAll(replies);
+ this.replies.addAll(limitedReplies);
return this;
}
+ @NotNull
+ public static List filterRemoteReplies(List sortedReplies) {
+ return sortedReplies.stream()
+ .filter(noOldReply()::invoke)
+ .limit(100)
+ .collect(toList());
+ }
+
/**
* Adds a reply to this Sone. If the given reply was not made by this Sone,
* nothing is added to this Sone.
diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt
index d87bd3c2e..cbfb62c5d 100644
--- a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt
@@ -1,6 +1,7 @@
package net.pterodactylus.sone.data
import java.util.Comparator.comparing
+import java.util.concurrent.TimeUnit
/**
* Predicate that returns whether a post is _not_ from the future,
@@ -9,6 +10,13 @@ import java.util.Comparator.comparing
@get:JvmName("noFuturePost")
val noFuturePost: (Post) -> Boolean = { it.time <= System.currentTimeMillis() }
+/**
+ * Predicate that returns whether a post less than a year old,
+ * i.e. whether it should be visible now.
+ */
+@get:JvmName("noOldPost")
+val noOldPost: (Post) -> Boolean = { it.time > (System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(365))) }
+
/**
* Comparator that orders posts by their time, newest posts first.
*/
diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt
index cfc940a20..f9403b84c 100644
--- a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt
@@ -18,6 +18,7 @@
package net.pterodactylus.sone.data
import java.util.Comparator.comparing
+import java.util.concurrent.TimeUnit
/**
* Comparator that orders replies by their time, newest replies first.
@@ -26,6 +27,13 @@ import java.util.Comparator.comparing
val newestReplyFirst: Comparator> =
comparing(Reply<*>::getTime).reversed()
+/**
+ * Predicate that returns whether a reply less than a year old,
+ * i.e. whether it should be visible now.
+ */
+@get:JvmName("noOldReply")
+val noOldReply: (Reply<*>) -> Boolean = { it.getTime() > (System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(365))) }
+
/**
* Predicate that returns whether a reply is _not_ from the future,
* i.e. whether it should be visible now.
diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt
index fb2312125..8ffe54f1d 100644
--- a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt
+++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt
@@ -18,9 +18,12 @@
package net.pterodactylus.sone.data
import net.pterodactylus.sone.data.impl.*
+import net.pterodactylus.sone.data.impl.SoneImpl.filterRemotePosts
+import net.pterodactylus.sone.data.impl.SoneImpl.filterRemoteReplies
import net.pterodactylus.sone.test.*
import org.hamcrest.MatcherAssert.*
import org.hamcrest.Matchers.*
+import java.util.concurrent.TimeUnit
import kotlin.test.*
/**
@@ -120,6 +123,30 @@ class SoneTest {
assertThat(postCountComparator.compare(sone1, sone2), equalTo(0))
}
+ @Test
+ fun `post filtering skips old posts`() {
+ var oldPosts = listOf(createPost(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(400)));
+ assertThat(filterRemotePosts(oldPosts), empty())
+ }
+
+ @Test
+ fun `post filtering keeps recent posts`() {
+ var recentPosts = listOf(createPost(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(300)));
+ assertThat(filterRemotePosts(recentPosts), hasSize(recentPosts.size));
+ }
+
+ @Test
+ fun `reply filtering skips old replies`() {
+ var oldReplies = listOf(emptyPostReply(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(400)));
+ assertThat(filterRemoteReplies(oldReplies), empty())
+ }
+
+ @Test
+ fun `reply filtering keeps recent replies`() {
+ var recentReplies = listOf(emptyPostReply(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(300)));
+ assertThat(filterRemoteReplies(recentReplies), hasSize(recentReplies.size))
+ }
+
@Test
fun `image count comparator sorts Sones correctly if number of images is different`() {
val sone1 = object : IdOnlySone("1") {
From 5ed3ca7f6678dc2d1c64851779d9786d36c4393e Mon Sep 17 00:00:00 2001
From: Arne Babenhauserheide
Date: Sat, 15 Jul 2023 15:04:32 +0200
Subject: [PATCH 2/6] Add GUI option for download backwards limit and count
limit
---
.../pterodactylus/sone/core/Preferences.kt | 24 ++++++++++
.../sone/core/PreferencesLoader.kt | 10 +++++
.../DownloadBackwardsLimitChangedEvent.kt | 3 ++
.../event/DownloadCountLimitChangedEvent.kt | 3 ++
.../sone/web/pages/OptionsPage.kt | 6 +++
src/main/resources/i18n/sone.de.properties | 2 +
src/main/resources/i18n/sone.en.properties | 2 +
src/main/resources/templates/options.html | 18 ++++++++
.../sone/web/pages/OptionsPageTest.kt | 44 +++++++++++++++++++
9 files changed, 112 insertions(+)
create mode 100644 src/main/kotlin/net/pterodactylus/sone/core/event/DownloadBackwardsLimitChangedEvent.kt
create mode 100644 src/main/kotlin/net/pterodactylus/sone/core/event/DownloadCountLimitChangedEvent.kt
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt
index fe1e3c3ec..36364fab5 100644
--- a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt
@@ -21,6 +21,8 @@ import com.google.common.eventbus.EventBus
import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent
import net.pterodactylus.sone.core.event.StrictFilteringActivatedEvent
import net.pterodactylus.sone.core.event.StrictFilteringDeactivatedEvent
+import net.pterodactylus.sone.core.event.DownloadBackwardsLimitChangedEvent
+import net.pterodactylus.sone.core.event.DownloadCountLimitChangedEvent
import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired
import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS
import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent
@@ -47,6 +49,26 @@ class Preferences(private val eventBus: EventBus) {
eventBus.post(PreferenceChangedEvent("InsertionDelay", insertionDelay))
}
+ private val _downloadBackwardsLimit = DefaultOption(365) { it in -1..MAX_VALUE }
+ val downloadBackwardsLimit: Int get() = _downloadBackwardsLimit.get()
+ var newDownloadBackwardsLimit: Int?
+ get() = unsupported
+ set(value) {
+ _downloadBackwardsLimit.set(value)
+ eventBus.post(DownloadBackwardsLimitChangedEvent(downloadBackwardsLimit))
+ eventBus.post(PreferenceChangedEvent("downloadBackwardsLimit", downloadBackwardsLimit))
+ }
+
+ private val _downloadCountLimit = DefaultOption(100) { it in -1..MAX_VALUE }
+ val downloadCountLimit: Int get() = _downloadCountLimit.get()
+ var newDownloadCountLimit: Int?
+ get() = unsupported
+ set(value) {
+ _downloadCountLimit.set(value)
+ eventBus.post(DownloadCountLimitChangedEvent(downloadCountLimit))
+ eventBus.post(PreferenceChangedEvent("downloadCountLimit", downloadCountLimit))
+ }
+
private val _postsPerPage = DefaultOption(10) { it in 1..MAX_VALUE }
val postsPerPage: Int get() = _postsPerPage.get()
var newPostsPerPage: Int?
@@ -116,6 +138,8 @@ class Preferences(private val eventBus: EventBus) {
fun saveTo(configuration: Configuration) {
configuration.getIntValue("Option/ConfigurationVersion").value = 0
configuration.getIntValue("Option/InsertionDelay").value = _insertionDelay.real
+ configuration.getIntValue("Option/DownloadBackwardsLimit").value = _downloadBackwardsLimit.real
+ configuration.getIntValue("Option/DownloadCountLimit").value = _downloadCountLimit.real
configuration.getIntValue("Option/PostsPerPage").value = _postsPerPage.real
configuration.getIntValue("Option/ImagesPerPage").value = _imagesPerPage.real
configuration.getIntValue("Option/CharactersPerPost").value = _charactersPerPost.real
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt
index 62b60ae9d..2b71a8e93 100644
--- a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt
@@ -10,6 +10,8 @@ class PreferencesLoader(private val preferences: Preferences) {
fun loadFrom(configuration: Configuration) {
loadInsertionDelay(configuration)
+ loadDownloadBackwardsLimit(configuration)
+ loadDownloadCountLimit(configuration)
loadPostsPerPage(configuration)
loadImagesPerPage(configuration)
loadCharactersPerPost(configuration)
@@ -24,6 +26,14 @@ class PreferencesLoader(private val preferences: Preferences) {
preferences.newInsertionDelay = configuration.getIntValue("Option/InsertionDelay").getValue(null)
}
+ private fun loadDownloadBackwardsLimit(configuration: Configuration) {
+ preferences.newDownloadBackwardsLimit = configuration.getIntValue("Option/DownloadBackwardsLimit").getValue(null)
+ }
+
+ private fun loadDownloadCountLimit(configuration: Configuration) {
+ preferences.newDownloadCountLimit = configuration.getIntValue("Option/DownloadCountLimit").getValue(null)
+ }
+
private fun loadPostsPerPage(configuration: Configuration) {
preferences.newPostsPerPage = configuration.getIntValue("Option/PostsPerPage").getValue(null)
}
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/DownloadBackwardsLimitChangedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/DownloadBackwardsLimitChangedEvent.kt
new file mode 100644
index 000000000..c9d7e2224
--- /dev/null
+++ b/src/main/kotlin/net/pterodactylus/sone/core/event/DownloadBackwardsLimitChangedEvent.kt
@@ -0,0 +1,3 @@
+package net.pterodactylus.sone.core.event
+
+data class DownloadBackwardsLimitChangedEvent(val insertionDelay: Int)
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/DownloadCountLimitChangedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/DownloadCountLimitChangedEvent.kt
new file mode 100644
index 000000000..0109f6c37
--- /dev/null
+++ b/src/main/kotlin/net/pterodactylus/sone/core/event/DownloadCountLimitChangedEvent.kt
@@ -0,0 +1,3 @@
+package net.pterodactylus.sone.core.event
+
+data class DownloadCountLimitChangedEvent(val insertionDelay: Int)
diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt
index 9465a6a28..c9f09255e 100644
--- a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt
@@ -51,6 +51,8 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
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 downloadBackwardsLimit = soneRequest.parameters["download-backwards-limit"]?.toIntOrNull()
+ val downloadCountLimit = soneRequest.parameters["download-count-limit"]?.toIntOrNull()
val fcpFullAccessRequired = soneRequest.parameters["fcp-full-access-required"]?.toIntOrNull()
if (cantSetOption { soneRequest.core.preferences.newPostsPerPage = postsPerPage }) fieldsWithErrors += "posts-per-page"
@@ -58,6 +60,8 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
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"
+ if (cantSetOption { soneRequest.core.preferences.newDownloadBackwardsLimit = downloadBackwardsLimit }) fieldsWithErrors += "download-backwards-limit"
+ if (cantSetOption { soneRequest.core.preferences.newDownloadCountLimit = downloadCountLimit }) fieldsWithErrors += "download-count-limit"
fcpFullAccessRequired?.also { if (cantSetOption { soneRequest.core.preferences.newFcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequired] }) fieldsWithErrors += "fcp-full-access-required" }
if (fieldsWithErrors.isEmpty()) {
@@ -77,6 +81,8 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
}
soneRequest.core.preferences.let { preferences ->
templateContext["insertion-delay"] = preferences.insertionDelay
+ templateContext["download-backwards-limit"] = preferences.downloadBackwardsLimit
+ templateContext["download-count-limit"] = preferences.downloadCountLimit
templateContext["characters-per-post"] = preferences.charactersPerPost
templateContext["fcp-full-access-required"] = preferences.fcpFullAccessRequired.ordinal
templateContext["images-per-page"] = preferences.imagesPerPage
diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties
index cd42c458a..13ed14ff9 100644
--- a/src/main/resources/i18n/sone.de.properties
+++ b/src/main/resources/i18n/sone.de.properties
@@ -63,6 +63,8 @@ Page.Options.Option.LoadLinkedImages.Trusted.Description=Nur Bilder aus Nachrich
Page.Options.Option.LoadLinkedImages.Always.Description=Immer verlinkte Bilder laden. Warnung: Verlinkte Bilder können beliebiges Bildmaterial enthalten!
Page.Options.Section.RuntimeOptions.Title=Laufzeitverhalten
Page.Options.Option.InsertionDelay.Description=Anzahl der Sekunden, die vor dem Hochladen einer Sone nach einer Änderung gewartet wird.
+Page.Options.Option.DownloadBackwardsLimit.Description=Maximalalter von Nachrichten und Antworten (-1 to disable). Ältere werden von nicht-lokalen Sones verworfen. Betrifft nur zukünftige Downloads.
+Page.Options.Option.DownloadCountLimit.Description=Maximalzahl Nachrichten und Antworten, die je entfernter Sone geladen werden (-1 für beliebig viele), neuste zuerst. Nachrichten und Antworten werden unabhängig gezählt. Betrifft nur zukünftige Downloads.
Page.Options.Option.PostsPerPage.Description=Anzahl der Nachrichten pro Seite.
Page.Options.Option.ImagesPerPage.Description=Anzahl der Bilder pro Seite.
Page.Options.Option.CharactersPerPost.Description=Die Anzahl der Zeichen, die eine Nachricht enthalten muss, damit sie gekürzt angezeigt wird (-1 für „nie kürzen“). Die Anzahl der tatsächlich angezeigten Zeichen wird in der nächsten Option konfiguriert.
diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties
index 7c65dcf84..6e77b35ba 100644
--- a/src/main/resources/i18n/sone.en.properties
+++ b/src/main/resources/i18n/sone.en.properties
@@ -63,6 +63,8 @@ Page.Options.Option.LoadLinkedImages.Trusted.Description=Only load images linked
Page.Options.Option.LoadLinkedImages.Always.Description=Always load linked images. Be warned: some images might be disturbing or considered offensive.
Page.Options.Section.RuntimeOptions.Title=Runtime Behaviour
Page.Options.Option.InsertionDelay.Description=The number of seconds the Sone inserter waits after a modification of a Sone before it is being inserted.
+Page.Options.Option.DownloadBackwardsLimit.Description=The maximum age in days of posts and replies (-1 to disable). Older posts and replies are discarded for non-local sones. Affects only newly downloaded Sones.
+Page.Options.Option.DownloadCountLimit.Description=The number of posts and replies to retrieve per non-local Sone (-1 to disable), newest first. Posts and replies are counted independently. Affects only newly downloaded Sones.
Page.Options.Option.PostsPerPage.Description=The number of posts to display on a page before pagination controls are being shown.
Page.Options.Option.ImagesPerPage.Description=The number of images to display on a page before pagination controls are being shown.
Page.Options.Option.CharactersPerPost.Description=The number of characters to display from a post before cutting it off and showing a link to expand it (-1 to disable). The actual length of the snippet is determined by the option below.
diff --git a/src/main/resources/templates/options.html b/src/main/resources/templates/options.html
index 9ef5ceb70..beec5b748 100644
--- a/src/main/resources/templates/options.html
+++ b/src/main/resources/templates/options.html
@@ -5,6 +5,12 @@
getTranslation("WebInterface.DefaultText.Option.InsertionDelay", function(insertionDelayDefaultText) {
registerInputTextareaSwap("#sone #options input[name=insertion-delay]", insertionDelayDefaultText, "insertion-delay", true, true);
});
+ getTranslation("WebInterface.DefaultText.Option.DownloadBackwardsLimit", function(downloadBackwardsLimitDefaultText) {
+ registerInputTextareaSwap("#sone #options input[name=download-backwards-limit]", downloadBackwardsLimitDefaultText, "download-backwards-limit", true, true);
+ });
+ getTranslation("WebInterface.DefaultText.Option.DownloadCountLimit", function(downloadCountLimitDefaultText) {
+ registerInputTextareaSwap("#sone #options input[name=download-count-limit]", downloadCountLimitDefaultText, "download-count-limit", true, true);
+ });
getTranslation("WebInterface.DefaultText.Option.PostsPerPage", function(postsPerPageText) {
registerInputTextareaSwap("#sone #options input[name=posts-per-page]", postsPerPageText, "posts-per-page", true, true);
});
@@ -122,6 +128,18 @@ <%= Page.Options.Section.RuntimeOptions.Title|l10n|html>
<%/if>
+ <%= Page.Options.Option.DownloadBackwardsLimit.Description|l10n|html>
+ <%if =download-backwards-limit|in collection=fieldErrors>
+ <%= Page.Options.Warnings.ValueNotChanged|l10n|html>
+ <%/if>
+
+
+ <%= Page.Options.Option.DownloadCountLimit.Description|l10n|html>
+ <%if =download-count-limit|in collection=fieldErrors>
+ <%= Page.Options.Warnings.ValueNotChanged|l10n|html>
+ <%/if>
+
+
<%= Page.Options.Option.PostsPerPage.Description|l10n|html>
<%if =posts-per-page|in collection=fieldErrors>
<%= Page.Options.Warnings.ValueNotChanged|l10n|html>
diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
index d679e85f8..0bf1066b5 100644
--- a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
+++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
@@ -20,6 +20,8 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
@Before
fun setupPreferences() {
core.preferences.newInsertionDelay = 1
+ core.preferences.newDownloadBackwardsLimit = 30
+ core.preferences.newDownloadCountLimit = 10
core.preferences.newCharactersPerPost = 50
core.preferences.newFcpFullAccessRequired = WRITING
core.preferences.newImagesPerPage = 4
@@ -69,6 +71,8 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
assertThat(templateContext["enable-sone-insert-notifications"], equalTo(true))
assertThat(templateContext["load-linked-images"], equalTo("FOLLOWED"))
assertThat(templateContext["show-custom-avatars"], equalTo("FOLLOWED"))
+ assertThat(templateContext["download-backwards-limit"], equalTo(30))
+ assertThat(templateContext["download-count-limit"], equalTo(10))
assertThat(templateContext["insertion-delay"], equalTo(1))
assertThat(templateContext["characters-per-post"], equalTo(50))
assertThat(templateContext["fcp-full-access-required"], equalTo(1))
@@ -199,6 +203,46 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
verifyThatPreferencesCanBeSet("insertion-delay", "foo", 60) { core.preferences.insertionDelay }
}
+ @Test
+ fun `download backwards limit can not be set to less than -1 days`() {
+ verifyThatWrongValueForPreferenceIsDetected("download-backwards-limit", "-2")
+ }
+
+ @Test
+ fun `download backwards limit can be set to 0 days`() {
+ verifyThatPreferencesCanBeSet("download-backwards-limit", "0", 0) { core.preferences.downloadBackwardsLimit }
+ }
+
+ @Test
+ fun `download backwards limit can be set to -1 days`() {
+ verifyThatPreferencesCanBeSet("download-backwards-limit", "-1", -1) { core.preferences.downloadBackwardsLimit }
+ }
+
+ @Test
+ fun `setting download backwards limit to an invalid value will reset it`() {
+ verifyThatPreferencesCanBeSet("download-backwards-limit", "foo", 365) { core.preferences.downloadBackwardsLimit }
+ }
+
+ @Test
+ fun `download count limit can not be set to less than -1 posts or replies`() {
+ verifyThatWrongValueForPreferenceIsDetected("download-count-limit", "-2")
+ }
+
+ @Test
+ fun `download count limit can be set to -1 posts or replies`() {
+ verifyThatPreferencesCanBeSet("download-count-limit", "-1", -1) { core.preferences.downloadCountLimit }
+ }
+
+ @Test
+ fun `download count limit can be set to 0 posts or replies`() {
+ verifyThatPreferencesCanBeSet("download-count-limit", "0", 0) { core.preferences.downloadCountLimit }
+ }
+
+ @Test
+ fun `setting download count limit to an invalid value will reset it`() {
+ verifyThatPreferencesCanBeSet("download-count-limit", "foo", 100) { core.preferences.downloadCountLimit }
+ }
+
@Test
fun `characters per post can not be set to less than -1`() {
verifyThatWrongValueForPreferenceIsDetected("characters-per-post", "-2")
From 8f79754a874d42e4b6dd7b53d3bc97ceaf1eb794 Mon Sep 17 00:00:00 2001
From: Arne Babenhauserheide
Date: Sat, 15 Jul 2023 15:05:06 +0200
Subject: [PATCH 3/6] Add missing "delay" in test description
---
.../kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
index 0bf1066b5..921c89311 100644
--- a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
+++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
@@ -199,7 +199,7 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
}
@Test
- fun `setting insertion to an invalid value will reset it`() {
+ fun `setting insertion delay to an invalid value will reset it`() {
verifyThatPreferencesCanBeSet("insertion-delay", "foo", 60) { core.preferences.insertionDelay }
}
From 21c1608357eb209a311701412397231295617311 Mon Sep 17 00:00:00 2001
From: Arne Babenhauserheide
Date: Thu, 4 Jul 2024 21:27:34 +0200
Subject: [PATCH 4/6] Connect config and post/reply limiting
Also add missing default text for the options
---
.../net/pterodactylus/sone/core/Core.java | 4 +++
.../net/pterodactylus/sone/data/Sone.java | 8 +++++
.../pterodactylus/sone/data/SoneOptions.java | 29 ++++++++++++++--
.../sone/data/impl/IdOnlySone.java | 24 +++++++++++++
.../sone/data/impl/SoneImpl.java | 34 ++++++++++++-------
.../pterodactylus/sone/core/Preferences.kt | 22 ------------
.../sone/core/PreferencesLoader.kt | 10 ------
.../net/pterodactylus/sone/data/Post.kt | 6 ++--
.../net/pterodactylus/sone/data/Reply.kt | 6 ++--
.../sone/web/pages/OptionsPage.kt | 22 ++++++++----
.../sone/web/pages/SearchPage.kt | 7 ++--
src/main/resources/i18n/sone.de.properties | 2 ++
src/main/resources/i18n/sone.en.properties | 2 ++
src/main/resources/templates/options.html | 24 ++++++-------
.../net/pterodactylus/sone/data/SoneTest.kt | 18 ++++++----
.../sone/web/pages/OptionsPageTest.kt | 16 ++++-----
16 files changed, 149 insertions(+), 85 deletions(-)
diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java
index da1b2f853..7b375f6e2 100644
--- a/src/main/java/net/pterodactylus/sone/core/Core.java
+++ b/src/main/java/net/pterodactylus/sone/core/Core.java
@@ -951,6 +951,8 @@ public void loadSone(Sone sone) {
sone.getOptions().setShowNewSoneNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewSones").getValue(true));
sone.getOptions().setShowNewPostNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").getValue(true));
sone.getOptions().setShowNewReplyNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").getValue(true));
+ sone.getOptions().setDownloadBackwardsLimitDays(configuration.getIntValue(sonePrefix + "/Options/DownloadBackwardsLimit").getValue(365));
+ sone.getOptions().setDownloadCountLimit(configuration.getIntValue(sonePrefix + "/Options/DownloadCountLimit").getValue(100));
sone.getOptions().setShowCustomAvatars(LoadExternalContent.valueOf(configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").getValue(LoadExternalContent.NEVER.name())));
sone.getOptions().setLoadLinkedImages(LoadExternalContent.valueOf(configuration.getStringValue(sonePrefix + "/Options/LoadLinkedImages").getValue(LoadExternalContent.NEVER.name())));
@@ -1430,6 +1432,8 @@ private synchronized void saveSone(Sone sone) {
configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewSones").setValue(sone.getOptions().isShowNewSoneNotifications());
configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").setValue(sone.getOptions().isShowNewPostNotifications());
configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").setValue(sone.getOptions().isShowNewReplyNotifications());
+ configuration.getIntValue(sonePrefix + "/Options/DownloadBackwardsLimit").setValue(sone.getOptions().getDownloadBackwardsLimitDays());
+ configuration.getIntValue(sonePrefix + "/Options/DownloadCountLimit").setValue(sone.getOptions().getDownloadCountLimit());
configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").setValue(sone.getOptions().getShowCustomAvatars().name());
configuration.getStringValue(sonePrefix + "/Options/LoadLinkedImages").setValue(sone.getOptions().getLoadLinkedImages().name());
diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java
index 2b0a2eb42..75137d20a 100644
--- a/src/main/java/net/pterodactylus/sone/data/Sone.java
+++ b/src/main/java/net/pterodactylus/sone/data/Sone.java
@@ -24,6 +24,8 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
+
import net.pterodactylus.sone.freenet.wot.Identity;
import freenet.keys.FreenetURI;
@@ -228,6 +230,9 @@ public enum SoneStatus {
@Nonnull
Sone setPosts(@Nonnull Collection posts);
+ @NotNull
+ List filterRemotePosts(List sortedPosts);
+
/**
* Adds the given post to this Sone. The post will not be added if its {@link
* Post#getSone() Sone} is not this Sone.
@@ -263,6 +268,9 @@ public enum SoneStatus {
@Nonnull
Sone setReplies(@Nonnull Collection replies);
+ @NotNull
+ List filterRemoteReplies(List sortedReplies);
+
/**
* Adds a reply to this Sone. If the given reply was not made by this Sone,
* nothing is added to this Sone.
diff --git a/src/main/java/net/pterodactylus/sone/data/SoneOptions.java b/src/main/java/net/pterodactylus/sone/data/SoneOptions.java
index d7089d966..8e53f98a0 100644
--- a/src/main/java/net/pterodactylus/sone/data/SoneOptions.java
+++ b/src/main/java/net/pterodactylus/sone/data/SoneOptions.java
@@ -23,6 +23,10 @@ public interface SoneOptions {
boolean isShowNewReplyNotifications();
void setShowNewReplyNotifications(boolean showNewReplyNotifications);
+ int getDownloadBackwardsLimitDays();
+ void setDownloadBackwardsLimitDays(int days);
+ int getDownloadCountLimit();
+ void setDownloadCountLimit(int count);
LoadExternalContent getShowCustomAvatars();
void setShowCustomAvatars(LoadExternalContent showCustomAvatars);
@@ -62,9 +66,10 @@ public class DefaultSoneOptions implements SoneOptions {
private boolean showNewSoneNotifications = true;
private boolean showNewPostNotifications = true;
private boolean showNewReplyNotifications = true;
+ private int downloadBackwardsLimitDays = 365;
+ private int downloadCountLimitDays = 100;
private LoadExternalContent showCustomAvatars = NEVER;
private LoadExternalContent loadLinkedImages = NEVER;
-
@Override
public boolean isAutoFollow() {
return autoFollow;
@@ -115,6 +120,27 @@ public void setShowNewReplyNotifications(boolean showNewReplyNotifications) {
this.showNewReplyNotifications = showNewReplyNotifications;
}
+ @Override
+ public int getDownloadBackwardsLimitDays() {
+ return downloadBackwardsLimitDays;
+ }
+
+ @Override
+ public void setDownloadBackwardsLimitDays(int downloadBackwardsLimitDays) {
+ this.downloadBackwardsLimitDays = downloadBackwardsLimitDays;
+ }
+
+ @Override
+ public int getDownloadCountLimit() {
+ return downloadCountLimitDays;
+ }
+
+ @Override
+ public void setDownloadCountLimit(int count) {
+ this.downloadCountLimitDays = count;
+
+ }
+
@Override
public LoadExternalContent getShowCustomAvatars() {
return showCustomAvatars;
@@ -135,7 +161,6 @@ public LoadExternalContent getLoadLinkedImages() {
public void setLoadLinkedImages(@Nonnull LoadExternalContent loadLinkedImages) {
this.loadLinkedImages = loadLinkedImages;
}
-
}
}
diff --git a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java
index ddd96b951..6e8bdd7b1 100644
--- a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java
+++ b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java
@@ -7,10 +7,16 @@
import java.util.List;
import java.util.Set;
+import org.jetbrains.annotations.NotNull;
+
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Client;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
+
+import static java.util.stream.Collectors.toList;
+import static net.pterodactylus.sone.data.PostKt.noOldPost;
+import static net.pterodactylus.sone.data.ReplyKt.noOldReply;
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.data.SoneOptions;
@@ -130,6 +136,15 @@ public Sone setPosts(Collection posts) {
return this;
}
+ @NotNull
+ @Override
+ public List filterRemotePosts(List sortedPosts) {
+ return sortedPosts.stream()
+ .filter(post -> noOldPost().invoke(post, this.getOptions().getDownloadBackwardsLimitDays()))
+ .limit(this.getOptions().getDownloadCountLimit())
+ .collect(toList());
+ }
+
@Override
public void addPost(Post post) {
}
@@ -148,6 +163,15 @@ public Sone setReplies(Collection replies) {
return this;
}
+ @NotNull
+ @Override
+ public List filterRemoteReplies(List sortedReplies) {
+ return sortedReplies.stream()
+ .filter(reply -> noOldReply().invoke(reply, this.getOptions().getDownloadBackwardsLimitDays()))
+ .limit(this.getOptions().getDownloadCountLimit())
+ .collect(toList());
+ }
+
@Override
public void addReply(PostReply reply) {
}
diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
index 9e233be63..5a072ed0f 100644
--- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
+++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
@@ -389,9 +389,9 @@ public Sone setPosts(@Nonnull Collection posts) {
sortedPosts = new ArrayList<>(posts);
}
sortedPosts.sort(newestPostFirst());
- List limitedPosts = this.local
- ? sortedPosts
- : filterRemotePosts(sortedPosts);
+ List limitedPosts = this.local
+ ? sortedPosts
+ : this.filterRemotePosts(sortedPosts);
synchronized (this) {
this.posts.clear();
this.posts.addAll(limitedPosts);
@@ -399,11 +399,20 @@ public Sone setPosts(@Nonnull Collection posts) {
return this;
}
+ /**
+ * Filters posts of downloaded Sones.
+ *
+ * @param sortedPosts
+ * The new (and only) posts of this Sone
+ *
+ * @return posts limited by download-count-limit and may time backwards.
+ * */
+ @Override
@NotNull
- public static List filterRemotePosts(List sortedPosts) {
+ public List filterRemotePosts(List sortedPosts) {
return sortedPosts.stream()
- .filter(noOldPost()::invoke)
- .limit(100)
+ .filter(post -> noOldPost().invoke(post, this.getOptions().getDownloadBackwardsLimitDays()))
+ .limit(this.getOptions().getDownloadCountLimit())
.collect(toList());
}
@@ -456,19 +465,20 @@ public Sone setReplies(@Nonnull Collection replies) {
sortedReplies = new ArrayList<>(replies);
}
sortedReplies.sort(newestReplyFirst());
- List limitedReplies = this.local
- ? sortedReplies
- : filterRemoteReplies(sortedReplies);
+ List limitedReplies = this.local
+ ? sortedReplies
+ : this.filterRemoteReplies(sortedReplies);
this.replies.clear();
this.replies.addAll(limitedReplies);
return this;
}
+ @Override
@NotNull
- public static List filterRemoteReplies(List sortedReplies) {
+ public List filterRemoteReplies(List sortedReplies) {
return sortedReplies.stream()
- .filter(noOldReply()::invoke)
- .limit(100)
+ .filter(reply -> noOldReply().invoke(reply, this.getOptions().getDownloadBackwardsLimitDays()))
+ .limit(this.getOptions().getDownloadCountLimit())
.collect(toList());
}
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt
index 36364fab5..9d9d04b8c 100644
--- a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt
@@ -49,26 +49,6 @@ class Preferences(private val eventBus: EventBus) {
eventBus.post(PreferenceChangedEvent("InsertionDelay", insertionDelay))
}
- private val _downloadBackwardsLimit = DefaultOption(365) { it in -1..MAX_VALUE }
- val downloadBackwardsLimit: Int get() = _downloadBackwardsLimit.get()
- var newDownloadBackwardsLimit: Int?
- get() = unsupported
- set(value) {
- _downloadBackwardsLimit.set(value)
- eventBus.post(DownloadBackwardsLimitChangedEvent(downloadBackwardsLimit))
- eventBus.post(PreferenceChangedEvent("downloadBackwardsLimit", downloadBackwardsLimit))
- }
-
- private val _downloadCountLimit = DefaultOption(100) { it in -1..MAX_VALUE }
- val downloadCountLimit: Int get() = _downloadCountLimit.get()
- var newDownloadCountLimit: Int?
- get() = unsupported
- set(value) {
- _downloadCountLimit.set(value)
- eventBus.post(DownloadCountLimitChangedEvent(downloadCountLimit))
- eventBus.post(PreferenceChangedEvent("downloadCountLimit", downloadCountLimit))
- }
-
private val _postsPerPage = DefaultOption(10) { it in 1..MAX_VALUE }
val postsPerPage: Int get() = _postsPerPage.get()
var newPostsPerPage: Int?
@@ -138,8 +118,6 @@ class Preferences(private val eventBus: EventBus) {
fun saveTo(configuration: Configuration) {
configuration.getIntValue("Option/ConfigurationVersion").value = 0
configuration.getIntValue("Option/InsertionDelay").value = _insertionDelay.real
- configuration.getIntValue("Option/DownloadBackwardsLimit").value = _downloadBackwardsLimit.real
- configuration.getIntValue("Option/DownloadCountLimit").value = _downloadCountLimit.real
configuration.getIntValue("Option/PostsPerPage").value = _postsPerPage.real
configuration.getIntValue("Option/ImagesPerPage").value = _imagesPerPage.real
configuration.getIntValue("Option/CharactersPerPost").value = _charactersPerPost.real
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt
index 2b71a8e93..62b60ae9d 100644
--- a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt
@@ -10,8 +10,6 @@ class PreferencesLoader(private val preferences: Preferences) {
fun loadFrom(configuration: Configuration) {
loadInsertionDelay(configuration)
- loadDownloadBackwardsLimit(configuration)
- loadDownloadCountLimit(configuration)
loadPostsPerPage(configuration)
loadImagesPerPage(configuration)
loadCharactersPerPost(configuration)
@@ -26,14 +24,6 @@ class PreferencesLoader(private val preferences: Preferences) {
preferences.newInsertionDelay = configuration.getIntValue("Option/InsertionDelay").getValue(null)
}
- private fun loadDownloadBackwardsLimit(configuration: Configuration) {
- preferences.newDownloadBackwardsLimit = configuration.getIntValue("Option/DownloadBackwardsLimit").getValue(null)
- }
-
- private fun loadDownloadCountLimit(configuration: Configuration) {
- preferences.newDownloadCountLimit = configuration.getIntValue("Option/DownloadCountLimit").getValue(null)
- }
-
private fun loadPostsPerPage(configuration: Configuration) {
preferences.newPostsPerPage = configuration.getIntValue("Option/PostsPerPage").getValue(null)
}
diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt
index cbfb62c5d..3a654f0e0 100644
--- a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt
@@ -1,7 +1,8 @@
package net.pterodactylus.sone.data
import java.util.Comparator.comparing
-import java.util.concurrent.TimeUnit
+import kotlin.time.ExperimentalTime
+import kotlin.time.days
/**
* Predicate that returns whether a post is _not_ from the future,
@@ -14,8 +15,9 @@ val noFuturePost: (Post) -> Boolean = { it.time <= System.currentTimeMillis() }
* Predicate that returns whether a post less than a year old,
* i.e. whether it should be visible now.
*/
+@OptIn(ExperimentalTime::class)
@get:JvmName("noOldPost")
-val noOldPost: (Post) -> Boolean = { it.time > (System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(365))) }
+val noOldPost: (Post, Int) -> Boolean = { p: Post, days: Int -> p.time > (System.currentTimeMillis() - days.days.inMilliseconds) }
/**
* Comparator that orders posts by their time, newest posts first.
diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt
index f9403b84c..73c34de0a 100644
--- a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt
@@ -18,7 +18,8 @@
package net.pterodactylus.sone.data
import java.util.Comparator.comparing
-import java.util.concurrent.TimeUnit
+import kotlin.time.ExperimentalTime
+import kotlin.time.days
/**
* Comparator that orders replies by their time, newest replies first.
@@ -31,8 +32,9 @@ val newestReplyFirst: Comparator> =
* Predicate that returns whether a reply less than a year old,
* i.e. whether it should be visible now.
*/
+@OptIn(ExperimentalTime::class)
@get:JvmName("noOldReply")
-val noOldReply: (Reply<*>) -> Boolean = { it.getTime() > (System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(365))) }
+val noOldReply: (Reply<*>, Int) -> Boolean = { r: Reply<*>, days: Int -> r.getTime() > (System.currentTimeMillis() - days.days.inMilliseconds) }
/**
* Predicate that returns whether a reply is _not_ from the future,
diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt
index c9f09255e..e33b8998d 100644
--- a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt
@@ -29,6 +29,8 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
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
+ val downloadBackwardsLimitDays = soneRequest.parameters["download-backwards-limit"].emptyToNull
+ val downloadCountLimitDays = soneRequest.parameters["download-count-limit"].emptyToNull
options.isAutoFollow = autoFollow
options.isSoneInsertNotificationEnabled = enableSoneInsertNotification
@@ -37,7 +39,17 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
options.isShowNewReplyNotifications = showNewReplyNotification
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" }
- }
+ downloadBackwardsLimitDays?.also { if (cantSetOption { options.downloadBackwardsLimitDays = downloadBackwardsLimitDays.toInt() }) fieldsWithErrors += "download-backwards-limit" }
+ if (options.downloadBackwardsLimitDays < -1 || downloadBackwardsLimitDays.isNullOrBlank()) {
+ options.downloadBackwardsLimitDays = 365
+ fieldsWithErrors += "download-backwards-limit"
+ }
+ downloadCountLimitDays?.also { if (cantSetOption { options.downloadCountLimit = downloadCountLimitDays.toInt() }) fieldsWithErrors += "download-count-limit" }
+ if (options.downloadCountLimit < -1 || downloadCountLimitDays.isNullOrBlank()) {
+ options.downloadCountLimit = 100
+ fieldsWithErrors += "download-count-limit"
+ }
+ }
val fullAccessRequired = "require-full-access" in soneRequest.parameters
val fcpInterfaceActive = "fcp-interface-active" in soneRequest.parameters
val strictFiltering = "strict-filtering" in soneRequest.parameters
@@ -51,8 +63,6 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
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 downloadBackwardsLimit = soneRequest.parameters["download-backwards-limit"]?.toIntOrNull()
- val downloadCountLimit = soneRequest.parameters["download-count-limit"]?.toIntOrNull()
val fcpFullAccessRequired = soneRequest.parameters["fcp-full-access-required"]?.toIntOrNull()
if (cantSetOption { soneRequest.core.preferences.newPostsPerPage = postsPerPage }) fieldsWithErrors += "posts-per-page"
@@ -60,8 +70,6 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
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"
- if (cantSetOption { soneRequest.core.preferences.newDownloadBackwardsLimit = downloadBackwardsLimit }) fieldsWithErrors += "download-backwards-limit"
- if (cantSetOption { soneRequest.core.preferences.newDownloadCountLimit = downloadCountLimit }) fieldsWithErrors += "download-count-limit"
fcpFullAccessRequired?.also { if (cantSetOption { soneRequest.core.preferences.newFcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequired] }) fieldsWithErrors += "fcp-full-access-required" }
if (fieldsWithErrors.isEmpty()) {
@@ -76,13 +84,13 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
templateContext["show-notification-new-posts"] = options.isShowNewPostNotifications
templateContext["show-notification-new-replies"] = options.isShowNewReplyNotifications
templateContext["enable-sone-insert-notifications"] = options.isSoneInsertNotificationEnabled
+ templateContext["download-count-limit"] = options.downloadCountLimit
+ templateContext["download-backwards-limit"] = options.downloadBackwardsLimitDays
templateContext["load-linked-images"] = options.loadLinkedImages.toString()
templateContext["show-custom-avatars"] = options.showCustomAvatars.toString()
}
soneRequest.core.preferences.let { preferences ->
templateContext["insertion-delay"] = preferences.insertionDelay
- templateContext["download-backwards-limit"] = preferences.downloadBackwardsLimit
- templateContext["download-count-limit"] = preferences.downloadCountLimit
templateContext["characters-per-post"] = preferences.charactersPerPost
templateContext["fcp-full-access-required"] = preferences.fcpFullAccessRequired.ordinal
templateContext["images-per-page"] = preferences.imagesPerPage
diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt
index 9bbc3e5ba..bc9e24d7f 100644
--- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt
+++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt
@@ -11,13 +11,16 @@ 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.*
+import kotlin.time.ExperimentalTime
+import kotlin.time.minutes
+import kotlin.time.toJavaDuration
/**
* This page lets the user search for posts and replies that contain certain
* words.
*/
+@OptIn(ExperimentalTime::class)
@TemplatePath("/templates/search.html")
@ToadletPath("search.html")
class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer, ticker: Ticker = Ticker.systemTicker()) :
@@ -27,7 +30,7 @@ class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer:
constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
this(webInterface, loaders, templateRenderer, Ticker.systemTicker())
- private val cache: Cache, Pagination> = CacheBuilder.newBuilder().ticker(ticker).expireAfterAccess(5, MINUTES).build()
+ private val cache: Cache, Pagination> = CacheBuilder.newBuilder().ticker(ticker).expireAfterAccess(5.minutes.toJavaDuration()).build()
override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
val startTime = System.currentTimeMillis()
diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties
index 13ed14ff9..15db1eaa0 100644
--- a/src/main/resources/i18n/sone.de.properties
+++ b/src/main/resources/i18n/sone.de.properties
@@ -417,6 +417,8 @@ WebInterface.DefaultText.BirthMonth=Monat
WebInterface.DefaultText.BirthYear=Jahr
WebInterface.DefaultText.FieldName=Feldname
WebInterface.DefaultText.Option.InsertionDelay=Zeit, die vor dem Hochladen einer geänderten Sone gewartet wird (in Sekunden)
+WebInterface.DefaultText.Option.DownloadBackwardsLimit=Maximal-Alter von Nachrichten Anderer (in Tagen)
+WebInterface.DefaultText.Option.DownloadCountLimit=Maximal-Zahl von Posts und von Antworten
WebInterface.DefaultText.Search=Was suchen Sie?
WebInterface.DefaultText.CreateAlbum.Name=Albumtitel
WebInterface.DefaultText.CreateAlbum.Description=Albumbeschreibung
diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties
index 6e77b35ba..d88ed51a5 100644
--- a/src/main/resources/i18n/sone.en.properties
+++ b/src/main/resources/i18n/sone.en.properties
@@ -430,6 +430,8 @@ WebInterface.DefaultText.Option.PostsPerPage=Number of posts to show on a page
WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
WebInterface.DefaultText.Option.CharactersPerPost=Number of characters a post must have to be shortened
WebInterface.DefaultText.Option.PostCutOffLength=Number of characters for the snippet of the shortened post
+WebInterface.DefaultText.Option.DownloadCountLimit=Maximum number of posts and of replies
+WebInterface.DefaultText.Option.DownloadBackwardsLimit=Maximum age of messages of others (in days)
WebInterface.Button.Comment=Comment
WebInterface.Confirmation.DeletePostButton=Yes, delete!
WebInterface.Confirmation.DeleteReplyButton=Yes, delete!
diff --git a/src/main/resources/templates/options.html b/src/main/resources/templates/options.html
index beec5b748..bca9274a0 100644
--- a/src/main/resources/templates/options.html
+++ b/src/main/resources/templates/options.html
@@ -66,6 +66,18 @@ <%= Page.Options.Section.SoneSpecificOptions.Title|l10n|html>
<%= Page.Options.Option.ShowNotificationNewReplies.Description|l10n|html>
+ <%= Page.Options.Option.DownloadBackwardsLimit.Description|l10n|html>
+ <%if =download-backwards-limit|in collection=fieldErrors>
+ <%= Page.Options.Warnings.ValueNotChanged|l10n|html>
+ <%/if>
+ disabled="disabled"<%/if> value="<% download-backwards-limit|html>" />
+
+ <%= Page.Options.Option.DownloadCountLimit.Description|l10n|html>
+ <%if =download-count-limit|in collection=fieldErrors>
+ <%= Page.Options.Warnings.ValueNotChanged|l10n|html>
+ <%/if>
+ disabled="disabled"<%/if> value="<% download-count-limit|html>" />
+
<%= Page.Options.Section.AvatarOptions.Title|l10n|html>
<%= Page.Options.Option.ShowAvatars.Description|l10n|html>
@@ -128,18 +140,6 @@ <%= Page.Options.Section.RuntimeOptions.Title|l10n|html>
<%/if>
- <%= Page.Options.Option.DownloadBackwardsLimit.Description|l10n|html>
- <%if =download-backwards-limit|in collection=fieldErrors>
- <%= Page.Options.Warnings.ValueNotChanged|l10n|html>
- <%/if>
-
-
- <%= Page.Options.Option.DownloadCountLimit.Description|l10n|html>
- <%if =download-count-limit|in collection=fieldErrors>
- <%= Page.Options.Warnings.ValueNotChanged|l10n|html>
- <%/if>
-
-
<%= Page.Options.Option.PostsPerPage.Description|l10n|html>
<%if =posts-per-page|in collection=fieldErrors>
<%= Page.Options.Warnings.ValueNotChanged|l10n|html>
diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt
index 8ffe54f1d..c941b819d 100644
--- a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt
+++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt
@@ -18,8 +18,6 @@
package net.pterodactylus.sone.data
import net.pterodactylus.sone.data.impl.*
-import net.pterodactylus.sone.data.impl.SoneImpl.filterRemotePosts
-import net.pterodactylus.sone.data.impl.SoneImpl.filterRemoteReplies
import net.pterodactylus.sone.test.*
import org.hamcrest.MatcherAssert.*
import org.hamcrest.Matchers.*
@@ -126,25 +124,33 @@ class SoneTest {
@Test
fun `post filtering skips old posts`() {
var oldPosts = listOf(createPost(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(400)));
- assertThat(filterRemotePosts(oldPosts), empty())
+ val sone1 = object : IdOnlySone("1"){
+ }
+ assertThat(sone1.filterRemotePosts(oldPosts), empty())
}
@Test
fun `post filtering keeps recent posts`() {
var recentPosts = listOf(createPost(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(300)));
- assertThat(filterRemotePosts(recentPosts), hasSize(recentPosts.size));
+ val sone1 = object : IdOnlySone("1"){
+ }
+ assertThat(sone1.filterRemotePosts(recentPosts), hasSize(recentPosts.size));
}
@Test
fun `reply filtering skips old replies`() {
var oldReplies = listOf(emptyPostReply(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(400)));
- assertThat(filterRemoteReplies(oldReplies), empty())
+ val sone1 = object : IdOnlySone("1"){
+ }
+ assertThat(sone1.filterRemoteReplies(oldReplies), empty())
}
@Test
fun `reply filtering keeps recent replies`() {
var recentReplies = listOf(emptyPostReply(time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(300)));
- assertThat(filterRemoteReplies(recentReplies), hasSize(recentReplies.size))
+ val sone1 = object : IdOnlySone("1"){
+ }
+ assertThat(sone1.filterRemoteReplies(recentReplies), hasSize(recentReplies.size))
}
@Test
diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
index 921c89311..a48856378 100644
--- a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
+++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
@@ -20,8 +20,6 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
@Before
fun setupPreferences() {
core.preferences.newInsertionDelay = 1
- core.preferences.newDownloadBackwardsLimit = 30
- core.preferences.newDownloadCountLimit = 10
core.preferences.newCharactersPerPost = 50
core.preferences.newFcpFullAccessRequired = WRITING
core.preferences.newImagesPerPage = 4
@@ -40,6 +38,8 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
isShowNewReplyNotifications = true
isShowNewSoneNotifications = true
isSoneInsertNotificationEnabled = true
+ downloadBackwardsLimitDays = 30
+ downloadCountLimit = 10
loadLinkedImages = FOLLOWED
showCustomAvatars = FOLLOWED
})
@@ -210,17 +210,17 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
@Test
fun `download backwards limit can be set to 0 days`() {
- verifyThatPreferencesCanBeSet("download-backwards-limit", "0", 0) { core.preferences.downloadBackwardsLimit }
+ verifyThatOptionCanBeSet("download-backwards-limit", "0", 0) { currentSone.options.downloadBackwardsLimitDays }
}
@Test
fun `download backwards limit can be set to -1 days`() {
- verifyThatPreferencesCanBeSet("download-backwards-limit", "-1", -1) { core.preferences.downloadBackwardsLimit }
+ verifyThatOptionCanBeSet("download-backwards-limit", "-1", -1) { currentSone.options.downloadBackwardsLimitDays }
}
@Test
fun `setting download backwards limit to an invalid value will reset it`() {
- verifyThatPreferencesCanBeSet("download-backwards-limit", "foo", 365) { core.preferences.downloadBackwardsLimit }
+ verifyThatOptionCanBeSet("download-backwards-limit", "foo", 365) { currentSone.options.downloadBackwardsLimitDays }
}
@Test
@@ -230,17 +230,17 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
@Test
fun `download count limit can be set to -1 posts or replies`() {
- verifyThatPreferencesCanBeSet("download-count-limit", "-1", -1) { core.preferences.downloadCountLimit }
+ verifyThatOptionCanBeSet("download-count-limit", "-1", -1) { currentSone.options.downloadCountLimit }
}
@Test
fun `download count limit can be set to 0 posts or replies`() {
- verifyThatPreferencesCanBeSet("download-count-limit", "0", 0) { core.preferences.downloadCountLimit }
+ verifyThatOptionCanBeSet("download-count-limit", "0", 0) { currentSone.options.downloadCountLimit }
}
@Test
fun `setting download count limit to an invalid value will reset it`() {
- verifyThatPreferencesCanBeSet("download-count-limit", "foo", 100) { core.preferences.downloadCountLimit }
+ verifyThatOptionCanBeSet("download-count-limit", "foo", 100) { currentSone.options.downloadCountLimit }
}
@Test
From cfb862457dde28eba3dd805ade3ea61627e1e307 Mon Sep 17 00:00:00 2001
From: Arne Babenhauserheide
Date: Thu, 4 Jul 2024 22:00:58 +0200
Subject: [PATCH 5/6] Harmonize order of Webinterface Option Default Text
---
src/main/resources/i18n/sone.de.properties | 10 +++++-----
src/main/resources/i18n/sone.en.properties | 12 ++++++------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties
index 15db1eaa0..fdab72ebc 100644
--- a/src/main/resources/i18n/sone.de.properties
+++ b/src/main/resources/i18n/sone.de.properties
@@ -416,9 +416,13 @@ WebInterface.DefaultText.BirthDay=Tag
WebInterface.DefaultText.BirthMonth=Monat
WebInterface.DefaultText.BirthYear=Jahr
WebInterface.DefaultText.FieldName=Feldname
-WebInterface.DefaultText.Option.InsertionDelay=Zeit, die vor dem Hochladen einer geänderten Sone gewartet wird (in Sekunden)
+WebInterface.DefaultText.Option.CharactersPerPost=Anzahl der Zeichen, die eine Nachricht haben muss, damit er gekürzt wird
WebInterface.DefaultText.Option.DownloadBackwardsLimit=Maximal-Alter von Nachrichten Anderer (in Tagen)
WebInterface.DefaultText.Option.DownloadCountLimit=Maximal-Zahl von Posts und von Antworten
+WebInterface.DefaultText.Option.ImagesPerPage=Anzahl der Bilder pro Seite
+WebInterface.DefaultText.Option.InsertionDelay=Zeit, die vor dem Hochladen einer geänderten Sone gewartet wird (in Sekunden)
+WebInterface.DefaultText.Option.PostCutOffLength=Anzahl der Zeichen, die von einer gekürzten Nachricht angezeigt werden
+WebInterface.DefaultText.Option.PostsPerPage=Anzahl der Nachrichten pro Seite
WebInterface.DefaultText.Search=Was suchen Sie?
WebInterface.DefaultText.CreateAlbum.Name=Albumtitel
WebInterface.DefaultText.CreateAlbum.Description=Albumbeschreibung
@@ -428,10 +432,6 @@ WebInterface.DefaultText.UploadImage.Title=Bildtitel
WebInterface.DefaultText.UploadImage.Description=Bildbeschreibung
WebInterface.DefaultText.EditImage.Title=Bildtitel
WebInterface.DefaultText.EditImage.Description=Bildbeschreibung
-WebInterface.DefaultText.Option.PostsPerPage=Anzahl der Nachrichten pro Seite
-WebInterface.DefaultText.Option.ImagesPerPage=Anzahl der Bilder pro Seite
-WebInterface.DefaultText.Option.CharactersPerPost=Anzahl der Zeichen, die eine Nachricht haben muss, damit er gekürzt wird
-WebInterface.DefaultText.Option.PostCutOffLength=Anzahl der Zeichen, die von einer gekürzten Nachricht angezeigt werden
WebInterface.Button.Comment=Antworten
WebInterface.Confirmation.DeletePostButton=Ja, löschen!
WebInterface.Confirmation.DeleteReplyButton=Ja, löschen!
diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties
index d88ed51a5..e518fd1f7 100644
--- a/src/main/resources/i18n/sone.en.properties
+++ b/src/main/resources/i18n/sone.en.properties
@@ -416,7 +416,13 @@ WebInterface.DefaultText.BirthDay=Day
WebInterface.DefaultText.BirthMonth=Month
WebInterface.DefaultText.BirthYear=Year
WebInterface.DefaultText.FieldName=Field name
+WebInterface.DefaultText.Option.CharactersPerPost=Number of characters a post must have to be shortened
+WebInterface.DefaultText.Option.DownloadBackwardsLimit=Maximum age of messages of others (in days)
+WebInterface.DefaultText.Option.DownloadCountLimit=Maximum number of posts and of replies
+WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
WebInterface.DefaultText.Option.InsertionDelay=Time to wait after a Sone is modified before insert (in seconds)
+WebInterface.DefaultText.Option.PostCutOffLength=Number of characters for the snippet of the shortened post
+WebInterface.DefaultText.Option.PostsPerPage=Number of posts to show on a page
WebInterface.DefaultText.Search=What are you looking for?
WebInterface.DefaultText.CreateAlbum.Name=Album title
WebInterface.DefaultText.CreateAlbum.Description=Album description
@@ -426,12 +432,6 @@ WebInterface.DefaultText.UploadImage.Title=Image title
WebInterface.DefaultText.UploadImage.Description=Image description
WebInterface.DefaultText.EditImage.Title=Image title
WebInterface.DefaultText.EditImage.Description=Image description
-WebInterface.DefaultText.Option.PostsPerPage=Number of posts to show on a page
-WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
-WebInterface.DefaultText.Option.CharactersPerPost=Number of characters a post must have to be shortened
-WebInterface.DefaultText.Option.PostCutOffLength=Number of characters for the snippet of the shortened post
-WebInterface.DefaultText.Option.DownloadCountLimit=Maximum number of posts and of replies
-WebInterface.DefaultText.Option.DownloadBackwardsLimit=Maximum age of messages of others (in days)
WebInterface.Button.Comment=Comment
WebInterface.Confirmation.DeletePostButton=Yes, delete!
WebInterface.Confirmation.DeleteReplyButton=Yes, delete!
From 003f5c1d31d1f6ffab6ffaf24c364171fdd633fd Mon Sep 17 00:00:00 2001
From: Arne Babenhauserheide
Date: Sat, 15 Mar 2025 01:28:01 +0100
Subject: [PATCH 6/6] Fix indentation (tabify)
---
.../net/pterodactylus/sone/web/pages/OptionsPageTest.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
index a48856378..7a97eb3af 100644
--- a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
+++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
@@ -38,8 +38,8 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
isShowNewReplyNotifications = true
isShowNewSoneNotifications = true
isSoneInsertNotificationEnabled = true
- downloadBackwardsLimitDays = 30
- downloadCountLimit = 10
+ downloadBackwardsLimitDays = 30
+ downloadCountLimit = 10
loadLinkedImages = FOLLOWED
showCustomAvatars = FOLLOWED
})