website/pkg/controller/comment/post_comment.go

264 lines
8.1 KiB
Go
Raw Normal View History

package comment
import (
2023-06-16 02:40:40 +00:00
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"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"
"code.nonshy.com/nonshy/website/pkg/templates"
)
// PostComment view - for previewing or submitting your comment.
func PostComment() http.HandlerFunc {
tmpl := templates.Must("comment/post_comment.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Query params.
var (
tableName = r.FormValue("table_name")
tableID uint64
editCommentID = r.FormValue("edit") // edit your comment
isDelete = r.FormValue("delete") == "true"
intent = r.FormValue("intent") // preview or submit
message = r.PostFormValue("message") // comment body
comment *models.Comment // if editing a comment
fromURL = r.FormValue("next") // what page to send back to
)
// Parse the table ID param.
if idStr := r.FormValue("table_id"); idStr == "" {
session.FlashError(w, r, "Comment table ID required.")
templates.Redirect(w, "/")
return
} else {
if idInt, err := strconv.Atoi(idStr); err != nil {
session.FlashError(w, r, "Comment table ID invalid.")
templates.Redirect(w, "/")
return
} else {
tableID = uint64(idInt)
}
}
// Redirect URL must be relative.
if !strings.HasPrefix(fromURL, "/") {
// Maybe it's URL encoded?
fromURL, _ = url.QueryUnescape(fromURL)
if !strings.HasPrefix(fromURL, "/") {
fromURL = "/"
}
}
// Validate everything else.
if _, ok := models.CommentableTables[tableName]; !ok {
session.FlashError(w, r, "You can not comment on that.")
templates.Redirect(w, "/")
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
}
// Who will we notify about this comment? e.g. if commenting on a photo,
// this is the user who owns the photo.
var notifyUser *models.User
switch tableName {
case "photos":
if photo, err := models.GetPhoto(tableID); err == nil {
if user, err := models.GetUser(photo.UserID); err == nil {
notifyUser = user
} else {
log.Error("Comments: couldn't get NotifyUser for photo ID %d (user ID %d): %s",
tableID, photo.UserID, err,
)
}
} else {
log.Error("Comments: couldn't get NotifyUser for photo ID %d: %s", tableID, err)
}
}
// Are we editing or deleting our comment?
if len(editCommentID) > 0 {
if i, err := strconv.Atoi(editCommentID); err == nil {
if found, err := models.GetComment(uint64(i)); err == nil {
comment = found
// Verify that it is indeed OUR comment to manage:
// - If the current user posted it
// - If we are an admin
// - If we are the notifyUser for this comment (they can delete, not edit).
if currentUser.ID != comment.UserID && !currentUser.IsAdmin &&
!(notifyUser != nil && currentUser.ID == notifyUser.ID && isDelete) {
templates.ForbiddenPage(w, r)
return
}
// Initialize the form w/ the content of this message.
if r.Method == http.MethodGet {
message = comment.Message
}
// Are we DELETING this comment?
if isDelete {
// Revoke notifications.
models.RemoveNotification("comments", comment.ID)
if err := comment.Delete(); err != nil {
session.FlashError(w, r, "Error deleting your commenting: %s", err)
} else {
session.Flash(w, r, "Your comment has been deleted.")
// Log the change.
models.LogDeleted(&models.User{ID: comment.UserID}, currentUser, "comments", comment.ID, "Deleted a comment.", comment)
}
// Refresh cached like counts.
if tableName == "photos" {
if err := models.UpdatePhotoCachedCounts(tableID); err != nil {
log.Error("UpdatePhotoCachedCount(%d): %s", tableID, err)
}
}
templates.Redirect(w, fromURL)
return
}
} else {
// Comment not found - show the Forbidden page anyway.
templates.ForbiddenPage(w, r)
return
}
} else {
templates.NotFoundPage(w, r)
return
}
}
// Submitting the form.
if r.Method == http.MethodPost {
// Default intent is preview unless told to submit.
if intent == "submit" {
2022-08-27 03:07:55 +00:00
// A message is required.
if message == "" {
session.FlashError(w, r, "A message is required for your comment.")
templates.Redirect(w, fromURL)
return
}
// Are we modifying an existing comment?
if comment != nil {
comment.Message = message
if err := comment.Save(); err != nil {
session.FlashError(w, r, "Couldn't save comment: %s", err)
} else {
session.Flash(w, r, "Comment updated!")
// Log the change.
models.LogUpdated(&models.User{ID: comment.UserID}, currentUser, "comments", comment.ID, "Updated a comment.\n\n---\n\n"+comment.Message, nil)
}
templates.Redirect(w, fromURL)
return
}
// Create the comment.
if comment, err := models.AddComment(
currentUser,
tableName,
tableID,
message,
); err != nil {
session.FlashError(w, r, "Couldn't create comment: %s", err)
} else {
session.Flash(w, r, "Comment added!")
templates.Redirect(w, fromURL)
// Refresh cached comment counts.
if tableName == "photos" {
if err := models.UpdatePhotoCachedCounts(tableID); err != nil {
log.Error("UpdatePhotoCachedCount(%d): %s", tableID, err)
}
}
// Log the change.
models.LogCreated(currentUser, "comments", comment.ID, "Posted a new comment.\n\n---\n\n"+message)
// Notify the recipient of the comment.
if notifyUser != nil && notifyUser.ID != currentUser.ID && !notifyUser.NotificationOptOut(config.NotificationOptOutComments) {
notif := &models.Notification{
UserID: notifyUser.ID,
AboutUser: *currentUser,
Type: models.NotificationComment,
TableName: comment.TableName,
TableID: comment.TableID,
Message: message,
Link: fmt.Sprintf("/go/comment?id=%d", comment.ID),
}
if err := models.CreateNotification(notif); err != nil {
log.Error("Couldn't create Comment notification: %s", err)
}
}
// Notify subscribers to this comment thread (filter the subscribers by the blocking status of the current user).
for _, userID := range models.FilterBlockingUserIDs(currentUser, models.GetSubscribers(comment.TableName, comment.TableID)) {
if notifyUser != nil && userID == notifyUser.ID {
// Don't notify the recipient twice.
continue
} else if userID == currentUser.ID {
// Don't notify the poster of the comment.
continue
}
notif := &models.Notification{
UserID: userID,
AboutUser: *currentUser,
Type: models.NotificationAlsoCommented,
TableName: comment.TableName,
TableID: comment.TableID,
Message: message,
Link: fmt.Sprintf("/go/comment?id=%d", comment.ID),
}
if err := models.CreateNotification(notif); err != nil {
log.Error("Couldn't create Comment notification for subscriber %d: %s", userID, err)
}
}
// Subscribe the current user to this comment thread, so they are
// notified if other users add followup comments.
if !currentUser.NotificationOptOut(config.NotificationOptOutSubscriptions) {
if _, err := models.SubscribeTo(currentUser, comment.TableName, comment.TableID); err != nil {
log.Error("Couldn't subscribe user %d to comment thread %s/%d: %s",
currentUser.ID, comment.TableName, comment.TableID, err,
)
}
}
return
}
}
}
var vars = map[string]interface{}{
"Intent": intent,
"EditCommentID": editCommentID,
"Message": message,
"TableName": tableName,
"TableID": tableID,
"Next": fromURL,
}
if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}