FEATURE: support to mute all categories by default. (#8295)

Instead of enabling `suppress_from_latest` setting on many categories now we can enable `mute_all_categories_by_default` site setting. Then users should opt-in to categories for them to appear in the latest and categories pages.
This commit is contained in:
Vinoth Kannan 2019-11-08 08:28:11 +05:30 committed by GitHub
parent deec2cf578
commit ba5b78a348
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 147 additions and 26 deletions

View File

@ -27,14 +27,16 @@
</div>
<div class="instructions">{{i18n 'user.watched_first_post_categories_instructions'}}</div>
<div class="controls tracking-controls">
<label>{{d-icon "d-muted"}} {{i18n 'user.muted_categories'}}</label>
{{#if canSee}}
<a class="show-tracking" href="{{unbound model.mutedTopicsPath}}">{{i18n 'user.tracked_topics_link'}}</a>
{{/if}}
{{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
</div>
<div class="instructions">{{i18n (if hideMutedTags 'user.muted_categories_instructions' 'user.muted_categories_instructions_dont_hide')}}</div>
{{#unless siteSettings.mute_all_categories_by_default}}
<div class="controls tracking-controls">
<label>{{d-icon "d-muted"}} {{i18n 'user.muted_categories'}}</label>
{{#if canSee}}
<a class="show-tracking" href="{{unbound model.mutedTopicsPath}}">{{i18n 'user.tracked_topics_link'}}</a>
{{/if}}
{{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
</div>
<div class="instructions">{{i18n (if hideMutedTags 'user.muted_categories_instructions' 'user.muted_categories_instructions_dont_hide')}}</div>
{{/unless}}
</div>
{{plugin-outlet name="user-preferences-categories" args=(hash model=model save=(action "save"))}}

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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."

View File

@ -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

View File

@ -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 (

View File

@ -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

View File

@ -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

View File

@ -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)