From d765fde6cd50ec75bd9e70fdb2ee58dd9c31afe8 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Wed, 21 Aug 2024 22:25:59 -0700 Subject: [PATCH] User-owned Forum Improvements * Private forums: CanBeSeenBy moderators, approved followers, its owner and admin users. * Note: the endpoint to subscribe to the forum won't allow users to follow the private forum, so approved followers can not be created at this time, except by adding them as moderators. * Admins: when creating a forum they can choose "no category" to create it as an unofficial community forum. * Code cleanup * More feature flag checking --- pkg/controller/forum/forum.go | 2 +- pkg/controller/forum/forums.go | 24 ++++++++++-------- pkg/controller/forum/thread.go | 7 ++---- pkg/models/forum_membership.go | 35 ++++++++++++++++++++++++++ web/templates/forum/add_edit.html | 17 +++++++++---- web/templates/forum/admin.html | 2 +- web/templates/partials/forum_tabs.html | 2 ++ 7 files changed, 66 insertions(+), 23 deletions(-) diff --git a/pkg/controller/forum/forum.go b/pkg/controller/forum/forum.go index d939f92..d3be6cb 100644 --- a/pkg/controller/forum/forum.go +++ b/pkg/controller/forum/forum.go @@ -37,7 +37,7 @@ func Forum() http.HandlerFunc { } // Is it a private forum? - if forum.Private && !currentUser.IsAdmin { + if !forum.CanBeSeenBy(currentUser) { templates.NotFoundPage(w, r) return } diff --git a/pkg/controller/forum/forums.go b/pkg/controller/forum/forums.go index 9d80ec1..8972fc7 100644 --- a/pkg/controller/forum/forums.go +++ b/pkg/controller/forum/forums.go @@ -51,17 +51,19 @@ func Landing() http.HandlerFunc { categorized := models.CategorizeForums(forums, config.ForumCategories) // Inject the "My List" Category if the user subscribes to forums. - myList, err := models.PaginateForums(currentUser, nil, nil, true, pager) - if err != nil { - session.FlashError(w, r, "Couldn't get your followed forums: %s", err) - } else { - forums = append(forums, myList...) - categorized = append([]*models.CategorizedForum{ - { - Category: "My List", - Forums: myList, - }, - }, categorized...) + if config.UserForumsEnabled { + myList, err := models.PaginateForums(currentUser, nil, nil, true, pager) + if err != nil { + session.FlashError(w, r, "Couldn't get your followed forums: %s", err) + } else { + forums = append(forums, myList...) + categorized = append([]*models.CategorizedForum{ + { + Category: "My List", + Forums: myList, + }, + }, categorized...) + } } // Map statistics for these forums. diff --git a/pkg/controller/forum/thread.go b/pkg/controller/forum/thread.go index 74a24f1..3872c50 100644 --- a/pkg/controller/forum/thread.go +++ b/pkg/controller/forum/thread.go @@ -52,17 +52,14 @@ func Thread() http.HandlerFunc { } // Is it a private forum? - if forum.Private && !currentUser.IsAdmin { + if !forum.CanBeSeenBy(currentUser) { templates.NotFoundPage(w, r) return } // Can we moderate this forum? (from a user-owned forum perspective, // e.g. can we delete threads and posts, not edit them) - var canModerate bool - if currentUser.HasAdminScope(config.ScopeForumModerator) || forum.OwnerID == currentUser.ID { - canModerate = true - } + var canModerate = forum.CanBeModeratedBy(currentUser) // Ping the view count on this thread. if err := thread.View(currentUser.ID); err != nil { diff --git a/pkg/models/forum_membership.go b/pkg/models/forum_membership.go index 1eee49b..2201be1 100644 --- a/pkg/models/forum_membership.go +++ b/pkg/models/forum_membership.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "code.nonshy.com/nonshy/website/pkg/config" "code.nonshy.com/nonshy/website/pkg/log" "gorm.io/gorm" ) @@ -79,6 +80,40 @@ func (f *Forum) AddModerator(user *User) (*ForumMembership, error) { return fm, err } +// CanBeSeenBy checks whether the user can see a private forum. +// +// Admins, owners, moderators and approved followers can see it. +// +// Note: this may invoke a DB query to check for moderator. +func (f *Forum) CanBeSeenBy(user *User) bool { + if !f.Private || user.IsAdmin || user.ID == f.OwnerID { + return true + } + + if fm, err := GetForumMembership(user, f); err == nil { + return fm.Approved || fm.IsModerator + } + + return false +} + +// CanBeModeratedBy checks whether the user can moderate this forum. +// +// Admins, owners and moderators can do so. +// +// Note: this may invoke a DB query to check for moderator. +func (f *Forum) CanBeModeratedBy(user *User) bool { + if user.HasAdminScope(config.ScopeForumModerator) || f.OwnerID == user.ID { + return true + } + + if fm, err := GetForumMembership(user, f); err == nil { + return fm.IsModerator + } + + return false +} + // RemoveModerator will unset a user's moderator flag on this forum. func (f *Forum) RemoveModerator(user *User) (*ForumMembership, error) { fm, err := GetForumMembership(user, f) diff --git a/web/templates/forum/add_edit.html b/web/templates/forum/add_edit.html index 35ec208..d222848 100644 --- a/web/templates/forum/add_edit.html +++ b/web/templates/forum/add_edit.html @@ -93,11 +93,18 @@
diff --git a/web/templates/forum/admin.html b/web/templates/forum/admin.html index 8c47da8..b0ee37e 100644 --- a/web/templates/forum/admin.html +++ b/web/templates/forum/admin.html @@ -97,7 +97,7 @@

{{.Title}}

- /f/{{.Fragment}} + /f/{{.Fragment}} {{if .Category}}{{.Category}}{{end}} by {{.Owner.Username}} diff --git a/web/templates/partials/forum_tabs.html b/web/templates/partials/forum_tabs.html index fea64f9..f71122d 100644 --- a/web/templates/partials/forum_tabs.html +++ b/web/templates/partials/forum_tabs.html @@ -16,11 +16,13 @@ Variables that your template should set: Categories + {{if .FeatureUserForumsEnabled}}
  • Explore
  • + {{end}}
  • Newest