From f9a2d471f57c4284fb1d142c00ce63323543f918 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sun, 29 Oct 2023 12:29:11 -0700 Subject: [PATCH] My User Notes page --- pkg/controller/account/user_note.go | 130 ++++++++++++++++++++ pkg/models/user_note.go | 41 +++++++ pkg/router/router.go | 1 + web/templates/account/dashboard.html | 6 + web/templates/account/my_user_notes.html | 144 +++++++++++++++++++++++ web/templates/account/settings.html | 9 ++ web/templates/account/user_notes.html | 6 + 7 files changed, 337 insertions(+) create mode 100644 web/templates/account/my_user_notes.html diff --git a/pkg/controller/account/user_note.go b/pkg/controller/account/user_note.go index abd9aa3..4bb8bdd 100644 --- a/pkg/controller/account/user_note.go +++ b/pkg/controller/account/user_note.go @@ -4,8 +4,10 @@ import ( "net/http" "net/url" "regexp" + "strconv" "code.nonshy.com/nonshy/website/pkg/config" + "code.nonshy.com/nonshy/website/pkg/log" "code.nonshy.com/nonshy/website/pkg/middleware" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/session" @@ -159,3 +161,131 @@ func UserNotes() http.HandlerFunc { } }) } + +// My user notes page (/notes/me) +func MyNotes() http.HandlerFunc { + tmpl := templates.Must("account/my_user_notes.html") + + // Whitelist for ordering options. + var sortWhitelist = []string{ + "updated_at desc", + "updated_at asc", + "username desc", + "username asc", + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Filter parameters. + var ( + search = r.FormValue("search") + sort = r.FormValue("sort") + sortOK bool + ) + + // Sort options. + for _, v := range sortWhitelist { + if sort == v { + sortOK = true + break + } + } + if !sortOK { + sort = sortWhitelist[0] + } + + // Get the current user. + currentUser, err := session.CurrentUser(r) + if err != nil { + session.FlashError(w, r, "You must be signed in to view this page.") + templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String())) + return + } + + // Is the site under a Maintenance Mode restriction? + if middleware.MaintenanceMode(currentUser, w, r) { + return + } + + // Are we deleting a note? + if r.Method == http.MethodPost { + var ( + intent = r.PostFormValue("intent") + idStr = r.PostFormValue("id") + ) + + noteID, err := strconv.Atoi(idStr) + if err != nil { + session.FlashError(w, r, "Invalid note ID.") + templates.Redirect(w, r.URL.Path) + return + } + + note, err := models.GetNote(uint64(noteID)) + if err != nil { + session.FlashError(w, r, "Couldn't find that note.") + templates.Redirect(w, r.URL.Path) + return + } + + // Assert it is our note to edit. + if note.UserID != currentUser.ID { + session.FlashError(w, r, "That is not your note to edit.") + templates.Redirect(w, r.URL.Path) + return + } + + if intent == "delete" { + // Delete it! + if err := note.Delete(); err != nil { + session.FlashError(w, r, "Error deleting the note: %s.", err) + templates.Redirect(w, r.URL.Path) + return + } + session.Flash(w, r, "That note has been deleted!") + } + templates.Redirect(w, r.URL.Path) + return + } + + var ( + pager = &models.Pagination{ + Page: 1, + PerPage: config.PageSizeAdminUserNotes, + Sort: sort, + } + userIDs = []uint64{} + ) + pager.ParsePage(r) + + notes, err := models.PaginateMyUserNotes(currentUser, search, pager) + if err != nil { + session.FlashError(w, r, "Error getting your user notes: %s", err) + templates.Redirect(w, "/") + return + } + + // Map user IDs to users. + for _, note := range notes { + userIDs = append(userIDs, note.AboutUserID) + } + userMap, err := models.MapUsers(currentUser, userIDs) + if err != nil { + log.Error("MyUserNotes: couldn't MapUsers: %s", err) + } + + vars := map[string]interface{}{ + "Notes": notes, + "Pager": pager, + "UserMap": userMap, + + // Search filters + "Search": search, + "Sort": sort, + } + + if err := tmpl.Execute(w, r, vars); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + }) +} diff --git a/pkg/models/user_note.go b/pkg/models/user_note.go index c4040bc..a69f929 100644 --- a/pkg/models/user_note.go +++ b/pkg/models/user_note.go @@ -36,6 +36,13 @@ func GetNoteBetweenUsers(currentUser *User, user *User) *UserNote { return note } +// GetNote finds a user note by its ID. +func GetNote(id uint64) (*UserNote, error) { + p := &UserNote{} + result := DB.First(&p, id) + return p, result.Error +} + // CountNotesAboutUser returns the number of notes (the current user) has about the other user. // // For regular user, will return zero or one; for admins, will return the total count of notes @@ -93,6 +100,40 @@ func PaginateUserNotes(user *User, pager *Pagination) ([]*UserNote, error) { return notes, result.Error } +// PaginateMyUserNotes shows all notes written by the current user about others. +func PaginateMyUserNotes(currentUser *User, search string, pager *Pagination) ([]*UserNote, error) { + var ( + notes = []*UserNote{} + wheres = []string{} + placeholders = []interface{}{} + ilike = "%" + search + "%" + ) + + wheres = append(wheres, "user_notes.user_id = ?") + placeholders = append(placeholders, currentUser.ID) + + // Searching? + if search != "" { + wheres = append(wheres, "(users.username ILIKE ? OR user_notes.message ILIKE ?)") + placeholders = append(placeholders, ilike, ilike) + } + + query := DB.Joins("JOIN users ON users.id = user_notes.about_user_id").Where( + strings.Join(wheres, " AND "), + placeholders..., + ).Order( + pager.Sort, + ) + + query.Model(&UserNote{}).Count(&pager.Total) + + result := query.Offset( + pager.GetOffset(), + ).Limit(pager.PerPage).Find(¬es) + + return notes, result.Error +} + // Save the note. func (p *UserNote) Save() error { if p.ID == 0 { diff --git a/pkg/router/router.go b/pkg/router/router.go index 95ec68c..eb1469d 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -59,6 +59,7 @@ func New() http.Handler { mux.Handle("/photo/private", middleware.LoginRequired(photo.Private())) mux.Handle("/photo/private/share", middleware.LoginRequired(photo.Share())) mux.Handle("/notes/u/", middleware.LoginRequired(account.UserNotes())) + mux.Handle("/notes/me", middleware.LoginRequired(account.MyNotes())) mux.Handle("/messages", middleware.LoginRequired(inbox.Inbox())) mux.Handle("/messages/read/", middleware.LoginRequired(inbox.Inbox())) mux.Handle("/messages/compose", middleware.LoginRequired(inbox.Compose())) diff --git a/web/templates/account/dashboard.html b/web/templates/account/dashboard.html index 7f8d332..c7fc735 100644 --- a/web/templates/account/dashboard.html +++ b/web/templates/account/dashboard.html @@ -221,6 +221,12 @@ Blocked Users +
  • + + + My User Notes + +
  • diff --git a/web/templates/account/my_user_notes.html b/web/templates/account/my_user_notes.html new file mode 100644 index 0000000..25f659b --- /dev/null +++ b/web/templates/account/my_user_notes.html @@ -0,0 +1,144 @@ +{{define "title"}} + My User Notes +{{end}} +{{define "content"}} + +{{end}} diff --git a/web/templates/account/settings.html b/web/templates/account/settings.html index 2ec385d..bf9190b 100644 --- a/web/templates/account/settings.html +++ b/web/templates/account/settings.html @@ -120,6 +120,15 @@

  • + +
  • + + My User Notes +

    + Browse and search private notes you have written about others. +

    +
    +
  • diff --git a/web/templates/account/user_notes.html b/web/templates/account/user_notes.html index 361c7fd..67342af 100644 --- a/web/templates/account/user_notes.html +++ b/web/templates/account/user_notes.html @@ -84,6 +84,12 @@

    +
    + + + Browse and search all my notes NEW! +
    +