From 198849eebc5057d4be53bf880490c4a8465a2edf Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sat, 27 Apr 2024 19:46:22 -0700 Subject: [PATCH] Correctly revoke AlsoPosted notification on comment deletion --- pkg/controller/forum/new_post.go | 2 +- pkg/models/notification.go | 28 ++++++++++++++++++++++++++++ pkg/models/thread.go | 5 +++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/pkg/controller/forum/new_post.go b/pkg/controller/forum/new_post.go index 9eab5d1..dc5417c 100644 --- a/pkg/controller/forum/new_post.go +++ b/pkg/controller/forum/new_post.go @@ -392,7 +392,7 @@ func NewPost() http.HandlerFunc { TableName: "threads", TableID: thread.ID, Message: message, - Link: fmt.Sprintf("/forum/thread/%d%s#p%d", thread.ID, queryString, reply.ID), + Link: fmt.Sprintf("/go/comment?id=%d", reply.ID), } if err := models.CreateNotification(notif); err != nil { log.Error("Couldn't create thread reply notification for subscriber %d: %s", userID, err) diff --git a/pkg/models/notification.go b/pkg/models/notification.go index 56e54e4..a04acb3 100644 --- a/pkg/models/notification.go +++ b/pkg/models/notification.go @@ -2,6 +2,7 @@ package models import ( "errors" + "fmt" "strings" "time" @@ -100,6 +101,33 @@ func RemoveNotification(tableName string, tableID uint64) error { return result.Error } +// RemoveAlsoPostedNotification removes a 'has also posted' notification if the comment is later deleted. +// +// This is specialized for deleting replies to forum threads where subscribers were notified that the +// user has AlsoPosted on that thread. If the user deletes their comment, this specific notification +// needs to be revoked from people who received it before, so the head of their original comment is not +// leaked on their notifications page. +// +// These notifications have a Type=also_posted TableName=threads TableID=threads.ID with the only hard +// link to the specific comment on that thread being the hyperlink URL that goes to their comment. +func RemoveAlsoPostedNotification(thread *Thread, commentID uint64) error { + // Match the specific notification by its link URL. + var ( + // Modern link URL ('/go/comment?id=1234' which finds the right page to see the comment) + newLink = fmt.Sprintf("/go/comment?id=%d", commentID) + + // Legacy link URL ('/forum/thread/123?page=4#p456') which embeds the thread ID, an + // optional query string (page number) and the comment ID anchor. + legacyLink = fmt.Sprintf("/forum/thread/%d%%#p%d", thread.ID, commentID) + ) + + result := DB.Where( + "type = ? AND table_name = 'threads' AND table_id = ? AND (link = ? OR link LIKE ?)", + NotificationAlsoPosted, thread.ID, newLink, legacyLink, + ).Delete(&Notification{}) + return result.Error +} + // RemoveNotificationBulk about several table IDs, e.g. when bulk removing private photo upload // notifications for everybody on the site. func RemoveNotificationBulk(tableName string, tableIDs []uint64) error { diff --git a/pkg/models/thread.go b/pkg/models/thread.go index 3959b93..cd5986d 100644 --- a/pkg/models/thread.go +++ b/pkg/models/thread.go @@ -184,6 +184,11 @@ func (t *Thread) DeleteReply(comment *Comment) error { return errors.New("that comment doesn't belong to this thread") } + // Revoke any notifications sent to subscribers when this reply was first created. + if err := RemoveAlsoPostedNotification(t, comment.ID); err != nil { + log.Error("Thread.DeleteReply: RemoveAlsoPostedNotification: %s", err) + } + // Is this the primary comment that started the thread? If so, delete the whole thread. if comment.ID == t.CommentID { log.Error("DeleteReply(%d): this is the parent comment of a thread (%d '%s'), remove the whole thread", comment.ID, t.ID, t.Title)