diff --git a/app/assets/javascripts/admin/components/site-setting.js.es6 b/app/assets/javascripts/admin/components/site-setting.js.es6 index 78696c6e06c..4c1d76cad80 100644 --- a/app/assets/javascripts/admin/components/site-setting.js.es6 +++ b/app/assets/javascripts/admin/components/site-setting.js.es6 @@ -1,10 +1,54 @@ import BufferedContent from "discourse/mixins/buffered-content"; import SiteSetting from "admin/models/site-setting"; import SettingComponent from "admin/mixins/setting-component"; +import showModal from "discourse/lib/show-modal"; +import AboutRoute from "discourse/routes/about"; export default Ember.Component.extend(BufferedContent, SettingComponent, { - _save() { + update(key, value, updateExistingUsers = false) { + if (updateExistingUsers) { + return SiteSetting.update(key, value, { updateExistingUsers: true }); + } else { + return SiteSetting.update(key, value); + } + }, + + _save(callback) { + const defaultCategoriesSettings = [ + "default_categories_watching", + "default_categories_tracking", + "default_categories_muted", + "default_categories_watching_first_post" + ]; const setting = this.buffered; - return SiteSetting.update(setting.get("setting"), setting.get("value")); + const key = setting.get("setting"); + const value = setting.get("value"); + + if (defaultCategoriesSettings.includes(key)) { + AboutRoute.create() + .model() + .then(result => { + const controller = showModal("site-setting-default-categories", { + model: { + count: result.stats.user_count, + key: key.replace(/_/g, " ") + }, + admin: true + }); + + controller.setProperties({ + onClose: () => { + const updateExistingUsers = controller.get("updateExistingUsers"); + if (updateExistingUsers === true) { + callback(this.update(key, value, true)); + } else if (updateExistingUsers === false) { + callback(this.update(key, value)); + } + } + }); + }); + } else { + callback(this.update(key, value)); + } } }); diff --git a/app/assets/javascripts/admin/controllers/modals/site-setting-default-categories.js.es6 b/app/assets/javascripts/admin/controllers/modals/site-setting-default-categories.js.es6 new file mode 100644 index 00000000000..5b856f1dbc6 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/modals/site-setting-default-categories.js.es6 @@ -0,0 +1,19 @@ +import ModalFunctionality from "discourse/mixins/modal-functionality"; + +export default Ember.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"); + } + } +}); diff --git a/app/assets/javascripts/admin/mixins/setting-component.js.es6 b/app/assets/javascripts/admin/mixins/setting-component.js.es6 index 18be48a2d19..a2d5b3a4de7 100644 --- a/app/assets/javascripts/admin/mixins/setting-component.js.es6 +++ b/app/assets/javascripts/admin/mixins/setting-component.js.es6 @@ -111,21 +111,23 @@ export default Ember.Mixin.create({ actions: { save() { - this._save() - .then(() => { - this.set("validationMessage", null); - this.commitBuffer(); - if (AUTO_REFRESH_ON_SAVE.includes(this.get("setting.setting"))) { - this.afterSave(); - } - }) - .catch(e => { - if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) { - this.set("validationMessage", e.jqXHR.responseJSON.errors[0]); - } else { - this.set("validationMessage", I18n.t("generic_error")); - } - }); + this._save(result => { + result + .then(() => { + this.set("validationMessage", null); + this.commitBuffer(); + if (AUTO_REFRESH_ON_SAVE.includes(this.get("setting.setting"))) { + this.afterSave(); + } + }) + .catch(e => { + if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) { + this.set("validationMessage", e.jqXHR.responseJSON.errors[0]); + } else { + this.set("validationMessage", I18n.t("generic_error")); + } + }); + }); }, cancel() { diff --git a/app/assets/javascripts/admin/models/site-setting.js.es6 b/app/assets/javascripts/admin/models/site-setting.js.es6 index ca1c175864d..7760a611148 100644 --- a/app/assets/javascripts/admin/models/site-setting.js.es6 +++ b/app/assets/javascripts/admin/models/site-setting.js.es6 @@ -25,9 +25,14 @@ SiteSetting.reopenClass({ }); }, - update(key, value) { + update(key, value, opts = {}) { const data = {}; data[key] = value; + + if (opts["updateExistingUsers"] === true) { + data["updateExistingUsers"] = true; + } + return ajax(`/admin/site_settings/${key}`, { type: "PUT", data }); } }); diff --git a/app/assets/javascripts/admin/templates/modal/site-setting-default-categories.hbs b/app/assets/javascripts/admin/templates/modal/site-setting-default-categories.hbs new file mode 100644 index 00000000000..e56bf01c654 --- /dev/null +++ b/app/assets/javascripts/admin/templates/modal/site-setting-default-categories.hbs @@ -0,0 +1,8 @@ +{{#d-modal-body class="incoming-emails" rawTitle=model.key}} +{{i18n "admin.site_settings.default_categories.modal_description" count=model.count}} +{{/d-modal-body}} + + diff --git a/app/controllers/admin/site_settings_controller.rb b/app/controllers/admin/site_settings_controller.rb index 546d7974f1f..7a066924c36 100644 --- a/app/controllers/admin/site_settings_controller.rb +++ b/app/controllers/admin/site_settings_controller.rb @@ -20,7 +20,42 @@ class Admin::SiteSettingsController < Admin::AdminController value = Upload.find_by(url: value) || '' end + update_existing_users = params[:updateExistingUsers].present? + previous_category_ids = (SiteSetting.send(id) || "").split("|") if update_existing_users + SiteSetting.set_and_log(id, value, current_user) + + if update_existing_users + new_category_ids = (value || "").split("|") + + case id + when "default_categories_watching" + notification_level = NotificationLevels.all[:watching] + when "default_categories_tracking" + notification_level = NotificationLevels.all[:tracking] + when "default_categories_muted" + notification_level = NotificationLevels.all[:muted] + when "default_categories_watching_first_post" + notification_level = NotificationLevels.all[:watching_first_post] + end + + (previous_category_ids - new_category_ids).each do |category_id| + CategoryUser.where(category_id: category_id, notification_level: notification_level).delete_all + end + + (new_category_ids - previous_category_ids).each do |category_id| + skip_user_ids = CategoryUser.where(category_id: category_id).pluck(:user_id) + + User.where.not(id: skip_user_ids).select(:id).find_in_batches do |users| + category_users = [] + users.each { |user| category_users << { category_id: category_id, user_id: user.id, notification_level: notification_level } } + CategoryUser.insert_all!(category_users) + end + + CategoryUser.where(category_id: category_id, notification_level: notification_level).first_or_create!(notification_level: notification_level) + end + end + render body: nil end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index df10b602077..c12494cc5ba 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -4304,6 +4304,10 @@ en: dashboard: "Dashboard" secret_list: invalid_input: "Input fields cannot be empty or contain vertical bar character." + default_categories: + 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" badges: title: Badges diff --git a/spec/requests/admin/site_settings_controller_spec.rb b/spec/requests/admin/site_settings_controller_spec.rb index e134ba06694..3d3d70ca56a 100644 --- a/spec/requests/admin/site_settings_controller_spec.rb +++ b/spec/requests/admin/site_settings_controller_spec.rb @@ -53,6 +53,44 @@ describe Admin::SiteSettingsController do expect(SiteSetting.test_setting).to eq('') end + describe 'default categories' do + let(:user1) { Fabricate(:user) } + let(:user2) { Fabricate(:user) } + let(:watching) { NotificationLevels.all[:watching] } + let(:tracking) { NotificationLevels.all[:tracking] } + + let(:category_ids) { 3.times.collect { Fabricate(:category).id } } + + before do + SiteSetting.setting(:default_categories_watching, category_ids.first(2).join("|")) + CategoryUser.create!(category_id: category_ids.last, notification_level: tracking, user: user2) + end + + after do + SiteSetting.setting(:default_categories_watching, "") + end + + it 'should update existing users user preference' do + put "/admin/site_settings/default_categories_watching.json", params: { + default_categories_watching: category_ids.last(2).join("|"), + updateExistingUsers: true + } + + expect(CategoryUser.where(category_id: category_ids.first, notification_level: watching).count).to eq(0) + expect(CategoryUser.where(category_id: category_ids.last, notification_level: watching).count).to eq(User.count - 1) + end + + it 'should not update existing users user preference' do + expect { + put "/admin/site_settings/default_categories_watching.json", params: { + default_categories_watching: category_ids.last(2).join("|") + } + }.to change { CategoryUser.where(category_id: category_ids.first, notification_level: watching).count }.by(0) + + expect(CategoryUser.where(category_id: category_ids.last, notification_level: watching).count).to eq(0) + end + end + describe 'upload site settings' do it 'can remove the site setting' do SiteSetting.test_upload = Fabricate(:upload)