From: David ‘Bombe’ Roden Date: Mon, 14 Nov 2016 06:00:35 +0000 (+0100) Subject: Make loading of linked images configurable X-Git-Tag: 0.9.7^2~435 X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=885f3980c49778280a748c630788212a68ac4ed6;p=Sone.git Make loading of linked images configurable --- diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 8c4264a..a4fde13 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -1071,6 +1071,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, sone.getOptions().setShowNewPostNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").getValue(true)); sone.getOptions().setShowNewReplyNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").getValue(true)); 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()))); /* if we’re still here, Sone was loaded successfully. */ synchronized (sone) { @@ -1548,6 +1549,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").setValue(sone.getOptions().isShowNewPostNotifications()); configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").setValue(sone.getOptions().isShowNewReplyNotifications()); configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").setValue(sone.getOptions().getShowCustomAvatars().name()); + configuration.getStringValue(sonePrefix + "/Options/LoadLinkedImages").setValue(sone.getOptions().getLoadLinkedImages().name()); configuration.save(); diff --git a/src/main/java/net/pterodactylus/sone/data/SoneOptions.java b/src/main/java/net/pterodactylus/sone/data/SoneOptions.java index b5e42ee..5f05447 100644 --- a/src/main/java/net/pterodactylus/sone/data/SoneOptions.java +++ b/src/main/java/net/pterodactylus/sone/data/SoneOptions.java @@ -2,6 +2,8 @@ package net.pterodactylus.sone.data; import static net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.NEVER; +import javax.annotation.Nonnull; + /** * All Sone-specific options. * @@ -27,6 +29,9 @@ public interface SoneOptions { LoadExternalContent getShowCustomAvatars(); void setShowCustomAvatars(LoadExternalContent showCustomAvatars); + @Nonnull LoadExternalContent getLoadLinkedImages(); + void setLoadLinkedImages(@Nonnull LoadExternalContent loadLinkedImages); + /** * Possible values for all options that are related to loading external content. * @@ -64,6 +69,7 @@ public interface SoneOptions { private boolean showNewPostNotifications = true; private boolean showNewReplyNotifications = true; private LoadExternalContent showCustomAvatars = NEVER; + private LoadExternalContent loadLinkedImages = NEVER; @Override public boolean isAutoFollow() { @@ -125,6 +131,17 @@ public interface SoneOptions { this.showCustomAvatars = showCustomAvatars; } + @Nonnull + @Override + public LoadExternalContent getLoadLinkedImages() { + return loadLinkedImages; + } + + @Override + public void setLoadLinkedImages(@Nonnull LoadExternalContent loadLinkedImages) { + this.loadLinkedImages = loadLinkedImages; + } + } } diff --git a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java index 4aaf232..341bc0c 100644 --- a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java +++ b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java @@ -76,6 +76,8 @@ public class OptionsPage extends SoneTemplatePage { currentSone.getOptions().setShowNewReplyNotifications(showNotificationNewReplies); String showCustomAvatars = request.getHttpRequest().getPartAsStringFailsafe("show-custom-avatars", 32); currentSone.getOptions().setShowCustomAvatars(LoadExternalContent.valueOf(showCustomAvatars)); + String loadLinkedImages = request.getHttpRequest().getPartAsStringFailsafe("load-linked-images", 32); + currentSone.getOptions().setLoadLinkedImages(LoadExternalContent.valueOf(loadLinkedImages)); webInterface.getCore().touchConfiguration(); } Integer insertionDelay = parseInt(request.getHttpRequest().getPartAsStringFailsafe("insertion-delay", 16), null); @@ -145,6 +147,7 @@ public class OptionsPage extends SoneTemplatePage { templateContext.set("show-notification-new-posts", currentSone.getOptions().isShowNewPostNotifications()); templateContext.set("show-notification-new-replies", currentSone.getOptions().isShowNewReplyNotifications()); templateContext.set("show-custom-avatars", currentSone.getOptions().getShowCustomAvatars().name()); + templateContext.set("load-linked-images", currentSone.getOptions().getLoadLinkedImages().name()); } templateContext.set("insertion-delay", preferences.getInsertionDelay()); templateContext.set("posts-per-page", preferences.getPostsPerPage()); diff --git a/src/main/kotlin/net/pterodactylus/sone/template/LinkedElementsFilter.kt b/src/main/kotlin/net/pterodactylus/sone/template/LinkedElementsFilter.kt index ca94423..c9414ea 100644 --- a/src/main/kotlin/net/pterodactylus/sone/template/LinkedElementsFilter.kt +++ b/src/main/kotlin/net/pterodactylus/sone/template/LinkedElementsFilter.kt @@ -2,6 +2,13 @@ package net.pterodactylus.sone.template import net.pterodactylus.sone.core.ElementLoader import net.pterodactylus.sone.core.LinkedElement +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.ALWAYS +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.FOLLOWED +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.MANUALLY_TRUSTED +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.NEVER +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.TRUSTED +import net.pterodactylus.sone.freenet.wot.OwnIdentity import net.pterodactylus.sone.text.FreenetLinkPart import net.pterodactylus.sone.text.Part import net.pterodactylus.util.template.Filter @@ -15,10 +22,36 @@ class LinkedElementsFilter(private val elementLoader: ElementLoader) : Filter { @Suppress("UNCHECKED_CAST") override fun format(templateContext: TemplateContext?, data: Any?, parameters: MutableMap?) = - (data as? Iterable) - ?.filterIsInstance() - ?.map { elementLoader.loadElement(it.link) } - ?.filter { !it.failed } - ?: listOf() + if (showLinkedImages(templateContext?.get("currentSone") as Sone?, parameters?.get("sone") as Sone?)) { + (data as? Iterable) + ?.filterIsInstance() + ?.map { elementLoader.loadElement(it.link) } + ?.filter { !it.failed } + ?: listOf() + } else { + listOf() + } + + private fun showLinkedImages(currentSone: Sone?, sone: Sone?): Boolean { + return (currentSone != null) && (sone != null) && ((currentSone == sone) || currentSoneAllowsImagesFromSone(currentSone, sone)) + } + + private fun currentSoneAllowsImagesFromSone(currentSone: Sone, externalSone: Sone) = + when (currentSone.options.loadLinkedImages) { + NEVER -> false + MANUALLY_TRUSTED -> externalSone.isLocal || currentSone.explicitelyTrusts(externalSone) + FOLLOWED -> externalSone.isLocal || currentSone.hasFriend(externalSone.id) + TRUSTED -> externalSone.isLocal || currentSone.implicitelyTrusts(externalSone) + ALWAYS -> true + } + + private fun Sone.implicitelyTrusts(other: Sone): Boolean { + val explicitTrust = other.identity.getTrust(this.identity as OwnIdentity)?.explicit + val implicitTrust = other.identity.getTrust(this.identity as OwnIdentity)?.implicit + return ((explicitTrust != null) && (explicitTrust > 0)) || ((explicitTrust == null) && (implicitTrust != null) && (implicitTrust > 0)) + } + + private fun Sone.explicitelyTrusts(other: Sone) = + other.identity.getTrust(this.identity as OwnIdentity)?.explicit ?: -1 > 0 } diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties index 62e9807..aabcd67 100644 --- a/src/main/resources/i18n/sone.de.properties +++ b/src/main/resources/i18n/sone.de.properties @@ -52,6 +52,13 @@ Page.Options.Option.ShowAvatars.Followed.Description=Nur benutzerdefinierte Avat Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=Nur benutzerdefinierte Avatare von Sones, denen Sie manuell einen Vertrauenswert von mehr als 0 zugewiesen haben, anzeigen. Page.Options.Option.ShowAvatars.Trusted.Description=Nur benutzerdefinierte Avatare von Sones, die einen berechneten Vertrauenswert von mehr als 0 haben, anzeigen. Page.Options.Option.ShowAvatars.Always.Description=Immer benutzerdefinierte Avatare anzeigen. Warnung: Benutzerdefinierte Avatare können beliebiges Bildmaterial enthalten! +Page.Options.Section.LoadLinkedImagesOptions.Title=Verlinkte Bilder laden +Page.Options.Option.LoadLinkedImages.Description=Sone kann automatisch in Nachrichten und Antworten verlinkte Bilder laden. Diese Bilder werden nur aus Freenet geladen, niemals aus dem Internet! +Page.Options.Option.LoadLinkedImages.Never.Description=Niemals verlinkte Bilder laden. +Page.Options.Option.LoadLinkedImages.Followed.Description=Nur Bilder aus Nachrichten von Sones, denen Sie folgen, laden. +Page.Options.Option.LoadLinkedImages.ManuallyTrusted.Description=Nur Bilder aus Nachrichten von Sones, denen Sie manuell einen Vertrauenswert von mehr als 0 zugewiesen haben, laden. +Page.Options.Option.LoadLinkedImages.Trusted.Description=Nur Bilder aus Nachrichten von Sones, die einen berechneten Vertrauenswert von mehr als 0 haben, laden. +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.PostsPerPage.Description=Anzahl der Nachrichten pro Seite. diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties index ae57376..f7695a0 100644 --- a/src/main/resources/i18n/sone.en.properties +++ b/src/main/resources/i18n/sone.en.properties @@ -52,6 +52,13 @@ Page.Options.Option.ShowAvatars.Followed.Description=Only show avatars for Sones Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=Only show avatars for Sones that you have manually assigned a trust value larger than 0 to. Page.Options.Option.ShowAvatars.Trusted.Description=Only show avatars for Sones that have a trust value larger than 0. Page.Options.Option.ShowAvatars.Always.Description=Always show custom avatars. Be warned: some avatars might contain disturbing or offensive imagery. +Page.Options.Section.LoadLinkedImagesOptions.Title=Load Linked Images +Page.Options.Option.LoadLinkedImages.Description=Sone can automatically try to load images that are linked to in posts and replies. This will only ever load images from Freenet, never from the internet! +Page.Options.Option.LoadLinkedImages.Never.Description=Never load linked images. +Page.Options.Option.LoadLinkedImages.Followed.Description=Only load images linked from Sones that you follow. +Page.Options.Option.LoadLinkedImages.ManuallyTrusted.Description=Only load images linked from Sones that you have manually assigned a trust value larger than 0 to. +Page.Options.Option.LoadLinkedImages.Trusted.Description=Only load images linked from Sones that have a trust value larger than 0. +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.PostsPerPage.Description=The number of posts to display on a page before pagination controls are being shown. diff --git a/src/main/resources/i18n/sone.es.properties b/src/main/resources/i18n/sone.es.properties index ab992e4..7649bad 100644 --- a/src/main/resources/i18n/sone.es.properties +++ b/src/main/resources/i18n/sone.es.properties @@ -52,6 +52,13 @@ Page.Options.Option.ShowAvatars.Followed.Description=Solo mostrar avatares de lo Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=Solo mostrar avatares de Sones a los que has asignado manualmente un valor de veracidad mayor que 0. Page.Options.Option.ShowAvatars.Trusted.Description=Solo mostrar avatares para Sones que tienen un valor de veracidad mayor que 0. Page.Options.Option.ShowAvatars.Always.Description=Mostrar siempre avatares customizados. Advertencia, algunos avatares pueden contener imágenes perturbadoras o ofensivas. +Page.Options.Section.LoadLinkedImagesOptions.Title=Load Linked Images +Page.Options.Option.LoadLinkedImages.Description=Sone can automatically try to load images that are linked to in posts and replies. This will only ever load images from Freenet, never from the internet! +Page.Options.Option.LoadLinkedImages.Never.Description=Never load linked images. +Page.Options.Option.LoadLinkedImages.Followed.Description=Only load images linked from Sones that you follow. +Page.Options.Option.LoadLinkedImages.ManuallyTrusted.Description=Only load images linked from Sones that you have manually assigned a trust value larger than 0 to. +Page.Options.Option.LoadLinkedImages.Trusted.Description=Only load images linked from Sones that have a trust value larger than 0. +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=Comportamiento de la ejecución. Page.Options.Option.InsertionDelay.Description=Número de segundos que se esperarán para insertar el Sone tras una modificación. Page.Options.Option.PostsPerPage.Description=Número de publicaciones que se mostrarán en una página antes de que se muestren los controles de navegación. @@ -464,3 +471,4 @@ Notification.Mention.Text=Has sido mencionado en las siguientes publicaciones: Notification.SoneIsInserting.Text=Tu Sone sone://{0} está siendo insertado. Notification.SoneIsInserted.Text=Tu Sone sone://{0} ha sido insertado en {1,number} {1,choice,0#segundos|1#segundo|1<% shortText> <%if !shortText|match value=renderedText><%if !raw><%= View.Post.ShowMore|l10n|html><%/if><%/if> <%if !shortText|match value=renderedText><%if !raw><%/if><%/if> - <% parsedText|linked-elements|store key==linkedElements> + <% parsedText|linked-elements sone=post.sone|store key==linkedElements> <% foreach linkedElements linkedElement> <% first>
diff --git a/src/main/resources/templates/include/viewReply.html b/src/main/resources/templates/include/viewReply.html index 6091d40..211c1d3 100644 --- a/src/main/resources/templates/include/viewReply.html +++ b/src/main/resources/templates/include/viewReply.html @@ -23,7 +23,7 @@
<% shortText>
<%if !shortText|match value=renderedText><%if !raw><%= View.Post.ShowMore|l10n|html><%/if><%/if> <%if !shortText|match value=renderedText><%if !raw><%/if><%/if> - <% parsedText|linked-elements|store key==linkedElements> + <% parsedText|linked-elements sone=reply.sone|store key==linkedElements> <% foreach linkedElements linkedElement> <% first>
diff --git a/src/main/resources/templates/options.html b/src/main/resources/templates/options.html index 4ec88cb..34a1196 100644 --- a/src/main/resources/templates/options.html +++ b/src/main/resources/templates/options.html @@ -96,6 +96,33 @@ +

<%= Page.Options.Section.LoadLinkedImagesOptions.Title|l10n|html>

+ +

<%= Page.Options.Option.LoadLinkedImages.Description|l10n|html>

+ +
    +
  • + checked="checked"<%/if>/> + <%=Page.Options.Option.LoadLinkedImages.Never.Description|l10n|html> +
  • +
  • + checked="checked"<%/if>/> + <%=Page.Options.Option.LoadLinkedImages.Followed.Description|l10n|html> +
  • +
  • + checked="checked"<%/if>/> + <%=Page.Options.Option.LoadLinkedImages.ManuallyTrusted.Description|l10n|html> +
  • +
  • + checked="checked"<%/if>/> + <%=Page.Options.Option.LoadLinkedImages.Trusted.Description|l10n|html> +
  • +
  • + checked="checked"<%/if>/> + <%=Page.Options.Option.LoadLinkedImages.Always.Description|l10n|html> +
  • +
+

<%= Page.Options.Section.RuntimeOptions.Title|l10n|html>

<%= Page.Options.Option.InsertionDelay.Description|l10n|html>

diff --git a/src/test/java/net/pterodactylus/sone/web/BookmarksPageTest.java b/src/test/java/net/pterodactylus/sone/web/BookmarksPageTest.java index 46b0a18..0c85a88 100644 --- a/src/test/java/net/pterodactylus/sone/web/BookmarksPageTest.java +++ b/src/test/java/net/pterodactylus/sone/web/BookmarksPageTest.java @@ -38,7 +38,7 @@ public class BookmarksPageTest extends WebPageTest { Post post3 = createPost(true, 2000L); Set bookmarkedPosts = createBookmarkedPosts(post1, post2, post3); when(core.getBookmarkedPosts()).thenReturn(bookmarkedPosts); - when(core.getPreferences().getPostsPerPage()).thenReturn(5); + core.getPreferences().setPostsPerPage(5); page.processTemplate(freenetRequest, templateContext); assertThat((Collection) templateContext.get("posts"), contains(post1, post3, post2)); assertThat(((Pagination) templateContext.get("pagination")).getItems(), contains(post1, post3, post2)); @@ -61,7 +61,7 @@ public class BookmarksPageTest extends WebPageTest { Post post3 = createPost(false, 2000L); Set bookmarkedPosts = createBookmarkedPosts(post1, post2, post3); when(core.getBookmarkedPosts()).thenReturn(bookmarkedPosts); - when(core.getPreferences().getPostsPerPage()).thenReturn(5); + core.getPreferences().setPostsPerPage(5); page.processTemplate(freenetRequest, templateContext); assertThat((Collection) templateContext.get("posts"), contains(post2, post1)); assertThat(((Pagination) templateContext.get("pagination")).getItems(), contains(post2, post1)); diff --git a/src/test/java/net/pterodactylus/sone/web/CreateSonePageTest.java b/src/test/java/net/pterodactylus/sone/web/CreateSonePageTest.java index aab40c9..c7bb2f2 100644 --- a/src/test/java/net/pterodactylus/sone/web/CreateSonePageTest.java +++ b/src/test/java/net/pterodactylus/sone/web/CreateSonePageTest.java @@ -132,14 +132,14 @@ public class CreateSonePageTest extends WebPageTest { @Test public void doNotShowCreateSoneInMenuIfFullAccessRequiredButClientHasNoFullAccess() { - when(core.getPreferences().isRequireFullAccess()).thenReturn(true); + core.getPreferences().setRequireFullAccess(true); when(toadletContext.isAllowedFullAccess()).thenReturn(false); assertThat(page.isEnabled(toadletContext), is(false)); } @Test public void showCreateSoneInMenuIfNotLoggedInAndClientHasFullAccess() { - when(core.getPreferences().isRequireFullAccess()).thenReturn(true); + core.getPreferences().setRequireFullAccess(true); when(toadletContext.isAllowedFullAccess()).thenReturn(true); unsetCurrentSone(); assertThat(page.isEnabled(toadletContext), is(true)); diff --git a/src/test/java/net/pterodactylus/sone/web/NewPageTest.java b/src/test/java/net/pterodactylus/sone/web/NewPageTest.java index e110969..223fcb2 100644 --- a/src/test/java/net/pterodactylus/sone/web/NewPageTest.java +++ b/src/test/java/net/pterodactylus/sone/web/NewPageTest.java @@ -26,7 +26,7 @@ public class NewPageTest extends WebPageTest { @Before public void setupNumberOfPostsPerPage() { - when(webInterface.getCore().getPreferences().getPostsPerPage()).thenReturn(5); + webInterface.getCore().getPreferences().setPostsPerPage(5); } @Test diff --git a/src/test/java/net/pterodactylus/sone/web/WebPageTest.java b/src/test/java/net/pterodactylus/sone/web/WebPageTest.java index 3e2df88..9dff571 100644 --- a/src/test/java/net/pterodactylus/sone/web/WebPageTest.java +++ b/src/test/java/net/pterodactylus/sone/web/WebPageTest.java @@ -16,9 +16,11 @@ import java.util.List; import java.util.Set; import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.core.Preferences; import net.pterodactylus.sone.core.UpdateChecker; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.notify.Notification; @@ -30,6 +32,7 @@ import freenet.clients.http.ToadletContext; import freenet.support.api.HTTPRequest; import com.google.common.base.Optional; +import com.google.common.eventbus.EventBus; import org.junit.Before; import org.junit.Rule; import org.junit.rules.ExpectedException; @@ -48,6 +51,7 @@ public abstract class WebPageTest { protected final Template template = new Template(); protected final WebInterface webInterface = mock(WebInterface.class, RETURNS_DEEP_STUBS); + protected final EventBus eventBus = mock(EventBus.class); protected final Core core = webInterface.getCore(); protected final Sone currentSone = mock(Sone.class); @@ -76,6 +80,7 @@ public abstract class WebPageTest { public final void setupCore() { UpdateChecker updateChecker = mock(UpdateChecker.class); when(core.getUpdateChecker()).thenReturn(updateChecker); + when(core.getPreferences()).thenReturn(new Preferences(eventBus)); when(core.getLocalSone(anyString())).thenReturn(null); when(core.getLocalSones()).thenReturn(localSones); when(core.getSone(anyString())).thenReturn(Optional.absent()); @@ -94,6 +99,11 @@ public abstract class WebPageTest { when(webInterface.getNotifications(currentSone)).thenReturn(new ArrayList()); } + @Before + public void setupSone() { + when(currentSone.getOptions()).thenReturn(new DefaultSoneOptions()); + } + protected void unsetCurrentSone() { when(webInterface.getCurrentSone(toadletContext)).thenReturn(null); when(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null); diff --git a/src/test/kotlin/net/pterodactylus/sone/template/LinkedElementsFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/LinkedElementsFilterTest.kt index 1f6f681..1e83b85 100644 --- a/src/test/kotlin/net/pterodactylus/sone/template/LinkedElementsFilterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/template/LinkedElementsFilterTest.kt @@ -2,12 +2,24 @@ package net.pterodactylus.sone.template import net.pterodactylus.sone.core.ElementLoader import net.pterodactylus.sone.core.LinkedElement +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.ALWAYS +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.FOLLOWED +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.MANUALLY_TRUSTED +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.TRUSTED +import net.pterodactylus.sone.freenet.wot.OwnIdentity +import net.pterodactylus.sone.freenet.wot.Trust import net.pterodactylus.sone.test.mock import net.pterodactylus.sone.text.FreenetLinkPart import net.pterodactylus.sone.text.LinkPart +import net.pterodactylus.sone.text.Part import net.pterodactylus.sone.text.PlainTextPart +import net.pterodactylus.util.template.TemplateContext import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.contains +import org.hamcrest.Matchers.emptyIterable +import org.junit.Before import org.junit.Test import org.mockito.Mockito.`when` @@ -18,24 +30,218 @@ class LinkedElementsFilterTest { private val imageLoader = mock() private val filter = LinkedElementsFilter(imageLoader) + private val templateContext = TemplateContext() + private val parameters = mutableMapOf() + private val sone = createSone() + private val remoteSone = createSone("remote-id") + private val parts: List = listOf( + PlainTextPart("text"), + LinkPart("http://link", "link"), + FreenetLinkPart("KSK@link", "link", false), + FreenetLinkPart("KSK@loading.png", "link", false), + FreenetLinkPart("KSK@link.png", "link", false) + ) - @Test - fun `filter finds all loaded freenet images`() { - val parts = listOf( - PlainTextPart("text"), - LinkPart("http://link", "link"), - FreenetLinkPart("KSK@link", "link", false), - FreenetLinkPart("KSK@loading.png", "link", false), - FreenetLinkPart("KSK@link.png", "link", false) - ) + @Before + fun setupSone() { + `when`(sone.options).thenReturn(DefaultSoneOptions()) + } + + @Before + fun setupImageLoader() { `when`(imageLoader.loadElement("KSK@link")).thenReturn(LinkedElement("KSK@link", failed = true)) `when`(imageLoader.loadElement("KSK@loading.png")).thenReturn(LinkedElement("KSK@loading.png", loading = true)) `when`(imageLoader.loadElement("KSK@link.png")).thenReturn(LinkedElement("KSK@link.png")) - val loadedImages = filter.format(null, parts, null) + } + + @Test + fun `filter does not find any image if there is no template context`() { + assertThat(filter.format(null, parts, parameters), emptyIterable()) + } + + @Test + fun `filter does not find any image if there is no current sone`() { + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter does not find any images if there is no remote sone`() { + sone.options.loadLinkedImages = ALWAYS + templateContext.set("currentSone", sone) + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter does not find any images if sone does not allow to load images`() { + templateContext.set("currentSone", sone) + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter finds all loaded freenet images from the sone itself`() { + templateContext.set("currentSone", sone) + parameters["sone"] = sone + verifyThatImagesArePresent() + } + + @Test + fun `filter finds images if the remote sone is local`() { + sone.options.loadLinkedImages = MANUALLY_TRUSTED + templateContext.set("currentSone", sone) + `when`(remoteSone.isLocal).thenReturn(true) + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + @Test + fun `filter does not find images if local sone requires manual trust and remote sone has not trust`() { + sone.options.loadLinkedImages = MANUALLY_TRUSTED + templateContext.set("currentSone", sone) + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter does not find images if local sone requires manual trust and remote sone has only implicit trust`() { + sone.options.loadLinkedImages = MANUALLY_TRUSTED + templateContext.set("currentSone", sone) + `when`(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(null, 100, null)) + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter does not find images if local sone requires manual trust and remote sone has explicit trust of zero`() { + sone.options.loadLinkedImages = MANUALLY_TRUSTED + templateContext.set("currentSone", sone) + `when`(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(0, null, null)) + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter finds images if local sone requires manual trust and remote sone has explicit trust of one`() { + sone.options.loadLinkedImages = MANUALLY_TRUSTED + templateContext.set("currentSone", sone) + `when`(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(1, null, null)) + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + @Test + fun `filter does not find images if local sone requires following and remote sone is not followed`() { + sone.options.loadLinkedImages = FOLLOWED + templateContext["currentSone"] = sone + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter finds images if local sone requires following and remote sone is followed`() { + sone.options.loadLinkedImages = FOLLOWED + `when`(sone.hasFriend("remote-id")).thenReturn(true) + templateContext["currentSone"] = sone + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + @Test + fun `filter finds images if local sone requires following and remote sone is the same as the local sone`() { + sone.options.loadLinkedImages = FOLLOWED + templateContext["currentSone"] = sone + parameters["sone"] = sone + verifyThatImagesArePresent() + } + + @Test + fun `filter finds images if following is required and remote sone is a local sone`() { + sone.options.loadLinkedImages = FOLLOWED + templateContext["currentSone"] = sone + `when`(remoteSone.isLocal).thenReturn(true) + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + @Test + fun `filter does not find images if any trust is required and remote sone does not have any trust`() { + sone.options.loadLinkedImages = TRUSTED + templateContext["currentSone"] = sone + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter does not find images if any trust is required and remote sone has implicit trust of zero`() { + sone.options.loadLinkedImages = TRUSTED + templateContext["currentSone"] = sone + `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(null, 0, null)) + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter finds images if any trust is required and remote sone has implicit trust of one`() { + sone.options.loadLinkedImages = TRUSTED + templateContext["currentSone"] = sone + `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(null, 1, null)) + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + @Test + fun `filter does not find images if any trust is required and remote sone has explicit trust of zero but implicit trust of one`() { + sone.options.loadLinkedImages = TRUSTED + templateContext["currentSone"] = sone + `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(0, 1, null)) + parameters["sone"] = remoteSone + verifyThatImagesAreNotPresent() + } + + @Test + fun `filter finds images if any trust is required and remote sone has explicit trust of one but no implicit trust`() { + sone.options.loadLinkedImages = TRUSTED + templateContext["currentSone"] = sone + `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(1, null, null)) + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + @Test + fun `filter finds images if any trust is required and remote sone is a local sone`() { + sone.options.loadLinkedImages = TRUSTED + templateContext["currentSone"] = sone + `when`(remoteSone.isLocal).thenReturn(true) + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + @Test + fun `filter finds images if no trust is required`() { + sone.options.loadLinkedImages = ALWAYS + templateContext["currentSone"] = sone + parameters["sone"] = remoteSone + verifyThatImagesArePresent() + } + + private fun verifyThatImagesArePresent() { + val loadedImages = filter.format(templateContext, parts, parameters) assertThat(loadedImages, contains( LinkedElement("KSK@loading.png", failed = false, loading = true), LinkedElement("KSK@link.png", failed = false, loading = false) )) } + private fun verifyThatImagesAreNotPresent() { + assertThat(filter.format(templateContext, parts, parameters), emptyIterable()) + } + + private fun createSone(id: String = "sone-id"): Sone { + val sone = mock() + `when`(sone.id).thenReturn(id) + `when`(sone.options).thenReturn(DefaultSoneOptions()) + `when`(sone.identity).thenReturn(mock()) + return sone + } + } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/OptionsPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/OptionsPageTest.kt new file mode 100644 index 0000000..af57a3a --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/web/OptionsPageTest.kt @@ -0,0 +1,33 @@ +package net.pterodactylus.sone.web + +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.ALWAYS +import net.pterodactylus.sone.web.WebTestUtils.redirectsTo +import net.pterodactylus.util.web.Method.POST +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.`is` +import org.junit.Test +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +/** + * Unit test for [OptionsPage]. + */ +class OptionsPageTest : WebPageTest() { + + private val page = OptionsPage(template, webInterface) + + @Test + fun `options page sets correct value for load-linked-images`() { + request("", POST) + addHttpRequestParameter("show-custom-avatars", "ALWAYS") + addHttpRequestParameter("load-linked-images", "ALWAYS") + expectedException.expect(redirectsTo("options.html")) + try { + page.handleRequest(freenetRequest, templateContext) + } finally { + assertThat(currentSone.options.loadLinkedImages, `is`(ALWAYS)) + verify(core, times(2)).touchConfiguration() + } + } + +}