2013-03-01 17:21:13 -05:00
require_dependency 'enum'
2013-02-05 14:16:51 -05:00
module SiteSettingExtension
2013-03-01 17:21:13 -05:00
def types
2013-06-11 11:39:55 -04:00
@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 [ RailsMultisite :: ConnectionManagement . current_db ] || = { }
end
def defaults
2013-02-25 19:42:20 +03:00
@defaults || = { }
2013-02-05 14:16:51 -05:00
end
2013-06-11 11:39:55 -04:00
def enums
@enums || = { }
end
def setting ( name , default = nil , opts = { } )
2013-02-25 19:42:20 +03:00
mutex . synchronize do
2013-02-05 14:16:51 -05:00
self . defaults [ name ] = default
current_value = current . has_key? ( name ) ? current [ name ] : default
2013-06-11 11:39:55 -04:00
enums [ name ] = opts [ :enum ] if opts [ :enum ]
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-03-01 17:27:59 -05:00
def client_setting ( name , default = nil )
2013-03-01 17:33:23 -05:00
setting ( name , default )
2013-02-05 14:16:51 -05:00
@@client_settings || = [ ]
@@client_settings << name
end
def client_settings
2013-02-25 19:42:20 +03: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
MultiJson . dump ( Hash [ * @@client_settings . map { | n | [ n , self . send ( n ) ] } . flatten ] )
end
end
# Retrieve all settings
def all_settings
@defaults . map do | s , v |
2013-03-01 12:45:25 -05:00
value = send ( s )
2013-06-11 11:39:55 -04:00
type = types [ get_data_type ( s , value ) ]
2013-02-05 14:16:51 -05:00
{ setting : s ,
description : description ( s ) ,
default : v ,
2013-06-11 11:39:55 -04:00
type : type . to_s ,
value : value . to_s } . merge ( type == :enum ? { valid_values : enum_class ( s ) . all_values } : { } )
2013-02-05 14:16:51 -05:00
end
end
def description ( setting )
I18n . t ( " site_settings. #{ setting } " )
end
# table is not in the db yet, initial migration, etc
def table_exists?
@table_exists = ActiveRecord :: Base . connection . table_exists? 'site_settings' if @table_exists == nil
@table_exists
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!
return unless table_exists?
mutex . synchronize do
2013-02-05 14:16:51 -05:00
ensure_listen_for_changes
old = current
changes = [ ]
deletions = [ ]
all_settings = SiteSetting . select ( [ :name , :value , :data_type ] )
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 )
new_hash . each do | name , value |
changes << [ name , value ] if ! old . has_key? ( name ) || old [ name ] != value
end
2013-02-25 19:42:20 +03:00
old . each do | name , value |
deletions << [ name , value ] unless new_hash . has_key? ( name )
2013-02-05 14:16:51 -05:00
end
if deletions . length > 0 || changes . length > 0
@current = new_hash
2013-02-25 19:42:20 +03: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
2013-02-07 16:11:30 -05:00
Rails . cache . delete ( SiteSettingExtension . client_settings_cache_key )
2013-02-05 14:16:51 -05:00
end
end
def ensure_listen_for_changes
unless @subscribed
pid = process_id
2013-02-25 19:42:20 +03:00
MessageBus . subscribe ( " /site_settings " ) do | msg |
2013-02-05 14:16:51 -05:00
message = msg . data
2013-02-25 19:42:20 +03:00
if message [ " process " ] != pid
2013-02-05 14:16:51 -05:00
begin
2013-04-05 17:43:48 +11:00
@last_message_processed = msg . global_id
2013-02-05 14:16:51 -05:00
# picks a db
2013-02-25 19:42:20 +03:00
MessageBus . on_connect . call ( msg . site_id )
2013-02-05 14:16:51 -05:00
SiteSetting . refresh!
ensure
MessageBus . on_disconnect . call ( msg . site_id )
end
end
end
@subscribed = true
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
@@process_id || = SecureRandom . uuid
end
def remove_override! ( name )
2013-02-25 19:42:20 +03:00
return unless table_exists?
2013-03-23 20:32:59 +05:30
SiteSetting . where ( name : name ) . destroy_all
2013-02-05 14:16:51 -05:00
end
def add_override! ( name , val )
return unless table_exists?
2013-03-23 20:32:59 +05:30
setting = SiteSetting . where ( name : name ) . first
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
2013-03-01 17:21:13 -05:00
if type == types [ :fixnum ] && ! ( Fixnum === val )
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-02-05 14:16:51 -05:00
if setting
setting . value = val
2013-02-25 19:42:20 +03:00
setting . data_type = type
2013-02-05 14:16:51 -05:00
setting . save
2013-02-25 19:42:20 +03:00
else
2013-03-23 20:32:59 +05:30
SiteSetting . create! ( name : name , value : val , data_type : type )
2013-02-05 14:16:51 -05:00
end
2013-04-05 17:43:48 +11:00
@last_message_sent = MessageBus . publish ( '/site_settings' , { process : process_id } )
2013-02-05 14:16:51 -05:00
end
2013-02-25 19:42:20 +03:00
protected
2013-02-05 14:16:51 -05:00
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-02-05 14:16:51 -05:00
2013-06-11 11:39:55 -04:00
if enums [ name ]
types [ :enum ]
elsif String === val
2013-03-01 17:21:13 -05:00
types [ :string ]
2013-02-25 19:42:20 +03:00
elsif Fixnum === val
2013-03-01 17:21:13 -05:00
types [ :fixnum ]
2013-02-25 19:42:20 +03:00
elsif TrueClass === val || FalseClass === val
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
2013-06-11 11:39:55 -04:00
when types [ :string ] , types [ :enum ]
2013-02-05 14:16:51 -05:00
value
2013-03-01 17:21:13 -05:00
when types [ :bool ]
2013-02-05 14:16:51 -05:00
value == " t "
2013-03-01 17:21:13 -05:00
when types [ :null ]
2013-02-05 14:16:51 -05:00
nil
end
end
def setup_methods ( name , current_value )
2013-02-25 19:42:20 +03:00
# trivial multi db support, we can optimize this later
2013-02-05 14:16:51 -05:00
db = RailsMultisite :: ConnectionManagement . current_db
2013-02-25 19:42:20 +03:00
2013-02-05 14:16:51 -05:00
@@containers || = { }
@@containers [ db ] || = { }
@@containers [ db ] [ name ] = current_value
setter = ( " #{ name } = " ) . sub ( " ? " , " " )
2013-02-25 19:42:20 +03:00
eval " define_singleton_method : #{ name } do
2013-02-05 14:16:51 -05:00
c = @@containers [ RailsMultisite :: ConnectionManagement . current_db ]
c = c [ name ] if c
c
end
2013-02-25 19:42:20 +03:00
2013-02-05 14:16:51 -05:00
define_singleton_method : #{setter} do |val|
add_override! ( : #{name}, val)
refresh!
end
"
end
def method_missing ( method , * args , & block )
as_question = method . to_s . gsub ( / \ ?$ / , '' )
2013-02-25 19:42:20 +03:00
if respond_to? ( as_question )
return send ( as_question , * args , & block )
2013-02-05 14:16:51 -05:00
end
super ( method , * args , & block )
end
2013-06-11 11:39:55 -04:00
def enum_class ( name )
enums [ name ] = enums [ name ] . constantize unless enums [ name ] . is_a? ( Class )
enums [ name ]
end
2013-02-05 14:16:51 -05:00
end