From a18a7c70f7ed50ef8db69e006c4d644618748dbb Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Thu, 29 Jan 2026 11:36:03 -0700 Subject: [PATCH] did stick/unstick, freeze/unfreeze, and archive/unarchive topic for three quick wins --- conference_ops.go | 69 ++++++++++++++++++++++++++++++++++++++++++++ database/audit.go | 4 +-- database/topic.go | 42 +++++++++++++++++++++++++++ docs/MISSINGFUNCS.md | 6 ++-- main.go | 3 ++ ui/views/posts.jet | 6 ++-- 6 files changed, 122 insertions(+), 8 deletions(-) diff --git a/conference_ops.go b/conference_ops.go index 4df308f..e21d023 100644 --- a/conference_ops.go +++ b/conference_ops.go @@ -202,6 +202,75 @@ func HideTopic(ctxt ui.AmContext) (string, any, error) { return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number), nil } +/* FreezeTopic freezes or unfreezes the current topic. + * 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 FreezeTopic(ctxt ui.AmContext) (string, any, error) { + conf := ctxt.GetScratch("currentConference").(*database.Conference) + myLevel := ctxt.GetScratch("levelInConference").(uint16) + topic := ctxt.GetScratch("currentTopic").(*database.Topic) + if !conf.TestPermission("Conference.Hide", myLevel) { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, ENOPERM) + } + err := topic.SetFrozen(ctxt.Ctx(), !topic.Frozen, ctxt.CurrentUser(), ctxt.RemoteIP()) + if err != nil { + return ui.ErrorPage(ctxt, err) + } + return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number), nil +} + +/* ArchiveTopic archives or unarchives the current topic. + * 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 ArchiveTopic(ctxt ui.AmContext) (string, any, error) { + conf := ctxt.GetScratch("currentConference").(*database.Conference) + myLevel := ctxt.GetScratch("levelInConference").(uint16) + topic := ctxt.GetScratch("currentTopic").(*database.Topic) + if !conf.TestPermission("Conference.Hide", myLevel) { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, ENOPERM) + } + err := topic.SetArchived(ctxt.Ctx(), !topic.Archived, ctxt.CurrentUser(), ctxt.RemoteIP()) + if err != nil { + return ui.ErrorPage(ctxt, err) + } + return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number), nil +} + +/* StickTopic sticks or unsticks the current topic. + * 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 StickTopic(ctxt ui.AmContext) (string, any, error) { + conf := ctxt.GetScratch("currentConference").(*database.Conference) + myLevel := ctxt.GetScratch("levelInConference").(uint16) + topic := ctxt.GetScratch("currentTopic").(*database.Topic) + if !conf.TestPermission("Conference.Hide", myLevel) { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, ENOPERM) + } + err := topic.SetSticky(ctxt.Ctx(), !topic.Sticky, ctxt.CurrentUser(), ctxt.RemoteIP()) + if err != nil { + return ui.ErrorPage(ctxt, err) + } + return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number), nil +} + /* HideMessage hides or shows a topic message. * Parameters: * ctxt - The AmContext for the request. diff --git a/database/audit.go b/database/audit.go index a3341b0..6871580 100644 --- a/database/audit.go +++ b/database/audit.go @@ -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 @@ -66,7 +66,7 @@ const ( AuditConferenceMembership = 305 AuditConferenceCreateTopic = 306 AuditConferenceDeleteTopic = 307 - AudotConferenceFreezeTopic = 308 + AuditConferenceFreezeTopic = 308 AuditConferenceArchiveTopic = 309 AuditConferencePostMessage = 310 AuditConferenceHideMessage = 311 diff --git a/database/topic.go b/database/topic.go index c168392..2a6f7fb 100644 --- a/database/topic.go +++ b/database/topic.go @@ -129,6 +129,48 @@ func (t *Topic) SetHidden(ctx context.Context, u *User, hidden bool) error { return err } +// SetFrozen sets a topic's "frozen" state. +func (t *Topic) SetFrozen(ctx context.Context, frozen bool, u *User, ipaddr string) error { + var ar *AuditRecord = nil + defer func() { + AmStoreAudit(ar) + }() + _, err := amdb.ExecContext(ctx, "UPDATE topics SET frozen = ? WHERE topicid = ?", frozen, t.TopicId) + if err == nil { + t.Frozen = frozen + ar = AmNewAudit(AuditConferenceFreezeTopic, u.Uid, ipaddr, fmt.Sprintf("topic=%d", t.TopicId), fmt.Sprintf("frozen=%t", frozen)) + } + return err +} + +// SetArchived sets a topic's "archived" state. +func (t *Topic) SetArchived(ctx context.Context, archived bool, u *User, ipaddr string) error { + var ar *AuditRecord = nil + defer func() { + AmStoreAudit(ar) + }() + _, err := amdb.ExecContext(ctx, "UPDATE topics SET archived = ? WHERE topicid = ?", archived, t.TopicId) + if err == nil { + t.Archived = archived + ar = AmNewAudit(AuditConferenceArchiveTopic, u.Uid, ipaddr, fmt.Sprintf("topic=%d", t.TopicId), fmt.Sprintf("archived=%t", archived)) + } + return err +} + +// SetSticky sets a topic's "sticky" state. +func (t *Topic) SetSticky(ctx context.Context, sticky bool, u *User, ipaddr string) error { + var ar *AuditRecord = nil + defer func() { + AmStoreAudit(ar) + }() + _, err := amdb.ExecContext(ctx, "UPDATE topics SET sticky = ? where topicid = ?", sticky, t.TopicId) + if err == nil { + t.Sticky = sticky + ar = AmNewAudit(AuditConferenceStickyTopic, u.Uid, ipaddr, fmt.Sprintf("topic=%d", t.TopicId), fmt.Sprintf("sticky=%t", sticky)) + } + return err +} + // IsBozo returns true if the specified test UID is filtered for the specified user. func (t *Topic) IsBozo(ctx context.Context, u *User, testUid int32) (bool, error) { if u.IsAnon { diff --git a/docs/MISSINGFUNCS.md b/docs/MISSINGFUNCS.md index 8553576..3ba6f91 100644 --- a/docs/MISSINGFUNCS.md +++ b/docs/MISSINGFUNCS.md @@ -36,9 +36,9 @@ _(italicized items can be deferred)_ - Subscribe to Topic - Send invite - ~~Filtered Users (list/remove)~~ - - Stick/Unstick - - Freeze/Unfreeze - - Archive/Unarchive + - ~~Stick/Unstick~~ + - ~~Freeze/Unfreeze~~ + - ~~Archive/Unarchive~~ - Delete - ~~Post Scribble~~ - ~~Post Nuke~~ diff --git a/main.go b/main.go index 9866e1d..e1917dd 100644 --- a/main.go +++ b/main.go @@ -107,6 +107,9 @@ func setupEcho() *echo.Echo { confGroup.POST("/r/:topic", ui.AmWrap(PostInTopic), ui.SetTopic) opsGroup := confGroup.Group("/op/:topic", ui.SetTopic) opsGroup.GET("/hide", ui.AmWrap(HideTopic)) + opsGroup.GET("/freeze", ui.AmWrap(FreezeTopic)) + opsGroup.GET("/archive", ui.AmWrap(ArchiveTopic)) + opsGroup.GET("/stick", ui.AmWrap(StickTopic)) opsGroup.GET("/hide/:msg", ui.AmWrap(HideMessage)) opsGroup.GET("/scribble/:msg", ui.AmWrap(ScribbleMessage)) opsGroup.GET("/nuke/:msg", ui.AmWrap(NukeMessage)) diff --git a/ui/views/posts.jet b/ui/views/posts.jet index 5b54784..cad95cd 100644 --- a/ui/views/posts.jet +++ b/ui/views/posts.jet @@ -43,19 +43,19 @@
{{ if canStick }} - {{ if isSticky }}Unstick Topic{{ else }}Stick Topic{{ end }} {{ end }} {{ if canFreeze }} - {{ if isFrozen }}Unfreeze Topic{{ else }}Freeze Topic{{ end }} {{ end }} {{ if canArchive }} - {{ if isArchived }}Unarchive Topic{{ else }}Archive Topic{{ end }}