discourse/app/models/group_user.rb
Martin Brennan adb4eee153
DEV: Make more group-based settings client: false (#25585)
Affects the following settings:

* whispers_allowed_groups
* anonymous_posting_allowed_groups
* personal_message_enabled_groups
* shared_drafts_allowed_groups
* here_mention_allowed_groups
* uploaded_avatars_allowed_groups
* ignore_allowed_groups

This turns off `client: true` for these group-based settings,
because there is no guarantee that the current user gets all
their group memberships serialized to the client. Better to check
server-side first.
2024-02-08 09:43:34 +10:00

239 lines
6.9 KiB
Ruby

# frozen_string_literal: true
class GroupUser < ActiveRecord::Base
belongs_to :group
belongs_to :user
after_save :update_title
after_destroy :grant_other_available_title
after_save :set_primary_group
after_destroy :remove_primary_and_flair_group, :recalculate_trust_level
before_create :set_notification_level
after_save :grant_trust_level
after_save :set_category_notifications
after_save :set_tag_notifications
after_commit :increase_group_user_count, on: [:create]
after_commit :decrease_group_user_count, on: [:destroy]
def self.notification_levels
NotificationLevels.all
end
def self.ensure_consistency!(last_seen = 1.hour.ago)
update_first_unread_pm(last_seen)
end
def self.update_first_unread_pm(last_seen, limit: 10_000)
DB.exec(
<<~SQL,
UPDATE group_users gu
SET first_unread_pm_at = Y.min_date
FROM (
SELECT
X.group_id,
X.user_id,
X.min_date
FROM (
SELECT
gu.group_id,
gu.user_id,
COALESCE(Z.min_date, :now) min_date
FROM group_users gu
LEFT JOIN (
SELECT
gu2.group_id,
gu2.user_id,
MIN(t.updated_at) min_date
FROM group_users gu2
INNER JOIN topic_allowed_groups tag ON tag.group_id = gu2.group_id
INNER JOIN topics t ON t.id = tag.topic_id
INNER JOIN users u ON u.id = gu2.user_id
LEFT JOIN topic_users tu ON t.id = tu.topic_id AND tu.user_id = gu2.user_id
WHERE t.deleted_at IS NULL
AND t.archetype = :archetype
AND tu.last_read_post_number < CASE
WHEN u.admin OR u.moderator #{SiteSetting.whispers_allowed_groups_map.any? ? "OR gu2.group_id IN (:whisperers_group_ids)" : ""}
THEN t.highest_staff_post_number
ELSE t.highest_post_number
END
AND (COALESCE(tu.notification_level, 1) >= 2)
GROUP BY gu2.user_id, gu2.group_id
) AS Z ON Z.user_id = gu.user_id AND Z.group_id = gu.group_id
) AS X
WHERE X.user_id IN (
SELECT id
FROM users
WHERE last_seen_at IS NOT NULL
AND last_seen_at > :last_seen
ORDER BY last_seen_at DESC
LIMIT :limit
)
) Y
WHERE gu.user_id = Y.user_id AND gu.group_id = Y.group_id
SQL
archetype: Archetype.private_message,
last_seen: last_seen,
limit: limit,
now: 10.minutes.ago,
whisperers_group_ids: SiteSetting.whispers_allowed_groups_map,
)
end
protected
def set_notification_level
self.notification_level = group&.default_notification_level || 3
end
def set_primary_group
user.update!(primary_group: group) if group.primary_group
end
def remove_primary_and_flair_group
return if self.destroyed_by_association&.active_record == User # User is being destroyed, so don't try to update
updates = {}
updates[:primary_group_id] = nil if user.primary_group_id == group_id
updates[:flair_group_id] = nil if user.flair_group_id == group_id
user.update(updates) if updates.present?
end
def grant_other_available_title
if group.title.present? && group.title == user.title
user.update_attribute(:title, user.next_best_title)
end
end
def update_title
if group.title.present?
DB.exec(
"
UPDATE users SET title = :title
WHERE (title IS NULL OR title = '') AND id = :id",
id: user_id,
title: group.title,
)
end
end
def grant_trust_level
return if group.grant_trust_level.nil?
TrustLevelGranter.grant(group.grant_trust_level, user)
end
def recalculate_trust_level
return if group.grant_trust_level.nil?
return if self.destroyed_by_association&.active_record == User # User is being destroyed, so don't try to recalculate
Promotion.recalculate(user, use_previous_trust_level: true)
end
def set_category_notifications
self.class.set_category_notifications(group, user)
end
def self.set_category_notifications(group, user)
group_levels =
group
.group_category_notification_defaults
.each_with_object({}) do |r, h|
h[r.notification_level] ||= []
h[r.notification_level] << r.category_id
end
return if group_levels.empty?
user_levels =
CategoryUser
.where(user_id: user.id)
.each_with_object({}) do |r, h|
h[r.notification_level] ||= []
h[r.notification_level] << r.category_id
end
higher_level_category_ids = user_levels.values.flatten
%i[muted regular tracking watching_first_post watching].each do |level|
level_num = NotificationLevels.all[level]
higher_level_category_ids -= (user_levels[level_num] || [])
if group_category_ids = group_levels[level_num]
CategoryUser.batch_set(
user,
level,
group_category_ids + (user_levels[level_num] || []) - higher_level_category_ids,
)
end
end
end
def set_tag_notifications
self.class.set_tag_notifications(group, user)
end
def self.set_tag_notifications(group, user)
group_levels =
group
.group_tag_notification_defaults
.each_with_object({}) do |r, h|
h[r.notification_level] ||= []
h[r.notification_level] << r.tag_id
end
return if group_levels.empty?
user_levels =
TagUser
.where(user_id: user.id)
.each_with_object({}) do |r, h|
h[r.notification_level] ||= []
h[r.notification_level] << r.tag_id
end
higher_level_tag_ids = user_levels.values.flatten
%i[muted regular tracking watching_first_post watching].each do |level|
level_num = NotificationLevels.all[level]
higher_level_tag_ids -= (user_levels[level_num] || [])
if group_tag_ids = group_levels[level_num]
TagUser.batch_set(
user,
level,
group_tag_ids + (user_levels[level_num] || []) - higher_level_tag_ids,
)
end
end
end
def increase_group_user_count
Group.increment_counter(:user_count, self.group_id)
end
def decrease_group_user_count
Group.decrement_counter(:user_count, self.group_id)
end
end
# == Schema Information
#
# Table name: group_users
#
# id :integer not null, primary key
# group_id :integer not null
# user_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# owner :boolean default(FALSE), not null
# notification_level :integer default(2), not null
# first_unread_pm_at :datetime not null
#
# Indexes
#
# index_group_users_on_group_id_and_user_id (group_id,user_id) UNIQUE
# index_group_users_on_user_id_and_group_id (user_id,group_id) UNIQUE
#