2017-08-06 21:43:09 -04:00
|
|
|
require_dependency 'site_settings/deprecated_settings'
|
|
|
|
require_dependency 'site_settings/type_supervisor'
|
|
|
|
require_dependency 'site_settings/defaults_provider'
|
2013-06-22 23:35:06 -04:00
|
|
|
require_dependency 'site_settings/db_provider'
|
2013-03-01 17:21:13 -05:00
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
module SiteSettingExtension
|
2017-08-06 21:43:09 -04:00
|
|
|
include SiteSettings::DeprecatedSettings
|
|
|
|
extend Forwardable
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
def_delegator :defaults, :site_locale, :default_locale
|
|
|
|
def_delegator :defaults, :site_locale=, :default_locale=
|
|
|
|
def_delegator :defaults, :has_setting?
|
|
|
|
def_delegators 'SiteSettings::TypeSupervisor', :types, :supported_types
|
2015-08-06 21:41:48 -04:00
|
|
|
|
2013-06-22 23:35:06 -04:00
|
|
|
# part 1 of refactor, centralizing the dependency here
|
|
|
|
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
|
|
|
|
|
2015-02-03 16:47:06 -05:00
|
|
|
def shadowed_settings
|
|
|
|
@shadowed_settings ||= []
|
|
|
|
end
|
|
|
|
|
2013-10-23 19:05:51 -04:00
|
|
|
def hidden_settings
|
|
|
|
@hidden_settings ||= []
|
|
|
|
end
|
|
|
|
|
2014-02-21 00:52:11 -05:00
|
|
|
def refresh_settings
|
|
|
|
@refresh_settings ||= []
|
|
|
|
end
|
|
|
|
|
2015-08-27 18:55:19 -04:00
|
|
|
def client_settings
|
|
|
|
@client_settings ||= []
|
|
|
|
end
|
|
|
|
|
2015-01-28 22:53:02 -05:00
|
|
|
def previews
|
|
|
|
@previews ||= {}
|
|
|
|
end
|
|
|
|
|
2013-11-15 14:32:33 -05:00
|
|
|
def setting(name_arg, default = nil, opts = {})
|
2013-11-13 14:02:47 -05:00
|
|
|
name = name_arg.to_sym
|
2017-10-26 20:12:44 -04:00
|
|
|
|
|
|
|
shadowed_val = nil
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
mutex.synchronize do
|
2017-08-07 02:15:32 -04:00
|
|
|
defaults.load_setting(
|
|
|
|
name,
|
|
|
|
default,
|
|
|
|
opts.extract!(*SiteSettings::DefaultsProvider::CONSUMED_OPTS)
|
|
|
|
)
|
2017-08-06 21:31:50 -04:00
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
categories[name] = opts[:category] || :uncategorized
|
2015-02-06 06:08:37 -05:00
|
|
|
|
2014-03-30 01:32:33 -04:00
|
|
|
if opts[:hidden]
|
2013-10-23 19:05:51 -04:00
|
|
|
hidden_settings << name
|
|
|
|
end
|
2015-01-28 22:53:02 -05:00
|
|
|
|
2015-02-03 16:47:06 -05:00
|
|
|
if opts[:shadowed_by_global] && GlobalSetting.respond_to?(name)
|
2016-04-04 02:36:32 -04:00
|
|
|
val = GlobalSetting.send(name)
|
2016-08-11 04:04:45 -04:00
|
|
|
|
2016-04-04 02:36:32 -04:00
|
|
|
unless val.nil? || (val == ''.freeze)
|
2017-10-26 20:12:44 -04:00
|
|
|
shadowed_val = val
|
2016-02-09 19:54:40 -05:00
|
|
|
hidden_settings << name
|
|
|
|
shadowed_settings << name
|
|
|
|
end
|
2015-02-03 16:47:06 -05:00
|
|
|
end
|
|
|
|
|
2014-03-30 01:32:33 -04:00
|
|
|
if opts[:refresh]
|
2014-02-21 00:52:11 -05:00
|
|
|
refresh_settings << name
|
|
|
|
end
|
2014-06-18 10:49:21 -04:00
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
if opts[:client]
|
|
|
|
client_settings << name.to_sym
|
|
|
|
end
|
|
|
|
|
2015-01-28 22:53:02 -05:00
|
|
|
if opts[:preview]
|
|
|
|
previews[name] = opts[:preview]
|
|
|
|
end
|
|
|
|
|
2017-08-07 02:15:32 -04:00
|
|
|
type_supervisor.load_setting(
|
|
|
|
name,
|
|
|
|
opts.extract!(*SiteSettings::TypeSupervisor::CONSUMED_OPTS)
|
|
|
|
)
|
2014-03-28 01:36:17 -04:00
|
|
|
|
2017-10-26 20:12:44 -04:00
|
|
|
if !shadowed_val.nil?
|
|
|
|
setup_shadowed_methods(name, shadowed_val)
|
|
|
|
else
|
|
|
|
setup_methods(name)
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-05 15:21:55 -04:00
|
|
|
def settings_hash
|
|
|
|
result = {}
|
2017-08-06 21:43:09 -04:00
|
|
|
defaults.each_key do |s|
|
2013-04-05 15:21:55 -04:00
|
|
|
result[s] = send(s).to_s
|
|
|
|
end
|
|
|
|
result
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
def client_settings_json
|
|
|
|
Rails.cache.fetch(SiteSettingExtension.client_settings_cache_key, expires_in: 30.minutes) do
|
2013-10-09 19:32:03 -04:00
|
|
|
client_settings_json_uncached
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-10-09 19:32:03 -04:00
|
|
|
def client_settings_json_uncached
|
2017-07-27 21:20:09 -04:00
|
|
|
MultiJson.dump(Hash[*@client_settings.map { |n| [n, self.send(n)] }.flatten])
|
2013-10-09 19:32:03 -04:00
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
# Retrieve all settings
|
2017-07-27 21:20:09 -04:00
|
|
|
def all_settings(include_hidden = false)
|
2017-08-06 21:43:09 -04:00
|
|
|
defaults
|
|
|
|
.reject { |s, _| !include_hidden && hidden_settings.include?(s) }
|
2013-10-23 19:05:51 -04:00
|
|
|
.map do |s, v|
|
2017-08-06 21:43:09 -04:00
|
|
|
value = send(s)
|
|
|
|
opts = {
|
|
|
|
setting: s,
|
|
|
|
description: description(s),
|
|
|
|
default: defaults[s].to_s,
|
|
|
|
value: value.to_s,
|
|
|
|
category: categories[s],
|
|
|
|
preview: previews[s]
|
|
|
|
}.merge(type_supervisor.type_hash(s))
|
2015-08-06 21:41:48 -04:00
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
opts
|
|
|
|
end.unshift(defaults.locale_setting_hash)
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def description(setting)
|
|
|
|
I18n.t("site_settings.#{setting}")
|
|
|
|
end
|
|
|
|
|
|
|
|
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 { |s|
|
|
|
|
[s.name.to_sym, type_supervisor.to_rb_value(s.name, s.value, s.data_type)]
|
2014-03-28 01:36:17 -04:00
|
|
|
}.to_a.flatten)]
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
defaults_view = defaults.all
|
|
|
|
|
|
|
|
# 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
|
2015-09-09 06:59:49 -04:00
|
|
|
shadowed_settings.each { |ss| new_hash[ss] = GlobalSetting.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)
|
2015-09-09 06:59:49 -04:00
|
|
|
changes.each { |name, val| current[name] = val }
|
2017-08-06 21:43:09 -04:00
|
|
|
deletions.each { |name, _| current[name] = defaults_view[name] }
|
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
|
|
|
|
unless @subscribed
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.subscribe("/site_settings") do |message|
|
2013-06-12 22:41:27 -04:00
|
|
|
process_message(message)
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
@subscribed = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-12 22:41:27 -04:00
|
|
|
def process_message(message)
|
|
|
|
data = message.data
|
|
|
|
if data["process"] != process_id
|
|
|
|
begin
|
|
|
|
@last_message_processed = message.global_id
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.on_connect.call(message.site_id)
|
2013-06-22 23:35:06 -04:00
|
|
|
refresh!
|
2013-06-12 22:41:27 -04:00
|
|
|
ensure
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.on_disconnect.call(message.site_id)
|
2013-06-12 22:41:27 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-05 02:43:48 -04:00
|
|
|
def diags
|
|
|
|
{
|
|
|
|
last_message_processed: @last_message_processed
|
|
|
|
}
|
|
|
|
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)
|
2013-06-22 23:35:06 -04:00
|
|
|
provider.destroy(name)
|
|
|
|
current[name] = defaults[name]
|
2014-03-28 01:36:17 -04:00
|
|
|
clear_cache!
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2015-03-02 12:12:19 -05:00
|
|
|
def add_override!(name, val)
|
2017-08-06 21:43:09 -04:00
|
|
|
val, type = type_supervisor.to_db_value(name, val)
|
2013-06-22 23:35:06 -04:00
|
|
|
provider.save(name, val, type)
|
2017-08-06 21:43:09 -04:00
|
|
|
current[name] = type_supervisor.to_rb_value(name, val)
|
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!
|
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!
|
2017-07-27 21:20:09 -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)
|
2017-07-27 21:20:09 -04:00
|
|
|
MessageBus.publish('/client_settings', name: name, value: self.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
|
|
|
|
|
2018-04-12 11:09:09 -04:00
|
|
|
HOSTNAME_SETTINGS ||= %w{
|
|
|
|
disabled_image_download_domains onebox_domains_blacklist exclude_rel_nofollow_domains
|
|
|
|
email_domains_blacklist email_domains_whitelist white_listed_spam_host_domains
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
value.split("|").map { |url| get_hostname(url) }.compact.uniq.join("|")
|
|
|
|
else
|
|
|
|
value
|
2014-07-24 08:00:15 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-27 13:05:35 -05:00
|
|
|
def set(name, value)
|
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)
|
2014-01-27 13:05:35 -05:00
|
|
|
self.send("#{name}=", value)
|
2014-02-21 00:52:11 -05:00
|
|
|
Discourse.request_refresh! if requires_refresh?(name)
|
2014-01-27 13:05:35 -05:00
|
|
|
else
|
2015-04-23 08:40:12 -04:00
|
|
|
raise ArgumentError.new("Either no setting named '#{name}' exists or value provided is invalid")
|
2014-01-27 13:05:35 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-07-27 21:20:09 -04:00
|
|
|
def set_and_log(name, value, user = Discourse.system_user)
|
2016-04-26 13:08:19 -04:00
|
|
|
prev_value = send(name)
|
|
|
|
set(name, value)
|
2018-05-14 13:05:55 -04:00
|
|
|
if has_setting?(name)
|
2018-05-15 00:07:13 -04:00
|
|
|
value = prev_value = "[FILTERED]" if name.to_s =~ /_secret/
|
2018-05-14 13:05:55 -04:00
|
|
|
StaffActionLogger.new(user).log_site_setting_change(name, prev_value, value)
|
|
|
|
end
|
2016-04-26 13:08:19 -04:00
|
|
|
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!
|
|
|
|
Rails.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|
|
2017-07-27 21:20:09 -04:00
|
|
|
changes << [name, value] if !old.has_key?(name) || old[name] != value
|
2013-06-12 22:41:27 -04:00
|
|
|
end
|
|
|
|
|
2017-07-27 21:20:09 -04:00
|
|
|
old.each do |name, value|
|
|
|
|
deletions << [name, value] unless new_hash.has_key?(name)
|
2013-06-12 22:41:27 -04:00
|
|
|
end
|
|
|
|
|
2017-07-27 21:20:09 -04:00
|
|
|
[changes, deletions]
|
2013-06-12 22:41:27 -04:00
|
|
|
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|
|
|
|
|
Rails.logger.warn("An attempt was to change #{clean_name} SiteSetting to #{val} however it is shadowed so this will be ignored!")
|
|
|
|
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
|
|
|
|
2015-02-11 22:44:40 -05:00
|
|
|
define_singleton_method clean_name do
|
2017-10-12 03:13:11 -04:00
|
|
|
if (c = current[name]).nil?
|
2014-03-28 01:36:17 -04:00
|
|
|
refresh!
|
|
|
|
current[name]
|
2017-10-12 03:13:11 -04:00
|
|
|
else
|
|
|
|
c
|
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
|
|
|
|
2015-02-11 23:07:17 -05:00
|
|
|
define_singleton_method "#{clean_name}?" do
|
2015-02-11 23:01:14 -05:00
|
|
|
self.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)
|
2018-04-12 11:09:09 -04:00
|
|
|
url.strip!
|
|
|
|
|
|
|
|
host = begin
|
|
|
|
URI.parse(url)&.host
|
2018-03-28 04:20:08 -04:00
|
|
|
rescue URI::InvalidURIError
|
2018-04-12 11:09:09 -04:00
|
|
|
nil
|
2014-07-24 08:00:15 -04:00
|
|
|
end
|
2018-03-28 04:20:08 -04:00
|
|
|
|
2018-04-12 11:09:09 -04:00
|
|
|
host ||= begin
|
|
|
|
URI.parse("http://#{url}")&.host
|
|
|
|
rescue URI::InvalidURIError
|
|
|
|
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
|
|
|
|
|
|
|
|
def logger
|
|
|
|
Rails.logger
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|