From 59c1db1f275b92f67cd3ddb4a44e0f5a6e67b8c0 Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Sun, 22 Feb 2026 17:56:04 -0700 Subject: [PATCH] landed Community Admin - Set Community Category --- communityadmin.go | 57 +++++++++++++++++++++++++ database/category.go | 7 +--- database/community.go | 14 +++++++ docs/MISSINGFUNCS.md | 2 +- main.go | 1 + ui/menudefs.yaml | 4 +- ui/views/comm_category.jet | 86 ++++++++++++++++++++++++++++++++++++++ ui/views/menu.jet | 10 +++-- 8 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 ui/views/comm_category.jet diff --git a/communityadmin.go b/communityadmin.go index df3a43f..bc6225d 100644 --- a/communityadmin.go +++ b/communityadmin.go @@ -422,6 +422,63 @@ func CommunityAudit(ctxt ui.AmContext) (string, any) { return "framed", "audit.jet" } +/* CommunityCategory handles setting the community category. + * Parameters: + * ctxt - The AmContext for the request. + * Returns: + * Command string dictating what to be rendered. + * Data as a parameter for the command string. + * Standard Go error status. + */ +func CommunityCategory(ctxt ui.AmContext) (string, any) { + if ctxt.GlobalFlags().Get(database.GlobalFlagNoCategories) { + return "error", "This instance of Amsterdam does not use the categorization system." + } + comm := ctxt.CurrentCommunity() + if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { + return "error", ENOACCESS + } + + if setId := ctxt.QueryParamInt("set", -1); setId >= 0 { + err := comm.SetCategory(ctxt.Ctx(), int32(setId), ctxt.CurrentUser(), ctxt.RemoteIP()) + if err != nil { + return "error", err + } + return "redirect", fmt.Sprintf("/comm/%s/admin", comm.Alias) + } + + currentCat, err := database.AmGetCategory(ctxt.Ctx(), comm.CategoryId) + if err != nil { + return "error", err + } + + displayId := ctxt.QueryParamInt("d", int(comm.CategoryId)) + var newCat []*database.Category + if displayId >= 0 { + newCat, err = database.AmGetCategoryHierarchy(ctxt.Ctx(), int32(displayId)) + if err != nil { + return "error", err + } + } else { + newCat = make([]*database.Category, 0) + } + + subCats, err := database.AmGetSubCategories(ctxt.Ctx(), int32(displayId)) + if err != nil { + return "error", err + } + + ctxt.VarMap().Set("commName", comm.Name) + ctxt.VarMap().Set("oldCat", currentCat) + ctxt.VarMap().Set("newCat", newCat) + ctxt.VarMap().Set("newCatId", displayId) + ctxt.VarMap().Set("subCats", subCats) + ctxt.VarMap().Set("backLink", fmt.Sprintf("/comm/%s/admin", comm.Alias)) + ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/admin/category", comm.Alias)) + ctxt.SetFrameTitle("Set Community Category") + return "framed", "comm_category.jet" +} + /* CreateCommunityForm renders the form for creating a new community. * Parameters: * ctxt - The AmContext for the request. diff --git a/database/category.go b/database/category.go index d0e28e4..6073119 100644 --- a/database/category.go +++ b/database/category.go @@ -143,11 +143,8 @@ func AmGetCategoryHierarchy(ctx context.Context, catid int32) ([]*Category, erro p = c.Parent } // reverse the array for return - rc := make([]*Category, 0, len(ia)) - for i := range ia { - rc = append(rc, ia[len(ia)-(i+1)]) - } - return rc, nil + slices.Reverse(ia) + return ia, nil } /* AmGetSubCategories returns a list of all subcategories of the given category ID. diff --git a/database/community.go b/database/community.go index 9a40f6b..7b58dee 100644 --- a/database/community.go +++ b/database/community.go @@ -461,6 +461,20 @@ func (c *Community) SaveFlags(ctx context.Context, f *util.OptionSet) error { return err } +// SetCategory sets the community's category ID. +func (c *Community) SetCategory(ctx context.Context, catId int32, u *User, ipaddr string) error { + c.Mutex.Lock() + defer c.Mutex.Unlock() + _, err := amdb.ExecContext(ctx, "UPDATE communities SET catid = ? WHERE commid = ?", catId, c.Id) + if err == nil { + if catId != c.CategoryId { + AmStoreAudit(AmNewCommAudit(AuditCommunityCategory, u.Uid, c.Id, ipaddr, fmt.Sprintf("catid=%d", catId))) + } + c.CategoryId = catId + } + return err +} + // SetProfileData sets all the "settable" profile data func (c *Community) SetProfileData(ctx context.Context, name string, alias string, synopsis *string, rules *string, language *string, joinkey *string, membersonly bool, hideDirectory bool, hideSearch bool, read_lvl uint16, write_lvl uint16, diff --git a/docs/MISSINGFUNCS.md b/docs/MISSINGFUNCS.md index 2008fdd..63e2c5d 100644 --- a/docs/MISSINGFUNCS.md +++ b/docs/MISSINGFUNCS.md @@ -24,7 +24,7 @@ _(italicized items can be deferred)_ - ~~Create New~~ - ~~Conferences List honor "hide in list" flag~~ - Community Admin Menu: - - Set Community Category + - ~~Set Community Category~~ - Membership Control - E-Mail to All Members - ~~Display Audit Records~~ diff --git a/main.go b/main.go index bfc079f..e922b2f 100644 --- a/main.go +++ b/main.go @@ -116,6 +116,7 @@ func setupEcho() *echo.Echo { commGroup.GET("/admin/logo", ui.AmWrap(CommunityLogoForm)) commGroup.POST("/admin/logo", ui.AmWrap(EditCommunityLogo)) commGroup.Match(GetAndPost, "/admin/audit", ui.AmWrap(CommunityAudit)) + commGroup.GET("/admin/category", ui.AmWrap(CommunityCategory)) // conference group commGroup.GET("/create_conf", ui.AmWrap(CreateConferenceForm)) diff --git a/ui/menudefs.yaml b/ui/menudefs.yaml index 3cd71e5..8b8311e 100644 --- a/ui/menudefs.yaml +++ b/ui/menudefs.yaml @@ -53,7 +53,7 @@ menudefs: link: "/TODO/sysadmin/import" permission: "Global.SysAdminAccess" - id: "communityadmin" - title: "Community Administration" + title: "Community Administration:" subtitle: "[CNAME]" permSet: "community" warning: > @@ -64,7 +64,7 @@ menudefs: link: "/comm/[CID]/admin/profile" permission: "Community.Write" - text: "Set Community Category" - link: "/TODO/comm/[CID]/admin/category" + link: "/comm/[CID]/admin/category" permission: "Community.Write" ifdef: "USECAT" - text: "Set Community Services" diff --git a/ui/views/comm_category.jet b/ui/views/comm_category.jet new file mode 100644 index 0000000..d1a15c4 --- /dev/null +++ b/ui/views/comm_category.jet @@ -0,0 +1,86 @@ +{* + * Amsterdam Web Communities System + * Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *} +
+ +
+
+

Set Community Category:

+

{{ commName }}

+
+
+
+ + + + +
+
Previous community category: {{ oldCat.Name }}
+
+ +
+
Current category:
+
+ {{ if len(newCat) == 0 }} + Top + {{ else }} + Top: + {{ range i, c := newCat }} + {{ if i == (len(newCat) - 1) }} + {{ c.Name }} + {{ else }} + {{ c.Name }}: + {{ end }} + {{ end }} + {{ if oldCat.CatId != newCatId }} + *️⃣ + {{ end }} + {{ end }} +
+
+ +
+
Subcategories:
+ {{ if len(subCats) == 0 }} +
(None)
+ {{ else }} + + + {{ range i, c := subCats }} + + + + + {{ end }} + +
+ 🟣 + {{ c.Name }} + + {{ if c.CatId != oldCat.CatId }} + *️⃣ + {{ else }} +   + {{ end }} +
+ {{ end }} +
+ + +
+

Changing the Community's Category:

+

Click on a subcategory name to make that category the currently-displayed one.

+

Click on a *️⃣ icon to set that category as the community's new category and return to the Community Administration menu.

+
+ +
diff --git a/ui/views/menu.jet b/ui/views/menu.jet index 7401994..a1a9252 100644 --- a/ui/views/menu.jet +++ b/ui/views/menu.jet @@ -1,6 +1,6 @@ {* * Amsterdam Web Communities System - * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -9,9 +9,13 @@
-

{{ menu.Title }}

{{ if menu.Subtitle != "" }} - {{ menu.Subtitle }} +
+

{{ menu.Title }}

+

{{ menu.Subtitle }}

+
+ {{ else }} +

{{ menu.Title }}

{{ end }}