2013-03-01 17:21:13 -05:00
require_dependency 'enum'
2013-06-23 13:35:06 +10: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
2013-06-23 13:35:06 +10: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-03-01 17:21:13 -05:00
def types
2014-03-29 22:32:33 -07:00
@types || = Enum . new ( :string , :time , :fixnum , :float , :bool , :null , :enum , :list )
2013-02-05 14:16:51 -05:00
end
def mutex
@mutex || = Mutex . new
end
def current
2014-03-28 16:36:17 +11:00
@containers || = { }
@containers [ provider . current_site ] || = { }
2013-02-05 14:16:51 -05:00
end
def defaults
2013-02-25 19:42:20 +03:00
@defaults || = { }
2013-02-05 14:16:51 -05:00
end
2013-11-13 14:02:47 -05:00
def categories
@categories || = { }
end
2013-06-11 11:39:55 -04:00
def enums
@enums || = { }
end
2014-03-29 22:32:33 -07:00
def lists
@lists || = [ ]
end
2013-10-24 10:05:51 +11:00
def hidden_settings
@hidden_settings || = [ ]
end
2014-02-20 21:52:11 -08:00
def refresh_settings
@refresh_settings || = [ ]
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
2013-02-25 19:42:20 +03:00
mutex . synchronize do
2013-02-05 14:16:51 -05:00
self . defaults [ name ] = default
2013-11-15 14:32:33 -05:00
categories [ name ] = opts [ :category ] || :uncategorized
2013-02-05 14:16:51 -05:00
current_value = current . has_key? ( name ) ? current [ name ] : default
2013-06-23 13:35:06 +10:00
if opts [ :enum ]
enum = opts [ :enum ]
enums [ name ] = enum . is_a? ( String ) ? enum . constantize : enum
end
2014-03-29 22:32:33 -07:00
if opts [ :list ]
lists << name
end
if opts [ :hidden ]
2013-10-24 10:05:51 +11:00
hidden_settings << name
end
2014-03-29 22:32:33 -07:00
if opts [ :refresh ]
2014-02-20 21:52:11 -08:00
refresh_settings << name
end
2014-03-28 16:36:17 +11:00
current [ name ] = current_value
2013-02-05 14:16:51 -05:00
setup_methods ( name , current_value )
end
end
2013-02-25 19:42:20 +03:00
# just like a setting, except that it is available in javascript via DiscourseSession
2013-11-15 14:32:33 -05:00
def client_setting ( name , default = nil , opts = { } )
setting ( name , default , opts )
2014-03-28 16:36:17 +11:00
@client_settings || = [ ]
@client_settings << name
2013-02-05 14:16:51 -05:00
end
def client_settings
2014-03-28 16:36:17 +11:00
@client_settings || = [ ]
2013-02-05 14:16:51 -05:00
end
2013-04-05 15:21:55 -04:00
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
2013-10-10 10:32:03 +11:00
client_settings_json_uncached
2013-02-05 14:16:51 -05:00
end
end
2013-10-10 10:32:03 +11:00
def client_settings_json_uncached
2014-03-28 16:36:17 +11:00
MultiJson . dump ( Hash [ * @client_settings . map { | n | [ n , self . send ( n ) ] } . flatten ] )
2013-10-10 10:32:03 +11:00
end
2013-02-05 14:16:51 -05:00
# Retrieve all settings
2013-10-24 10:05:51 +11:00
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? } : { } )
2013-10-24 10:05:51 +11:00
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 19:42:20 +03:00
def refresh!
mutex . synchronize do
2013-02-05 14:16:51 -05:00
ensure_listen_for_changes
old = current
2014-03-28 16:36:17 +11:00
new_hash = Hash [ * ( provider . all . map { | s |
[ s . name . intern , convert ( s . value , s . data_type ) ]
} . to_a . flatten ) ]
2013-02-05 14:16:51 -05:00
2014-03-28 16:36:17 +11:00
# add defaults, cause they are cached
2013-02-05 14:16:51 -05:00
new_hash = defaults . merge ( new_hash )
2014-03-28 16:36:17 +11:00
2013-06-13 12:41:27 +10:00
changes , deletions = diff_hash ( new_hash , old )
2013-02-05 14:16:51 -05:00
if deletions . length > 0 || changes . length > 0
2013-02-25 19:42:20 +03:00
changes . each do | name , val |
2014-03-28 16:36:17 +11:00
current [ name ] = val
2013-02-05 14:16:51 -05:00
end
deletions . each do | name , val |
2014-03-28 16:36:17 +11:00
current [ name ] = defaults [ name ]
2013-02-05 14:16:51 -05:00
end
end
2014-03-28 16:36:17 +11:00
clear_cache!
2013-02-05 14:16:51 -05:00
end
end
2013-06-13 12:41:27 +10:00
2013-02-05 14:16:51 -05:00
def ensure_listen_for_changes
unless @subscribed
2013-06-13 12:41:27 +10: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-13 12:41:27 +10: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 )
2013-06-23 13:35:06 +10:00
refresh!
2013-06-13 12:41:27 +10:00
ensure
MessageBus . on_disconnect . call ( message . site_id )
end
end
end
2013-04-05 17:43:48 +11:00
def diags
{
last_message_processed : @last_message_processed
}
end
2013-02-05 14:16:51 -05:00
def process_id
2014-03-28 16:36:17 +11:00
@process_id || = SecureRandom . uuid
2013-02-05 14:16:51 -05:00
end
2014-03-28 13:48:14 +11:00
def after_fork
2014-03-28 16:36:17 +11:00
@process_id = nil
ensure_listen_for_changes
2014-03-28 13:48:14 +11:00
end
2013-02-05 14:16:51 -05:00
def remove_override! ( name )
2013-06-23 13:35:06 +10:00
provider . destroy ( name )
current [ name ] = defaults [ name ]
2014-03-28 16:36:17 +11:00
clear_cache!
2013-02-05 14:16:51 -05:00
end
def add_override! ( name , val )
2013-06-11 11:39:55 -04:00
type = get_data_type ( name , defaults [ name ] )
2013-02-25 19:42:20 +03:00
2013-03-01 17:21:13 -05:00
if type == types [ :bool ] && val != true && val != false
2013-02-27 16:19:09 +00:00
val = ( val == " t " || val == " true " ) ? 't' : 'f'
2013-02-05 14:16:51 -05:00
end
2014-03-26 12:20:41 -07:00
if type == types [ :fixnum ] && ! val . is_a? ( Fixnum )
2013-02-05 14:16:51 -05:00
val = val . to_i
end
2013-03-01 17:21:13 -05:00
if type == types [ :null ] && val != ''
2013-06-11 11:39:55 -04:00
type = get_data_type ( name , val )
end
if type == types [ :enum ]
raise Discourse :: InvalidParameters . new ( :value ) unless enum_class ( name ) . valid_value? ( val )
2013-02-28 00:24:43 +00:00
end
2013-06-23 13:35:06 +10:00
provider . save ( name , val , type )
2014-03-28 16:36:17 +11:00
current [ name ] = convert ( val , type )
clear_cache!
2014-03-31 12:34:01 +11:00
end
2014-03-28 16:36:17 +11:00
2014-03-31 12:34:01 +11:00
def notify_changed!
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
2014-02-20 21:52:11 -08:00
def requires_refresh? ( name )
refresh_settings . include? ( name . to_sym )
end
2014-01-27 13:05:35 -05:00
def set ( name , value )
if has_setting? ( name )
self . send ( " #{ name } = " , value )
2014-02-20 21:52:11 -08:00
Discourse . request_refresh! if requires_refresh? ( name )
2014-01-27 13:05:35 -05:00
else
raise ArgumentError . new ( " No setting named #{ name } exists " )
end
end
2013-02-25 19:42:20 +03:00
protected
2013-02-05 14:16:51 -05:00
2014-03-28 16:36:17 +11:00
def clear_cache!
Rails . cache . delete ( SiteSettingExtension . client_settings_cache_key )
end
2013-06-13 12:41:27 +10: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
2013-06-11 11:39:55 -04:00
def get_data_type ( name , val )
2013-03-01 17:21:13 -05:00
return types [ :null ] if val . nil?
2013-06-13 12:41:27 +10:00
return types [ :enum ] if enums [ name ]
2014-03-29 22:32:33 -07:00
return types [ :list ] if lists . include? name
2013-02-05 14:16:51 -05:00
2013-06-13 12:41:27 +10:00
case val
when String
2013-03-01 17:21:13 -05:00
types [ :string ]
2013-06-13 12:41:27 +10:00
when Fixnum
2013-03-01 17:21:13 -05:00
types [ :fixnum ]
2013-06-13 12:41:27 +10:00
when TrueClass , FalseClass
2013-03-01 17:21:13 -05:00
types [ :bool ]
2013-02-25 19:42:20 +03:00
else
2013-02-05 14:16:51 -05:00
raise ArgumentError . new :val
end
end
def convert ( value , type )
2013-02-25 19:42:20 +03:00
case type
2013-03-01 17:21:13 -05:00
when types [ :fixnum ]
2013-02-05 14:16:51 -05:00
value . to_i
2014-03-29 22:32:33 -07:00
when types [ :string ] , types [ :list ] , types [ :enum ]
2013-02-05 14:16:51 -05:00
value
2013-03-01 17:21:13 -05:00
when types [ :bool ]
2013-06-23 13:35:06 +10:00
value == true || value == " t " || value == " true "
2013-03-01 17:21:13 -05:00
when types [ :null ]
2013-02-05 14:16:51 -05:00
nil
2014-03-29 22:32:33 -07:00
else
raise ArgumentError . new :type
2013-02-05 14:16:51 -05:00
end
end
def setup_methods ( name , current_value )
2013-09-03 17:39:56 +10:00
clean_name = name . to_s . sub ( " ? " , " " )
2013-02-05 14:16:51 -05:00
2013-09-03 17:39:56 +10:00
eval " define_singleton_method : #{ clean_name } do
2014-03-28 16:36:17 +11:00
c = @containers [ provider . current_site ]
if c
c [ name ]
else
refresh!
current [ name ]
end
2013-09-03 17:39:56 +10:00
end
2013-02-05 14:16:51 -05:00
2013-09-03 17:39:56 +10:00
define_singleton_method : #{clean_name}? do
2013-09-03 17:58:30 +10:00
#{clean_name}
2013-02-05 14:16:51 -05:00
end
2013-02-25 19:42:20 +03:00
2013-09-03 17:39:56 +10:00
define_singleton_method : #{clean_name}= do |val|
2013-02-05 14:16:51 -05:00
add_override! ( : #{name}, val)
end
"
end
2013-06-11 11:39:55 -04:00
def enum_class ( name )
enums [ name ]
end
2013-02-05 14:16:51 -05:00
end