discourse/lib/site_setting_extension.rb

291 lines
6.3 KiB
Ruby
Raw Normal View History

require_dependency 'enum'
require_dependency 'site_settings/db_provider'
2013-02-05 14:16:51 -05:00
module SiteSettingExtension
# part 1 of refactor, centralizing the dependency here
def provider=(val)
@provider = val
refresh!
end
def provider
@provider ||= SiteSettings::DbProvider.new(SiteSetting)
end
def types
@types ||= Enum.new(:string, :time, :fixnum, :float, :bool, :null, :enum)
2013-02-05 14:16:51 -05:00
end
def mutex
@mutex ||= Mutex.new
end
def current
@@containers ||= {}
@@containers[provider.current_site] ||= {}
2013-02-05 14:16:51 -05:00
end
def defaults
2013-02-25 11:42:20 -05:00
@defaults ||= {}
2013-02-05 14:16:51 -05:00
end
2013-11-13 14:02:47 -05:00
def categories
@categories ||= {}
end
def enums
@enums ||= {}
end
def hidden_settings
@hidden_settings ||= []
end
def setting(name_arg, default = nil, opts = {})
2013-11-13 14:02:47 -05:00
name = name_arg.to_sym
2013-02-25 11:42:20 -05:00
mutex.synchronize do
2013-02-05 14:16:51 -05:00
self.defaults[name] = default
categories[name] = opts[:category] || :uncategorized
2013-02-05 14:16:51 -05:00
current_value = current.has_key?(name) ? current[name] : default
if opts[:enum]
enum = opts[:enum]
enums[name] = enum.is_a?(String) ? enum.constantize : enum
end
if opts[:hidden] == true
hidden_settings << name
end
2013-02-05 14:16:51 -05:00
setup_methods(name, current_value)
end
end
2013-02-25 11:42:20 -05:00
# just like a setting, except that it is available in javascript via DiscourseSession
def client_setting(name, default = nil, opts = {})
setting(name, default, opts)
2013-02-05 14:16:51 -05:00
@@client_settings ||= []
@@client_settings << name
end
def client_settings
2013-02-25 11:42:20 -05:00
@@client_settings
2013-02-05 14:16:51 -05:00
end
def settings_hash
result = {}
@defaults.each do |s, v|
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
client_settings_json_uncached
2013-02-05 14:16:51 -05:00
end
end
def client_settings_json_uncached
MultiJson.dump(Hash[*@@client_settings.map{|n| [n, self.send(n)]}.flatten])
end
2013-02-05 14:16:51 -05:00
# Retrieve all settings
def all_settings(include_hidden=false)
@defaults
.reject{|s, v| hidden_settings.include?(s) || include_hidden}
.map do |s, v|
value = send(s)
type = types[get_data_type(s, value)]
{setting: s,
description: description(s),
default: v,
type: type.to_s,
2013-11-13 14:02:47 -05:00
value: value.to_s,
category: categories[s]}.merge( type == :enum ? {valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?} : {})
end
2013-02-05 14:16:51 -05:00
end
def description(setting)
I18n.t("site_settings.#{setting}")
end
def self.client_settings_cache_key
"client_settings_json"
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
old = current
all_settings = provider.all
2013-02-05 14:16:51 -05:00
new_hash = Hash[*(all_settings.map{|s| [s.name.intern, convert(s.value,s.data_type)]}.to_a.flatten)]
# add defaults
new_hash = defaults.merge(new_hash)
2013-06-12 22:41:27 -04:00
changes,deletions = diff_hash(new_hash, old)
2013-02-05 14:16:51 -05:00
if deletions.length > 0 || changes.length > 0
@current = new_hash
2013-02-25 11:42:20 -05:00
changes.each do |name, val|
setup_methods name, val
2013-02-05 14:16:51 -05:00
end
deletions.each do |name,val|
setup_methods name, defaults[name]
end
end
Rails.cache.delete(SiteSettingExtension.client_settings_cache_key)
2013-02-05 14:16:51 -05:00
end
end
2013-06-12 22:41:27 -04:00
2013-02-05 14:16:51 -05:00
def ensure_listen_for_changes
unless @subscribed
2013-06-12 22:41:27 -04:00
MessageBus.subscribe("/site_settings") do |message|
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
MessageBus.on_connect.call(message.site_id)
refresh!
2013-06-12 22:41:27 -04:00
ensure
MessageBus.on_disconnect.call(message.site_id)
end
end
end
def diags
{
last_message_processed: @last_message_processed
}
end
2013-02-05 14:16:51 -05:00
def process_id
@@process_id ||= SecureRandom.uuid
end
def remove_override!(name)
provider.destroy(name)
current[name] = defaults[name]
2013-02-05 14:16:51 -05:00
end
def add_override!(name,val)
type = get_data_type(name, defaults[name])
2013-02-25 11:42:20 -05:00
if type == types[:bool] && val != true && val != false
val = (val == "t" || val == "true") ? 't' : 'f'
2013-02-05 14:16:51 -05:00
end
if type == types[:fixnum] && !(Fixnum === val)
2013-02-05 14:16:51 -05:00
val = val.to_i
end
if type == types[:null] && val != ''
type = get_data_type(name, val)
end
if type == types[:enum]
raise Discourse::InvalidParameters.new(:value) unless enum_class(name).valid_value?(val)
end
provider.save(name, val, type)
@last_message_sent = MessageBus.publish('/site_settings', {process: process_id})
2013-02-05 14:16:51 -05:00
end
2014-01-27 13:05:35 -05:00
def has_setting?(name)
defaults.has_key?(name.to_sym) || defaults.has_key?("#{name}?".to_sym)
end
def set(name, value)
if has_setting?(name)
self.send("#{name}=", value)
else
raise ArgumentError.new("No setting named #{name} exists")
end
end
2013-02-25 11:42:20 -05:00
protected
2013-02-05 14:16:51 -05:00
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 do |name,value|
deletions << [name,value] unless new_hash.has_key?(name)
end
[changes,deletions]
end
def get_data_type(name,val)
return types[:null] if val.nil?
2013-06-12 22:41:27 -04:00
return types[:enum] if enums[name]
2013-02-05 14:16:51 -05:00
2013-06-12 22:41:27 -04:00
case val
when String
types[:string]
2013-06-12 22:41:27 -04:00
when Fixnum
types[:fixnum]
2013-06-12 22:41:27 -04:00
when TrueClass, FalseClass
types[:bool]
2013-02-25 11:42:20 -05:00
else
2013-02-05 14:16:51 -05:00
raise ArgumentError.new :val
end
end
def convert(value, type)
2013-02-25 11:42:20 -05:00
case type
when types[:fixnum]
2013-02-05 14:16:51 -05:00
value.to_i
when types[:string], types[:enum]
2013-02-05 14:16:51 -05:00
value
when types[:bool]
value == true || value == "t" || value == "true"
when types[:null]
2013-02-05 14:16:51 -05:00
nil
end
end
def setup_methods(name, current_value)
2013-02-25 11:42:20 -05:00
# trivial multi db support, we can optimize this later
current[name] = current_value
clean_name = name.to_s.sub("?", "")
2013-02-05 14:16:51 -05:00
eval "define_singleton_method :#{clean_name} do
c = @@containers[provider.current_site]
c = c[name] if c
c
end
2013-02-05 14:16:51 -05:00
define_singleton_method :#{clean_name}? do
#{clean_name}
2013-02-05 14:16:51 -05:00
end
2013-02-25 11:42:20 -05:00
define_singleton_method :#{clean_name}= do |val|
2013-02-05 14:16:51 -05:00
add_override!(:#{name}, val)
refresh!
end
"
end
def enum_class(name)
enums[name]
end
2013-02-05 14:16:51 -05:00
end