0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-11 02:59:07 +08:00
discourse/app
Régis Hanol fa0cabddbe
FIX: Notification counts not matching displayed notifications
Notification badge counts could get "stuck" — showing a number higher
than the actual visible notifications. This happened because count
queries used lightweight SQL (only filtering deleted topics), while
display queries applied full access control via Guardian. When a user
lost access to a topic — removed from a group/PM, or category
permissions changed — the notification persisted in counts but was
filtered from the displayed list.

Additionally, the `visible` scope had a logic error: the LEFT JOIN
condition `topics.id IS NULL OR topics.deleted_at IS NULL` incorrectly
included notifications for hard-deleted topics.

The fix introduces `NotificationQuery` (following the `BookmarkQuery`
pattern) — a centralized class that handles all notification filtering
at the SQL level, ensuring counts always match displayed notifications.

Access control uses a CASE on topic archetype: PMs check
topic_allowed_users + topic_allowed_groups; regular topics check
category read_restricted against the user's secure_category_ids. Shared
drafts are excluded when the user lacks access. Admins without
suppress_secured_categories_from_admin skip all checks. Badge
notifications are handled via Ruby post-processing when enable_badges
is false.

To prevent DB bloat from orphaned notifications, model callbacks
enqueue DeleteInaccessibleNotifications when access is revoked:

- TopicAllowedUser after_destroy — enqueues with topic_id
- TopicAllowedGroup after_destroy — enqueues with topic_id
- GroupUser after_destroy — enqueues with user_id + group_id
- Category after_update — enqueues with category_id when permissions change

The job supports three modes: topic_id (checks each user via Guardian),
category_id (iterates topics, delegates to topic_id mode), and
user_id + group_id (finds affected topics, bulk-deletes where user
lost access).

Redo of #27589
Ref - t/121464
2026-06-17 10:32:53 +02:00
..
assets UX: add cancel + footernote (#40940) 2026-06-16 15:35:07 +02:00
controllers FIX: Notification counts not matching displayed notifications 2026-06-17 10:32:53 +02:00
helpers DEV: Rename EmberCli to EmberAssets (#40938) 2026-06-16 12:56:56 +02:00
jobs FIX: Notification counts not matching displayed notifications 2026-06-17 10:32:53 +02:00
mailers FIX: allow custom email sender name via translation overrides (#40674) 2026-06-09 14:03:03 +04:00
models FIX: Notification counts not matching displayed notifications 2026-06-17 10:32:53 +02:00
queries/reports FIX: Enforce can_see_ip checks across admin IP features (#40019) 2026-05-19 11:37:20 +08:00
serializers FIX: Notification counts not matching displayed notifications 2026-06-17 10:32:53 +02:00
services PERF: Parallelize section loading in new dashboard (#40970) 2026-06-17 16:29:10 +08:00
views DEV: Rename EmberCli to EmberAssets (#40938) 2026-06-16 12:56:56 +02:00