From 85fd6ac5a204f50c7d3446373f7b664c5bba9c96 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Fri, 23 Aug 2024 22:24:38 -0700 Subject: [PATCH] Thread Moderator Buttons: Pin and Lock * The bottoms of threads have moderator buttons now, to easily Pin or Unpin the thread (for Owners + Admins) or to Lock/Unlock the thread (all moderators). --- pkg/controller/forum/add_edit.go | 5 ++ pkg/controller/forum/moderators.go | 89 ++++++++++++++++++++++++++++++ pkg/router/router.go | 1 + web/templates/base.html | 2 +- web/templates/forum/thread.html | 48 ++++++++++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) diff --git a/pkg/controller/forum/add_edit.go b/pkg/controller/forum/add_edit.go index ba196f2..0456cec 100644 --- a/pkg/controller/forum/add_edit.go +++ b/pkg/controller/forum/add_edit.go @@ -170,6 +170,11 @@ func AddEdit() http.HandlerFunc { forum.Private, )) + // If this is a Community forum, subscribe the owner to it immediately. + if forum.Category == "" { + models.CreateForumMembership(currentUser, forum) + } + return } else { session.FlashError(w, r, "Error creating the forum: %s", err) diff --git a/pkg/controller/forum/moderators.go b/pkg/controller/forum/moderators.go index 0c1485c..1f0a847 100644 --- a/pkg/controller/forum/moderators.go +++ b/pkg/controller/forum/moderators.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + "code.nonshy.com/nonshy/website/pkg/config" "code.nonshy.com/nonshy/website/pkg/log" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/session" @@ -138,3 +139,91 @@ func ManageModerators() http.HandlerFunc { } }) } + +// ModerateThread endpoint - perform a mod action like pinning or locking a thread. +func ModerateThread() http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Query params. + var ( + threadID, err = strconv.Atoi(r.PathValue("id")) + intent = r.PostFormValue("intent") + nextURL = fmt.Sprintf("/forum/thread/%d", threadID) + ) + + if err != nil { + session.FlashError(w, r, "Invalid thread ID.") + templates.Redirect(w, nextURL) + return + } + + // Get the current user. + currentUser, err := session.CurrentUser(r) + if err != nil { + session.FlashError(w, r, "Couldn't get current user: %s", err) + templates.Redirect(w, "/") + return + } + + // Get this thread. + thread, err := models.GetThread(uint64(threadID)) + if err != nil { + templates.NotFoundPage(w, r) + return + } + + // Get its forum. + forum, err := models.GetForum(thread.ForumID) + if err != nil { + templates.NotFoundPage(w, r) + return + } + + // User must at least be able to moderate. + if !forum.CanBeModeratedBy(currentUser) { + templates.ForbiddenPage(w, r) + return + } + + // Does the user have Ownership level access (including privileged admins) + var isOwner = forum.OwnerID == currentUser.ID || currentUser.HasAdminScope(config.ScopeForumAdmin) + + /**** + * Moderator level permissions. + ***/ + switch intent { + case "lock": + thread.NoReply = true + session.Flash(w, r, "This thread has been locked and will not be accepting any new replies.") + case "unlock": + thread.NoReply = false + session.Flash(w, r, "This thread has been unlocked and can accept new replies again.") + default: + if !isOwner { + // End of the road. + templates.ForbiddenPage(w, r) + return + } + } + + /**** + * Owner + Admin level permissions. + ***/ + switch intent { + case "pin": + thread.Pinned = true + session.Flash(w, r, "This thread is now pinned to the top of the forum.") + case "unpin": + thread.Pinned = false + session.Flash(w, r, "This thread will no longer be pinned to the top of the forum.") + default: + session.FlashError(w, r, "Unknown moderator action.") + } + + // Save changes to the thread. + if err := thread.Save(); err != nil { + session.FlashError(w, r, "Error saving thread: %s", err) + } + + templates.Redirect(w, nextURL) + }) +} diff --git a/pkg/router/router.go b/pkg/router/router.go index 1b62f49..1bad99b 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -87,6 +87,7 @@ func New() http.Handler { mux.Handle("GET /forum", middleware.CertRequired(forum.Landing())) mux.Handle("/forum/post", middleware.CertRequired(forum.NewPost())) mux.Handle("GET /forum/thread/{id}", middleware.CertRequired(forum.Thread())) + mux.Handle("POST /forum/thread/{id}/moderate", middleware.CertRequired(forum.ModerateThread())) mux.Handle("GET /forum/explore", middleware.CertRequired(forum.Explore())) mux.Handle("GET /forum/newest", middleware.CertRequired(forum.Newest())) mux.Handle("GET /forum/search", middleware.CertRequired(forum.Search())) diff --git a/web/templates/base.html b/web/templates/base.html index 7473620..bbc56a0 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -17,7 +17,7 @@ {{else}} {{end}} - + {{template "title" .}} - {{ .Title }} diff --git a/web/templates/forum/thread.html b/web/templates/forum/thread.html index ffafbf1..f7b91aa 100644 --- a/web/templates/forum/thread.html +++ b/web/templates/forum/thread.html @@ -368,6 +368,54 @@ {{SimplePager .Pager}} + +{{if .CanModerate}} +
+
+ {{InputCSRF}} + +
+

+ +

+ + + {{if or (eq .Forum.OwnerID .CurrentUser.ID) (.CurrentUser.HasAdminScope "admin.forum.manage")}} +

+ +

+ {{end}} + + +

+ +

+
+
+
+{{end}} + {{if .Thread.NoReply}}