mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 08:18:18 +08:00
5e4c0e2caa
To add an extra layer of security, we sanitize settings before shipping them to the client. We don't sanitize those that have the "html" type. The CookedPostProcessor already uses Loofah for sanitization, so I chose to also use it for this. I added it to our gemfile since we installed it as a transitive dependency.
237 lines
9.5 KiB
Ruby
237 lines
9.5 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(sanitize_plain_text_settings: true),
|
|
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 = SiteSetting.send(id) || "" if update_existing_users
|
|
|
|
SiteSetting.set_and_log(id, value, current_user)
|
|
|
|
if update_existing_users
|
|
new_value = 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.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("|")
|
|
|
|
case id
|
|
when "default_categories_watching"
|
|
notification_level = NotificationLevels.all[:watching]
|
|
when "default_categories_tracking"
|
|
notification_level = NotificationLevels.all[:tracking]
|
|
when "default_categories_muted"
|
|
notification_level = NotificationLevels.all[:muted]
|
|
when "default_categories_watching_first_post"
|
|
notification_level = NotificationLevels.all[:watching_first_post]
|
|
when "default_categories_regular"
|
|
notification_level = NotificationLevels.all[:regular]
|
|
end
|
|
|
|
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
|
|
|
|
case id
|
|
when "default_tags_watching"
|
|
notification_level = NotificationLevels.all[:watching]
|
|
when "default_tags_tracking"
|
|
notification_level = NotificationLevels.all[:tracking]
|
|
when "default_tags_muted"
|
|
notification_level = NotificationLevels.all[:muted]
|
|
when "default_tags_watching_first_post"
|
|
notification_level = NotificationLevels.all[:watching_first_post]
|
|
end
|
|
|
|
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 = params[id] || ""
|
|
|
|
raise_access_hidden_setting(id)
|
|
previous_value = SiteSetting.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.where(user_option => previous_value).count
|
|
elsif id.start_with?("default_categories_")
|
|
previous_category_ids = previous_value.split("|")
|
|
new_category_ids = new_value.split("|")
|
|
|
|
case id
|
|
when "default_categories_watching"
|
|
notification_level = NotificationLevels.all[:watching]
|
|
when "default_categories_tracking"
|
|
notification_level = NotificationLevels.all[:tracking]
|
|
when "default_categories_muted"
|
|
notification_level = NotificationLevels.all[:muted]
|
|
when "default_categories_watching_first_post"
|
|
notification_level = NotificationLevels.all[:watching_first_post]
|
|
when "default_categories_regular"
|
|
notification_level = NotificationLevels.all[:regular]
|
|
end
|
|
|
|
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)
|
|
|
|
case id
|
|
when "default_tags_watching"
|
|
notification_level = TagUser.notification_levels[:watching]
|
|
when "default_tags_tracking"
|
|
notification_level = TagUser.notification_levels[:tracking]
|
|
when "default_tags_muted"
|
|
notification_level = TagUser.notification_levels[:muted]
|
|
when "default_tags_watching_first_post"
|
|
notification_level = TagUser.notification_levels[:watching_first_post]
|
|
end
|
|
|
|
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
|
|
|
|
end
|