mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 14:19:49 +08:00
5176c689e9
At some point in the past we decided to rename the 'regular' notification state of topics/categories to 'normal'. However, some UI copy was missed when the initial renaming was done so this commit changes the spots that were missed to the new name.
288 lines
9.4 KiB
Ruby
288 lines
9.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class CategoryUser < ActiveRecord::Base
|
|
belongs_to :category
|
|
belongs_to :user
|
|
|
|
def self.lookup(user, level)
|
|
self.where(user: user, notification_level: notification_levels[level])
|
|
end
|
|
|
|
def self.notification_levels
|
|
NotificationLevels.all
|
|
end
|
|
|
|
def self.watching_levels
|
|
[notification_levels[:watching], notification_levels[:watching_first_post]]
|
|
end
|
|
|
|
def self.batch_set(user, level, category_ids)
|
|
level_num = notification_levels[level]
|
|
category_ids = Category.where(id: category_ids).pluck(:id)
|
|
|
|
changed = false
|
|
|
|
# Update pre-existing category users
|
|
if category_ids.present? && CategoryUser
|
|
.where(user_id: user.id, category_id: category_ids)
|
|
.where.not(notification_level: level_num)
|
|
.update_all(notification_level: level_num) > 0
|
|
|
|
changed = true
|
|
end
|
|
|
|
# Remove extraneous category users
|
|
if CategoryUser.where(user_id: user.id, notification_level: level_num)
|
|
.where.not(category_id: category_ids)
|
|
.delete_all > 0
|
|
|
|
changed = true
|
|
end
|
|
|
|
if category_ids.present?
|
|
params = {
|
|
user_id: user.id,
|
|
level_num: level_num,
|
|
}
|
|
|
|
sql = <<~SQL
|
|
INSERT INTO category_users (user_id, category_id, notification_level)
|
|
SELECT :user_id, :category_id, :level_num
|
|
ON CONFLICT DO NOTHING
|
|
SQL
|
|
|
|
# we could use VALUES here but it would introduce a string
|
|
# into the query, plus it is a bit of a micro optimisation
|
|
category_ids.each do |category_id|
|
|
params[:category_id] = category_id
|
|
if DB.exec(sql, params) > 0
|
|
changed = true
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
if changed
|
|
auto_watch(user_id: user.id)
|
|
auto_track(user_id: user.id)
|
|
end
|
|
|
|
changed
|
|
end
|
|
|
|
def self.set_notification_level_for_category(user, level, category_id)
|
|
record = CategoryUser.where(user: user, category_id: category_id).first
|
|
|
|
return if record && record.notification_level == level
|
|
|
|
if record.present?
|
|
record.notification_level = level
|
|
record.save!
|
|
else
|
|
begin
|
|
CategoryUser.create!(user: user, category_id: category_id, notification_level: level)
|
|
rescue ActiveRecord::RecordNotUnique
|
|
# does not matter
|
|
end
|
|
end
|
|
|
|
auto_watch(user_id: user.id)
|
|
auto_track(user_id: user.id)
|
|
end
|
|
|
|
def self.auto_track(opts = {})
|
|
|
|
builder = DB.build <<~SQL
|
|
UPDATE topic_users tu
|
|
SET notification_level = :tracking,
|
|
notifications_reason_id = :auto_track_category
|
|
FROM topics t, category_users cu
|
|
/*where*/
|
|
SQL
|
|
|
|
builder.where("tu.topic_id = t.id AND
|
|
cu.category_id = t.category_id AND
|
|
cu.user_id = tu.user_id AND
|
|
cu.notification_level = :tracking AND
|
|
tu.notification_level = :regular")
|
|
|
|
if category_id = opts[:category_id]
|
|
builder.where("t.category_id = :category_id", category_id: category_id)
|
|
end
|
|
|
|
if topic_id = opts[:topic_id]
|
|
builder.where("tu.topic_id = :topic_id", topic_id: topic_id)
|
|
end
|
|
|
|
if user_id = opts[:user_id]
|
|
builder.where("tu.user_id = :user_id", user_id: user_id)
|
|
end
|
|
|
|
builder.exec(
|
|
tracking: notification_levels[:tracking],
|
|
regular: notification_levels[:regular],
|
|
auto_track_category: TopicUser.notification_reasons[:auto_track_category]
|
|
)
|
|
end
|
|
|
|
def self.auto_watch(opts = {})
|
|
|
|
builder = DB.build <<~SQL
|
|
UPDATE topic_users tu
|
|
SET notification_level =
|
|
CASE WHEN should_track THEN :tracking
|
|
WHEN should_watch THEN :watching
|
|
ELSE notification_level
|
|
END,
|
|
notifications_reason_id =
|
|
CASE WHEN should_track THEN null
|
|
WHEN should_watch THEN :auto_watch_category
|
|
ELSE notifications_reason_id
|
|
END
|
|
FROM (
|
|
SELECT tu1.topic_id,
|
|
tu1.user_id,
|
|
CASE WHEN
|
|
cu.user_id IS NULL AND tu1.notification_level = :watching AND tu1.notifications_reason_id = :auto_watch_category THEN true
|
|
ELSE false
|
|
END should_track,
|
|
CASE WHEN
|
|
cu.user_id IS NOT NULL AND tu1.notification_level in (:regular, :tracking) THEN true
|
|
ELSE false
|
|
END should_watch
|
|
|
|
FROM topic_users tu1
|
|
JOIN topics t ON t.id = tu1.topic_id
|
|
LEFT JOIN category_users cu ON cu.category_id = t.category_id AND cu.user_id = tu1.user_id AND cu.notification_level = :watching
|
|
/*where2*/
|
|
) as X
|
|
|
|
/*where*/
|
|
SQL
|
|
|
|
builder.where("X.topic_id = tu.topic_id AND X.user_id = tu.user_id")
|
|
builder.where("should_watch OR should_track")
|
|
|
|
if category_id = opts[:category_id]
|
|
builder.where2("t.category_id = :category_id", category_id: category_id)
|
|
end
|
|
|
|
if topic_id = opts[:topic_id]
|
|
builder.where("tu.topic_id = :topic_id", topic_id: topic_id)
|
|
builder.where2("tu1.topic_id = :topic_id", topic_id: topic_id)
|
|
end
|
|
|
|
if user_id = opts[:user_id]
|
|
builder.where("tu.user_id = :user_id", user_id: user_id)
|
|
builder.where2("tu1.user_id = :user_id", user_id: user_id)
|
|
end
|
|
|
|
builder.exec(
|
|
watching: notification_levels[:watching],
|
|
tracking: notification_levels[:tracking],
|
|
regular: notification_levels[:regular],
|
|
auto_watch_category: TopicUser.notification_reasons[:auto_watch_category]
|
|
)
|
|
|
|
end
|
|
|
|
def self.ensure_consistency!
|
|
DB.exec <<~SQL
|
|
DELETE FROM category_users
|
|
WHERE user_id IN (
|
|
SELECT cu.user_id FROM category_users cu
|
|
LEFT JOIN users u ON u.id = cu.user_id
|
|
WHERE u.id IS NULL
|
|
)
|
|
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(user)
|
|
# Anonymous users have all default categories set to regular tracking,
|
|
# except for default muted categories which stay muted.
|
|
if user.blank?
|
|
notification_levels = [
|
|
SiteSetting.default_categories_watching.split("|"),
|
|
SiteSetting.default_categories_tracking.split("|"),
|
|
SiteSetting.default_categories_watching_first_post.split("|"),
|
|
SiteSetting.default_categories_normal.split("|")
|
|
].flatten.map do |id|
|
|
[id.to_i, self.notification_levels[:regular]]
|
|
end
|
|
|
|
notification_levels += SiteSetting.default_categories_muted.split("|").map do |id|
|
|
[id.to_i, self.notification_levels[:muted]]
|
|
end
|
|
else
|
|
notification_levels = CategoryUser.where(user: user).pluck(:category_id, :notification_level)
|
|
end
|
|
|
|
Hash[*notification_levels.flatten]
|
|
end
|
|
|
|
def self.lookup_for(user, category_ids)
|
|
return {} if user.blank? || category_ids.blank?
|
|
create_lookup(CategoryUser.where(category_id: category_ids, user_id: user.id))
|
|
end
|
|
|
|
def self.create_lookup(category_users)
|
|
category_users.each_with_object({}) do |category_user, acc|
|
|
acc[category_user.category_id] = category_user
|
|
end
|
|
end
|
|
|
|
def self.muted_category_ids_query(user, include_direct: false)
|
|
query = Category
|
|
query = query.where.not(parent_category_id: nil) if !include_direct
|
|
query = query
|
|
.joins("LEFT JOIN categories categories2 ON categories2.id = categories.parent_category_id")
|
|
.joins("LEFT JOIN category_users ON category_users.category_id = categories.id AND category_users.user_id = #{user.id}")
|
|
.joins("LEFT JOIN category_users category_users2 ON category_users2.category_id = categories2.id AND category_users2.user_id = #{user.id}")
|
|
|
|
direct_category_muted_sql = "COALESCE(category_users.notification_level, #{CategoryUser.default_notification_level}) = #{CategoryUser.notification_levels[:muted]}"
|
|
parent_category_muted_sql =
|
|
"(category_users.id IS NULL AND COALESCE(category_users2.notification_level, #{CategoryUser.default_notification_level}) = #{notification_levels[:muted]})"
|
|
|
|
conditions = [parent_category_muted_sql]
|
|
conditions.push(direct_category_muted_sql) if include_direct
|
|
if SiteSetting.max_category_nesting === 3
|
|
query = query
|
|
.joins("LEFT JOIN categories categories3 ON categories3.id = categories2.parent_category_id")
|
|
.joins("LEFT JOIN category_users category_users3 ON category_users3.category_id = categories3.id AND category_users3.user_id = #{user.id}")
|
|
grandparent_category_muted_sql = "(category_users.id IS NULL AND category_users2.id IS NULL AND COALESCE(category_users3.notification_level, #{CategoryUser.default_notification_level}) = #{notification_levels[:muted]})"
|
|
conditions.push(grandparent_category_muted_sql)
|
|
end
|
|
|
|
query.where(conditions.join(" OR "))
|
|
end
|
|
|
|
def self.muted_category_ids(user)
|
|
muted_category_ids_query(user, include_direct: true).pluck("categories.id")
|
|
end
|
|
|
|
def self.indirectly_muted_category_ids(user)
|
|
muted_category_ids_query(user).pluck("categories.id")
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: category_users
|
|
#
|
|
# id :integer not null, primary key
|
|
# category_id :integer not null
|
|
# user_id :integer not null
|
|
# notification_level :integer not null
|
|
# last_seen_at :datetime
|
|
#
|
|
# Indexes
|
|
#
|
|
# idx_category_users_category_id_user_id (category_id,user_id) UNIQUE
|
|
# idx_category_users_user_id_category_id (user_id,category_id) UNIQUE
|
|
# index_category_users_on_user_id_and_last_seen_at (user_id,last_seen_at)
|
|
#
|