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).
This commit is contained in:
parent
90d0d10ee5
commit
85fd6ac5a2
|
@ -170,6 +170,11 @@ func AddEdit() http.HandlerFunc {
|
||||||
forum.Private,
|
forum.Private,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
// If this is a Community forum, subscribe the owner to it immediately.
|
||||||
|
if forum.Category == "" {
|
||||||
|
models.CreateForumMembership(currentUser, forum)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
session.FlashError(w, r, "Error creating the forum: %s", err)
|
session.FlashError(w, r, "Error creating the forum: %s", err)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"code.nonshy.com/nonshy/website/pkg/config"
|
||||||
"code.nonshy.com/nonshy/website/pkg/log"
|
"code.nonshy.com/nonshy/website/pkg/log"
|
||||||
"code.nonshy.com/nonshy/website/pkg/models"
|
"code.nonshy.com/nonshy/website/pkg/models"
|
||||||
"code.nonshy.com/nonshy/website/pkg/session"
|
"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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ func New() http.Handler {
|
||||||
mux.Handle("GET /forum", middleware.CertRequired(forum.Landing()))
|
mux.Handle("GET /forum", middleware.CertRequired(forum.Landing()))
|
||||||
mux.Handle("/forum/post", middleware.CertRequired(forum.NewPost()))
|
mux.Handle("/forum/post", middleware.CertRequired(forum.NewPost()))
|
||||||
mux.Handle("GET /forum/thread/{id}", middleware.CertRequired(forum.Thread()))
|
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/explore", middleware.CertRequired(forum.Explore()))
|
||||||
mux.Handle("GET /forum/newest", middleware.CertRequired(forum.Newest()))
|
mux.Handle("GET /forum/newest", middleware.CertRequired(forum.Newest()))
|
||||||
mux.Handle("GET /forum/search", middleware.CertRequired(forum.Search()))
|
mux.Handle("GET /forum/search", middleware.CertRequired(forum.Search()))
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/nonshy-prefers-dark.css?build={{.BuildHash}}">
|
<link rel="stylesheet" type="text/css" href="/static/css/nonshy-prefers-dark.css?build={{.BuildHash}}">
|
||||||
{{end}}
|
{{end}}
|
||||||
<link rel="stylesheet" href="/static/fontawesome-free-6.1.2-web/css/all.css">
|
<link rel="stylesheet" href="/static/fontawesome-free-6.6.0-web/css/all.css">
|
||||||
<link rel="stylesheet" href="/static/css/theme.css?build={{.BuildHash}}">
|
<link rel="stylesheet" href="/static/css/theme.css?build={{.BuildHash}}">
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
<title>{{template "title" .}} - {{ .Title }}</title>
|
<title>{{template "title" .}} - {{ .Title }}</title>
|
||||||
|
|
|
@ -368,6 +368,54 @@
|
||||||
{{SimplePager .Pager}}
|
{{SimplePager .Pager}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Moderator controls -->
|
||||||
|
{{if .CanModerate}}
|
||||||
|
<div class="block p-2">
|
||||||
|
<form method="POST" action="/forum/thread/{{.Thread.ID}}/moderate">
|
||||||
|
{{InputCSRF}}
|
||||||
|
|
||||||
|
<div class="field has-addons">
|
||||||
|
<p class="control">
|
||||||
|
<button type="button" class="button is-small has-text-info"
|
||||||
|
onclick="alert('Note: These are your moderator controls for this forum thread.')">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fa fa-user-tie"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Pin/Unpin -->
|
||||||
|
{{if or (eq .Forum.OwnerID .CurrentUser.ID) (.CurrentUser.HasAdminScope "admin.forum.manage")}}
|
||||||
|
<p class="control">
|
||||||
|
<button type="submit" class="button is-small has-text-success"
|
||||||
|
name="intent"
|
||||||
|
value="{{if .Thread.Pinned}}un{{end}}pin"
|
||||||
|
onclick="return confirm('Are you sure you want to {{if .Thread.Pinned}}un{{end}}pin this thread to the top of the forum?')">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-thumbtack{{if .Thread.Pinned}}-slash{{end}}"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{if .Thread.Pinned}}Unp{{else}}P{{end}}in thread</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<!-- Lock/Unlock -->
|
||||||
|
<p class="control">
|
||||||
|
<button type="submit" class="button is-small has-text-warning"
|
||||||
|
name="intent"
|
||||||
|
value="{{if .Thread.NoReply}}un{{end}}lock"
|
||||||
|
onclick="return confirm('Do you want to {{if .Thread.NoReply}}UN{{end}}LOCK this thread?\n\nA locked thread will not accept any new replies.')">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fa fa-ban"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{if .Thread.NoReply}}Unl{{else}}L{{end}}ock replies</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if .Thread.NoReply}}
|
{{if .Thread.NoReply}}
|
||||||
<div class="block notification is-warning is-light">
|
<div class="block notification is-warning is-light">
|
||||||
<i class="fa fa-ban pr-2"></i>
|
<i class="fa fa-ban pr-2"></i>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user