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"}}
+
+
+
+
+
+
+
+
+ {{template "title" .}}
+
+
+
+
+
+
+
+ {{$Root := .}}
+
+
+
+
+ You have saved {{.Pager.Total}} note{{Pluralize64 .Pager.Total}} about other members on {{PrettyTitle}} (page {{.Pager.Page}} of {{.Pager.Pages}}).
+
+
+
+
+
+ {{SimplePager .Pager}}
+
+ {{range .Notes}}
+
+ {{$User := $Root.UserMap.Get .AboutUserID}}
+
+
+
+
+
{{$User.Username}}
+ {{if $User.IsAdmin}}
+
+
+
+ Admin
+
+
+ {{end}}
+
+
+
+
About:
+
{{$User.Username}}
+ {{if $User.IsAdmin}}
+
+ Admin
+
+ {{end}}
+
+
{{.Message}}
+
+
+
+
+
+
+
+
+ {{end}}
+
+
+
+{{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 @@
+
+