diff --git a/app/assets/javascripts/admin/mixins/setting-component.js b/app/assets/javascripts/admin/mixins/setting-component.js
index 44609e88cff..3b1ec456689 100644
--- a/app/assets/javascripts/admin/mixins/setting-component.js
+++ b/app/assets/javascripts/admin/mixins/setting-component.js
@@ -173,6 +173,7 @@ export default Mixin.create({
"default_categories_tracking",
"default_categories_muted",
"default_categories_watching_first_post",
+ "default_categories_regular",
"default_tags_watching",
"default_tags_tracking",
"default_tags_muted",
diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/categories.js b/app/assets/javascripts/discourse/app/controllers/preferences/categories.js
index 13baaf6fd29..b11b85399f2 100644
--- a/app/assets/javascripts/discourse/app/controllers/preferences/categories.js
+++ b/app/assets/javascripts/discourse/app/controllers/preferences/categories.js
@@ -9,6 +9,7 @@ export default Controller.extend({
this.saveAttrNames = [
"muted_category_ids",
+ "regular_category_ids",
"watched_category_ids",
"tracked_category_ids",
"watched_first_post_category_ids"
@@ -19,10 +20,13 @@ export default Controller.extend({
"model.watchedCategories",
"model.watchedFirstPostCategories",
"model.trackedCategories",
+ "model.regularCategories",
"model.mutedCategories"
)
- selectedCategories(watched, watchedFirst, tracked, muted) {
- return [].concat(watched, watchedFirst, tracked, muted).filter(t => t);
+ selectedCategories(watched, watchedFirst, tracked, regular, muted) {
+ return []
+ .concat(watched, watchedFirst, tracked, regular, muted)
+ .filter(t => t);
},
@discourseComputed
diff --git a/app/assets/javascripts/discourse/app/models/user.js b/app/assets/javascripts/discourse/app/models/user.js
index 2267727bd58..33ef2829ae2 100644
--- a/app/assets/javascripts/discourse/app/models/user.js
+++ b/app/assets/javascripts/discourse/app/models/user.js
@@ -332,25 +332,27 @@ const User = RestModel.extend({
var updatedState = {};
- ["muted", "watched", "tracked", "watched_first_post"].forEach(s => {
- if (fields === undefined || fields.includes(s + "_category_ids")) {
- let prop =
- s === "watched_first_post"
- ? "watchedFirstPostCategories"
- : s + "Categories";
- let cats = this.get(prop);
- if (cats) {
- let cat_ids = cats.map(c => c.get("id"));
- updatedState[s + "_category_ids"] = cat_ids;
+ ["muted", "regular", "watched", "tracked", "watched_first_post"].forEach(
+ s => {
+ if (fields === undefined || fields.includes(s + "_category_ids")) {
+ let prop =
+ s === "watched_first_post"
+ ? "watchedFirstPostCategories"
+ : s + "Categories";
+ let cats = this.get(prop);
+ if (cats) {
+ let cat_ids = cats.map(c => c.get("id"));
+ updatedState[s + "_category_ids"] = cat_ids;
- // HACK: denote lack of categories
- if (cats.length === 0) {
- cat_ids = [-1];
+ // HACK: denote lack of categories
+ if (cats.length === 0) {
+ cat_ids = [-1];
+ }
+ data[s + "_category_ids"] = cat_ids;
}
- data[s + "_category_ids"] = cat_ids;
}
}
- });
+ );
[
"muted_tags",
@@ -704,6 +706,14 @@ const User = RestModel.extend({
this.set("mutedCategories", Category.findByIds(this.muted_category_ids));
},
+ @observes("regular_category_ids")
+ updateRegularCategories() {
+ this.set(
+ "regularCategories",
+ Category.findByIds(this.regular_category_ids)
+ );
+ },
+
@observes("tracked_category_ids")
updateTrackedCategories() {
this.set(
diff --git a/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs b/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs
index 7b36d0f3ffd..e76d19a27ad 100644
--- a/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs
+++ b/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs
@@ -37,7 +37,17 @@
{{i18n "user.watched_first_post_categories_instructions"}}
- {{#unless siteSettings.mute_all_categories_by_default}}
+ {{#if siteSettings.mute_all_categories_by_default}}
+
+
+ {{category-selector
+ categories=model.regularCategories
+ blocklist=selectedCategories
+ onChange=(action (mut model.regularCategories))
+ }}
+
+ {{i18n "user.regular_categories_instructions"}}
+ {{else}}
{{#if canSee}}
@@ -50,7 +60,7 @@
}}
{{i18n (if hideMutedTags "user.muted_categories_instructions" "user.muted_categories_instructions_dont_hide")}}
- {{/unless}}
+ {{/if}}
{{plugin-outlet name="user-preferences-categories" args=(hash model=model save=(action "save"))}}
diff --git a/app/controllers/admin/site_settings_controller.rb b/app/controllers/admin/site_settings_controller.rb
index 46b74093308..3b7ac11b41d 100644
--- a/app/controllers/admin/site_settings_controller.rb
+++ b/app/controllers/admin/site_settings_controller.rb
@@ -54,6 +54,8 @@ class Admin::SiteSettingsController < Admin::AdminController
notification_level = NotificationLevels.all[:muted]
when "default_categories_watching_first_post"
notification_level = NotificationLevels.all[:watching_first_post]
+ when "default_categories_regular"
+ notification_level = NotificationLevels.all[:regular]
end
CategoryUser.where(category_id: (previous_category_ids - new_category_ids), notification_level: notification_level).delete_all
@@ -131,6 +133,8 @@ class Admin::SiteSettingsController < Admin::AdminController
notification_level = NotificationLevels.all[:muted]
when "default_categories_watching_first_post"
notification_level = NotificationLevels.all[:watching_first_post]
+ when "default_categories_regular"
+ notification_level = NotificationLevels.all[:regular]
end
user_ids = CategoryUser.where(category_id: previous_category_ids - new_category_ids, notification_level: notification_level).distinct.pluck(:user_id)
diff --git a/app/models/category_user.rb b/app/models/category_user.rb
index afcd56ce193..04fe4691b02 100644
--- a/app/models/category_user.rb
+++ b/app/models/category_user.rb
@@ -207,6 +207,7 @@ class CategoryUser < ActiveRecord::Base
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_tracking.split("|"),
SiteSetting.default_categories_watching_first_post.split("|"),
+ SiteSetting.default_categories_regular.split("|")
].flatten.map { |id| [id.to_i, self.notification_levels[:regular]] }
notification_levels += SiteSetting.default_categories_muted.split("|").map { |id| [id.to_i, self.notification_levels[:muted]] }
diff --git a/app/models/user.rb b/app/models/user.rb
index 440ed6519d8..7f64e5a48b3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1448,7 +1448,7 @@ class User < ActiveRecord::Base
values = []
- %w{watching watching_first_post tracking muted}.each do |s|
+ %w{watching watching_first_post tracking regular muted}.each do |s|
category_ids = SiteSetting.get("default_categories_#{s}").split("|").map(&:to_i)
category_ids.each do |category_id|
next if category_id == 0
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 0192d19ceaa..3b95db221f0 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -35,6 +35,7 @@ class UserSerializer < UserCardSerializer
private_attributes :locale,
:muted_category_ids,
+ :regular_category_ids,
:watched_tags,
:watching_first_post_tags,
:tracked_tags,
@@ -219,6 +220,10 @@ class UserSerializer < UserCardSerializer
CategoryUser.lookup(object, :muted).pluck(:category_id)
end
+ def regular_category_ids
+ CategoryUser.lookup(object, :regular).pluck(:category_id)
+ end
+
def tracked_category_ids
CategoryUser.lookup(object, :tracking).pluck(:category_id)
end
diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb
index 50e2e007612..913e5ee7cd0 100644
--- a/app/services/user_updater.rb
+++ b/app/services/user_updater.rb
@@ -6,6 +6,7 @@ class UserUpdater
watched_first_post_category_ids: :watching_first_post,
watched_category_ids: :watching,
tracked_category_ids: :tracking,
+ regular_category_ids: :regular,
muted_category_ids: :muted
}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 9cc2bc82345..4ed06ab689e 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -984,6 +984,8 @@ en:
muted_categories: "Muted"
muted_categories_instructions: "You will not be notified of anything about new topics in these categories, and they will not appear on the categories or latest pages."
muted_categories_instructions_dont_hide: "You will not be notified of anything about new topics in these categories."
+ regular_categories: "Regular"
+ regular_categories_instructions: "These category topics will be displayed in the `latest` and `top` topics list."
no_category_access: "As a moderator you have limited category access, save is disabled."
delete_account: "Delete My Account"
delete_account_confirm: "Are you sure you want to permanently delete your account? This action cannot be undone!"
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 61f2214efe8..66515d8118c 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -2172,6 +2172,7 @@ en:
default_categories_tracking: "List of categories that are tracked by default."
default_categories_muted: "List of categories that are muted by default."
default_categories_watching_first_post: "List of categories in which first post in each new topic will be watched by default."
+ default_categories_regular: "List of categories that are normal by default. Useful when `mute_all_categories_by_default` site setting is enabled."
mute_all_categories_by_default: "Set the default notification level of all the categories to muted. Require users opt-in to categories for them to appear in 'latest' and 'categories' pages. If you wish to amend the defaults for anonymous users set 'default_categories_' settings."
default_tags_watching: "List of tags that are watched by default."
diff --git a/config/site_settings.yml b/config/site_settings.yml
index fa5dd1611ab..e33e84ea0bc 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -2174,6 +2174,9 @@ user_preferences:
default_categories_watching_first_post:
type: category_list
default: ""
+ default_categories_regular:
+ type: category_list
+ default: ""
mute_all_categories_by_default:
default: false
client: true
diff --git a/lib/site_settings/validations.rb b/lib/site_settings/validations.rb
index 55da27c8b50..c49b83910b0 100644
--- a/lib/site_settings/validations.rb
+++ b/lib/site_settings/validations.rb
@@ -23,7 +23,8 @@ module SiteSettings::Validations
default_categories_selected = [
SiteSetting.default_categories_tracking.split("|"),
SiteSetting.default_categories_muted.split("|"),
- SiteSetting.default_categories_watching_first_post.split("|")
+ SiteSetting.default_categories_watching_first_post.split("|"),
+ SiteSetting.default_categories_regular.split("|")
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
@@ -35,7 +36,8 @@ module SiteSettings::Validations
default_categories_selected = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_muted.split("|"),
- SiteSetting.default_categories_watching_first_post.split("|")
+ SiteSetting.default_categories_watching_first_post.split("|"),
+ SiteSetting.default_categories_regular.split("|")
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
@@ -47,7 +49,8 @@ module SiteSettings::Validations
default_categories_selected = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_tracking.split("|"),
- SiteSetting.default_categories_watching_first_post.split("|")
+ SiteSetting.default_categories_watching_first_post.split("|"),
+ SiteSetting.default_categories_regular.split("|")
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
@@ -59,7 +62,21 @@ module SiteSettings::Validations
default_categories_selected = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_tracking.split("|"),
- SiteSetting.default_categories_muted.split("|")
+ SiteSetting.default_categories_muted.split("|"),
+ SiteSetting.default_categories_regular.split("|")
+ ].flatten.map(&:to_i).to_set
+
+ validate_default_categories(category_ids, default_categories_selected)
+ end
+
+ def validate_default_categories_regular(new_val)
+ category_ids = validate_category_ids(new_val)
+
+ default_categories_selected = [
+ SiteSetting.default_categories_watching.split("|"),
+ SiteSetting.default_categories_tracking.split("|"),
+ SiteSetting.default_categories_muted.split("|"),
+ SiteSetting.default_categories_watching_first_post.split("|")
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
diff --git a/lib/topic_query.rb b/lib/topic_query.rb
index 7c073b963cd..df2e60f42e2 100644
--- a/lib/topic_query.rb
+++ b/lib/topic_query.rb
@@ -891,7 +891,8 @@ class TopicQuery
category_ids = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_tracking.split("|"),
- SiteSetting.default_categories_watching_first_post.split("|")
+ SiteSetting.default_categories_watching_first_post.split("|"),
+ SiteSetting.default_categories_regular.split("|")
].flatten.map(&:to_i)
category_ids << category_id if category_id.present? && category_ids.exclude?(category_id)
diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb
index fdfd67dabbd..86e18d1fac4 100644
--- a/spec/components/topic_query_spec.rb
+++ b/spec/components/topic_query_spec.rb
@@ -338,6 +338,11 @@ describe TopicQuery do
expect(TopicQuery.new.list_latest.topics.map(&:id)).to include(topic.id)
end
+ it 'should include default regular category topics in latest list for anonymous users' do
+ SiteSetting.default_categories_regular = category.id.to_s
+ expect(TopicQuery.new.list_latest.topics.map(&:id)).to include(topic.id)
+ end
+
it 'should include topics when filtered by category' do
topic_query = TopicQuery.new(user, category: topic.category_id)
expect(topic_query.list_latest.topics.map(&:id)).to include(topic.id)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index cea4a75126a..2d770a05dd2 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1645,6 +1645,7 @@ describe User do
fab!(:category1) { Fabricate(:category) }
fab!(:category2) { Fabricate(:category) }
fab!(:category3) { Fabricate(:category) }
+ fab!(:category4) { Fabricate(:category) }
before do
SiteSetting.default_email_digest_frequency = 1440 # daily
@@ -1666,6 +1667,7 @@ describe User do
SiteSetting.default_categories_tracking = category1.id.to_s
SiteSetting.default_categories_muted = category2.id.to_s
SiteSetting.default_categories_watching_first_post = category3.id.to_s
+ SiteSetting.default_categories_regular = category4.id.to_s
end
it "has overriden preferences" do
@@ -1688,6 +1690,7 @@ describe User do
expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([category1.id])
expect(CategoryUser.lookup(user, :muted).pluck(:category_id)).to eq([category2.id])
expect(CategoryUser.lookup(user, :watching_first_post).pluck(:category_id)).to eq([category3.id])
+ expect(CategoryUser.lookup(user, :regular).pluck(:category_id)).to eq([category4.id])
end
it "does not set category preferences for staged users" do
@@ -1696,6 +1699,7 @@ describe User do
expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([])
expect(CategoryUser.lookup(user, :muted).pluck(:category_id)).to eq([])
expect(CategoryUser.lookup(user, :watching_first_post).pluck(:category_id)).to eq([])
+ expect(CategoryUser.lookup(user, :regular).pluck(:category_id)).to eq([])
end
end
diff --git a/spec/serializers/site_serializer_spec.rb b/spec/serializers/site_serializer_spec.rb
index 6574b3e4b2c..22a55ced834 100644
--- a/spec/serializers/site_serializer_spec.rb
+++ b/spec/serializers/site_serializer_spec.rb
@@ -4,9 +4,9 @@ require 'rails_helper'
describe SiteSerializer do
let(:guardian) { Guardian.new }
+ let(:category) { Fabricate(:category) }
it "includes category custom fields only if its preloaded" do
- category = Fabricate(:category)
category.custom_fields["enable_marketplace"] = true
category.save_custom_fields
@@ -18,4 +18,14 @@ describe SiteSerializer do
data = MultiJson.dump(described_class.new(Site.new(guardian), scope: guardian, root: false))
expect(data).to include("enable_marketplace")
end
+
+ it "returns correct notification level for categories" do
+ SiteSetting.mute_all_categories_by_default = true
+ SiteSetting.default_categories_regular = category.id.to_s
+
+ serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
+ categories = serialized[:categories]
+ expect(categories[0][:notification_level]).to eq(0)
+ expect(categories[-1][:notification_level]).to eq(1)
+ end
end
diff --git a/spec/serializers/web_hook_user_serializer_spec.rb b/spec/serializers/web_hook_user_serializer_spec.rb
index ef6934d0041..75ca07f5b04 100644
--- a/spec/serializers/web_hook_user_serializer_spec.rb
+++ b/spec/serializers/web_hook_user_serializer_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe WebHookUserSerializer do
it 'should only include the required keys' do
count = serializer.as_json.keys.count
- difference = count - 49
+ difference = count - 50
expect(difference).to eq(0), lambda {
message = (difference < 0 ?
diff --git a/test/javascripts/fixtures/user_fixtures.js b/test/javascripts/fixtures/user_fixtures.js
index 287bb901b8a..d88adf77926 100644
--- a/test/javascripts/fixtures/user_fixtures.js
+++ b/test/javascripts/fixtures/user_fixtures.js
@@ -179,6 +179,7 @@ export default {
skip_new_user_tips: false,
enable_quoting: true,
muted_category_ids: [],
+ regular_category_ids: [],
tracked_category_ids: [],
watched_category_ids: [3],
watched_first_post_category_ids: [],
@@ -2499,6 +2500,7 @@ export default {
can_delete_all_posts: true,
locale: null,
muted_category_ids: [],
+ regular_category_ids: [],
watched_tags: [],
watching_first_post_tags: [],
tracked_tags: [],
@@ -2857,6 +2859,7 @@ export default {
associated_accounts: [],
locale: "en_US",
muted_category_ids: [],
+ regular_category_ids: [],
watched_tags: [],
watching_first_post_tags: [],
tracked_tags: [],