mirror of
https://github.com/discourse/discourse.git
synced 2025-05-31 21:55:25 +08:00
FEATURE: set notification levels when added to a group (#10378)
* FEATURE: set notification levels when added to a group This feature allows admins and group owners to define default category and tag tracking levels that will be applied to user preferences automatically at the time when users are added to the group. Users are free to change those preferences afterwards. When removed from a group, the user's notification preferences aren't changed.
This commit is contained in:
@ -23,6 +23,7 @@ module Roleable
|
||||
set_permission('moderator', true)
|
||||
auto_approve_user
|
||||
enqueue_staff_welcome_message(:moderator)
|
||||
set_default_notification_levels(:moderators)
|
||||
end
|
||||
|
||||
def revoke_moderation!
|
||||
@ -34,6 +35,7 @@ module Roleable
|
||||
set_permission('admin', true)
|
||||
auto_approve_user
|
||||
enqueue_staff_welcome_message(:admin)
|
||||
set_default_notification_levels(:admins)
|
||||
end
|
||||
|
||||
def revoke_admin!
|
||||
@ -52,6 +54,13 @@ module Roleable
|
||||
save_and_refresh_staff_groups!
|
||||
end
|
||||
|
||||
def set_default_notification_levels(group_name)
|
||||
Group.set_category_and_tag_default_notification_levels!(self, group_name)
|
||||
if group_name == :admins || group_name == :moderators
|
||||
Group.set_category_and_tag_default_notification_levels!(self, :staff)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def auto_approve_user
|
||||
|
@ -29,6 +29,8 @@ class Group < ActiveRecord::Base
|
||||
has_many :group_histories, dependent: :destroy
|
||||
has_many :category_reviews, class_name: 'Category', foreign_key: :reviewable_by_group_id, dependent: :nullify
|
||||
has_many :reviewables, foreign_key: :reviewable_by_group_id, dependent: :nullify
|
||||
has_many :group_category_notification_defaults, dependent: :destroy
|
||||
has_many :group_tag_notification_defaults, dependent: :destroy
|
||||
|
||||
belongs_to :flair_upload, class_name: 'Upload'
|
||||
|
||||
@ -51,6 +53,7 @@ class Group < ActiveRecord::Base
|
||||
after_commit :trigger_group_created_event, on: :create
|
||||
after_commit :trigger_group_updated_event, on: :update
|
||||
after_commit :trigger_group_destroyed_event, on: :destroy
|
||||
after_commit :set_default_notifications, on: [:create, :update]
|
||||
|
||||
def expire_cache
|
||||
ApplicationSerializer.expire_cache_fragment!("group_names")
|
||||
@ -382,6 +385,13 @@ class Group < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def self.set_category_and_tag_default_notification_levels!(user, group_name)
|
||||
if group = lookup_group(group_name)
|
||||
GroupUser.set_category_notifications(group, user)
|
||||
GroupUser.set_tag_notifications(group, user)
|
||||
end
|
||||
end
|
||||
|
||||
def self.refresh_automatic_group!(name)
|
||||
return unless id = AUTO_GROUPS[name]
|
||||
|
||||
@ -755,6 +765,32 @@ class Group < ActiveRecord::Base
|
||||
flair_icon.presence || flair_upload&.short_path
|
||||
end
|
||||
|
||||
[:muted, :tracking, :watching, :watching_first_post].each do |level|
|
||||
define_method("#{level}_category_ids=") do |category_ids|
|
||||
@category_notifications ||= {}
|
||||
@category_notifications[level] = category_ids
|
||||
end
|
||||
|
||||
define_method("#{level}_tags=") do |tag_names|
|
||||
@tag_notifications ||= {}
|
||||
@tag_notifications[level] = tag_names
|
||||
end
|
||||
end
|
||||
|
||||
def set_default_notifications
|
||||
if @category_notifications
|
||||
@category_notifications.each do |level, category_ids|
|
||||
GroupCategoryNotificationDefault.batch_set(self, level, category_ids)
|
||||
end
|
||||
end
|
||||
|
||||
if @tag_notifications
|
||||
@tag_notifications.each do |level, tag_names|
|
||||
GroupTagNotificationDefault.batch_set(self, level, tag_names)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def imap_mailboxes
|
||||
return [] if self.imap_server.blank? ||
|
||||
self.email_username.blank? ||
|
||||
|
77
app/models/group_category_notification_default.rb
Normal file
77
app/models/group_category_notification_default.rb
Normal file
@ -0,0 +1,77 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class GroupCategoryNotificationDefault < ActiveRecord::Base
|
||||
belongs_to :group
|
||||
belongs_to :category
|
||||
|
||||
def self.notification_levels
|
||||
NotificationLevels.all
|
||||
end
|
||||
|
||||
def self.lookup(group, level)
|
||||
self.where(group: group, notification_level: notification_levels[level])
|
||||
end
|
||||
|
||||
def self.batch_set(group, level, category_ids)
|
||||
level_num = notification_levels[level]
|
||||
category_ids = Category.where(id: category_ids).pluck(:id)
|
||||
|
||||
changed = false
|
||||
|
||||
# Update pre-existing
|
||||
if category_ids.present? && GroupCategoryNotificationDefault
|
||||
.where(group_id: group.id, category_id: category_ids)
|
||||
.where.not(notification_level: level_num)
|
||||
.update_all(notification_level: level_num) > 0
|
||||
|
||||
changed = true
|
||||
end
|
||||
|
||||
# Remove extraneous category users
|
||||
if GroupCategoryNotificationDefault
|
||||
.where(group_id: group.id, notification_level: level_num)
|
||||
.where.not(category_id: category_ids)
|
||||
.delete_all > 0
|
||||
|
||||
changed = true
|
||||
end
|
||||
|
||||
if category_ids.present?
|
||||
params = {
|
||||
group_id: group.id,
|
||||
level_num: level_num,
|
||||
}
|
||||
|
||||
sql = <<~SQL
|
||||
INSERT INTO group_category_notification_defaults (group_id, category_id, notification_level)
|
||||
SELECT :group_id, :category_id, :level_num
|
||||
ON CONFLICT DO NOTHING
|
||||
SQL
|
||||
|
||||
# we could use VALUES here but it would introduce a string
|
||||
# into the query, plus it is a bit of a micro optimisation
|
||||
category_ids.each do |category_id|
|
||||
params[:category_id] = category_id
|
||||
if DB.exec(sql, params) > 0
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
changed
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: group_category_notification_defaults
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# group_id :integer not null
|
||||
# category_id :integer not null
|
||||
# notification_level :integer not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# idx_group_category_notification_defaults_unique (group_id,category_id) UNIQUE
|
||||
#
|
57
app/models/group_tag_notification_default.rb
Normal file
57
app/models/group_tag_notification_default.rb
Normal file
@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class GroupTagNotificationDefault < ActiveRecord::Base
|
||||
belongs_to :group
|
||||
belongs_to :tag
|
||||
|
||||
def self.notification_levels
|
||||
NotificationLevels.all
|
||||
end
|
||||
|
||||
def self.lookup(group, level)
|
||||
self.where(group: group, notification_level: notification_levels[level])
|
||||
end
|
||||
|
||||
def self.batch_set(group, level, tag_names)
|
||||
tag_names ||= []
|
||||
changed = false
|
||||
|
||||
records = self.where(group: group, notification_level: notification_levels[level])
|
||||
old_ids = records.pluck(:tag_id)
|
||||
|
||||
tag_ids = tag_names.empty? ? [] : Tag.where_name(tag_names).pluck(:id)
|
||||
|
||||
Tag.where_name(tag_names).joins(:target_tag).each do |tag|
|
||||
tag_ids[tag_ids.index(tag.id)] = tag.target_tag_id
|
||||
end
|
||||
|
||||
tag_ids.uniq!
|
||||
|
||||
remove = (old_ids - tag_ids)
|
||||
if remove.present?
|
||||
records.where('tag_id in (?)', remove).destroy_all
|
||||
changed = true
|
||||
end
|
||||
|
||||
(tag_ids - old_ids).each do |id|
|
||||
self.create!(group: group, tag_id: id, notification_level: notification_levels[level])
|
||||
changed = true
|
||||
end
|
||||
|
||||
changed
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: group_tag_notification_defaults
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# group_id :integer not null
|
||||
# tag_id :integer not null
|
||||
# notification_level :integer not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# idx_group_tag_notification_defaults_unique (group_id,tag_id) UNIQUE
|
||||
#
|
@ -12,6 +12,8 @@ class GroupUser < ActiveRecord::Base
|
||||
|
||||
before_create :set_notification_level
|
||||
after_save :grant_trust_level
|
||||
after_save :set_category_notifications
|
||||
after_save :set_tag_notifications
|
||||
|
||||
def self.notification_levels
|
||||
NotificationLevels.all
|
||||
@ -64,6 +66,70 @@ class GroupUser < ActiveRecord::Base
|
||||
|
||||
Promotion.recalculate(user)
|
||||
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
|
||||
|
||||
[:muted, :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
|
||||
|
||||
[:muted, :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
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
@ -19,23 +19,50 @@ class TagUser < ActiveRecord::Base
|
||||
records = TagUser.where(user: user, notification_level: notification_levels[level])
|
||||
old_ids = records.pluck(:tag_id)
|
||||
|
||||
tag_ids = tags.empty? ? [] : Tag.where_name(tags).pluck(:id)
|
||||
tag_ids = if tags.empty?
|
||||
[]
|
||||
elsif tags.first&.is_a?(String)
|
||||
Tag.where_name(tags).pluck(:id)
|
||||
else
|
||||
tags
|
||||
end
|
||||
|
||||
Tag.where_name(tags).joins(:target_tag).each do |tag|
|
||||
Tag.where(id: tag_ids).joins(:target_tag).each do |tag|
|
||||
tag_ids[tag_ids.index(tag.id)] = tag.target_tag_id
|
||||
end
|
||||
|
||||
tag_ids.uniq!
|
||||
|
||||
if tag_ids.present? &&
|
||||
TagUser.where(user_id: user.id, tag_id: tag_ids)
|
||||
.where
|
||||
.not(notification_level: notification_levels[level])
|
||||
.update_all(notification_level: notification_levels[level]) > 0
|
||||
|
||||
changed = true
|
||||
end
|
||||
|
||||
remove = (old_ids - tag_ids)
|
||||
if remove.present?
|
||||
records.where('tag_id in (?)', remove).destroy_all
|
||||
changed = true
|
||||
end
|
||||
|
||||
(tag_ids - old_ids).each do |id|
|
||||
TagUser.create!(user: user, tag_id: id, notification_level: notification_levels[level])
|
||||
changed = true
|
||||
now = Time.zone.now
|
||||
|
||||
new_records_attrs = (tag_ids - old_ids).map do |tag_id|
|
||||
{
|
||||
user_id: user.id,
|
||||
tag_id: tag_id,
|
||||
notification_level: notification_levels[level],
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}
|
||||
end
|
||||
|
||||
unless new_records_attrs.empty?
|
||||
result = TagUser.insert_all(new_records_attrs)
|
||||
changed = true if result.rows.length > 0
|
||||
end
|
||||
|
||||
if changed
|
||||
|
Reference in New Issue
Block a user