website/pkg/controller/forum/add_edit.go
Noah Petherbridge b8146ae485 Forum Creation Quotas
Add minimum quotas for users to earn the ability to create custom forums.

The entry requirements that could earn the first forum include:
1. Having a Certified account status for at least 45 days.
2. Having written 10 posts or replies in the forums.

Additional quota is granted in increasing difficulty based on the count of
forum posts created.

Other changes:

* Admin view of Manage Forums can filter for official/community.
* "Certified Since" now shown on profile pages.
* Update FAQ page for Forums feature.
2024-08-22 21:57:14 -07:00

202 lines
6.0 KiB
Go

package forum
import (
"fmt"
"net/http"
"strconv"
"strings"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/models"
"code.nonshy.com/nonshy/website/pkg/session"
"code.nonshy.com/nonshy/website/pkg/templates"
)
// AddEdit page.
func AddEdit() http.HandlerFunc {
tmpl := templates.Must("forum/add_edit.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Are we editing a forum or creating a new one?
var editID uint64
if editStr := r.FormValue("id"); editStr != "" {
if i, err := strconv.Atoi(editStr); err == nil {
editID = uint64(i)
} else {
session.FlashError(w, r, "Edit parameter: id was not an integer")
templates.Redirect(w, "/forum/admin")
return
}
}
// 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
}
// If editing, look up the existing forum.
var forum *models.Forum
if editID > 0 {
if found, err := models.GetForum(editID); err != nil {
session.FlashError(w, r, "Couldn't get forum: %s", err)
templates.Redirect(w, "/forum/admin")
return
} else {
// Do we have permission?
if !found.CanEdit(currentUser) {
templates.ForbiddenPage(w, r)
return
}
forum = found
}
}
// If we are over our quota for User Forums, do not allow creating a new one.
if forum == nil && !currentUser.HasAdminScope(config.ScopeForumAdmin) && currentUser.ForumQuotaRemaining() <= 0 {
session.FlashError(w, r, "You do not currently have spare quota to create a new forum.")
templates.Redirect(w, "/forum/admin")
return
}
// Saving?
if r.Method == http.MethodPost {
var (
title = strings.TrimSpace(r.PostFormValue("title"))
fragment = strings.TrimSpace(strings.ToLower(r.PostFormValue("fragment")))
description = strings.TrimSpace(r.PostFormValue("description"))
category = strings.TrimSpace(r.PostFormValue("category"))
isExplicit = r.PostFormValue("explicit") == "true"
isPrivileged = r.PostFormValue("privileged") == "true"
isPermitPhotos = r.PostFormValue("permit_photos") == "true"
isPrivate = r.PostFormValue("private") == "true"
)
// Sanity check admin-only settings -> default these to OFF.
if !currentUser.HasAdminScope(config.ScopeForumAdmin) {
isPrivileged = false
isPermitPhotos = false
isPrivate = false
}
// Were we editing an existing forum?
if forum != nil {
diffs := []models.FieldDiff{
models.NewFieldDiff("Title", forum.Title, title),
models.NewFieldDiff("Description", forum.Description, description),
models.NewFieldDiff("Category", forum.Category, category),
models.NewFieldDiff("Explicit", forum.Explicit, isExplicit),
}
forum.Title = title
forum.Description = description
forum.Category = category
forum.Explicit = isExplicit
// Forum Admin-only options: if the current viewer is not a forum admin, do not change these settings.
// e.g.: the front-end checkboxes are hidden and don't want to accidentally unset these!
if currentUser.HasAdminScope(config.ScopeForumAdmin) {
diffs = append(diffs,
models.NewFieldDiff("Privileged", forum.Privileged, isPrivileged),
models.NewFieldDiff("PermitPhotos", forum.PermitPhotos, isPermitPhotos),
models.NewFieldDiff("Private", forum.Private, isPrivate),
)
forum.Privileged = isPrivileged
forum.PermitPhotos = isPermitPhotos
forum.Private = isPrivate
}
// Save it.
if err := forum.Save(); err == nil {
session.Flash(w, r, "Forum has been updated!")
templates.Redirect(w, "/forum/admin")
// Log the change.
models.LogUpdated(currentUser, nil, "forums", forum.ID, "Updated the forum's settings.", diffs)
return
} else {
session.FlashError(w, r, "Error saving the forum: %s", err)
}
} else {
// Validate the fragment. Front-end enforces the pattern so this
// is just a sanity check.
if m := FragmentRegexp.FindStringSubmatch(fragment); m == nil {
session.FlashError(w, r, "The fragment format is invalid.")
templates.Redirect(w, "/forum/admin")
return
}
// Ensure the fragment is unique.
if _, err := models.ForumByFragment(fragment); err == nil {
session.FlashError(w, r, "The forum fragment is already in use.")
} else {
// Create the forum.
forum = &models.Forum{
Owner: *currentUser,
Category: category,
Fragment: fragment,
Title: title,
Description: description,
Explicit: isExplicit,
Privileged: isPrivileged,
PermitPhotos: isPermitPhotos,
Private: isPrivate,
}
if err := models.CreateForum(forum); err == nil {
session.Flash(w, r, "The forum has been created!")
templates.Redirect(w, "/forum/admin")
// Log the change.
models.LogCreated(currentUser, "forums", forum.ID, fmt.Sprintf(
"Created a new forum.\n\n"+
"* Category: %s\n"+
"* Title: %s\n"+
"* Fragment: %s\n"+
"* Description: %s\n"+
"* Explicit: %v\n"+
"* Privileged: %v\n"+
"* Photos: %v\n"+
"* Private: %v",
forum.Category,
forum.Title,
forum.Fragment,
forum.Description,
forum.Explicit,
forum.Privileged,
forum.PermitPhotos,
forum.Private,
))
return
} else {
session.FlashError(w, r, "Error creating the forum: %s", err)
}
}
}
}
// Get the list of moderators.
var mods []*models.User
if forum != nil {
mods, err = forum.GetModerators()
if err != nil {
session.FlashError(w, r, "Error getting moderators list: %s", err)
}
}
var vars = map[string]interface{}{
"EditID": editID,
"EditForum": forum,
"Categories": config.ForumCategories,
"Moderators": mods,
}
if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}