discourse/lib/site_setting_extension.rb

234 lines
5.2 KiB
Ruby
Raw Normal View History

require_dependency 'enum'
2013-02-06 03:16:51 +08:00
module SiteSettingExtension
def types
@types ||= Enum.new(:string, :time, :fixnum, :float, :bool, :null)
2013-02-06 03:16:51 +08:00
end
def mutex
@mutex ||= Mutex.new
end
def current
@@containers ||= {}
@@containers[RailsMultisite::ConnectionManagement.current_db] ||= {}
end
def defaults
2013-02-26 00:42:20 +08:00
@defaults ||= {}
2013-02-06 03:16:51 +08:00
end
def setting(name, default = nil)
2013-02-26 00:42:20 +08:00
mutex.synchronize do
2013-02-06 03:16:51 +08:00
self.defaults[name] = default
current_value = current.has_key?(name) ? current[name] : default
setup_methods(name, current_value)
end
end
2013-02-26 00:42:20 +08:00
# just like a setting, except that it is available in javascript via DiscourseSession
def client_setting(name, default = nil)
2013-03-02 06:33:23 +08:00
setting(name,default)
2013-02-06 03:16:51 +08:00
@@client_settings ||= []
@@client_settings << name
end
def client_settings
2013-02-26 00:42:20 +08:00
@@client_settings
2013-02-06 03:16:51 +08:00
end
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|
value = send(s)
2013-02-06 03:16:51 +08:00
{setting: s,
description: description(s),
default: v,
type: types[get_data_type(value)].to_s,
value: value.to_s}
2013-02-06 03:16:51 +08: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-26 00:42:20 +08:00
def refresh!
return unless table_exists?
mutex.synchronize do
2013-02-06 03:16:51 +08: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-26 00:42:20 +08:00
old.each do |name,value|
deletions << [name,value] unless new_hash.has_key?(name)
2013-02-06 03:16:51 +08:00
end
if deletions.length > 0 || changes.length > 0
@current = new_hash
2013-02-26 00:42:20 +08:00
changes.each do |name, val|
setup_methods name, val
2013-02-06 03:16:51 +08:00
end
deletions.each do |name,val|
setup_methods name, defaults[name]
end
end
Rails.cache.delete(SiteSettingExtension.client_settings_cache_key)
2013-02-06 03:16:51 +08:00
end
end
def ensure_listen_for_changes
unless @subscribed
pid = process_id
2013-02-26 00:42:20 +08:00
MessageBus.subscribe("/site_settings") do |msg|
2013-02-06 03:16:51 +08:00
message = msg.data
2013-02-26 00:42:20 +08:00
if message["process"] != pid
2013-02-06 03:16:51 +08:00
begin
# picks a db
2013-02-26 00:42:20 +08:00
MessageBus.on_connect.call(msg.site_id)
2013-02-06 03:16:51 +08:00
SiteSetting.refresh!
ensure
MessageBus.on_disconnect.call(msg.site_id)
end
end
end
@subscribed = true
end
end
def process_id
@@process_id ||= SecureRandom.uuid
end
def remove_override!(name)
2013-02-26 00:42:20 +08:00
return unless table_exists?
SiteSetting.where(name: name).destroy_all
2013-02-06 03:16:51 +08:00
end
def add_override!(name,val)
return unless table_exists?
setting = SiteSetting.where(name: name).first
2013-02-06 03:16:51 +08:00
type = get_data_type(defaults[name])
2013-02-26 00:42:20 +08:00
if type == types[:bool] && val != true && val != false
val = (val == "t" || val == "true") ? 't' : 'f'
2013-02-06 03:16:51 +08:00
end
if type == types[:fixnum] && !(Fixnum === val)
2013-02-06 03:16:51 +08:00
val = val.to_i
end
if type == types[:null] && val != ''
type = get_data_type(val)
end
2013-02-06 03:16:51 +08:00
if setting
setting.value = val
2013-02-26 00:42:20 +08:00
setting.data_type = type
2013-02-06 03:16:51 +08:00
setting.save
2013-02-26 00:42:20 +08:00
else
SiteSetting.create!(name: name, value: val, data_type: type)
2013-02-06 03:16:51 +08:00
end
MessageBus.publish('/site_settings', {process: process_id})
end
2013-02-26 00:42:20 +08:00
protected
2013-02-06 03:16:51 +08:00
def get_data_type(val)
return types[:null] if val.nil?
2013-02-06 03:16:51 +08:00
if String === val
types[:string]
2013-02-26 00:42:20 +08:00
elsif Fixnum === val
types[:fixnum]
2013-02-26 00:42:20 +08:00
elsif TrueClass === val || FalseClass === val
types[:bool]
2013-02-26 00:42:20 +08:00
else
2013-02-06 03:16:51 +08:00
raise ArgumentError.new :val
end
end
def convert(value, type)
2013-02-26 00:42:20 +08:00
case type
when types[:fixnum]
2013-02-06 03:16:51 +08:00
value.to_i
when types[:string]
2013-02-06 03:16:51 +08:00
value
when types[:bool]
2013-02-06 03:16:51 +08:00
value == "t"
when types[:null]
2013-02-06 03:16:51 +08:00
nil
end
end
def setup_methods(name, current_value)
2013-02-26 00:42:20 +08:00
# trivial multi db support, we can optimize this later
2013-02-06 03:16:51 +08:00
db = RailsMultisite::ConnectionManagement.current_db
2013-02-26 00:42:20 +08:00
2013-02-06 03:16:51 +08:00
@@containers ||= {}
@@containers[db] ||= {}
@@containers[db][name] = current_value
setter = ("#{name}=").sub("?","")
2013-02-26 00:42:20 +08:00
eval "define_singleton_method :#{name} do
2013-02-06 03:16:51 +08:00
c = @@containers[RailsMultisite::ConnectionManagement.current_db]
c = c[name] if c
c
end
2013-02-26 00:42:20 +08:00
2013-02-06 03:16:51 +08: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-26 00:42:20 +08:00
if respond_to?(as_question)
return send(as_question, *args, &block)
2013-02-06 03:16:51 +08:00
end
super(method, *args, &block)
end
end