2018-05-31 22:22:47 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
module SiteSettingExtension
|
2017-08-06 21:43:09 -04:00
|
|
|
include SiteSettings::DeprecatedSettings
|
2021-10-27 10:33:07 -04:00
|
|
|
include HasSanitizableFields
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2018-05-31 22:22:47 -04:00
|
|
|
# support default_locale being set via global settings
|
|
|
|
# this also adds support for testing the extension and global settings
|
|
|
|
# for site locale
|
|
|
|
def self.extended(klass)
|
|
|
|
if GlobalSetting.respond_to?(:default_locale) && GlobalSetting.default_locale.present?
|
2019-05-06 22:22:37 -04:00
|
|
|
# protected
|
2018-05-31 22:22:47 -04:00
|
|
|
klass.send :setup_shadowed_methods, :default_locale, GlobalSetting.default_locale
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# we need a default here to support defaults per locale
|
|
|
|
def default_locale=(val)
|
|
|
|
val = val.to_s
|
|
|
|
raise Discourse::InvalidParameters.new(:value) unless LocaleSiteSetting.valid_value?(val)
|
|
|
|
if val != self.default_locale
|
|
|
|
add_override!(:default_locale, val)
|
|
|
|
refresh!
|
|
|
|
Discourse.request_refresh!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_locale?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# set up some sort of default so we can look stuff up
|
|
|
|
def default_locale
|
|
|
|
# note optimised cause this is called a lot so avoiding .presence which
|
|
|
|
# adds 2 method calls
|
|
|
|
locale = current[:default_locale]
|
|
|
|
if locale && !locale.blank?
|
|
|
|
locale
|
|
|
|
else
|
|
|
|
SiteSettings::DefaultsProvider::DEFAULT_LOCALE
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def has_setting?(v)
|
|
|
|
defaults.has_setting?(v)
|
|
|
|
end
|
|
|
|
|
|
|
|
def supported_types
|
|
|
|
SiteSettings::TypeSupervisor.supported_types
|
|
|
|
end
|
|
|
|
|
|
|
|
def types
|
|
|
|
SiteSettings::TypeSupervisor.types
|
|
|
|
end
|
2015-08-06 21:41:48 -04:00
|
|
|
|
2018-05-24 22:15:45 -04:00
|
|
|
def listen_for_changes=(val)
|
|
|
|
@listen_for_changes = val
|
|
|
|
end
|
|
|
|
|
2013-06-22 23:35:06 -04:00
|
|
|
def provider=(val)
|
|
|
|
@provider = val
|
|
|
|
refresh!
|
|
|
|
end
|
|
|
|
|
|
|
|
def provider
|
|
|
|
@provider ||= SiteSettings::DbProvider.new(SiteSetting)
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def mutex
|
|
|
|
@mutex ||= Mutex.new
|
|
|
|
end
|
|
|
|
|
|
|
|
def current
|
2014-03-28 01:36:17 -04:00
|
|
|
@containers ||= {}
|
|
|
|
@containers[provider.current_site] ||= {}
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def defaults
|
2017-08-06 21:43:09 -04:00
|
|
|
@defaults ||= SiteSettings::DefaultsProvider.new(self)
|
2013-11-13 14:02:47 -05:00
|
|
|
end
|
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
def type_supervisor
|
|
|
|
@type_supervisor ||= SiteSettings::TypeSupervisor.new(defaults)
|
2014-06-01 10:37:51 -04:00
|
|
|
end
|
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
def categories
|
|
|
|
@categories ||= {}
|
2017-08-06 21:31:50 -04:00
|
|
|
end
|
|
|
|
|
2024-09-02 19:25:45 -04:00
|
|
|
def areas
|
|
|
|
@areas ||= {}
|
|
|
|
end
|
|
|
|
|
2024-04-17 18:53:52 -04:00
|
|
|
def mandatory_values
|
|
|
|
@mandatory_values ||= {}
|
|
|
|
end
|
|
|
|
|
2015-02-03 16:47:06 -05:00
|
|
|
def shadowed_settings
|
|
|
|
@shadowed_settings ||= []
|
|
|
|
end
|
|
|
|
|
2024-06-19 02:01:24 -04:00
|
|
|
def requires_confirmation_settings
|
|
|
|
@requires_confirmation_settings ||= {}
|
|
|
|
end
|
|
|
|
|
2023-10-31 10:09:51 -04:00
|
|
|
def hidden_settings_provider
|
|
|
|
@hidden_settings_provider ||= SiteSettings::HiddenProvider.new
|
|
|
|
end
|
|
|
|
|
2013-10-23 19:05:51 -04:00
|
|
|
def hidden_settings
|
2023-10-31 10:09:51 -04:00
|
|
|
hidden_settings_provider.all
|
2013-10-23 19:05:51 -04:00
|
|
|
end
|
|
|
|
|
2014-02-21 00:52:11 -05:00
|
|
|
def refresh_settings
|
2018-05-31 22:22:47 -04:00
|
|
|
@refresh_settings ||= [:default_locale]
|
2014-02-21 00:52:11 -05:00
|
|
|
end
|
|
|
|
|
2015-08-27 18:55:19 -04:00
|
|
|
def client_settings
|
2018-05-31 22:22:47 -04:00
|
|
|
@client_settings ||= [:default_locale]
|
2015-08-27 18:55:19 -04:00
|
|
|
end
|
|
|
|
|
2015-01-28 22:53:02 -05:00
|
|
|
def previews
|
|
|
|
@previews ||= {}
|
|
|
|
end
|
|
|
|
|
2018-06-02 09:57:52 -04:00
|
|
|
def secret_settings
|
|
|
|
@secret_settings ||= []
|
|
|
|
end
|
|
|
|
|
2020-05-10 07:07:45 -04:00
|
|
|
def plugins
|
|
|
|
@plugins ||= {}
|
|
|
|
end
|
|
|
|
|
2023-05-24 19:53:57 -04:00
|
|
|
def load_settings(file, plugin: nil)
|
|
|
|
SiteSettings::YamlLoader
|
|
|
|
.new(file)
|
|
|
|
.load do |category, name, default, opts|
|
|
|
|
setting(name, default, opts.merge(category: category, plugin: plugin))
|
2015-02-03 16:47:06 -05:00
|
|
|
end
|
2023-05-10 09:21:48 -04:00
|
|
|
end
|
|
|
|
|
2023-11-06 01:36:20 -05:00
|
|
|
def deprecated_settings
|
|
|
|
@deprecated_settings ||= SiteSettings::DeprecatedSettings::SETTINGS.map(&:first).to_set
|
|
|
|
end
|
|
|
|
|
2013-04-05 15:21:55 -04:00
|
|
|
def settings_hash
|
|
|
|
result = {}
|
2018-11-19 22:59:38 -05:00
|
|
|
|
2018-05-31 22:22:47 -04:00
|
|
|
defaults.all.keys.each do |s|
|
2018-11-19 22:59:38 -05:00
|
|
|
result[s] = if deprecated_settings.include?(s.to_s)
|
2019-05-06 19:41:05 -04:00
|
|
|
public_send(s, warn: false).to_s
|
2018-11-19 22:59:38 -05:00
|
|
|
else
|
2019-05-06 19:41:05 -04:00
|
|
|
public_send(s).to_s
|
2018-11-19 22:59:38 -05:00
|
|
|
end
|
2013-04-05 15:21:55 -04:00
|
|
|
end
|
2018-11-19 22:59:38 -05:00
|
|
|
|
2013-04-05 15:21:55 -04:00
|
|
|
result
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
def client_settings_json
|
2024-07-11 05:30:53 -04:00
|
|
|
key = SiteSettingExtension.client_settings_cache_key
|
|
|
|
json = Discourse.cache.fetch(key, expires_in: 30.minutes) { client_settings_json_uncached }
|
|
|
|
Rails.logger.error("Nil client_settings_json from the cache for '#{key}'") if json.nil?
|
|
|
|
json || ""
|
|
|
|
rescue => e
|
|
|
|
Rails.logger.error("Error while retrieving client_settings_json: #{e.message}")
|
|
|
|
""
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2013-10-09 19:32:03 -04:00
|
|
|
def client_settings_json_uncached
|
2018-11-14 02:03:02 -05:00
|
|
|
MultiJson.dump(
|
|
|
|
Hash[
|
2024-07-11 05:30:53 -04:00
|
|
|
*@client_settings.flat_map do |name|
|
|
|
|
value =
|
|
|
|
if deprecated_settings.include?(name.to_s)
|
|
|
|
public_send(name, warn: false)
|
|
|
|
else
|
|
|
|
public_send(name)
|
|
|
|
end
|
|
|
|
type = type_supervisor.get_type(name)
|
|
|
|
value = value.to_s if type == :upload
|
|
|
|
value = value.map(&:to_s).join("|") if type == :uploaded_image_list
|
|
|
|
|
|
|
|
[name, value]
|
|
|
|
end
|
2023-01-09 07:10:19 -05:00
|
|
|
],
|
2018-11-14 02:03:02 -05:00
|
|
|
)
|
2024-07-11 05:30:53 -04:00
|
|
|
rescue => e
|
|
|
|
Rails.logger.error("Error while generating client_settings_json_uncached: #{e.message}")
|
|
|
|
nil
|
2013-10-09 19:32:03 -04:00
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
# Retrieve all settings
|
2024-03-17 18:50:39 -04:00
|
|
|
def all_settings(
|
|
|
|
include_hidden: false,
|
|
|
|
include_locale_setting: true,
|
|
|
|
only_overridden: false,
|
|
|
|
filter_categories: nil,
|
2024-06-14 06:07:27 -04:00
|
|
|
filter_plugin: nil,
|
2024-06-30 22:40:37 -04:00
|
|
|
filter_names: nil,
|
2024-09-02 19:25:45 -04:00
|
|
|
filter_allowed_hidden: nil,
|
|
|
|
filter_area: nil
|
2024-03-17 18:50:39 -04:00
|
|
|
)
|
2018-05-31 22:22:47 -04:00
|
|
|
locale_setting_hash = {
|
|
|
|
setting: "default_locale",
|
|
|
|
default: SiteSettings::DefaultsProvider::DEFAULT_LOCALE,
|
|
|
|
category: "required",
|
|
|
|
description: description("default_locale"),
|
|
|
|
type: SiteSetting.types[SiteSetting.types[:enum]],
|
|
|
|
preview: nil,
|
|
|
|
value: self.default_locale,
|
|
|
|
valid_values: LocaleSiteSetting.values,
|
|
|
|
translate_names: LocaleSiteSetting.translate_names?,
|
|
|
|
}
|
|
|
|
|
2024-03-17 18:50:39 -04:00
|
|
|
include_locale_setting = false if filter_categories.present? || filter_plugin.present?
|
|
|
|
|
2018-05-31 22:22:47 -04:00
|
|
|
defaults
|
|
|
|
.all(default_locale)
|
2023-05-10 09:21:48 -04:00
|
|
|
.reject do |setting_name, _|
|
|
|
|
plugins[name] && !Discourse.plugins_by_name[plugins[name]].configurable?
|
|
|
|
end
|
2024-06-30 22:40:37 -04:00
|
|
|
.select do |setting_name, _|
|
|
|
|
is_hidden = hidden_settings.include?(setting_name)
|
|
|
|
|
|
|
|
next true if !is_hidden
|
|
|
|
next false if !include_hidden
|
|
|
|
next true if filter_allowed_hidden.nil?
|
|
|
|
|
|
|
|
filter_allowed_hidden.include?(setting_name)
|
|
|
|
end
|
2024-03-17 18:50:39 -04:00
|
|
|
.select do |setting_name, _|
|
|
|
|
if filter_categories && filter_categories.any?
|
|
|
|
filter_categories.include?(categories[setting_name])
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
2024-09-02 19:25:45 -04:00
|
|
|
.select do |setting_name, _|
|
|
|
|
if filter_area
|
|
|
|
Array.wrap(areas[setting_name]).include?(filter_area)
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
2024-03-17 18:50:39 -04:00
|
|
|
.select do |setting_name, _|
|
|
|
|
if filter_plugin
|
|
|
|
plugins[setting_name] == filter_plugin
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
2022-10-16 22:33:15 -04:00
|
|
|
.map do |s, v|
|
|
|
|
type_hash = type_supervisor.type_hash(s)
|
|
|
|
default = defaults.get(s, default_locale).to_s
|
2019-01-02 02:29:17 -05:00
|
|
|
|
2022-10-16 22:33:15 -04:00
|
|
|
value = public_send(s)
|
|
|
|
value = value.map(&:to_s).join("|") if type_hash[:type].to_s == "uploaded_image_list"
|
2020-10-13 09:17:06 -04:00
|
|
|
|
2022-10-16 22:33:15 -04:00
|
|
|
if type_hash[:type].to_s == "upload" && default.to_i < Upload::SEEDED_ID_THRESHOLD
|
|
|
|
default = default_uploads[default.to_i]
|
|
|
|
end
|
2018-11-14 02:03:02 -05:00
|
|
|
|
2022-10-16 22:33:15 -04:00
|
|
|
opts = {
|
|
|
|
setting: s,
|
|
|
|
description: description(s),
|
2023-10-27 17:42:57 -04:00
|
|
|
keywords: keywords(s),
|
2022-10-16 22:33:15 -04:00
|
|
|
default: default,
|
|
|
|
value: value.to_s,
|
|
|
|
category: categories[s],
|
|
|
|
preview: previews[s],
|
|
|
|
secret: secret_settings.include?(s),
|
|
|
|
placeholder: placeholder(s),
|
2024-04-17 18:53:52 -04:00
|
|
|
mandatory_values: mandatory_values[s],
|
2024-06-19 02:01:24 -04:00
|
|
|
requires_confirmation: requires_confirmation_settings[s],
|
2022-10-16 22:33:15 -04:00
|
|
|
}.merge!(type_hash)
|
|
|
|
|
|
|
|
opts[:plugin] = plugins[s] if plugins[s]
|
|
|
|
|
|
|
|
opts
|
|
|
|
end
|
2024-03-17 18:50:39 -04:00
|
|
|
.select do |setting|
|
|
|
|
if only_overridden
|
|
|
|
setting[:value] != setting[:default]
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
2024-06-14 06:07:27 -04:00
|
|
|
.select do |setting|
|
|
|
|
if filter_names
|
|
|
|
filter_names.include?(setting[:setting].to_s)
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
2024-03-17 18:50:39 -04:00
|
|
|
.unshift(include_locale_setting && !only_overridden ? locale_setting_hash : nil)
|
|
|
|
.compact
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def description(setting)
|
2024-06-20 04:33:01 -04:00
|
|
|
I18n.t("site_settings.#{setting}", base_path: Discourse.base_path, default: "")
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2023-10-27 17:42:57 -04:00
|
|
|
def keywords(setting)
|
2024-04-02 23:06:31 -04:00
|
|
|
Array.wrap(I18n.t("site_settings.keywords.#{setting}", default: ""))
|
2023-10-27 17:42:57 -04:00
|
|
|
end
|
|
|
|
|
2018-10-15 01:03:53 -04:00
|
|
|
def placeholder(setting)
|
|
|
|
if !I18n.t("site_settings.placeholder.#{setting}", default: "").empty?
|
|
|
|
I18n.t("site_settings.placeholder.#{setting}")
|
2019-05-01 09:44:45 -04:00
|
|
|
elsif SiteIconManager.respond_to?("#{setting}_url")
|
|
|
|
SiteIconManager.public_send("#{setting}_url")
|
2018-10-15 01:03:53 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def self.client_settings_cache_key
|
2015-02-09 01:58:56 -05:00
|
|
|
# NOTE: we use the git version in the key to ensure
|
|
|
|
# that we don't end up caching the incorrect version
|
|
|
|
# in cases where we are cycling unicorns
|
|
|
|
"client_settings_json_#{Discourse.git_version}"
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# refresh all the site settings
|
2013-02-25 11:42:20 -05:00
|
|
|
def refresh!
|
|
|
|
mutex.synchronize do
|
2013-02-05 14:16:51 -05:00
|
|
|
ensure_listen_for_changes
|
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
new_hash =
|
|
|
|
Hash[
|
|
|
|
*(
|
|
|
|
defaults
|
|
|
|
.db_all
|
|
|
|
.map do |s|
|
|
|
|
[s.name.to_sym, type_supervisor.to_rb_value(s.name, s.value, s.data_type)]
|
2023-01-09 07:10:19 -05:00
|
|
|
end
|
2014-03-28 01:36:17 -04:00
|
|
|
.to_a
|
|
|
|
.flatten
|
2023-01-09 07:10:19 -05:00
|
|
|
)
|
2014-03-28 01:36:17 -04:00
|
|
|
]
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2018-05-31 22:22:47 -04:00
|
|
|
defaults_view = defaults.all(new_hash[:default_locale])
|
2017-08-06 21:43:09 -04:00
|
|
|
|
|
|
|
# add locale default and defaults based on default_locale, cause they are cached
|
2017-08-07 02:15:32 -04:00
|
|
|
new_hash = defaults_view.merge!(new_hash)
|
2014-03-28 01:36:17 -04:00
|
|
|
|
2015-02-09 17:28:55 -05:00
|
|
|
# add shadowed
|
2019-05-06 19:41:05 -04:00
|
|
|
shadowed_settings.each { |ss| new_hash[ss] = GlobalSetting.public_send(ss) }
|
2015-02-09 17:28:55 -05:00
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
changes, deletions = diff_hash(new_hash, current)
|
2018-11-15 22:02:51 -05:00
|
|
|
|
2019-06-04 12:37:00 -04:00
|
|
|
changes.each { |name, val| current[name] = val }
|
|
|
|
deletions.each { |name, _| current[name] = defaults_view[name] }
|
|
|
|
uploads.clear
|
2015-02-09 17:28:55 -05:00
|
|
|
|
2014-03-28 01:36:17 -04:00
|
|
|
clear_cache!
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def ensure_listen_for_changes
|
2018-05-24 22:15:45 -04:00
|
|
|
return if @listen_for_changes == false
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
unless @subscribed
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.subscribe("/site_settings") do |message|
|
2019-01-15 21:25:42 -05:00
|
|
|
process_message(message) if message.data["process"] != process_id
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
2019-01-15 21:25:42 -05:00
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
@subscribed = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-12 22:41:27 -04:00
|
|
|
def process_message(message)
|
2019-01-15 21:25:42 -05:00
|
|
|
begin
|
|
|
|
MessageBus.on_connect.call(message.site_id)
|
|
|
|
refresh!
|
|
|
|
ensure
|
|
|
|
MessageBus.on_disconnect.call(message.site_id)
|
2013-06-12 22:41:27 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def process_id
|
2014-03-28 01:36:17 -04:00
|
|
|
@process_id ||= SecureRandom.uuid
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2014-03-27 22:48:14 -04:00
|
|
|
def after_fork
|
2014-03-28 01:36:17 -04:00
|
|
|
@process_id = nil
|
|
|
|
ensure_listen_for_changes
|
2014-03-27 22:48:14 -04:00
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def remove_override!(name)
|
2019-04-18 11:48:01 -04:00
|
|
|
old_val = current[name]
|
2013-06-22 23:35:06 -04:00
|
|
|
provider.destroy(name)
|
2018-05-31 22:22:47 -04:00
|
|
|
current[name] = defaults.get(name, default_locale)
|
2021-11-22 10:43:12 -05:00
|
|
|
|
|
|
|
return if current[name] == old_val
|
|
|
|
|
2018-11-15 22:02:51 -05:00
|
|
|
clear_uploads_cache(name)
|
2014-03-28 01:36:17 -04:00
|
|
|
clear_cache!
|
2019-04-18 11:48:01 -04:00
|
|
|
if old_val != current[name]
|
|
|
|
DiscourseEvent.trigger(:site_setting_changed, name, old_val, current[name])
|
2023-01-09 07:10:19 -05:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2015-03-02 12:12:19 -05:00
|
|
|
def add_override!(name, val)
|
2019-04-18 11:48:01 -04:00
|
|
|
old_val = current[name]
|
2017-08-06 21:43:09 -04:00
|
|
|
val, type = type_supervisor.to_db_value(name, val)
|
2021-10-27 10:33:07 -04:00
|
|
|
|
|
|
|
sanitize_override = val.is_a?(String) && client_settings.include?(name)
|
|
|
|
|
|
|
|
sanitized_val = sanitize_override ? sanitize_field(val) : val
|
2024-04-17 18:53:52 -04:00
|
|
|
|
|
|
|
if mandatory_values[name.to_sym]
|
|
|
|
sanitized_val =
|
|
|
|
(mandatory_values[name.to_sym].split("|") | sanitized_val.to_s.split("|")).join("|")
|
|
|
|
end
|
|
|
|
|
2021-10-27 10:33:07 -04:00
|
|
|
provider.save(name, sanitized_val, type)
|
|
|
|
current[name] = type_supervisor.to_rb_value(name, sanitized_val)
|
2021-11-22 10:43:12 -05:00
|
|
|
|
|
|
|
return if current[name] == old_val
|
|
|
|
|
2018-11-15 22:02:51 -05:00
|
|
|
clear_uploads_cache(name)
|
2015-08-27 18:55:19 -04:00
|
|
|
notify_clients!(name) if client_settings.include? name
|
2014-03-28 01:36:17 -04:00
|
|
|
clear_cache!
|
2019-04-18 11:48:01 -04:00
|
|
|
if old_val != current[name]
|
|
|
|
DiscourseEvent.trigger(:site_setting_changed, name, old_val, current[name])
|
2023-01-09 07:10:19 -05:00
|
|
|
end
|
2014-03-30 21:34:01 -04:00
|
|
|
end
|
2014-03-28 01:36:17 -04:00
|
|
|
|
2014-03-30 21:34:01 -04:00
|
|
|
def notify_changed!
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.publish("/site_settings", process: process_id)
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2015-08-27 18:55:19 -04:00
|
|
|
def notify_clients!(name)
|
2019-05-06 19:41:05 -04:00
|
|
|
MessageBus.publish("/client_settings", name: name, value: self.public_send(name))
|
2015-08-27 18:55:19 -04:00
|
|
|
end
|
|
|
|
|
2014-02-21 00:52:11 -05:00
|
|
|
def requires_refresh?(name)
|
|
|
|
refresh_settings.include?(name.to_sym)
|
|
|
|
end
|
|
|
|
|
2024-10-15 22:09:07 -04:00
|
|
|
HOSTNAME_SETTINGS = %w[
|
2020-07-26 20:23:54 -04:00
|
|
|
disabled_image_download_domains
|
|
|
|
blocked_onebox_domains
|
|
|
|
exclude_rel_nofollow_domains
|
|
|
|
blocked_email_domains
|
|
|
|
allowed_email_domains
|
|
|
|
allowed_spam_host_domains
|
2023-01-09 07:10:19 -05:00
|
|
|
]
|
2018-04-12 11:09:09 -04:00
|
|
|
|
2014-07-24 08:00:15 -04:00
|
|
|
def filter_value(name, value)
|
2018-04-12 11:09:09 -04:00
|
|
|
if HOSTNAME_SETTINGS.include?(name)
|
2019-04-29 20:25:53 -04:00
|
|
|
value
|
|
|
|
.split("|")
|
|
|
|
.map do |url|
|
|
|
|
url.strip!
|
|
|
|
get_hostname(url)
|
2023-01-09 07:10:19 -05:00
|
|
|
end
|
2019-04-29 20:25:53 -04:00
|
|
|
.compact
|
|
|
|
.uniq
|
|
|
|
.join("|")
|
2018-04-12 11:09:09 -04:00
|
|
|
else
|
|
|
|
value
|
2014-07-24 08:00:15 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-05-06 21:00:09 -04:00
|
|
|
def set(name, value, options = nil)
|
2017-08-06 21:43:09 -04:00
|
|
|
if has_setting?(name)
|
2014-07-24 08:00:15 -04:00
|
|
|
value = filter_value(name, value)
|
2019-05-06 21:00:09 -04:00
|
|
|
if options
|
|
|
|
self.public_send("#{name}=", value, options)
|
|
|
|
else
|
|
|
|
self.public_send("#{name}=", value)
|
|
|
|
end
|
2014-02-21 00:52:11 -05:00
|
|
|
Discourse.request_refresh! if requires_refresh?(name)
|
2014-01-27 13:05:35 -05:00
|
|
|
else
|
2018-06-11 00:48:09 -04:00
|
|
|
raise Discourse::InvalidParameters.new(
|
|
|
|
"Either no setting named '#{name}' exists or value provided is invalid",
|
|
|
|
)
|
2014-01-27 13:05:35 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-06-13 00:59:49 -04:00
|
|
|
def set_and_log(name, value, user = Discourse.system_user, detailed_message = nil)
|
2018-05-14 13:05:55 -04:00
|
|
|
if has_setting?(name)
|
2019-05-06 19:41:05 -04:00
|
|
|
prev_value = public_send(name)
|
|
|
|
set(name, value)
|
2018-06-02 09:57:52 -04:00
|
|
|
value = prev_value = "[FILTERED]" if secret_settings.include?(name.to_sym)
|
2024-06-13 00:59:49 -04:00
|
|
|
StaffActionLogger.new(user).log_site_setting_change(
|
|
|
|
name,
|
|
|
|
prev_value,
|
|
|
|
value,
|
|
|
|
{ details: detailed_message }.compact_blank,
|
|
|
|
)
|
2019-05-06 19:48:14 -04:00
|
|
|
else
|
2023-04-18 02:32:18 -04:00
|
|
|
raise Discourse::InvalidParameters.new(
|
|
|
|
I18n.t("errors.site_settings.invalid_site_setting", name: name),
|
|
|
|
)
|
2019-05-06 21:00:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def get(name)
|
|
|
|
if has_setting?(name)
|
|
|
|
self.public_send(name)
|
|
|
|
else
|
2023-04-18 02:32:18 -04:00
|
|
|
raise Discourse::InvalidParameters.new(
|
|
|
|
I18n.t("errors.site_settings.invalid_site_setting", name: name),
|
|
|
|
)
|
2018-05-14 13:05:55 -04:00
|
|
|
end
|
2016-04-26 13:08:19 -04:00
|
|
|
end
|
|
|
|
|
2019-11-20 11:39:47 -05:00
|
|
|
if defined?(Rails::Console)
|
|
|
|
# Convenience method for debugging site setting issues
|
|
|
|
# Returns a hash with information about a specific setting
|
|
|
|
def info(name)
|
|
|
|
{
|
|
|
|
resolved_value: get(name),
|
|
|
|
default_value: defaults[name],
|
|
|
|
global_override: GlobalSetting.respond_to?(name) ? GlobalSetting.public_send(name) : nil,
|
|
|
|
database_value: provider.find(name)&.value,
|
|
|
|
refresh?: refresh_settings.include?(name),
|
|
|
|
client?: client_settings.include?(name),
|
|
|
|
secret?: secret_settings.include?(name),
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-10-28 18:40:31 -04:00
|
|
|
def valid_areas
|
|
|
|
Set.new(SiteSetting::VALID_AREAS | DiscoursePluginRegistry.site_setting_areas.to_a)
|
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
protected
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2014-03-28 01:36:17 -04:00
|
|
|
def clear_cache!
|
2019-11-26 20:35:14 -05:00
|
|
|
Discourse.cache.delete(SiteSettingExtension.client_settings_cache_key)
|
2015-09-28 02:44:03 -04:00
|
|
|
Site.clear_anon_cache!
|
2014-03-28 01:36:17 -04:00
|
|
|
end
|
|
|
|
|
2013-06-12 22:41:27 -04:00
|
|
|
def diff_hash(new_hash, old)
|
|
|
|
changes = []
|
|
|
|
deletions = []
|
|
|
|
|
|
|
|
new_hash.each do |name, value|
|
|
|
|
changes << [name, value] if !old.has_key?(name) || old[name] != value
|
|
|
|
end
|
|
|
|
|
|
|
|
old.each { |name, value| deletions << [name, value] unless new_hash.has_key?(name) }
|
|
|
|
|
|
|
|
[changes, deletions]
|
|
|
|
end
|
|
|
|
|
2017-10-26 20:12:44 -04:00
|
|
|
def setup_shadowed_methods(name, value)
|
|
|
|
clean_name = name.to_s.sub("?", "").to_sym
|
|
|
|
|
|
|
|
define_singleton_method clean_name do
|
|
|
|
value
|
|
|
|
end
|
|
|
|
|
|
|
|
define_singleton_method "#{clean_name}?" do
|
|
|
|
value
|
|
|
|
end
|
|
|
|
|
|
|
|
define_singleton_method "#{clean_name}=" do |val|
|
2021-05-03 03:25:02 -04:00
|
|
|
if value != val
|
|
|
|
Rails.logger.warn(
|
|
|
|
"An attempt was to change #{clean_name} SiteSetting to #{val} however it is shadowed so this will be ignored!",
|
|
|
|
)
|
2023-01-09 07:10:19 -05:00
|
|
|
end
|
2017-10-26 20:12:44 -04:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-02-03 16:47:06 -05:00
|
|
|
def setup_methods(name)
|
2015-02-11 23:07:17 -05:00
|
|
|
clean_name = name.to_s.sub("?", "").to_sym
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2020-10-13 09:17:06 -04:00
|
|
|
if type_supervisor.get_type(name) == :uploaded_image_list
|
|
|
|
define_singleton_method clean_name do
|
|
|
|
uploads_list = uploads[name]
|
|
|
|
return uploads_list if uploads_list
|
|
|
|
|
|
|
|
if (value = current[name]).nil?
|
|
|
|
refresh!
|
|
|
|
value = current[name]
|
|
|
|
end
|
|
|
|
|
2022-03-28 14:15:28 -04:00
|
|
|
return [] if value.empty?
|
|
|
|
|
2020-10-13 09:17:06 -04:00
|
|
|
value = value.split("|").map(&:to_i)
|
|
|
|
uploads_list = Upload.where(id: value).to_a
|
|
|
|
uploads[name] = uploads_list if uploads_list
|
|
|
|
end
|
|
|
|
elsif type_supervisor.get_type(name) == :upload
|
2018-11-14 02:03:02 -05:00
|
|
|
define_singleton_method clean_name do
|
|
|
|
upload = uploads[name]
|
|
|
|
return upload if upload
|
|
|
|
|
|
|
|
if (value = current[name]).nil?
|
|
|
|
refresh!
|
|
|
|
value = current[name]
|
|
|
|
end
|
|
|
|
|
|
|
|
value = value.to_i
|
|
|
|
|
2019-01-02 02:29:17 -05:00
|
|
|
if value != Upload::SEEDED_ID_THRESHOLD
|
2018-11-14 02:03:02 -05:00
|
|
|
upload = Upload.find_by(id: value)
|
|
|
|
uploads[name] = upload if upload
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
define_singleton_method clean_name do
|
2023-05-10 09:21:48 -04:00
|
|
|
if plugins[name]
|
|
|
|
plugin = Discourse.plugins_by_name[plugins[name]]
|
|
|
|
return false if !plugin.configurable? && plugin.enabled_site_setting == name
|
|
|
|
end
|
|
|
|
|
2024-04-17 18:53:52 -04:00
|
|
|
refresh! if current[name].nil?
|
|
|
|
value = current[name]
|
|
|
|
|
|
|
|
if mandatory_values[name]
|
|
|
|
return (mandatory_values[name].split("|") | value.to_s.split("|")).join("|")
|
2018-11-14 02:03:02 -05:00
|
|
|
end
|
2024-04-17 18:53:52 -04:00
|
|
|
value
|
2014-03-28 01:36:17 -04:00
|
|
|
end
|
2013-09-03 03:39:56 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2022-09-25 23:58:40 -04:00
|
|
|
# Any group_list setting, e.g. personal_message_enabled_groups, will have
|
|
|
|
# a getter defined with _map on the end, e.g. personal_message_enabled_groups_map,
|
|
|
|
# to avoid having to manually split and convert to integer for these settings.
|
|
|
|
if type_supervisor.get_type(name) == :group_list
|
|
|
|
define_singleton_method("#{clean_name}_map") do
|
|
|
|
self.public_send(clean_name).to_s.split("|").map(&:to_i)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-05-15 23:43:10 -04:00
|
|
|
# Same logic as above for other list type settings, with the caveat that normal
|
DEV: Add auto _map extension for simple/compact list SiteSettings (#20888)
Similar to the _map added for group_list SiteSettings in
e62e93f83a77adfa80b38fbfecf82bbee14e12fe, this commit adds
the same extension for simple and compact `list` type SiteSettings,
so developers do not have to do the `.to_s.split("|")` dance
themselves all the time.
For example:
```
SiteSetting.markdown_linkify_tlds
=> "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov|ddd"
SiteSetting.markdown_linkify_tlds_map
=> ["com", "net", "org", "io", "onion", "co", "tv", "ru", "cn", "us", "uk", "me", "de", "fr", "fi", "gov"]
```
2023-03-30 00:08:19 -04:00
|
|
|
# list settings are not necessarily integers, so we just want to handle the splitting.
|
2024-05-15 23:43:10 -04:00
|
|
|
if %i[list emoji_list tag_list].include?(type_supervisor.get_type(name))
|
2023-08-30 02:14:06 -04:00
|
|
|
list_type = type_supervisor.get_list_type(name)
|
|
|
|
|
|
|
|
if %w[simple compact].include?(list_type) || list_type.nil?
|
|
|
|
define_singleton_method("#{clean_name}_map") do
|
|
|
|
self.public_send(clean_name).to_s.split("|")
|
|
|
|
end
|
|
|
|
end
|
DEV: Add auto _map extension for simple/compact list SiteSettings (#20888)
Similar to the _map added for group_list SiteSettings in
e62e93f83a77adfa80b38fbfecf82bbee14e12fe, this commit adds
the same extension for simple and compact `list` type SiteSettings,
so developers do not have to do the `.to_s.split("|")` dance
themselves all the time.
For example:
```
SiteSetting.markdown_linkify_tlds
=> "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov|ddd"
SiteSetting.markdown_linkify_tlds_map
=> ["com", "net", "org", "io", "onion", "co", "tv", "ru", "cn", "us", "uk", "me", "de", "fr", "fi", "gov"]
```
2023-03-30 00:08:19 -04:00
|
|
|
end
|
|
|
|
|
2015-02-11 23:07:17 -05:00
|
|
|
define_singleton_method "#{clean_name}?" do
|
2019-05-06 19:41:05 -04:00
|
|
|
self.public_send clean_name
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
2013-02-25 11:42:20 -05:00
|
|
|
|
2015-02-11 23:07:17 -05:00
|
|
|
define_singleton_method "#{clean_name}=" do |val|
|
2015-02-11 22:44:40 -05:00
|
|
|
add_override!(name, val)
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-07-24 08:00:15 -04:00
|
|
|
def get_hostname(url)
|
2023-01-09 07:10:19 -05:00
|
|
|
host =
|
|
|
|
begin
|
2018-04-12 11:09:09 -04:00
|
|
|
URI.parse(url)&.host
|
2018-08-14 06:23:32 -04:00
|
|
|
rescue URI::Error
|
2023-01-09 07:10:19 -05:00
|
|
|
nil
|
|
|
|
end
|
2018-04-12 11:09:09 -04:00
|
|
|
|
|
|
|
host ||=
|
|
|
|
begin
|
|
|
|
URI.parse("http://#{url}")&.host
|
2018-08-14 06:23:32 -04:00
|
|
|
rescue URI::Error
|
2018-04-12 11:09:09 -04:00
|
|
|
nil
|
2018-03-28 04:20:08 -04:00
|
|
|
end
|
|
|
|
|
2018-04-12 11:09:09 -04:00
|
|
|
host.presence || url
|
2014-07-24 08:00:15 -04:00
|
|
|
end
|
|
|
|
|
2016-06-27 05:26:43 -04:00
|
|
|
private
|
|
|
|
|
2023-05-24 19:53:57 -04:00
|
|
|
def setting(name_arg, default = nil, opts = {})
|
|
|
|
name = name_arg.to_sym
|
|
|
|
|
|
|
|
if name == :default_locale
|
|
|
|
raise Discourse::InvalidParameters.new(
|
|
|
|
"Other settings depend on default locale, you can not configure it like this",
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
shadowed_val = nil
|
|
|
|
|
|
|
|
mutex.synchronize do
|
|
|
|
defaults.load_setting(name, default, opts.delete(:locale_default))
|
|
|
|
|
2024-04-17 18:53:52 -04:00
|
|
|
mandatory_values[name] = opts[:mandatory_values] if opts[:mandatory_values]
|
|
|
|
|
2024-06-19 02:01:24 -04:00
|
|
|
requires_confirmation_settings[name] = (
|
|
|
|
if SiteSettings::TypeSupervisor::REQUIRES_CONFIRMATION_TYPES.values.include?(
|
|
|
|
opts[:requires_confirmation],
|
|
|
|
)
|
|
|
|
opts[:requires_confirmation]
|
|
|
|
end
|
|
|
|
)
|
|
|
|
|
2023-05-24 19:53:57 -04:00
|
|
|
categories[name] = opts[:category] || :uncategorized
|
|
|
|
|
2024-09-02 19:25:45 -04:00
|
|
|
if opts[:area]
|
|
|
|
split_areas = opts[:area].split("|")
|
2024-10-28 18:40:31 -04:00
|
|
|
if split_areas.any? { |area| !SiteSetting.valid_areas.include?(area) }
|
2024-09-02 19:25:45 -04:00
|
|
|
raise Discourse::InvalidParameters.new(
|
2024-10-28 18:40:31 -04:00
|
|
|
"Area is invalid, valid areas are: #{SiteSetting.valid_areas.join(", ")}",
|
2024-09-02 19:25:45 -04:00
|
|
|
)
|
|
|
|
end
|
|
|
|
areas[name] = split_areas
|
|
|
|
end
|
2023-10-31 10:09:51 -04:00
|
|
|
hidden_settings_provider.add_hidden(name) if opts[:hidden]
|
2023-05-24 19:53:57 -04:00
|
|
|
|
|
|
|
if GlobalSetting.respond_to?(name)
|
|
|
|
val = GlobalSetting.public_send(name)
|
|
|
|
|
|
|
|
unless val.nil? || (val == "")
|
|
|
|
shadowed_val = val
|
2023-10-31 10:09:51 -04:00
|
|
|
hidden_settings_provider.add_hidden(name)
|
2023-05-24 19:53:57 -04:00
|
|
|
shadowed_settings << name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
refresh_settings << name if opts[:refresh]
|
|
|
|
|
|
|
|
client_settings << name.to_sym if opts[:client]
|
|
|
|
|
|
|
|
previews[name] = opts[:preview] if opts[:preview]
|
|
|
|
|
|
|
|
secret_settings << name if opts[:secret]
|
|
|
|
|
|
|
|
plugins[name] = opts[:plugin] if opts[:plugin]
|
|
|
|
|
|
|
|
type_supervisor.load_setting(
|
|
|
|
name,
|
|
|
|
opts.extract!(*SiteSettings::TypeSupervisor::CONSUMED_OPTS),
|
|
|
|
)
|
|
|
|
|
|
|
|
if !shadowed_val.nil?
|
|
|
|
setup_shadowed_methods(name, shadowed_val)
|
|
|
|
else
|
|
|
|
setup_methods(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-01-02 02:29:17 -05:00
|
|
|
def default_uploads
|
|
|
|
@default_uploads ||= {}
|
|
|
|
|
|
|
|
@default_uploads[provider.current_site] ||= begin
|
|
|
|
Upload.where("id < ?", Upload::SEEDED_ID_THRESHOLD).pluck(:id, :url).to_h
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-14 02:03:02 -05:00
|
|
|
def uploads
|
|
|
|
@uploads ||= {}
|
|
|
|
@uploads[provider.current_site] ||= {}
|
|
|
|
end
|
|
|
|
|
2018-11-15 22:02:51 -05:00
|
|
|
def clear_uploads_cache(name)
|
2020-10-13 09:17:06 -04:00
|
|
|
if (
|
|
|
|
type_supervisor.get_type(name) == :upload ||
|
|
|
|
type_supervisor.get_type(name) == :uploaded_image_list
|
|
|
|
) && uploads.has_key?(name)
|
2018-11-15 22:02:51 -05:00
|
|
|
uploads.delete(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-06-27 05:26:43 -04:00
|
|
|
def logger
|
|
|
|
Rails.logger
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|