Add page that renders linked elements
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 7 Apr 2017 20:55:21 +0000 (22:55 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 8 Apr 2017 13:22:57 +0000 (15:22 +0200)
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/kotlin/net/pterodactylus/sone/utils/Json.kt
src/main/kotlin/net/pterodactylus/sone/web/ajax/GetLinkedElementAjaxPage.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/web/ajax/GetLinkedElementAjaxPageTest.kt [new file with mode: 0644]

index 8f7203c..6ce38d4 100644 (file)
@@ -120,6 +120,7 @@ import net.pterodactylus.sone.web.ajax.EditImageAjaxPage;
 import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage;
 import net.pterodactylus.sone.web.ajax.FollowSoneAjaxPage;
 import net.pterodactylus.sone.web.ajax.GetLikesAjaxPage;
+import net.pterodactylus.sone.web.ajax.GetLinkedElementAjaxPage;
 import net.pterodactylus.sone.web.ajax.GetNotificationsAjaxPage;
 import net.pterodactylus.sone.web.ajax.GetPostAjaxPage;
 import net.pterodactylus.sone.web.ajax.GetReplyAjaxPage;
@@ -215,6 +216,7 @@ public class WebInterface {
        private final ReplyVisibilityFilter replyVisibilityFilter;
 
        private final ElementLoader elementLoader;
+       private final LinkedElementRenderFilter linkedElementRenderFilter;
        private final TimeTextConverter timeTextConverter = new TimeTextConverter();
        private final L10nFilter l10nFilter = new L10nFilter(this);
 
@@ -304,7 +306,7 @@ public class WebInterface {
                templateContextFactory.addFilter("shorten", shortenFilter = new ShortenFilter());
                templateContextFactory.addFilter("render", renderFilter = new RenderFilter(getCore(), templateContextFactory));
                templateContextFactory.addFilter("linked-elements", new LinkedElementsFilter(elementLoader));
-               templateContextFactory.addFilter("render-linked-element", new LinkedElementRenderFilter(templateContextFactory));
+               templateContextFactory.addFilter("render-linked-element", linkedElementRenderFilter = new LinkedElementRenderFilter(templateContextFactory));
                templateContextFactory.addFilter("reparse", new ReparseFilter());
                templateContextFactory.addFilter("unknown", new UnknownDateFilter(getL10n(), "View.Sone.Text.UnknownDate"));
                templateContextFactory.addFilter("format", new FormatFilter());
@@ -720,6 +722,7 @@ public class WebInterface {
                pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateReplyAjaxPage(this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new GetReplyAjaxPage(this, replyTemplate)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new GetPostAjaxPage(this, postTemplate)));
+               pageToadlets.add(pageToadletFactory.createPageToadlet(new GetLinkedElementAjaxPage(this, elementLoader, linkedElementRenderFilter)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new GetTimesAjaxPage(this, timeTextConverter, l10nFilter)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new MarkAsKnownAjaxPage(this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new DeletePostAjaxPage(this)));
index fc78b96..008c141 100644 (file)
@@ -1,10 +1,12 @@
 package net.pterodactylus.sone.utils
 
+import com.fasterxml.jackson.databind.JsonNode
 import com.fasterxml.jackson.databind.node.ArrayNode
 import com.fasterxml.jackson.databind.node.JsonNodeFactory.instance
 import com.fasterxml.jackson.databind.node.ObjectNode
 
 fun jsonObject(block: ObjectNode.() -> Unit): ObjectNode = ObjectNode(instance).apply(block)
 fun jsonArray(vararg objects: String?): ArrayNode = objects.fold(ArrayNode(instance), ArrayNode::add)
+fun jsonArray(vararg objects: JsonNode?): ArrayNode = objects.fold(ArrayNode(instance), ArrayNode::add)
 
 fun Iterable<ObjectNode>.toArray(): ArrayNode = fold(ArrayNode(instance), ArrayNode::add)
diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetLinkedElementAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetLinkedElementAjaxPage.kt
new file mode 100644 (file)
index 0000000..285c479
--- /dev/null
@@ -0,0 +1,48 @@
+package net.pterodactylus.sone.web.ajax
+
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.ObjectMapper
+import net.pterodactylus.sone.core.ElementLoader
+import net.pterodactylus.sone.core.LinkedElement
+import net.pterodactylus.sone.template.LinkedElementRenderFilter
+import net.pterodactylus.sone.utils.jsonArray
+import net.pterodactylus.sone.utils.jsonObject
+import net.pterodactylus.sone.web.WebInterface
+import net.pterodactylus.sone.web.page.FreenetRequest
+
+/**
+ * Renders linked elements after they have been loaded.
+ */
+class GetLinkedElementAjaxPage(webInterface: WebInterface, private val elementLoader: ElementLoader, private val linkedElementRenderFilter: LinkedElementRenderFilter):
+               JsonPage("getLinkedElement.ajax", webInterface) {
+
+       override fun createJsonObject(request: FreenetRequest): JsonReturnObject =
+                       request.httpRequest.getParam("elements", "[]").asJson()
+                                       .map(JsonNode::asText)
+                                       .map(elementLoader::loadElement)
+                                       .filterNot { it.loading }
+                                       .map { it to renderLinkedElement(it) }
+                                       .let { elements ->
+                                               jsonArray(
+                                                               *(elements.map { element ->
+                                                                       jsonObject {
+                                                                               put("link", element.first.link)
+                                                                               put("html", element.second)
+                                                                       }
+                                                               }.toTypedArray())
+                                               )
+                                       }.let { linkedElements ->
+                               createSuccessJsonObject().apply {
+                                       put("linkedElements", linkedElements)
+                               }
+                       }
+
+       override fun needsFormPassword() = false
+       override fun requiresLogin() = false
+
+       private fun String.asJson() = ObjectMapper().readTree(this).asIterable()
+
+       private fun renderLinkedElement(linkedElement: LinkedElement) =
+                       linkedElementRenderFilter.format(null, linkedElement, emptyMap())
+
+}
diff --git a/src/test/kotlin/net/pterodactylus/sone/web/ajax/GetLinkedElementAjaxPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/ajax/GetLinkedElementAjaxPageTest.kt
new file mode 100644 (file)
index 0000000..ea3be1a
--- /dev/null
@@ -0,0 +1,60 @@
+package net.pterodactylus.sone.web.ajax
+
+import com.fasterxml.jackson.databind.JsonNode
+import net.pterodactylus.sone.core.LinkedElement
+import net.pterodactylus.sone.template.LinkedElementRenderFilter
+import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.utils.jsonArray
+import net.pterodactylus.util.template.TemplateContext
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers
+import org.hamcrest.Matchers.equalTo
+import org.junit.Test
+import org.mockito.ArgumentMatchers
+
+/**
+ * Unit test for [GetLinkedElementAjaxPage].
+ */
+class GetLinkedElementAjaxPageTest: JsonPageTest() {
+
+       private val linkedElementRenderFilter = mock<LinkedElementRenderFilter>()
+       override var page: JsonPage = GetLinkedElementAjaxPage(webInterface, elementLoader, linkedElementRenderFilter)
+
+       @Test
+       fun `page returns correct path`() {
+           assertThat(page.path, equalTo("getLinkedElement.ajax"))
+       }
+
+       @Test
+       fun `page does not require login`() {
+           assertThat(page.requiresLogin(), equalTo(false))
+       }
+
+       @Test
+       fun `page does not require form password`() {
+           assertThat(page.needsFormPassword(), equalTo(false))
+       }
+
+       @Test
+       fun `only loaded linked elements are returned`() {
+           addRequestParameter("elements", jsonArray("KSK@foo.png", "KSK@foo.jpg", "KSK@foo.html").toString())
+               addLinkedElement("KSK@foo.png", true, false)
+               addLinkedElement("KSK@foo.jpg", false, false)
+               addLinkedElement("KSK@foo.html", false, true)
+               whenever(linkedElementRenderFilter.format(ArgumentMatchers.any<TemplateContext>(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer { invocation ->
+                       when (invocation.getArgument<LinkedElement>(1).link) {
+                               "KSK@foo.jpg" -> "jpeg-image"
+                               "KSK@foo.html" -> "html-page"
+                               else -> null
+                       }
+               }
+               assertThat(json.get("linkedElements").elements().asSequence().map { it.toMap() }.toList(), Matchers.containsInAnyOrder(
+                               mapOf<String, String?>("link" to "KSK@foo.jpg", "html" to "jpeg-image"),
+                               mapOf("link" to "KSK@foo.html", "html" to "html-page")
+               ))
+       }
+
+       private fun JsonNode.toMap() = fields().asSequence().map { it.key!! to if (it.value.isNull) null else it.value.asText()!! }.toMap()
+
+}