discourse/app/controllers/admin/site_settings_controller.rb
Gerhard Schlager eef17318c3
FIX: Applying default user options didn't work for boolean flags (#16890)
It also ensures that only human users are updated and replaces usage of `send` with `public_send`. Also, it adds more specs for existing code.
2022-05-23 15:20:51 +02:00

228 lines
8.7 KiB
Ruby

# frozen_string_literal: true
class Admin::SiteSettingsController < Admin::AdminController
rescue_from Discourse::InvalidParameters do |e|
render_json_error e.message, status: 422
end
def index
render_json_dump(
site_settings: SiteSetting.all_settings,
diags: SiteSetting.diags
)
end
def update
params.require(:id)
id = params[:id]
value = params[id]
value.strip! if value.is_a?(String)
new_setting_name = SiteSettings::DeprecatedSettings::SETTINGS.find do |old_name, new_name, _, _|
break new_name if old_name == id
end
id = new_setting_name if new_setting_name
raise_access_hidden_setting(id)
if SiteSetting.type_supervisor.get_type(id) == :uploaded_image_list
value = Upload.get_from_urls(value.split("|")).to_a
end
if SiteSetting.type_supervisor.get_type(id) == :upload
value = Upload.get_from_url(value) || ""
end
update_existing_users = params[:update_existing_user].present?
previous_value = value_or_default(SiteSetting.public_send(id)) if update_existing_users
SiteSetting.set_and_log(id, value, current_user)
if update_existing_users
new_value = value_or_default(value)
if (user_option = user_options[id.to_sym]).present?
if user_option == "text_size_key"
previous_value = UserOption.text_sizes[previous_value.to_sym]
new_value = UserOption.text_sizes[new_value.to_sym]
elsif user_option == "title_count_mode_key"
previous_value = UserOption.title_count_modes[previous_value.to_sym]
new_value = UserOption.title_count_modes[new_value.to_sym]
end
attrs = { user_option => new_value }
attrs[:email_digests] = (new_value.to_i != 0) if id == "default_email_digest_frequency"
UserOption.human_users.where(user_option => previous_value).update_all(attrs)
elsif id.start_with?("default_categories_")
previous_category_ids = previous_value.split("|")
new_category_ids = new_value.split("|")
notification_level = category_notification_level(id)
categories_to_unwatch = previous_category_ids - new_category_ids
CategoryUser.where(category_id: categories_to_unwatch, notification_level: notification_level).delete_all
TopicUser
.joins(:topic)
.where(notification_level: TopicUser.notification_levels[:watching],
notifications_reason_id: TopicUser.notification_reasons[:auto_watch_category],
topics: { category_id: categories_to_unwatch })
.update_all(notification_level: TopicUser.notification_levels[:regular])
(new_category_ids - previous_category_ids).each do |category_id|
skip_user_ids = CategoryUser.where(category_id: category_id).pluck(:user_id)
User.real.where(staged: false).where.not(id: skip_user_ids).select(:id).find_in_batches do |users|
category_users = []
users.each { |user| category_users << { category_id: category_id, user_id: user.id, notification_level: notification_level } }
CategoryUser.insert_all!(category_users)
end
end
elsif id.start_with?("default_tags_")
previous_tag_ids = Tag.where(name: previous_value.split("|")).pluck(:id)
new_tag_ids = Tag.where(name: new_value.split("|")).pluck(:id)
now = Time.zone.now
notification_level = tag_notification_level(id)
TagUser.where(tag_id: (previous_tag_ids - new_tag_ids), notification_level: notification_level).delete_all
(new_tag_ids - previous_tag_ids).each do |tag_id|
skip_user_ids = TagUser.where(tag_id: tag_id).pluck(:user_id)
User.real.where(staged: false).where.not(id: skip_user_ids).select(:id).find_in_batches do |users|
tag_users = []
users.each { |user| tag_users << { tag_id: tag_id, user_id: user.id, notification_level: notification_level, created_at: now, updated_at: now } }
TagUser.insert_all!(tag_users)
end
end
end
end
render body: nil
end
def user_count
params.require(:site_setting_id)
id = params[:site_setting_id]
raise Discourse::NotFound unless id.start_with?("default_")
new_value = value_or_default(params[id])
raise_access_hidden_setting(id)
previous_value = value_or_default(SiteSetting.public_send(id))
json = {}
if (user_option = user_options[id.to_sym]).present?
if user_option == "text_size_key"
previous_value = UserOption.text_sizes[previous_value.to_sym]
elsif user_option == "title_count_mode_key"
previous_value = UserOption.title_count_modes[previous_value.to_sym]
end
json[:user_count] = UserOption.human_users.where(user_option => previous_value).count
elsif id.start_with?("default_categories_")
previous_category_ids = previous_value.split("|")
new_category_ids = new_value.split("|")
notification_level = category_notification_level(id)
user_ids = CategoryUser.where(category_id: previous_category_ids - new_category_ids, notification_level: notification_level).distinct.pluck(:user_id)
user_ids += User
.real
.joins("CROSS JOIN categories c")
.joins("LEFT JOIN category_users cu ON users.id = cu.user_id AND c.id = cu.category_id")
.where(staged: false)
.where("c.id IN (?) AND cu.notification_level IS NULL", new_category_ids - previous_category_ids)
.distinct
.pluck("users.id")
json[:user_count] = user_ids.uniq.count
elsif id.start_with?("default_tags_")
previous_tag_ids = Tag.where(name: previous_value.split("|")).pluck(:id)
new_tag_ids = Tag.where(name: new_value.split("|")).pluck(:id)
notification_level = tag_notification_level(id)
user_ids = TagUser.where(tag_id: previous_tag_ids - new_tag_ids, notification_level: notification_level).distinct.pluck(:user_id)
user_ids += User
.real
.joins("CROSS JOIN tags t")
.joins("LEFT JOIN tag_users tu ON users.id = tu.user_id AND t.id = tu.tag_id")
.where(staged: false)
.where("t.id IN (?) AND tu.notification_level IS NULL", new_tag_ids - previous_tag_ids)
.distinct
.pluck("users.id")
json[:user_count] = user_ids.uniq.count
end
render json: json
end
private
def user_options
{
default_email_mailing_list_mode: "mailing_list_mode",
default_email_mailing_list_mode_frequency: "mailing_list_mode_frequency",
default_email_level: "email_level",
default_email_messages_level: "email_messages_level",
default_topics_automatic_unpin: "automatically_unpin_topics",
default_email_previous_replies: "email_previous_replies",
default_email_in_reply_to: "email_in_reply_to",
default_other_enable_quoting: "enable_quoting",
default_other_enable_defer: "enable_defer",
default_other_external_links_in_new_tab: "external_links_in_new_tab",
default_other_dynamic_favicon: "dynamic_favicon",
default_other_new_topic_duration_minutes: "new_topic_duration_minutes",
default_other_auto_track_topics_after_msecs: "auto_track_topics_after_msecs",
default_other_notification_level_when_replying: "notification_level_when_replying",
default_other_like_notification_frequency: "like_notification_frequency",
default_other_skip_new_user_tips: "skip_new_user_tips",
default_email_digest_frequency: "digest_after_minutes",
default_include_tl0_in_digests: "include_tl0_in_digests",
default_text_size: "text_size_key",
default_title_count_mode: "title_count_mode_key"
}
end
def raise_access_hidden_setting(id)
# note, as of Ruby 2.3 symbols are GC'd so this is considered safe
if SiteSetting.hidden_settings.include?(id.to_sym)
raise Discourse::InvalidParameters, "You are not allowed to change hidden settings"
end
end
def tag_notification_level(id)
case id
when "default_tags_watching"
NotificationLevels.all[:watching]
when "default_tags_tracking"
NotificationLevels.all[:tracking]
when "default_tags_muted"
NotificationLevels.all[:muted]
when "default_tags_watching_first_post"
NotificationLevels.all[:watching_first_post]
end
end
def category_notification_level(id)
case id
when "default_categories_watching"
NotificationLevels.all[:watching]
when "default_categories_tracking"
NotificationLevels.all[:tracking]
when "default_categories_muted"
NotificationLevels.all[:muted]
when "default_categories_watching_first_post"
NotificationLevels.all[:watching_first_post]
when "default_categories_regular"
NotificationLevels.all[:regular]
end
end
def value_or_default(value)
value.nil? ? "" : value
end
end