From 2a202f56661ecbfb1be46066a1df7c4dad3042e3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 10 Sep 2017 15:22:29 +0200 Subject: [PATCH] Replace get notifications ajax page with Kotlin version --- .../sone/web/ajax/GetNotificationsAjaxPage.java | 155 --------------------- .../kotlin/net/pterodactylus/sone/utils/Json.kt | 15 +- .../sone/web/ajax/GetNotificationsAjaxPage.kt | 76 ++++++++++ .../net/pterodactylus/sone/utils/JsonTest.kt | 15 +- 4 files changed, 101 insertions(+), 160 deletions(-) delete mode 100644 src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java create mode 100644 src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java deleted file mode 100644 index a61f4ec..0000000 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Sone - GetNotificationsAjaxPage.java - Copyright © 2011–2016 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.web.ajax; - -import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.main.SonePlugin; -import net.pterodactylus.sone.web.WebInterface; -import net.pterodactylus.sone.web.page.FreenetRequest; -import net.pterodactylus.util.notify.Notification; -import net.pterodactylus.util.notify.TemplateNotification; -import net.pterodactylus.util.template.TemplateContext; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -/** - * AJAX handler to return all current notifications. - * - * @author David ‘Bombe’ Roden - */ -public class GetNotificationsAjaxPage extends JsonPage { - - /** - * Creates a new “get notifications” AJAX handler. - * - * @param webInterface - * The Sone web interface - */ - public GetNotificationsAjaxPage(WebInterface webInterface) { - super("getNotifications.ajax", webInterface); - } - - // - // JSONPAGE METHODS - // - - /** - * {@inheritDoc} - */ - @Override - protected boolean needsFormPassword() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean requiresLogin() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - protected JsonReturnObject createJsonObject(FreenetRequest request) { - Sone currentSone = getCurrentSone(request.getToadletContext(), false); - List notifications = new ArrayList(webInterface.getNotifications(currentSone)); - Collections.sort(notifications, Notification.CREATED_TIME_SORTER); - ArrayNode jsonNotifications = new ArrayNode(instance); - for (Notification notification : notifications) { - jsonNotifications.add(createJsonNotification(request, notification)); - } - return createSuccessJsonObject().put("notificationHash", notifications.hashCode()).put("notifications", jsonNotifications).put("options", createJsonOptions(currentSone)); - } - - // - // PRIVATE METHODS - // - - /** - * Creates a JSON object from the given notification. - * - * @param request - * The request to load the session from - * @param notification - * The notification to create a JSON object - * @return The JSON object - */ - private JsonNode createJsonNotification(FreenetRequest request, Notification notification) { - ObjectNode jsonNotification = new ObjectNode(instance); - jsonNotification.put("id", notification.getId()); - StringWriter notificationWriter = new StringWriter(); - try { - if (notification instanceof TemplateNotification) { - TemplateContext templateContext = webInterface.getTemplateContextFactory().createTemplateContext().mergeContext(((TemplateNotification) notification).getTemplateContext()); - templateContext.set("core", webInterface.getCore()); - templateContext.set("currentSone", webInterface.getCurrentSoneWithoutCreatingSession(request.getToadletContext())); - templateContext.set("localSones", webInterface.getCore().getLocalSones()); - templateContext.set("request", request); - templateContext.set("currentVersion", SonePlugin.getPluginVersion()); - templateContext.set("hasLatestVersion", webInterface.getCore().getUpdateChecker().hasLatestVersion()); - templateContext.set("latestEdition", webInterface.getCore().getUpdateChecker().getLatestEdition()); - templateContext.set("latestVersion", webInterface.getCore().getUpdateChecker().getLatestVersion()); - templateContext.set("latestVersionTime", webInterface.getCore().getUpdateChecker().getLatestVersionDate()); - templateContext.set("notification", notification); - ((TemplateNotification) notification).render(templateContext, notificationWriter); - } else { - notification.render(notificationWriter); - } - } catch (IOException ioe1) { - /* StringWriter never throws, ignore. */ - } - jsonNotification.put("text", notificationWriter.toString()); - jsonNotification.put("createdTime", notification.getCreatedTime()); - jsonNotification.put("lastUpdatedTime", notification.getLastUpdatedTime()); - jsonNotification.put("dismissable", notification.isDismissable()); - return jsonNotification; - } - - /** - * Creates a JSON object that contains all options that are currently in - * effect for the given Sone (or overall, if the given Sone is {@code null} - * ). - * - * @param currentSone - * The current Sone (may be {@code null}) - * @return The current options - */ - private static JsonNode createJsonOptions(Sone currentSone) { - ObjectNode options = new ObjectNode(instance); - if (currentSone != null) { - options.put("ShowNotification/NewSones", currentSone.getOptions().isShowNewSoneNotifications()); - options.put("ShowNotification/NewPosts", currentSone.getOptions().isShowNewPostNotifications()); - options.put("ShowNotification/NewReplies", currentSone.getOptions().isShowNewReplyNotifications()); - } - return options; - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Json.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Json.kt index 2ee54ee..71c8e81 100644 --- a/src/main/kotlin/net/pterodactylus/sone/utils/Json.kt +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Json.kt @@ -6,8 +6,19 @@ 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 jsonObject(vararg properties: Pair) = jsonObject { - properties.forEach { put(it.first, it.second) } + +fun jsonObject(vararg properties: Pair) = jsonObject { + properties.forEach { + it.second.let { value -> + when (value) { + is String -> put(it.first, value) + is Int -> put(it.first, value) + is Long -> put(it.first, value) + is Boolean -> put(it.first, value) + else -> Unit + } + } + } } fun jsonArray(vararg objects: String?): ArrayNode = objects.fold(ArrayNode(instance), ArrayNode::add) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt new file mode 100644 index 0000000..d218a3d --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt @@ -0,0 +1,76 @@ +package net.pterodactylus.sone.web.ajax + +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.data.SoneOptions +import net.pterodactylus.sone.main.SonePlugin +import net.pterodactylus.sone.utils.jsonArray +import net.pterodactylus.sone.utils.jsonObject +import net.pterodactylus.sone.web.WebInterface +import net.pterodactylus.sone.web.page.FreenetRequest +import net.pterodactylus.util.notify.Notification +import net.pterodactylus.util.notify.TemplateNotification +import java.io.StringWriter + +/** + * AJAX handler to return all current notifications. + */ +class GetNotificationsAjaxPage(webInterface: WebInterface) : JsonPage("getNotifications.ajax", webInterface) { + + override fun needsFormPassword() = false + override fun requiresLogin() = false + + override fun createJsonObject(request: FreenetRequest) = + getCurrentSone(request.toadletContext, false).let { currentSone -> + webInterface.getNotifications(currentSone) + .sortedBy(Notification::getCreatedTime) + .let { notifications -> + createSuccessJsonObject().apply { + put("notificationHash", notifications.hashCode()) + put("options", currentSone?.options.asJsonObject) + put("notifications", notifications.asJsonObject(currentSone, request)) + } + } + } + + private fun Collection.asJsonObject(currentSone: Sone?, freenetRequest: FreenetRequest) = jsonArray( + *map { notification -> + jsonObject( + "id" to notification.id, + "createdTime" to notification.createdTime, + "lastUpdatedTime" to notification.lastUpdatedTime, + "dismissable" to notification.isDismissable, + "text" to if (notification is TemplateNotification) notification.render(currentSone, freenetRequest) else notification.render() + ) + }.toTypedArray() + ) + + private fun TemplateNotification.render(currentSone: Sone?, freenetRequest: FreenetRequest) = StringWriter().use { + val mergedTemplateContext = webInterface.templateContextFactory.createTemplateContext() + .mergeContext(templateContext) + .apply { + this["core"] = webInterface.core + this["currentSone"] = currentSone + this["localSones"] = webInterface.core.localSones + this["request"] = freenetRequest + this["currentVersion"] = SonePlugin.getPluginVersion() + this["hasLatestVersion"] = webInterface.core.updateChecker.hasLatestVersion() + this["latestEdition"] = webInterface.core.updateChecker.latestEdition + this["latestVersion"] = webInterface.core.updateChecker.latestVersion + this["latestVersionTime"] = webInterface.core.updateChecker.latestVersionDate + this["notification"] = this@render + } + it.also { render(mergedTemplateContext, it) } + }.toString() + +} + +private val SoneOptions?.asJsonObject + get() = this?.let { options -> + jsonObject( + "ShowNotification/NewSones" to options.isShowNewSoneNotifications, + "ShowNotification/NewPosts" to options.isShowNewPostNotifications, + "ShowNotification/NewReplies" to options.isShowNewReplyNotifications + ) + } ?: jsonObject {} + +private fun Notification.render() = StringWriter().use { it.also { render(it) } }.toString() diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/JsonTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/JsonTest.kt index b2b0272..9c68e62 100644 --- a/src/test/kotlin/net/pterodactylus/sone/utils/JsonTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/utils/JsonTest.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.instanceOf +import org.hamcrest.Matchers.nullValue import org.junit.Test /** @@ -22,9 +23,17 @@ class JsonTest { } @Test - fun `object node is created with properties`() { - val objectNode = jsonObject("foo" to "bar", "baz" to "quo") - assertThat(objectNode.toString(), equalTo("{\"foo\":\"bar\",\"baz\":\"quo\"}")) + fun `object node is created with correctly-typed properties`() { + val objectNode = jsonObject("string" to "foo", "int" to 1, "long" to 2L, "boolean" to true, "other" to Any()) + assertThat(objectNode["string"].isTextual, equalTo(true)) + assertThat(objectNode["string"].asText(), equalTo("foo")) + assertThat(objectNode["int"].isInt, equalTo(true)) + assertThat(objectNode["int"].asInt(), equalTo(1)) + assertThat(objectNode["long"].isLong, equalTo(true)) + assertThat(objectNode["long"].asLong(), equalTo(2L)) + assertThat(objectNode["boolean"].isBoolean, equalTo(true)) + assertThat(objectNode["boolean"].asBoolean(), equalTo(true)) + assertThat(objectNode["other"], nullValue()) } @Test -- 2.7.4