Correctly revoke AlsoPosted notification on comment deletion

This commit is contained in:
Noah Petherbridge 2024-04-27 19:46:22 -07:00
parent a00aec7488
commit 198849eebc
3 changed files with 34 additions and 1 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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)