Noah
aa8d719fc4
* Add ability to (un)subscribe from comment threads on Forums and Photos. * Creating a forum post, replying to a post or adding a comment to a photo automatically subscribes you to be notified when somebody else adds a comment to the thing later. * At the top of each comment thread is a link to disable or re-enable your subscription. You can join a subscription without even needing to comment. If you click to disable notifications, they stay disabled even if you add another comment later.
233 lines
6.8 KiB
Go
233 lines
6.8 KiB
Go
package comment
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"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 {
|
|
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.")
|
|
}
|
|
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" {
|
|
// 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!")
|
|
}
|
|
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)
|
|
|
|
// Notify the recipient of the comment.
|
|
if notifyUser != nil && notifyUser.ID != currentUser.ID {
|
|
notif := &models.Notification{
|
|
UserID: notifyUser.ID,
|
|
AboutUser: *currentUser,
|
|
Type: models.NotificationComment,
|
|
TableName: comment.TableName,
|
|
TableID: comment.TableID,
|
|
Message: message,
|
|
Link: fromURL,
|
|
}
|
|
if err := models.CreateNotification(notif); err != nil {
|
|
log.Error("Couldn't create Comment notification: %s", err)
|
|
}
|
|
}
|
|
|
|
// Notify subscribers to this comment thread.
|
|
for _, userID := range 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: fromURL,
|
|
}
|
|
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 _, 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
|
|
}
|
|
})
|
|
}
|