mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 14:33:43 +08:00
3c5fb871c0
There is an edge case where the following occurs: 1. The user sets a bookmark reminder on a post/topic 2. The post/topic is changed to a PM before or after the reminder fires, and the notification remains unread by the user 3. The user opens their bookmark reminder notification list and they can still see the notification even though they cannot access the topic anymore There is a very low chance for information leaking here, since the only thing that could be exposed is the topic title if it changes to something sensitive. This commit filters the bookmark unread notifications by using the bookmarkable can_see? methods and also prevents sending reminder notifications for bookmarks the user can no longer see.
101 lines
3.1 KiB
Ruby
101 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class PostBookmarkable < BaseBookmarkable
|
|
include TopicPostBookmarkableHelper
|
|
|
|
def self.model
|
|
Post
|
|
end
|
|
|
|
def self.serializer
|
|
UserPostBookmarkSerializer
|
|
end
|
|
|
|
def self.preload_associations
|
|
[{ topic: %i[tags category] }, :user]
|
|
end
|
|
|
|
def self.list_query(user, guardian)
|
|
topics = Topic.listable_topics.secured(guardian)
|
|
pms = Topic.private_messages_for_user(user)
|
|
post_bookmarks =
|
|
user
|
|
.bookmarks_of_type("Post")
|
|
.joins(
|
|
"INNER JOIN posts ON posts.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Post'",
|
|
)
|
|
.joins("LEFT JOIN topics ON topics.id = posts.topic_id")
|
|
.joins("LEFT JOIN topic_users ON topic_users.topic_id = topics.id")
|
|
.where("topic_users.user_id = ?", user.id)
|
|
guardian.filter_allowed_categories(
|
|
post_bookmarks.merge(topics.or(pms)).merge(Post.secured(guardian)),
|
|
)
|
|
end
|
|
|
|
def self.search_query(bookmarks, query, ts_query, &bookmarkable_search)
|
|
bookmarkable_search.call(
|
|
bookmarks.joins(
|
|
"LEFT JOIN post_search_data ON post_search_data.post_id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Post'",
|
|
),
|
|
"#{ts_query} @@ post_search_data.search_data",
|
|
)
|
|
end
|
|
|
|
def self.reminder_handler(bookmark)
|
|
send_reminder_notification(
|
|
bookmark,
|
|
topic_id: bookmark.bookmarkable.topic_id,
|
|
post_number: bookmark.bookmarkable.post_number,
|
|
data: {
|
|
title: bookmark.bookmarkable.topic.title,
|
|
bookmarkable_url: bookmark.bookmarkable.url,
|
|
},
|
|
)
|
|
end
|
|
|
|
def self.reminder_conditions(bookmark)
|
|
bookmark.bookmarkable.present? && bookmark.bookmarkable.topic.present? &&
|
|
self.can_see?(bookmark.user.guardian, bookmark)
|
|
end
|
|
|
|
def self.can_see?(guardian, bookmark)
|
|
guardian.can_see_post?(bookmark.bookmarkable)
|
|
end
|
|
|
|
def self.bookmark_metadata(bookmark, user)
|
|
{
|
|
topic_bookmarked: Bookmark.for_user_in_topic(user.id, bookmark.bookmarkable.topic_id).exists?,
|
|
}
|
|
end
|
|
|
|
def self.validate_before_create(guardian, bookmarkable)
|
|
if bookmarkable.blank? || bookmarkable.topic.blank? ||
|
|
!guardian.can_see_topic?(bookmarkable.topic) || !guardian.can_see_post?(bookmarkable)
|
|
raise Discourse::InvalidAccess
|
|
end
|
|
end
|
|
|
|
def self.after_create(guardian, bookmark, opts)
|
|
sync_topic_user_bookmarked(guardian.user, bookmark.bookmarkable.topic, opts)
|
|
end
|
|
|
|
def self.after_destroy(guardian, bookmark, opts)
|
|
sync_topic_user_bookmarked(guardian.user, bookmark.bookmarkable.topic, opts)
|
|
end
|
|
|
|
def self.cleanup_deleted
|
|
related_topics = DB.query(<<~SQL, grace_time: 3.days.ago)
|
|
DELETE FROM bookmarks b
|
|
USING topics t, posts p
|
|
WHERE t.id = p.topic_id AND b.bookmarkable_id = p.id AND b.bookmarkable_type = 'Post'
|
|
AND (t.deleted_at < :grace_time OR p.deleted_at < :grace_time)
|
|
RETURNING t.id AS topic_id
|
|
SQL
|
|
|
|
related_topics_ids = related_topics.map(&:topic_id).uniq
|
|
related_topics_ids.each do |topic_id|
|
|
Jobs.enqueue(:sync_topic_user_bookmarked, topic_id: topic_id)
|
|
end
|
|
end
|
|
end
|