diff --git a/app/assets/javascripts/discourse/templates/preferences/categories.hbs b/app/assets/javascripts/discourse/templates/preferences/categories.hbs
index e264ef11375..671242a16a9 100644
--- a/app/assets/javascripts/discourse/templates/preferences/categories.hbs
+++ b/app/assets/javascripts/discourse/templates/preferences/categories.hbs
@@ -27,14 +27,16 @@
{{i18n 'user.watched_first_post_categories_instructions'}}
-
-
- {{#if canSee}}
-
{{i18n 'user.tracked_topics_link'}}
- {{/if}}
- {{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
-
- {{i18n (if hideMutedTags 'user.muted_categories_instructions' 'user.muted_categories_instructions_dont_hide')}}
+ {{#unless siteSettings.mute_all_categories_by_default}}
+
+
+ {{#if canSee}}
+
{{i18n 'user.tracked_topics_link'}}
+ {{/if}}
+ {{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
+
+ {{i18n (if hideMutedTags 'user.muted_categories_instructions' 'user.muted_categories_instructions_dont_hide')}}
+ {{/unless}}
{{plugin-outlet name="user-preferences-categories" args=(hash model=model save=(action "save"))}}
diff --git a/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 b/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6
index b11689796a6..be146f42857 100644
--- a/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6
+++ b/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6
@@ -173,7 +173,7 @@ export default createWidget("hamburger-menu", {
listCategories() {
const maxCategoriesToDisplay = this.siteSettings
.header_dropdown_category_count;
- let categories = this.site.get("categoriesByCount");
+ let categories = [];
if (this.currentUser) {
const allCategories = this.site
@@ -203,6 +203,10 @@ export default createWidget("hamburger-menu", {
.filter(c => !categories.includes(c))
.sort((a, b) => b.topic_count - a.topic_count)
);
+ } else {
+ categories = this.site
+ .get("categoriesByCount")
+ .filter(c => c.notification_level !== NotificationLevels.MUTED);
}
if (!this.siteSettings.allow_uncategorized_topics) {
diff --git a/app/models/category_list.rb b/app/models/category_list.rb
index f1f7b6a0d94..bc1d6a4980a 100644
--- a/app/models/category_list.rb
+++ b/app/models/category_list.rb
@@ -92,16 +92,12 @@ class CategoryList
@categories = @categories.to_a
- category_user = {}
- default_notification_level = nil
- unless @guardian.anonymous?
- category_user = Hash[*CategoryUser.where(user: @guardian.user).pluck(:category_id, :notification_level).flatten]
- default_notification_level = CategoryUser.notification_levels[:regular]
- end
+ notification_levels = CategoryUser.notification_levels_for(@guardian)
+ default_notification_level = CategoryUser.default_notification_level
allowed_topic_create = Set.new(Category.topic_create_allowed(@guardian).pluck(:id))
@categories.each do |category|
- category.notification_level = category_user[category.id] || default_notification_level
+ category.notification_level = notification_levels[category.id] || default_notification_level
category.permission = CategoryGroup.permission_types[:full] if allowed_topic_create.include?(category.id)
category.has_children = category.subcategories.present?
end
diff --git a/app/models/category_user.rb b/app/models/category_user.rb
index e25f917d322..d2dd1a4d363 100644
--- a/app/models/category_user.rb
+++ b/app/models/category_user.rb
@@ -197,6 +197,26 @@ class CategoryUser < ActiveRecord::Base
SQL
end
+ def self.default_notification_level
+ SiteSetting.mute_all_categories_by_default ? notification_levels[:muted] : notification_levels[:regular]
+ end
+
+ def self.notification_levels_for(guardian)
+ if guardian.anonymous?
+ notification_levels = [
+ SiteSetting.default_categories_watching.split("|"),
+ SiteSetting.default_categories_tracking.split("|"),
+ SiteSetting.default_categories_watching_first_post.split("|"),
+ ].flatten.map { |id| [id.to_i, 1] }
+
+ notification_levels += SiteSetting.default_categories_muted.split("|").map { |id| [id.to_i, 0] }
+ else
+ notification_levels = CategoryUser.where(user: guardian.user).pluck(:category_id, :notification_level)
+ end
+
+ Hash[*notification_levels.flatten]
+ end
+
end
# == Schema Information
diff --git a/app/models/site.rb b/app/models/site.rb
index c7330e1e254..d8bddf0a995 100644
--- a/app/models/site.rb
+++ b/app/models/site.rb
@@ -55,15 +55,11 @@ class Site
by_id = {}
- category_user = {}
- unless @guardian.anonymous?
- category_user = Hash[*CategoryUser.where(user: @guardian.user).pluck(:category_id, :notification_level).flatten]
- end
-
- regular = CategoryUser.notification_levels[:regular]
+ notification_levels = CategoryUser.notification_levels_for(@guardian)
+ default_notification_level = CategoryUser.default_notification_level
categories.each do |category|
- category.notification_level = category_user[category.id] || regular
+ category.notification_level = notification_levels[category.id] || default_notification_level
category.permission = CategoryGroup.permission_types[:full] if allowed_topic_create&.include?(category.id) || @guardian.is_admin?
category.has_children = with_children.include?(category.id)
by_id[category.id] = category
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index ffd59c83806..dac88f2d938 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -2067,6 +2067,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."
+ 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."
default_tags_tracking: "List of tags that are tracked by default."
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 63f1aecf1c1..02a4ae33860 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -2022,6 +2022,9 @@ user_preferences:
default_categories_watching_first_post:
type: category_list
default: ""
+ mute_all_categories_by_default:
+ default: false
+ client: true
default_tags_watching:
type: tag_list
diff --git a/lib/topic_query.rb b/lib/topic_query.rb
index 8a2c4637f33..dec1268030d 100644
--- a/lib/topic_query.rb
+++ b/lib/topic_query.rb
@@ -869,7 +869,34 @@ class TopicQuery
def remove_muted_categories(list, user, opts = nil)
category_id = get_category_id(opts[:exclude]) if opts
- if user
+ if SiteSetting.mute_all_categories_by_default
+ if user
+ list = list.references("cu")
+ .where("
+ NOT EXISTS (
+ SELECT 1
+ FROM categories c
+ LEFT OUTER JOIN category_users cu
+ ON c.id = cu.category_id AND cu.user_id = :user_id
+ WHERE c.id = topics.category_id
+ AND c.id <> :category_id
+ AND (COALESCE(cu.notification_level, :muted) = :muted)
+ AND (COALESCE(tu.notification_level, :regular) <= :regular)
+ )", user_id: user.id,
+ muted: CategoryUser.notification_levels[:muted],
+ regular: TopicUser.notification_levels[:regular],
+ category_id: category_id || -1)
+ else
+ category_ids = [
+ SiteSetting.default_categories_watching.split("|"),
+ SiteSetting.default_categories_tracking.split("|"),
+ SiteSetting.default_categories_watching_first_post.split("|")
+ ].flatten.map(&:to_i)
+ category_ids << category_id if category_id.present? && category_ids.exclude?(category_id)
+
+ list = list.where("topics.category_id IN (?)", category_ids) if category_ids.present?
+ end
+ elsif user
list = list.references("cu")
.where("
NOT EXISTS (
diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb
index e3e2ab2724e..aac94f9b67a 100644
--- a/spec/components/topic_query_spec.rb
+++ b/spec/components/topic_query_spec.rb
@@ -240,6 +240,39 @@ describe TopicQuery do
end
end
+ context 'mute_all_categories_by_default' do
+ fab!(:category) { Fabricate(:category_with_definition) }
+ fab!(:topic) { Fabricate(:topic, category: category) }
+
+ before do
+ SiteSetting.mute_all_categories_by_default = true
+ end
+
+ it 'should remove all topics from new and latest lists by default' do
+ expect(topic_query.list_new.topics.map(&:id)).not_to include(topic.id)
+ expect(topic_query.list_latest.topics.map(&:id)).not_to include(topic.id)
+ end
+
+ it 'should include tracked category topics in new and latest lists' do
+ topic = Fabricate(:topic, category: category)
+ CategoryUser.create!(user_id: user.id,
+ category_id: category.id,
+ notification_level: CategoryUser.notification_levels[:tracking])
+ expect(topic_query.list_new.topics.map(&:id)).to include(topic.id)
+ expect(topic_query.list_latest.topics.map(&:id)).to include(topic.id)
+ end
+
+ it 'should include default watched category topics in latest list for anonymous users' do
+ SiteSetting.default_categories_watching = 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)
+ end
+ end
+
context 'muted tags' do
it 'is removed from new and latest lists' do
SiteSetting.tagging_enabled = true
diff --git a/spec/models/category_list_spec.rb b/spec/models/category_list_spec.rb
index 85f4507894c..bc6e03963d4 100644
--- a/spec/models/category_list_spec.rb
+++ b/spec/models/category_list_spec.rb
@@ -65,6 +65,35 @@ describe CategoryList do
end
end
+ context "when mute_all_categories_by_default enabled" do
+ fab!(:category) { Fabricate(:category) }
+
+ before do
+ SiteSetting.mute_all_categories_by_default = true
+ end
+
+ it "removes the category by default" do
+ expect(category_list.categories).not_to include(category)
+ end
+
+ it "returns correct notification level for user tracking category" do
+ CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:tracking], category.id)
+ notification_level = category_list.categories.find { |c| c.id == category.id }.notification_level
+ expect(notification_level).to eq(CategoryUser.notification_levels[:tracking])
+ end
+
+ it "returns correct notification level in default categories for anonymous" do
+ SiteSetting.default_categories_watching = category.id.to_s
+ notification_level = CategoryList.new(Guardian.new).categories.find { |c| c.id == category.id }.notification_level
+ expect(notification_level).to eq(CategoryUser.notification_levels[:regular])
+ end
+
+ it "removes the default muted categories for anonymous" do
+ SiteSetting.default_categories_muted = category.id.to_s
+ expect(CategoryList.new(Guardian.new).categories).not_to include(category)
+ end
+ end
+
context "with a category" do
fab!(:topic_category) { Fabricate(:category_with_definition, num_featured_topics: 2) }
@@ -114,11 +143,11 @@ describe CategoryList do
expect(category.notification_level).to eq(NotificationLevels.all[:watching])
end
- it "returns no notication level for anonymous users" do
+ it "returns default notication level for anonymous users" do
category_list = CategoryList.new(Guardian.new(nil))
category = category_list.categories.find { |c| c.id == topic_category.id }
- expect(category.notification_level).to be_nil
+ expect(category.notification_level).to eq(NotificationLevels.all[:regular])
end
end
diff --git a/spec/models/site_spec.rb b/spec/models/site_spec.rb
index 53b5f7493e3..c97aafe10b6 100644
--- a/spec/models/site_spec.rb
+++ b/spec/models/site_spec.rb
@@ -41,6 +41,16 @@ describe Site do
end
+ it "returns correct notification level for categories" do
+ category = Fabricate(:category)
+ guardian = Guardian.new
+ expect(Site.new(guardian).categories.last.notification_level).to eq(1)
+ SiteSetting.mute_all_categories_by_default = true
+ expect(Site.new(guardian).categories.last.notification_level).to eq(0)
+ SiteSetting.default_categories_tracking = category.id.to_s
+ expect(Site.new(guardian).categories.last.notification_level).to eq(1)
+ end
+
it "omits categories users can not write to from the category list" do
category = Fabricate(:category)
user = Fabricate(:user)