FEATURE: new watched_precedence_over_muted setting (#22252)
New setting which allow admin to define behavior when topic is in watched category and muted topic and vice versa. If watched_precedence_over_muted setting is true, that topic is still visible in list of topics and notification is created. If watched_precedence_over_muted setting is false, that topic is not still visible in list of topics and notification is skipped as well.
This commit is contained in:
parent
4f7f9ef87c
commit
9cf981f1f1
|
@ -200,6 +200,10 @@ class PostAlerter
|
||||||
|
|
||||||
DiscourseEvent.trigger(:post_alerter_before_post, post, new_record, notified)
|
DiscourseEvent.trigger(:post_alerter_before_post, post, new_record, notified)
|
||||||
|
|
||||||
|
if !SiteSetting.watched_precedence_over_muted
|
||||||
|
notified = notified + category_or_tag_muters(post.topic)
|
||||||
|
end
|
||||||
|
|
||||||
if new_record
|
if new_record
|
||||||
if post.topic.private_message?
|
if post.topic.private_message?
|
||||||
# private messages
|
# private messages
|
||||||
|
@ -265,6 +269,18 @@ class PostAlerter
|
||||||
.pluck(:user_id)
|
.pluck(:user_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def category_or_tag_muters(topic)
|
||||||
|
User
|
||||||
|
.joins(
|
||||||
|
"LEFT JOIN category_users ON users.id = category_users.user_id AND category_users.category_id = #{topic.category_id.to_i} AND category_users.notification_level = #{CategoryUser.notification_levels[:muted].to_i}",
|
||||||
|
)
|
||||||
|
.joins("LEFT JOIN topic_tags ON topic_tags.topic_id = #{topic.id.to_i}")
|
||||||
|
.joins(
|
||||||
|
"LEFT JOIN tag_users ON users.id = tag_users.user_id AND tag_users.tag_id = topic_tags.tag_id AND tag_users.notification_level = #{TagUser.notification_levels[:muted].to_i}",
|
||||||
|
)
|
||||||
|
.where("category_users.id IS NOT NULL OR tag_users.id IS NOT NULL")
|
||||||
|
end
|
||||||
|
|
||||||
def notify_first_post_watchers(post, user_ids, notified = nil)
|
def notify_first_post_watchers(post, user_ids, notified = nil)
|
||||||
return [] if user_ids.blank?
|
return [] if user_ids.blank?
|
||||||
user_ids.uniq!
|
user_ids.uniq!
|
||||||
|
|
|
@ -2380,6 +2380,7 @@ en:
|
||||||
remove_muted_tags_from_latest: "Don't show topics tagged only with muted tags in the latest topic list."
|
remove_muted_tags_from_latest: "Don't show topics tagged only with muted tags in the latest topic list."
|
||||||
force_lowercase_tags: "Force all new tags to be entirely lowercase."
|
force_lowercase_tags: "Force all new tags to be entirely lowercase."
|
||||||
create_post_for_category_and_tag_changes: "Create a small action post when a topic's category or tags change"
|
create_post_for_category_and_tag_changes: "Create a small action post when a topic's category or tags change"
|
||||||
|
watched_precedence_over_muted: "Notify me about topics in categories or tags I’m watching that also belong to one I have muted"
|
||||||
|
|
||||||
company_name: "Company Name"
|
company_name: "Company Name"
|
||||||
governing_law: "Governing Law"
|
governing_law: "Governing Law"
|
||||||
|
|
|
@ -2820,6 +2820,10 @@ tags:
|
||||||
type: enum
|
type: enum
|
||||||
default: always
|
default: always
|
||||||
enum: RemoveMutedTagsFromLatestSiteSetting
|
enum: RemoveMutedTagsFromLatestSiteSetting
|
||||||
|
watched_precedence_over_muted:
|
||||||
|
client: true
|
||||||
|
default: false
|
||||||
|
|
||||||
force_lowercase_tags:
|
force_lowercase_tags:
|
||||||
default: true
|
default: true
|
||||||
client: true
|
client: true
|
||||||
|
|
|
@ -897,16 +897,34 @@ class TopicQuery
|
||||||
category_id = get_category_id(opts[:exclude]) if opts
|
category_id = get_category_id(opts[:exclude]) if opts
|
||||||
|
|
||||||
if user
|
if user
|
||||||
|
watched_tag_ids =
|
||||||
|
if SiteSetting.watched_precedence_over_muted
|
||||||
|
TagUser
|
||||||
|
.where(user: user)
|
||||||
|
.where("notification_level >= ?", TopicUser.notification_levels[:watching])
|
||||||
|
.pluck(:tag_id)
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
# OR watched_topic_tags.id IS NOT NULL",
|
||||||
list =
|
list =
|
||||||
list
|
list.references("cu").joins(
|
||||||
.references("cu")
|
|
||||||
.joins(
|
|
||||||
"LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = #{user.id}",
|
"LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = #{user.id}",
|
||||||
)
|
)
|
||||||
.where(
|
if watched_tag_ids.present?
|
||||||
|
list =
|
||||||
|
list.joins(
|
||||||
|
"LEFT JOIN topic_tags watched_topic_tags ON watched_topic_tags.topic_id = topics.id AND #{DB.sql_fragment("watched_topic_tags.tag_id IN (?)", watched_tag_ids)}",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
list =
|
||||||
|
list.where(
|
||||||
"topics.category_id = :category_id
|
"topics.category_id = :category_id
|
||||||
OR
|
OR
|
||||||
(COALESCE(category_users.notification_level, :default) <> :muted AND (topics.category_id IS NULL OR topics.category_id NOT IN(:indirectly_muted_category_ids)))
|
(COALESCE(category_users.notification_level, :default) <> :muted AND (topics.category_id IS NULL OR topics.category_id NOT IN(:indirectly_muted_category_ids)))
|
||||||
|
#{watched_tag_ids.present? ? "OR watched_topic_tags.id IS NOT NULL" : ""}
|
||||||
OR tu.notification_level > :regular",
|
OR tu.notification_level > :regular",
|
||||||
category_id: category_id || -1,
|
category_id: category_id || -1,
|
||||||
default: CategoryUser.default_notification_level,
|
default: CategoryUser.default_notification_level,
|
||||||
|
@ -971,8 +989,18 @@ class TopicQuery
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM topic_tags tt
|
FROM topic_tags tt
|
||||||
WHERE tt.tag_id IN (:tag_ids)
|
WHERE tt.tag_id IN (:tag_ids)
|
||||||
AND tt.topic_id = topics.id)",
|
AND tt.topic_id = topics.id
|
||||||
|
#{user && !opts[:skip_categories] ? "AND COALESCE(category_users.notification_level, :regular) < :watching_or_infinite" : ""})",
|
||||||
tag_ids: muted_tag_ids,
|
tag_ids: muted_tag_ids,
|
||||||
|
regular: CategoryUser.notification_levels[:regular],
|
||||||
|
watching_or_infinite:
|
||||||
|
(
|
||||||
|
if SiteSetting.watched_precedence_over_muted
|
||||||
|
CategoryUser.notification_levels[:watching]
|
||||||
|
else
|
||||||
|
99
|
||||||
|
end
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
list =
|
list =
|
||||||
|
@ -981,10 +1009,20 @@ class TopicQuery
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM topic_tags tt
|
FROM topic_tags tt
|
||||||
WHERE tt.tag_id NOT IN (:tag_ids)
|
WHERE (tt.tag_id NOT IN (:tag_ids)
|
||||||
AND tt.topic_id = topics.id
|
AND tt.topic_id = topics.id)
|
||||||
|
#{user && !opts[:skip_categories] ? "OR COALESCE(category_users.notification_level, :regular) >= :watching_or_infinite" : ""}
|
||||||
) OR NOT EXISTS (SELECT 1 FROM topic_tags tt WHERE tt.topic_id = topics.id)",
|
) OR NOT EXISTS (SELECT 1 FROM topic_tags tt WHERE tt.topic_id = topics.id)",
|
||||||
tag_ids: muted_tag_ids,
|
tag_ids: muted_tag_ids,
|
||||||
|
regular: CategoryUser.notification_levels[:regular],
|
||||||
|
watching_or_infinite:
|
||||||
|
(
|
||||||
|
if SiteSetting.watched_precedence_over_muted
|
||||||
|
CategoryUser.notification_levels[:watching]
|
||||||
|
else
|
||||||
|
99
|
||||||
|
end
|
||||||
|
),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,7 +49,7 @@ class TopicQuery
|
||||||
|
|
||||||
def list_private_messages_new(user, type = :user)
|
def list_private_messages_new(user, type = :user)
|
||||||
list = filter_private_message_new(user, type)
|
list = filter_private_message_new(user, type)
|
||||||
list = TopicQuery.remove_muted_tags(list, user)
|
list = TopicQuery.remove_muted_tags(list, user, skip_categories: true)
|
||||||
list = remove_dismissed(list, user)
|
list = remove_dismissed(list, user)
|
||||||
|
|
||||||
create_list(:private_messages, {}, list)
|
create_list(:private_messages, {}, list)
|
||||||
|
|
|
@ -2092,4 +2092,73 @@ RSpec.describe TopicQuery do
|
||||||
expect(original_topic_query.list_latest.topics.map(&:id)).to eq([topic2, topic1].map(&:id))
|
expect(original_topic_query.list_latest.topics.map(&:id)).to eq([topic2, topic1].map(&:id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "precedence of categories and tag setting" do
|
||||||
|
fab!(:watched_category) do
|
||||||
|
Fabricate(:category).tap do |category|
|
||||||
|
CategoryUser.create!(
|
||||||
|
user: user,
|
||||||
|
category: category,
|
||||||
|
notification_level: CategoryUser.notification_levels[:watching],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fab!(:muted_category) do
|
||||||
|
Fabricate(:category).tap do |category|
|
||||||
|
CategoryUser.create!(
|
||||||
|
user: user,
|
||||||
|
category: category,
|
||||||
|
notification_level: CategoryUser.notification_levels[:muted],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fab!(:watched_tag) do
|
||||||
|
Fabricate(:tag).tap do |tag|
|
||||||
|
TagUser.create!(
|
||||||
|
user: user,
|
||||||
|
tag: tag,
|
||||||
|
notification_level: TagUser.notification_levels[:watching],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fab!(:muted_tag) do
|
||||||
|
Fabricate(:tag).tap do |tag|
|
||||||
|
TagUser.create!(
|
||||||
|
user: user,
|
||||||
|
tag: tag,
|
||||||
|
notification_level: TagUser.notification_levels[:muted],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fab!(:topic) { Fabricate(:topic) }
|
||||||
|
fab!(:topic_in_watched_category_and_muted_tag) do
|
||||||
|
Fabricate(:topic, category: watched_category, tags: [muted_tag])
|
||||||
|
end
|
||||||
|
fab!(:topic_in_muted_category_and_watched_tag) do
|
||||||
|
Fabricate(:topic, category: muted_category, tags: [watched_tag])
|
||||||
|
end
|
||||||
|
fab!(:topic_in_watched_and_muted_tag) { Fabricate(:topic, tags: [watched_tag, muted_tag]) }
|
||||||
|
fab!(:topic_in_muted_category) { Fabricate(:topic, category: muted_category) }
|
||||||
|
fab!(:topic_in_muted_tag) { Fabricate(:topic, tags: [muted_tag]) }
|
||||||
|
|
||||||
|
context "when enabled" do
|
||||||
|
it "returns topics even if category or tag is muted but another tag or category is watched" do
|
||||||
|
SiteSetting.watched_precedence_over_muted = true
|
||||||
|
query = TopicQuery.new(user).list_latest
|
||||||
|
expect(query.topics.map(&:id)).to contain_exactly(
|
||||||
|
topic.id,
|
||||||
|
topic_in_watched_category_and_muted_tag.id,
|
||||||
|
topic_in_muted_category_and_watched_tag.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when disabled" do
|
||||||
|
it "returns topics without muted category or tag" do
|
||||||
|
SiteSetting.watched_precedence_over_muted = false
|
||||||
|
query = TopicQuery.new(user).list_latest
|
||||||
|
expect(query.topics.map(&:id)).to contain_exactly(topic.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -230,6 +230,8 @@ RSpec.describe TagUser do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets notification level to the highest one if there are multiple tags" do
|
it "sets notification level to the highest one if there are multiple tags" do
|
||||||
|
SiteSetting.watched_precedence_over_muted = true
|
||||||
|
|
||||||
TagUser.create!(
|
TagUser.create!(
|
||||||
user: user,
|
user: user,
|
||||||
tag: tracked_tag,
|
tag: tracked_tag,
|
||||||
|
|
|
@ -1871,6 +1871,77 @@ RSpec.describe PostAlerter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with category and tags" do
|
||||||
|
fab!(:muted_category) do
|
||||||
|
Fabricate(:category).tap do |category|
|
||||||
|
CategoryUser.set_notification_level_for_category(
|
||||||
|
user,
|
||||||
|
CategoryUser.notification_levels[:muted],
|
||||||
|
category.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fab!(:muted_tag) do
|
||||||
|
Fabricate(:tag).tap do |tag|
|
||||||
|
TagUser.create!(
|
||||||
|
user: user,
|
||||||
|
tag: tag,
|
||||||
|
notification_level: TagUser.notification_levels[:muted],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fab!(:watched_tag) do
|
||||||
|
Fabricate(:tag).tap do |tag|
|
||||||
|
TagUser.create!(
|
||||||
|
user: user,
|
||||||
|
tag: tag,
|
||||||
|
notification_level: TagUser.notification_levels[:watching],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fab!(:topic_with_muted_tag_and_watched_category) do
|
||||||
|
Fabricate(:topic, category: category, tags: [muted_tag])
|
||||||
|
end
|
||||||
|
fab!(:topic_with_muted_category_and_watched_tag) do
|
||||||
|
Fabricate(:topic, category: muted_category, tags: [watched_tag])
|
||||||
|
end
|
||||||
|
fab!(:topic_with_watched_category) { Fabricate(:topic, category: category) }
|
||||||
|
fab!(:post) { Fabricate(:post, topic: topic_with_muted_tag_and_watched_category) }
|
||||||
|
fab!(:post_2) { Fabricate(:post, topic: topic_with_muted_category_and_watched_tag) }
|
||||||
|
fab!(:post_3) { Fabricate(:post, topic: topic_with_watched_category) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
CategoryUser.set_notification_level_for_category(
|
||||||
|
user,
|
||||||
|
CategoryUser.notification_levels[:watching],
|
||||||
|
category.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "adds notification when watched_precedence_over_mute setting is true" do
|
||||||
|
SiteSetting.watched_precedence_over_muted = true
|
||||||
|
expect {
|
||||||
|
PostAlerter.post_created(topic_with_muted_tag_and_watched_category.posts.first)
|
||||||
|
}.to change { Notification.count }.by(1)
|
||||||
|
expect {
|
||||||
|
PostAlerter.post_created(topic_with_muted_category_and_watched_tag.posts.first)
|
||||||
|
}.to change { Notification.count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not add notification when watched_precedence_over_mute setting is false" do
|
||||||
|
SiteSetting.watched_precedence_over_muted = false
|
||||||
|
expect {
|
||||||
|
PostAlerter.post_created(topic_with_muted_tag_and_watched_category.posts.first)
|
||||||
|
}.not_to change { Notification.count }
|
||||||
|
expect {
|
||||||
|
PostAlerter.post_created(topic_with_muted_category_and_watched_tag.posts.first)
|
||||||
|
}.not_to change { Notification.count }
|
||||||
|
expect { PostAlerter.post_created(topic_with_watched_category.posts.first) }.to change {
|
||||||
|
Notification.count
|
||||||
|
}.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "with on change" do
|
context "with on change" do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
fab!(:other_tag) { Fabricate(:tag) }
|
fab!(:other_tag) { Fabricate(:tag) }
|
||||||
|
|
Loading…
Reference in New Issue