Forbid blank comments and rework error popup
authorLanius Trolling <lanius@laniustrolling.dev>
Mon, 27 Feb 2023 19:26:28 +0000 (14:26 -0500)
committerLanius Trolling <lanius@laniustrolling.dev>
Mon, 27 Feb 2023 19:26:28 +0000 (14:26 -0500)
src/main/kotlin/info/mechyrdia/data/views_comment.kt
src/main/kotlin/info/mechyrdia/lore/http_utils.kt
src/main/kotlin/info/mechyrdia/lore/view_tpl.kt
src/main/resources/static/init.js

index be3a69f4f29e3220fa35dd942efe95fce38b44ce..9fa64874432aa7bb6494a21e38006a91a972ecaa 100644 (file)
@@ -68,10 +68,13 @@ suspend fun ApplicationCall.newCommentRoute(): Nothing {
        val pagePath = pagePathParts.joinToString("/")
        
        val formParams = verifyCsrfToken()
-       val loggedInAs = currentNation() ?: redirectWithError("/auth/login", "You must be logged in to write comments")
+       val loggedInAs = currentNation() ?: redirectWithError("/auth/login", "You must be logged in to write comments", "comments")
        
        val contents = formParams.getOrFail("comment")
        
+       if (contents.isBlank())
+               redirectWithError("/lore/$pagePath", "Comments may not be blank", "comments")
+       
        val comment = Comment(
                id = Id(),
                submittedBy = loggedInAs.id,
@@ -99,9 +102,13 @@ suspend fun ApplicationCall.viewCommentRoute(): Nothing {
        val submitter = nationCache.getNation(comment.submittedBy)
        
        if (submitter.isBanned && currentNation?.id != comment.submittedBy && currentNation?.id != OwnerNationId)
-               redirectWithError("/lore/${comment.submittedIn}", "The user who posted that comment is banned from commenting")
+               redirectWithError("/lore/${comment.submittedIn}", "The user who posted that comment is banned from commenting", "comments")
+       
+       val queryParams = if (request.queryParameters.isEmpty())
+               ""
+       else "?${request.queryParameters.formUrlEncode()}"
        
-       redirect("/lore/${comment.submittedIn}#comment-$commentId")
+       redirect("/lore/${comment.submittedIn}$queryParams#comment-$commentId")
 }
 
 suspend fun ApplicationCall.editCommentRoute(): Nothing {
@@ -117,6 +124,9 @@ suspend fun ApplicationCall.editCommentRoute(): Nothing {
        
        val newContents = formParams.getOrFail("comment")
        
+       if (newContents.isBlank())
+               redirectWithError("/comment/view/$commentId", "Comments may not be blank")
+       
        // Check for null edits, i.e. edits that don't change anything
        if (newContents == oldComment.contents)
                redirect("/comment/view/$commentId")
index 6ded4da3889b5fbedabd8f3fe2f9d459bfa4b631..923c971a0ff75f954057bdb23859372d9e470c56 100644 (file)
@@ -6,7 +6,9 @@ data class HttpRedirectException(val url: String, val permanent: Boolean) : Runt
 
 fun redirect(url: String, permanent: Boolean = false): Nothing = throw HttpRedirectException(url, permanent)
 
-fun redirectWithError(url: String, error: String): Nothing {
-       val urlWithError = url + "?" + parametersOf("error", error).formUrlEncode()
+fun redirectWithError(url: String, error: String, hash: String? = null): Nothing {
+       val parameters = parametersOf("error", error).formUrlEncode()
+       val markedHash = hash?.let { "#$it" } ?: ""
+       val urlWithError = "$url?$parameters$markedHash"
        redirect(urlWithError, false)
 }
index 78066707453d40cd848671c4f0457599dbb32a1e..6ea34ea71209f927cfe2b78a5e6d247599dbac2e 100644 (file)
@@ -1,6 +1,9 @@
 package info.mechyrdia.lore
 
+import io.ktor.http.*
 import io.ktor.server.application.*
+import io.ktor.server.request.*
+import io.ktor.util.*
 import kotlinx.html.*
 
 fun ApplicationCall.page(pageTitle: String, navBar: List<NavItem>? = null, sidebar: Sidebar? = null, content: SECTIONS.() -> Unit): HTML.() -> Unit {
@@ -74,6 +77,24 @@ fun ApplicationCall.page(pageTitle: String, navBar: List<NavItem>? = null, sideb
                                }
                        }
                        
+                       request.queryParameters["error"]?.let { errorMessage ->
+                               div {
+                                       id = "error-popup"
+                                       
+                                       val paramsWithoutError = parametersOf(request.queryParameters.toMap() - "error")
+                                       val newQueryString = if (paramsWithoutError.isEmpty())
+                                               ""
+                                       else "?${paramsWithoutError.formUrlEncode()}"
+                                       attributes["data-redirect-url"] = "${request.path()}$newQueryString"
+                                       
+                                       div(classes = "bg")
+                                       div(classes = "msg") {
+                                               p { +errorMessage }
+                                               p { +"Click to close this popup" }
+                                       }
+                               }
+                       }
+                       
                        script(src = "/static/init.js") {}
                }
        }
index 5bca11644400402cbcf980be9d889f262fe9a054..419447ac2001cf7194947ba78475a9ac14bf769b 100644 (file)
 
        window.addEventListener("load", function () {
                // Error popup
-               const queryParams = new URLSearchParams(window.location.search);
-               if (queryParams.has("error")) {
-                       const errorMessage = queryParams.get("error");
-
-                       const errorPopup = document.createElement("div");
-                       errorPopup.id = "error-popup";
-
-                       const errorBg = document.createElement("div");
-                       errorBg.classList.add("bg");
-                       errorPopup.append(errorBg);
-
-                       const errorMsg = document.createElement("div");
-                       errorMsg.classList.add("msg");
-
-                       const errorP1 = document.createElement("p");
-                       errorP1.append(document.createTextNode(errorMessage));
-                       errorMsg.append(errorP1);
-
-                       const errorP2 = document.createElement("p");
-                       errorP2.append(document.createTextNode("Click to close this popup"));
-                       errorMsg.append(errorP2);
-
-                       errorPopup.append(errorMsg);
-
-                       document.body.append(errorPopup);
+               const errorPopup = document.getElementById("error-popup");
+               if (errorPopup != null) {
                        errorPopup.addEventListener("click", e => {
                                e.preventDefault();
-                               errorPopup.remove();
+
+                               const thisElement = e.currentTarget;
+                               const newUrl = window.location.origin + thisElement.getAttribute("data-redirect-url") + window.location.hash;
+                               window.history.replaceState({}, '', newUrl);
+
+                               thisElement.remove();
                        });
                }
        });