FEATURE: update existing users when group default notifications changed. (#13434)

Currently, the changes will only affect the users added after.
This commit is contained in:
Vinoth Kannan 2021-07-15 19:53:57 +05:30 committed by GitHub
parent ebce983a26
commit d6fc39c886
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 308 additions and 24 deletions

View File

@ -1,20 +1,5 @@
import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import ModalUpdateExistingUsers from "discourse/mixins/modal-update-existing-users";
export default Controller.extend(ModalFunctionality, {
onShow() {
this.set("updateExistingUsers", null);
},
actions: {
updateExistingUsers() {
this.set("updateExistingUsers", true);
this.send("closeModal");
},
cancel() {
this.set("updateExistingUsers", false);
this.send("closeModal");
},
},
});
export default Controller.extend(ModalFunctionality, ModalUpdateExistingUsers);

View File

@ -4,10 +4,12 @@ import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { popupAutomaticMembershipAlert } from "discourse/controllers/groups-new";
import showModal from "discourse/lib/show-modal";
export default Component.extend({
saving: null,
disabled: false,
updateExistingUsers: null,
@discourseComputed("saving")
savingText(saving) {
@ -28,14 +30,37 @@ export default Component.extend({
group.automatic_membership_email_domains
);
const opts = {};
if (this.updateExistingUsers !== null) {
opts.update_existing_users = this.updateExistingUsers;
}
return group
.save()
.save(opts)
.then((data) => {
if (data.user_count) {
const controller = showModal("group-default-notifications", {
model: {
count: data.user_count,
},
});
controller.set("onClose", () => {
this.updateExistingUsers = controller.updateExistingUsers;
this.send("save");
});
return;
}
if (data.route_to) {
DiscourseURL.routeTo(data.route_to);
}
this.set("saved", true);
this.setProperties({
saved: true,
updateExistingUsers: null,
});
if (this.afterSave) {
this.afterSave();

View File

@ -0,0 +1,5 @@
import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import ModalUpdateExistingUsers from "discourse/mixins/modal-update-existing-users";
export default Controller.extend(ModalFunctionality, ModalUpdateExistingUsers);

View File

@ -0,0 +1,19 @@
import Mixin from "@ember/object/mixin";
export default Mixin.create({
onShow() {
this.set("updateExistingUsers", null);
},
actions: {
updateExistingUsers() {
this.set("updateExistingUsers", true);
this.send("closeModal");
},
cancel() {
this.set("updateExistingUsers", false);
this.send("closeModal");
},
},
});

View File

@ -292,10 +292,10 @@ const Group = RestModel.extend({
});
},
save() {
save(opts = {}) {
return ajax(`/groups/${this.id}`, {
type: "PUT",
data: { group: this.asJSON() },
data: Object.assign({ group: this.asJSON() }, opts),
});
},

View File

@ -0,0 +1,8 @@
{{#d-modal-body title="groups.default_notifications.modal_title"}}
{{i18n "groups.default_notifications.modal_description" count=model.count}}
{{/d-modal-body}}
<div class="modal-footer">
{{d-button action=(action "updateExistingUsers") class="btn-primary" label="groups.default_notifications.modal_yes"}}
{{d-button action=(action "cancel") label="groups.default_notifications.modal_no"}}
</div>

View File

@ -155,10 +155,25 @@ class GroupsController < ApplicationController
params_with_permitted = group_params(automatic: group.automatic)
clear_disabled_email_settings(group, params_with_permitted)
categories, tags = []
if !group.automatic || current_user.admin
categories, tags = user_default_notifications(group, params_with_permitted)
if params[:update_existing_users].blank?
user_count = count_existing_users(group.group_users, categories, tags)
if user_count > 0
render json: { user_count: user_count }
return
end
end
end
if group.update(params_with_permitted)
GroupActionLogger.new(current_user, group, skip_guardian: true).log_change_group_settings
group.record_email_setting_changes!(current_user)
group.expire_imap_mailbox_cache
update_existing_users(group.group_users, categories, tags) if categories.present? || tags.present?
if guardian.can_see?(group)
render json: success_json
@ -757,4 +772,154 @@ class GroupsController < ApplicationController
params_with_permitted[:email_password] = nil
end
end
def user_default_notifications(group, params)
category_notifications = group.group_category_notification_defaults.pluck(:category_id, :notification_level).to_h
tag_notifications = group.group_tag_notification_defaults.pluck(:tag_id, :notification_level).to_h
categories = {}
tags = {}
NotificationLevels.all.each do |key, value|
category_ids = (params["#{key}_category_ids".to_sym] || []) - ["-1"]
category_ids.each do |category_id|
category_id = category_id.to_i
old_value = category_notifications[category_id]
metadata = {
old_value: old_value,
new_value: value
}
if old_value.blank?
metadata[:action] = :create
elsif old_value == value
category_notifications.delete(category_id)
next
else
metadata[:action] = :update
end
categories[category_id] = metadata
end
tag_names = (params["#{key}_tags".to_sym] || []) - ["-1"]
tag_ids = Tag.where(name: tag_names).pluck(:id)
tag_ids.each do |tag_id|
old_value = tag_notifications[tag_id]
metadata = {
old_value: old_value,
new_value: value
}
if old_value.blank?
metadata[:action] = :create
elsif old_value == value
tag_notifications.delete(tag_id)
next
else
metadata[:action] = :update
end
tags[tag_id] = metadata
end
end
(category_notifications.keys - categories.keys).each do |category_id|
categories[category_id] = { action: :delete, old_value: category_notifications[category_id] }
end
(tag_notifications.keys - tags.keys).each do |tag_id|
tags[tag_id] = { action: :delete, old_value: tag_notifications[tag_id] }
end
[categories, tags]
end
%i{
count
update
}.each do |action|
define_method("#{action}_existing_users") do |group_users, categories, tags|
return 0 if categories.blank? && tags.blank?
ids = []
categories.each do |category_id, data|
if data[:action] == :update || data[:action] == :delete
category_users = CategoryUser.where(category_id: category_id, notification_level: data[:old_value], user_id: group_users.select(:user_id))
if action == :update
category_users.delete_all
else
ids += category_users.pluck(:user_id)
end
categories.delete(category_id) if data[:action] == :delete && action == :update
end
end
tags.each do |tag_id, data|
if data[:action] == :update || data[:action] == :delete
tag_users = TagUser.where(tag_id: tag_id, notification_level: data[:old_value], user_id: group_users.select(:user_id))
if action == :update
tag_users.delete_all
else
ids += tag_users.pluck(:user_id)
end
tags.delete(tag_id) if data[:action] == :delete && action == :update
end
end
if categories.present? || tags.present?
group_users.select(:id, :user_id).find_in_batches do |batch|
user_ids = batch.pluck(:user_id)
categories.each do |category_id, data|
category_users = []
existing_users = CategoryUser.where(category_id: category_id, user_id: user_ids).where("notification_level IS NOT NULL")
skip_user_ids = existing_users.pluck(:user_id)
batch.each do |group_user|
next if skip_user_ids.include?(group_user.user_id)
category_users << { category_id: category_id, user_id: group_user.user_id, notification_level: data[:new_value] }
end
next if category_users.blank?
if action == :update
CategoryUser.insert_all!(category_users)
else
ids += category_users.pluck(:user_id)
end
end
tags.each do |tag_id, data|
tag_users = []
existing_users = TagUser.where(tag_id: tag_id, user_id: user_ids).where("notification_level IS NOT NULL")
skip_user_ids = existing_users.pluck(:user_id)
batch.each do |group_user|
next if skip_user_ids.include?(group_user.user_id)
tag_users << { tag_id: tag_id, user_id: group_user.user_id, notification_level: data[:new_value], created_at: Time.now, updated_at: Time.now }
end
next if tag_users.blank?
if action == :update
TagUser.insert_all!(tag_users)
else
ids += tag_users.pluck(:user_id)
end
end
end
end
ids.uniq.count
end
end
end

View File

@ -892,6 +892,11 @@ en:
flair_type:
icon: "Select an icon"
image: "Upload an image"
default_notifications:
modal_title: "User default notifications"
modal_description: "Would you like to apply this change historically? This will change preferences for %{count} existing users."
modal_yes: "Yes"
modal_no: "No, only apply change going forward"
user_action_groups:
"1": "Likes"

View File

@ -714,7 +714,8 @@ describe GroupsController do
name: 'testing',
tracking_category_ids: [category.id],
tracking_tags: [tag.name]
}
},
update_existing_users: false
}
end.to change { GroupHistory.count }.by(13)
@ -783,7 +784,8 @@ describe GroupsController do
members_visibility_level: 3,
tracking_category_ids: [category.id],
tracking_tags: [tag.name]
}
},
update_existing_users: false
}
expect(response.status).to eq(200)
@ -852,6 +854,75 @@ describe GroupsController do
expect(event[:event_name]).to eq(:group_updated)
expect(event[:params].first).to eq(group)
end
context "user default notifications" do
it "should update default notification preference for existing users" do
user1 = Fabricate(:user)
user2 = Fabricate(:user)
CategoryUser.create!(user: user1, category: category, notification_level: 4)
TagUser.create!(user: user1, tag: tag, notification_level: 4)
TagUser.create!(user: user2, tag: tag, notification_level: 4)
group.add(user1)
group.add(user2)
put "/groups/#{group.id}.json", params: {
group: {
flair_color: 'BBB',
name: 'testing',
incoming_email: 'test@mail.org',
primary_group: true,
automatic_membership_email_domains: 'test.org',
grant_trust_level: 2,
visibility_level: 1,
members_visibility_level: 3,
tracking_category_ids: [category.id],
tracking_tags: [tag.name]
}
}
expect(response.status).to eq(200)
expect(response.parsed_body["user_count"]).to eq(group.group_users.count - 1)
put "/groups/#{group.id}.json", params: {
group: {
flair_color: 'BBB',
name: 'testing',
incoming_email: 'test@mail.org',
primary_group: true,
automatic_membership_email_domains: 'test.org',
grant_trust_level: 2,
visibility_level: 1,
members_visibility_level: 3,
tracking_category_ids: [category.id],
tracking_tags: [tag.name]
},
update_existing_users: true
}
expect(response.status).to eq(200)
expect(response.parsed_body["success"]).to eq("OK")
put "/groups/#{group.id}.json", params: {
group: {
flair_color: 'BBB',
name: 'testing',
incoming_email: 'test@mail.org',
primary_group: true,
automatic_membership_email_domains: 'test.org',
grant_trust_level: 2,
visibility_level: 1,
members_visibility_level: 3,
watching_category_ids: [category.id],
tracking_tags: [tag.name]
},
update_existing_users: true
}
expect(response.status).to eq(200)
expect(response.parsed_body["success"]).to eq("OK")
expect(CategoryUser.exists?(user: user2, category: category, notification_level: 3)).to be_truthy
end
end
end
context "when user is a site moderator" do
@ -889,7 +960,8 @@ describe GroupsController do
members_visibility_level: 3,
tracking_category_ids: [category.id],
tracking_tags: [tag.name]
}
},
update_existing_users: false
}
expect(response.status).to eq(200)