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.
239 lines
7.5 KiB
Go
239 lines
7.5 KiB
Go
package forum
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
"code.nonshy.com/nonshy/website/pkg/markdown"
|
|
"code.nonshy.com/nonshy/website/pkg/models"
|
|
"code.nonshy.com/nonshy/website/pkg/session"
|
|
"code.nonshy.com/nonshy/website/pkg/templates"
|
|
)
|
|
|
|
// NewPost view.
|
|
func NewPost() http.HandlerFunc {
|
|
tmpl := templates.Must("forum/new_post.html")
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Query params.
|
|
var (
|
|
fragment = r.FormValue("to") // forum to (new post)
|
|
toThreadID = r.FormValue("thread") // add reply to a thread ID
|
|
quoteCommentID = r.FormValue("quote") // add reply to thread while quoting a comment
|
|
editCommentID = r.FormValue("edit") // edit your comment
|
|
intent = r.FormValue("intent") // preview or submit
|
|
title = r.FormValue("title") // for new forum post only
|
|
message = r.PostFormValue("message") // comment body
|
|
isPinned = r.PostFormValue("pinned") == "true" // owners or admins only
|
|
isExplicit = r.PostFormValue("explicit") == "true" // for thread only
|
|
isNoReply = r.PostFormValue("noreply") == "true" // for thread only
|
|
isDelete = r.FormValue("delete") == "true" // delete comment (along with edit=$id)
|
|
forum *models.Forum
|
|
thread *models.Thread // if replying to a thread
|
|
comment *models.Comment // if editing a comment
|
|
|
|
// If we are modifying a comment (post) and it's the OG post of the
|
|
// thread, we show and accept the thread settings to be updated as
|
|
// well (pinned, explicit, noreply)
|
|
isOriginalComment bool
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// Look up the forum itself.
|
|
if found, err := models.ForumByFragment(fragment); err != nil {
|
|
session.FlashError(w, r, "Couldn't post to forum %s: not found.", fragment)
|
|
templates.Redirect(w, "/forum")
|
|
return
|
|
} else {
|
|
forum = found
|
|
}
|
|
|
|
// Are we manipulating a reply to an existing thread?
|
|
if len(toThreadID) > 0 {
|
|
if i, err := strconv.Atoi(toThreadID); err == nil {
|
|
if found, err := models.GetThread(uint64(i)); err != nil {
|
|
session.FlashError(w, r, "Couldn't find that thread ID!")
|
|
templates.Redirect(w, fmt.Sprintf("/f/%s", forum.Fragment))
|
|
return
|
|
} else {
|
|
thread = found
|
|
}
|
|
}
|
|
}
|
|
|
|
// Are we pre-filling the message with a quotation of an existing comment?
|
|
if len(quoteCommentID) > 0 {
|
|
if i, err := strconv.Atoi(quoteCommentID); err == nil {
|
|
if comment, err := models.GetComment(uint64(i)); err == nil {
|
|
message = markdown.Quotify(comment.Message) + "\n\n"
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
if currentUser.ID != comment.UserID && !currentUser.IsAdmin {
|
|
templates.ForbiddenPage(w, r)
|
|
return
|
|
}
|
|
|
|
// Initialize the form w/ the content of this message.
|
|
if r.Method == http.MethodGet {
|
|
message = comment.Message
|
|
}
|
|
|
|
// Is this the OG thread of the post?
|
|
if thread.CommentID == comment.ID {
|
|
isOriginalComment = true
|
|
|
|
// Restore the checkbox option form values from thread settings.
|
|
if r.Method == http.MethodGet {
|
|
isPinned = thread.Pinned
|
|
isExplicit = thread.Explicit
|
|
isNoReply = thread.NoReply
|
|
}
|
|
}
|
|
|
|
// Are we DELETING this comment?
|
|
if isDelete {
|
|
if err := thread.DeleteReply(comment); err != nil {
|
|
session.FlashError(w, r, "Error deleting your post: %s", err)
|
|
} else {
|
|
session.Flash(w, r, "Your post has been deleted.")
|
|
}
|
|
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
|
|
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" {
|
|
// Are we modifying an existing comment?
|
|
if comment != nil {
|
|
comment.Message = message
|
|
|
|
// Can we update the thread props?
|
|
if isOriginalComment {
|
|
thread.Pinned = isPinned
|
|
thread.Explicit = isExplicit
|
|
thread.NoReply = isNoReply
|
|
if err := thread.Save(); err != nil {
|
|
session.FlashError(w, r, "Couldn't save thread properties: %s", err)
|
|
}
|
|
}
|
|
|
|
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, fmt.Sprintf("/forum/thread/%d", thread.ID))
|
|
return
|
|
}
|
|
|
|
// Are we replying to an existing thread?
|
|
if thread != nil {
|
|
if _, err := thread.Reply(currentUser, message); err != nil {
|
|
session.FlashError(w, r, "Couldn't add reply to thread: %s", err)
|
|
} else {
|
|
session.Flash(w, r, "Reply added to the thread!")
|
|
|
|
// Notify watchers about this new post.
|
|
for _, userID := range models.GetSubscribers("threads", thread.ID) {
|
|
if userID == currentUser.ID {
|
|
continue
|
|
}
|
|
|
|
notif := &models.Notification{
|
|
UserID: userID,
|
|
AboutUser: *currentUser,
|
|
Type: models.NotificationAlsoPosted,
|
|
TableName: "threads",
|
|
TableID: thread.ID,
|
|
Message: message,
|
|
Link: fmt.Sprintf("/forum/thread/%d", thread.ID),
|
|
}
|
|
if err := models.CreateNotification(notif); err != nil {
|
|
log.Error("Couldn't create thread reply notification for subscriber %d: %s", userID, err)
|
|
}
|
|
}
|
|
|
|
// Subscribe the current user to further responses on this thread.
|
|
if _, err := models.SubscribeTo(currentUser, "threads", thread.ID); err != nil {
|
|
log.Error("Couldn't subscribe user %d to forum thread %d: %s", currentUser.ID, thread.ID, err)
|
|
}
|
|
}
|
|
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
|
|
return
|
|
}
|
|
|
|
// Create a new thread?
|
|
if thread, err := models.CreateThread(
|
|
currentUser,
|
|
forum.ID,
|
|
title,
|
|
message,
|
|
isPinned,
|
|
isExplicit,
|
|
isNoReply,
|
|
); err != nil {
|
|
session.FlashError(w, r, "Couldn't create thread: %s", err)
|
|
} else {
|
|
session.Flash(w, r, "Thread created!")
|
|
|
|
// Subscribe the current user to responses on this thread.
|
|
if _, err := models.SubscribeTo(currentUser, "threads", thread.ID); err != nil {
|
|
log.Error("Couldn't subscribe user %d to forum thread %d: %s", currentUser.ID, thread.ID, err)
|
|
}
|
|
|
|
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
var vars = map[string]interface{}{
|
|
"Forum": forum,
|
|
"Thread": thread,
|
|
"Intent": intent,
|
|
"PostTitle": title,
|
|
"EditCommentID": editCommentID,
|
|
"EditThreadSettings": isOriginalComment,
|
|
"Message": message,
|
|
|
|
// Thread settings (for editing the original comment esp.)
|
|
"IsPinned": isPinned,
|
|
"IsExplicit": isExplicit,
|
|
"IsNoReply": isNoReply,
|
|
}
|
|
if err := tmpl.Execute(w, r, vars); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
})
|
|
}
|