mirror of
https://github.com/discourse/discourse.git
synced 2026-08-05 18:46:18 +08:00
Policy reminder jobs could create `topic_reminder` notifications for users who were in the policy group but could not see the policy post. That allowed restricted policy topics to be referenced by reminder notifications even though the topic itself remained private. This commit fixes `Jobs::DiscoursePolicy::CheckPolicy` to check post visibility for each missing policy user before replacing the reminder notification. Users who cannot see the post are skipped, while visible policy users continue to receive the reminder.
131 lines
4.3 KiB
Ruby
Vendored
131 lines
4.3 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module Jobs
|
|
module DiscoursePolicy
|
|
class CheckPolicy < ::Jobs::Scheduled
|
|
every 6.hours
|
|
|
|
def execute(args = nil)
|
|
sql = <<~SQL
|
|
SELECT p.id
|
|
FROM post_policies pp
|
|
JOIN posts p ON p.id = pp.post_id
|
|
JOIN topics t ON t.id = p.topic_id
|
|
WHERE t.deleted_at IS NULL
|
|
AND p.deleted_at IS NULL
|
|
AND t.archetype = 'regular'
|
|
AND (
|
|
(reminder = 'weekly' AND last_reminded_at < :weekly)
|
|
OR
|
|
(reminder = 'daily' AND last_reminded_at < :daily)
|
|
)
|
|
SQL
|
|
|
|
post_ids = DB.query_single(sql, weekly: 1.week.ago, daily: 1.day.ago)
|
|
|
|
if post_ids.size > 0
|
|
Post
|
|
.where(id: post_ids)
|
|
.find_each do |post|
|
|
post.post_policy.update(last_reminded_at: Time.zone.now)
|
|
|
|
missing_users(post).find_each do |user|
|
|
next unless user.guardian.can_see?(post)
|
|
|
|
clear_existing_notification(user, post)
|
|
user.notifications.create!(
|
|
notification_type: Notification.types[:topic_reminder],
|
|
topic_id: post.topic_id,
|
|
post_number: post.post_number,
|
|
data: { topic_title: post.topic.title, display_username: user.username }.to_json,
|
|
high_priority: true,
|
|
)
|
|
end
|
|
|
|
users_to_email(post).find_each do |user|
|
|
::DiscoursePolicy::PolicyMailer.send_email(user, post)
|
|
end
|
|
end
|
|
end
|
|
|
|
PostPolicy
|
|
.where("next_renew_at < ?", Time.zone.now)
|
|
.find_each do |policy|
|
|
policy
|
|
.policy_users
|
|
.accepted
|
|
.where("accepted_at < ?", policy.next_renew_at)
|
|
.update_all(expired_at: Time.zone.now)
|
|
next_renew = policy.renew_start
|
|
|
|
if policy.renew_days.to_i < 1 &&
|
|
!PostPolicy.renew_intervals.keys.include?(policy.renew_interval)
|
|
Rails.logger.warn("Invalid policy on post #{policy.post_id}")
|
|
elsif next_renew.present?
|
|
while next_renew < Time.zone.now
|
|
next_renew = calculate_next_renew_date(next_renew, policy)
|
|
end
|
|
end
|
|
|
|
policy.update(next_renew_at: next_renew)
|
|
end
|
|
|
|
DB.exec <<~SQL,
|
|
UPDATE policy_users pu
|
|
SET expired_at = :now
|
|
FROM post_policies pp
|
|
WHERE pp.id = pu.post_policy_id
|
|
AND pp.renew_start IS NULL
|
|
AND (pp.renew_days IS NOT NULL OR pp.renew_interval IS NOT NULL)
|
|
AND pu.accepted_at IS NOT NULL
|
|
AND pu.expired_at IS NULL
|
|
AND pu.revoked_at IS NULL
|
|
AND
|
|
(
|
|
(pp.renew_days IS NOT NULL AND pu.accepted_at < :now::timestamp - (INTERVAL '1 day' * pp.renew_days::integer)) OR
|
|
(pp.renew_interval = :monthly AND pu.accepted_at < :now::timestamp - (INTERVAL '1 month')) OR
|
|
(pp.renew_interval = :quarterly AND pu.accepted_at < :now::timestamp - (INTERVAL '1 month' * 3)) OR
|
|
(pp.renew_interval = :yearly AND pu.accepted_at < :now::timestamp - (INTERVAL '1 year'))
|
|
)
|
|
SQL
|
|
now: Time.zone.now,
|
|
monthly: PostPolicy.renew_intervals["monthly"],
|
|
quarterly: PostPolicy.renew_intervals["quarterly"],
|
|
yearly: PostPolicy.renew_intervals["yearly"]
|
|
end
|
|
|
|
def calculate_next_renew_date(date, policy)
|
|
case policy.renew_interval
|
|
when "monthly"
|
|
date + 1.month
|
|
when "quarterly"
|
|
date + 3.months
|
|
when "yearly"
|
|
date + 1.year
|
|
else
|
|
date + policy.renew_days.to_i.days
|
|
end
|
|
end
|
|
|
|
def missing_users(post)
|
|
post.post_policy.not_accepted_by
|
|
end
|
|
|
|
def users_to_email(post)
|
|
post.post_policy.emailed_by
|
|
end
|
|
|
|
def clear_existing_notification(user, post)
|
|
existing_notification =
|
|
Notification.find_by(
|
|
notification_type: Notification.types[:topic_reminder],
|
|
topic_id: post.topic_id,
|
|
post_number: post.post_number,
|
|
user: user,
|
|
)
|
|
return if existing_notification.blank?
|
|
existing_notification.delete
|
|
end
|
|
end
|
|
end
|
|
end
|