This repository has been archived on 2022-08-26. You can view files and clone it, but cannot push or open issues or pull requests.
gosocial/pkg/controller/forum/new_post.go
Noah Petherbridge bb08ec56ce Finish Forums + Likes & Notifications
Finish implementing the basic forum features:
* Pinned threads (admin or board owner only)
* Edit Thread settings when you edit the top-most comment.
* NoReply threads remove all the reply buttons.
* Explicit forums and threads are filtered out unless opted-in (admins
  always see them).
* Count the unique members who participated in each forum.
* Get the most recently updated thread to show on forum list page.
* Contact/Report page: handle receiving a comment ID to report on.

Implement Likes & Notifications
* Like buttons added to Photos and Profile Pages. Implemented via simple
  vanilla JS (likes.js) to make ajax requests to back-end to like/unlike.
* Notifications: for your photo or profile being liked. If you unlike,
  the existing notifications about the like are revoked.
* The notifications appear as an alert number in the nav bar and are read
  on the User Dashboard. Click to mark a notification as "read" or click
  the "mark all as read" button.

Update DeleteUser to scrub likes, notifications, threads, and comments.
2022-08-24 21:17:34 -07:00

207 lines
6.3 KiB
Go

package forum
import (
"fmt"
"net/http"
"strconv"
"git.kirsle.net/apps/gosocial/pkg/markdown"
"git.kirsle.net/apps/gosocial/pkg/models"
"git.kirsle.net/apps/gosocial/pkg/session"
"git.kirsle.net/apps/gosocial/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!")
}
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!")
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
}
})
}