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 } }) }