From f80f63a142ef8a8e194bc2664f48d2792f37be54 Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Sat, 21 Feb 2026 15:36:48 -0700 Subject: [PATCH] implemented "prune attachment" on post --- conference_ops.go | 58 ++++++++++++++++++++++++++++++++++++++++++++ database/audit.go | 1 + database/post.go | 15 ++++++++++++ docs/MISSINGFUNCS.md | 4 +-- main.go | 1 + ui/messagedefs.yaml | 24 ++++++++++++++++++ ui/views/posts.jet | 4 +++ 7 files changed, 105 insertions(+), 2 deletions(-) diff --git a/conference_ops.go b/conference_ops.go index fca36e7..9e52061 100644 --- a/conference_ops.go +++ b/conference_ops.go @@ -482,6 +482,64 @@ func NukeMessage(ctxt ui.AmContext) (string, any) { return mbox.Render(ctxt) } +/* PruneMessageAttachment prunes (removes and deletes) a message's attachmeent. + * Parameters: + * ctxt - The AmContext for the request. + * Returns: + * Command string dictating what to be rendered. + * Data as a parameter for the command string. + */ +func PruneMessageAttachment(ctxt ui.AmContext) (string, any) { + if ctxt.CurrentUser().IsAnon { + return "error", ENOPERM + } + conf := ctxt.GetScratch("currentConference").(*database.Conference) + myLevel := ctxt.GetScratch("levelInConference").(uint16) + topic := ctxt.GetScratch("currentTopic").(*database.Topic) + msgNum, err := strconv.Atoi(ctxt.URLParam("msg")) + if err != nil { + return "error", err + } + hdrs, err := database.AmGetPostRange(ctxt.Ctx(), topic, int32(msgNum), int32(msgNum)) + if err != nil { + return "error", err + } else if len(hdrs) != 1 { + return "error", EPOSTREF + } + if !conf.TestPermission("Conference.Nuke", myLevel) { + return "error", ENOPERM + } + + // Load the message box, and, if we have a valid "yes," then perform the prune! + mbox, err := ui.AmLoadMessageBox("prune") + if err != nil { + return "error", err + } + if mbox.Validate(ctxt, "yes") { + // do the pruning! + err := hdrs[0].PruneAttachment(ctxt.Ctx(), ctxt.CurrentUser(), ctxt.RemoteIP()) + if err != nil { + return "error", err + } + return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num) + } + + // Set up to display the message box. + link, err := hdrs[0].Link(ctxt.Ctx(), "community") + if err != nil { + return "error", err + } + creator, err := hdrs[0].Creator(ctxt.Ctx()) + if err != nil { + return "error", err + } + mbox.SetMessage(fmt.Sprintf(`You are about to prune the attachment of message <%s>, + originally composed by <%s>!`, link, creator.Username)) + mbox.SetLink("no", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num)) + mbox.SetLink("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/prune/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num)) + return mbox.Render(ctxt) +} + /* MoveMessageForm displays the form for moving a message.. * Parameters: * ctxt - The AmContext for the request. diff --git a/database/audit.go b/database/audit.go index f55a4f2..69577e3 100644 --- a/database/audit.go +++ b/database/audit.go @@ -77,6 +77,7 @@ const ( AuditConferenceDelete = 315 AuditConferenceMoveMessage = 316 AuditConferenceStickyTopic = 317 + AuditConferencePruneAttachment = 318 ) // auditWriteQueue is a channel to store audit records in the background. diff --git a/database/post.go b/database/post.go index c94e309..67bec12 100644 --- a/database/post.go +++ b/database/post.go @@ -223,6 +223,21 @@ func (p *PostHeader) HitAttachment(ctx context.Context) error { return err } +// PruneAttachment prunes (removes and deletes) the attachment of this post. +func (p *PostHeader) PruneAttachment(ctx context.Context, u *User, ipaddr string) error { + if p.ScribbleDate != nil && p.ScribbleUid != nil { + return errors.New("no attachment on scribbled post") + } + rs, err := amdb.ExecContext(ctx, "DELETE FROM postattach WHERE postid = ?", p.PostId) + if err == nil { + rowCount, err := rs.RowsAffected() + if err == nil && rowCount > 1 { + AmStoreAudit(AmNewAudit(AuditConferencePruneAttachment, u.Uid, ipaddr, fmt.Sprintf("post=%d", p.PostId))) + } + } + return err +} + // Text returns the text associated with a post. func (p *PostHeader) Text(ctx context.Context) (string, error) { var dbdata []PostData diff --git a/docs/MISSINGFUNCS.md b/docs/MISSINGFUNCS.md index 4daf0ec..8edcd45 100644 --- a/docs/MISSINGFUNCS.md +++ b/docs/MISSINGFUNCS.md @@ -73,6 +73,6 @@ _(italicized items can be deferred)_ - ~~Delete Conference~~ - ~~Add to Hotlist/Remove from Hotlist~~ - Actually implement pictures in posts -- Related to bugs in Export Messages caused by bad data: +- ~~Related to bugs in Export Messages caused by bad data:~~ - ~~Provide a per-conference flag that will set BuggyAttachment behavior~~ - - New feature: remove attachment from message (requires Conference.Nuke permission) + - ~~New feature: remove attachment from message (requires Conference.Nuke permission)~~ diff --git a/main.go b/main.go index c1e23fa..be0d731 100644 --- a/main.go +++ b/main.go @@ -160,6 +160,7 @@ func setupEcho() *echo.Echo { opsGroup.GET("/hide/:msg", ui.AmWrap(HideMessage)) opsGroup.GET("/scribble/:msg", ui.AmWrap(ScribbleMessage)) opsGroup.GET("/nuke/:msg", ui.AmWrap(NukeMessage)) + opsGroup.GET("/prune/:msg", ui.AmWrap(PruneMessageAttachment)) opsGroup.GET("/publish/:msg", ui.AmWrap(PublishMessage)) opsGroup.GET("/move/:msg", ui.AmWrap(MoveMessageForm)) opsGroup.POST("/move/:msg", ui.AmWrap(MoveMessage)) diff --git a/ui/messagedefs.yaml b/ui/messagedefs.yaml index 5b8c3eb..e171a40 100644 --- a/ui/messagedefs.yaml +++ b/ui/messagedefs.yaml @@ -31,6 +31,30 @@ messagedefs: tone: "green" icon: "✗" text: "No, Cancel" + - id: "prune" + title: "Prune Message Attachment" + tone: "red" + destructive: true + message: "You are about to prune a message's attachment!" + warningIcon: "💣" + warningLines: + - text: "Warning: This action cannot be undone!" + bold: true + - text: "Pruning this message's attachment will permanently delete it from the system." + bold: false + buttons: + - id: "yes" + link: "placeholder" + confirm: true + tone: "red" + icon: "✓" + text: "Yes, Prune It" + - id: "no" + link: "placeholder" + confirm: false + tone: "green" + icon: "✗" + text: "No, Cancel" - id: "deleteTopic" title: "Delete Topic" tone: "red" diff --git a/ui/views/posts.jet b/ui/views/posts.jet index 9bdb2ae..57295ce 100644 --- a/ui/views/posts.jet +++ b/ui/views/posts.jet @@ -138,6 +138,10 @@ {{ if canNuke }} Nuke + {{ if post_attach.Filename != "" }} + Prune Attach + {{ end }} {{ end }} {{ if canMove }}