2022-08-27 02:50:33 +00:00
|
|
|
package comment
|
|
|
|
|
|
|
|
import (
|
2023-06-16 02:40:40 +00:00
|
|
|
"fmt"
|
2022-08-27 02:50:33 +00:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2023-10-28 21:34:35 +00:00
|
|
|
"code.nonshy.com/nonshy/website/pkg/config"
|
2022-08-27 02:50:33 +00:00
|
|
|
"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 {
|
2023-10-28 21:34:35 +00:00
|
|
|
// Revoke notifications.
|
|
|
|
models.RemoveNotification("comments", comment.ID)
|
|
|
|
|
2022-08-27 02:50:33 +00:00
|
|
|
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.")
|
Change Logs
* Add a ChangeLog table to collect historic updates to various database tables.
* Created, Updated (with field diffs) and Deleted actions are logged, as well
as certification photo approves/denies.
* Specific items added to the change log:
* When a user photo is marked Explicit by an admin
* When users block/unblock each other
* When photo comments are posted, edited, and deleted
* When forums are created, edited, and deleted
* When forum comments are created, edited and deleted
* When a new forum thread is created
* When a user uploads or removes their own certification photo
* When an admin approves or rejects a certification photo
* When a user uploads, modifies or deletes their gallery photos
* When a friend request is sent
* When a friend request is accepted, ignored, or rejected
* When a friendship is removed
2024-02-26 01:03:36 +00:00
|
|
|
|
|
|
|
// Log the change.
|
|
|
|
models.LogDeleted(&models.User{ID: comment.UserID}, currentUser, "comments", comment.ID, "Deleted a comment.", comment)
|
2022-08-27 02:50:33 +00:00
|
|
|
}
|
2024-09-22 00:25:36 +00:00
|
|
|
|
|
|
|
// Refresh cached like counts.
|
|
|
|
if tableName == "photos" {
|
|
|
|
if err := models.UpdatePhotoCachedCounts(tableID); err != nil {
|
|
|
|
log.Error("UpdatePhotoCachedCount(%d): %s", tableID, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-27 02:50:33 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-08-27 02:50:33 +00:00
|
|
|
// 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!")
|
Change Logs
* Add a ChangeLog table to collect historic updates to various database tables.
* Created, Updated (with field diffs) and Deleted actions are logged, as well
as certification photo approves/denies.
* Specific items added to the change log:
* When a user photo is marked Explicit by an admin
* When users block/unblock each other
* When photo comments are posted, edited, and deleted
* When forums are created, edited, and deleted
* When forum comments are created, edited and deleted
* When a new forum thread is created
* When a user uploads or removes their own certification photo
* When an admin approves or rejects a certification photo
* When a user uploads, modifies or deletes their gallery photos
* When a friend request is sent
* When a friend request is accepted, ignored, or rejected
* When a friendship is removed
2024-02-26 01:03:36 +00:00
|
|
|
|
|
|
|
// Log the change.
|
|
|
|
models.LogUpdated(&models.User{ID: comment.UserID}, currentUser, "comments", comment.ID, "Updated a comment.\n\n---\n\n"+comment.Message, nil)
|
2022-08-27 02:50:33 +00:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
|
2024-09-22 00:25:36 +00:00
|
|
|
// Refresh cached comment counts.
|
|
|
|
if tableName == "photos" {
|
|
|
|
if err := models.UpdatePhotoCachedCounts(tableID); err != nil {
|
|
|
|
log.Error("UpdatePhotoCachedCount(%d): %s", tableID, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Change Logs
* Add a ChangeLog table to collect historic updates to various database tables.
* Created, Updated (with field diffs) and Deleted actions are logged, as well
as certification photo approves/denies.
* Specific items added to the change log:
* When a user photo is marked Explicit by an admin
* When users block/unblock each other
* When photo comments are posted, edited, and deleted
* When forums are created, edited, and deleted
* When forum comments are created, edited and deleted
* When a new forum thread is created
* When a user uploads or removes their own certification photo
* When an admin approves or rejects a certification photo
* When a user uploads, modifies or deletes their gallery photos
* When a friend request is sent
* When a friend request is accepted, ignored, or rejected
* When a friendship is removed
2024-02-26 01:03:36 +00:00
|
|
|
// Log the change.
|
|
|
|
models.LogCreated(currentUser, "comments", comment.ID, "Posted a new comment.\n\n---\n\n"+message)
|
|
|
|
|
2022-08-27 02:50:33 +00:00
|
|
|
// Notify the recipient of the comment.
|
2023-10-28 21:34:35 +00:00
|
|
|
if notifyUser != nil && notifyUser.ID != currentUser.ID && !notifyUser.NotificationOptOut(config.NotificationOptOutComments) {
|
2022-08-27 02:50:33 +00:00
|
|
|
notif := &models.Notification{
|
|
|
|
UserID: notifyUser.ID,
|
|
|
|
AboutUser: *currentUser,
|
|
|
|
Type: models.NotificationComment,
|
|
|
|
TableName: comment.TableName,
|
|
|
|
TableID: comment.TableID,
|
|
|
|
Message: message,
|
2024-01-06 06:14:42 +00:00
|
|
|
Link: fmt.Sprintf("/go/comment?id=%d", comment.ID),
|
2022-08-27 02:50:33 +00:00
|
|
|
}
|
|
|
|
if err := models.CreateNotification(notif); err != nil {
|
|
|
|
log.Error("Couldn't create Comment notification: %s", err)
|
|
|
|
}
|
|
|
|
}
|
2022-08-27 18:42:48 +00:00
|
|
|
|
2023-09-17 06:07:32 +00:00
|
|
|
// 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)) {
|
2022-08-27 18:42:48 +00:00
|
|
|
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,
|
2024-01-06 06:14:42 +00:00
|
|
|
Link: fmt.Sprintf("/go/comment?id=%d", comment.ID),
|
2022-08-27 18:42:48 +00:00
|
|
|
}
|
|
|
|
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.
|
2023-10-28 21:34:35 +00:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|
2022-08-27 18:42:48 +00:00
|
|
|
}
|
|
|
|
|
2022-08-27 02:50:33 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|