website/pkg/controller/admin/feedback.go
Noah Petherbridge 170cd11f9c Minor improvements
* Add a "Report" link to the footer of forums.
* Allow some non-admin users to view a private forum and its threads.
  * Moderators and approved followers can see it
  * Note: the endpoint to follow a forum won't let a user invite themselves
    to a private forum. Currently there is no way to approve a user except
    by also adding them as a moderator.
  * Explore and Newest tabs can show these private forums if viewable.
2024-08-21 23:06:06 -07:00

185 lines
5.4 KiB
Go

package admin
import (
"fmt"
"net/http"
"strconv"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/models"
"code.nonshy.com/nonshy/website/pkg/session"
"code.nonshy.com/nonshy/website/pkg/templates"
)
// Feedback controller (/admin/feedback)
func Feedback() http.HandlerFunc {
tmpl := templates.Must("admin/feedback.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Query params.
var (
acknowledged = r.FormValue("acknowledged") == "true"
intent = r.FormValue("intent")
visit = r.FormValue("visit") == "true" // visit the linked table ID
profile = r.FormValue("profile") == "true" // visit associated user profile
verdict = r.FormValue("verdict")
fb *models.Feedback
)
currentUser, err := session.CurrentUser(r)
if err != nil {
session.FlashError(w, r, "Couldn't get your current user: %s", err)
}
// Working on a target message?
if idStr := r.FormValue("id"); idStr != "" {
if idInt, err := strconv.Atoi(idStr); err != nil {
session.FlashError(w, r, "Couldn't parse id param: %s", err)
} else {
fb, err = models.GetFeedback(uint64(idInt))
if err != nil {
session.FlashError(w, r, "Couldn't load feedback message %d: %s", idInt, err)
}
}
}
// Are we visiting a linked resource (via TableID)?
if fb != nil && fb.TableID > 0 && visit {
switch fb.TableName {
case "users":
user, err := models.GetUser(fb.TableID)
if err != nil {
session.FlashError(w, r, "Couldn't visit user %d: %s", fb.TableID, err)
} else {
templates.Redirect(w, "/u/"+user.Username)
return
}
case "photos":
pic, err := models.GetPhoto(fb.TableID)
if err != nil {
session.FlashError(w, r, "Couldn't get photo %d: %s", fb.TableID, err)
} else {
// Going to the user's profile page?
if profile {
user, err := models.GetUser(pic.UserID)
if err != nil {
session.FlashError(w, r, "Couldn't visit user %d: %s", fb.TableID, err)
} else {
templates.Redirect(w, "/u/"+user.Username)
return
}
}
// Direct link to the photo.
templates.Redirect(w, fmt.Sprintf("/photo/view?id=%d", fb.TableID))
return
}
case "messages":
// To read this message we will need to impersonate the reporter.
user, err := models.GetUser(fb.UserID)
if err != nil {
session.FlashError(w, r, "Couldn't get reporting user ID %d: %s", fb.UserID, err)
} else {
if err := session.ImpersonateUser(w, r, user, currentUser, "Clicked from user reported Message via admin dashboard"); err != nil {
session.FlashError(w, r, "Couldn't impersonate user: %s", err)
} else {
// Redirect to the thread.
session.Flash(w, r, "NOTICE: You are now impersonating %s to view their inbox.", user.Username)
templates.Redirect(w, fmt.Sprintf("/messages/read/%d", fb.TableID))
return
}
}
case "comments":
// Get this comment.
comment, err := models.GetComment(fb.TableID)
if err != nil {
session.FlashError(w, r, "Couldn't get comment ID %d: %s", fb.TableID, err)
} else {
// What was the comment on?
switch comment.TableName {
case "threads":
// Visit the thread.
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", comment.TableID))
return
}
}
case "forums":
// Get this forum.
forum, err := models.GetForum(fb.TableID)
if err != nil {
session.FlashError(w, r, "Couldn't get comment ID %d: %s", fb.TableID, err)
} else {
templates.Redirect(w, fmt.Sprintf("/f/%s", forum.Fragment))
return
}
default:
session.FlashError(w, r, "Couldn't visit TableID %s/%d: not a supported TableName", fb.TableName, fb.TableID)
}
}
// Are we (un)acknowledging a message?
if r.Method == http.MethodPost {
if fb == nil {
session.FlashError(w, r, "Missing feedback ID for this POST!")
} else {
switch verdict {
case "acknowledge":
fb.Acknowledged = true
if err := fb.Save(); err != nil {
session.FlashError(w, r, "Couldn't save message: %s", err)
} else {
session.Flash(w, r, "Message acknowledged!")
}
case "unacknowledge":
fb.Acknowledged = false
if err := fb.Save(); err != nil {
session.FlashError(w, r, "Couldn't save message: %s", err)
} else {
session.Flash(w, r, "Message acknowledged!")
}
default:
session.FlashError(w, r, "Unsupported verdict: %s", verdict)
}
}
templates.Redirect(w, r.URL.Path)
return
}
// Get the feedback.
pager := &models.Pagination{
Page: 1,
PerPage: config.PageSizeAdminFeedback,
Sort: "updated_at desc",
}
pager.ParsePage(r)
page, err := models.PaginateFeedback(acknowledged, intent, pager)
if err != nil {
session.FlashError(w, r, "Couldn't load feedback from DB: %s", err)
}
// Map user IDs.
var userIDs = []uint64{}
for _, p := range page {
if p.UserID > 0 {
userIDs = append(userIDs, p.UserID)
}
}
userMap, err := models.MapUsers(currentUser, userIDs)
if err != nil {
session.FlashError(w, r, "Couldn't map user IDs: %s", err)
}
var vars = map[string]interface{}{
"Intent": intent,
"Acknowledged": acknowledged,
"Feedback": page,
"UserMap": userMap,
"Pager": pager,
}
if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}