Can't revert due to incompatibility of new site setting types.

Revert "Revert "FEATURE: Site settings defaults per locale""

This reverts commit 439fe8ba24.
This commit is contained in:
Guo Xiang Tan 2017-08-07 10:43:09 +09:00
parent 439fe8ba24
commit 3f24ed2b3e
28 changed files with 1373 additions and 364 deletions

View File

@ -64,16 +64,16 @@ export default Ember.Component.extend(BufferedContent, {
}.on("willDestroyElement"),
_save() {
const self = this,
setting = this.get('buffered');
SiteSetting.update(setting.get('setting'), setting.get('value')).then(function() {
self.set('validationMessage', null);
self.commitBuffer();
}).catch(function(e) {
const setting = this.get('buffered'),
action = SiteSetting.update(setting.get('setting'), setting.get('value'));
action.then(() => {
this.set('validationMessage', null);
this.commitBuffer();
}).catch((e) => {
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
self.set('validationMessage', e.jqXHR.responseJSON.errors[0]);
this.set('validationMessage', e.jqXHR.responseJSON.errors[0]);
} else {
self.set('validationMessage', I18n.t('generic_error'));
this.set('validationMessage', I18n.t('generic_error'));
}
});
},

View File

@ -48,7 +48,7 @@ SiteSetting.reopenClass({
update(key, value) {
const data = {};
data[key] = value;
return ajax("/admin/site_settings/" + key, { type: 'PUT', data });
return ajax(`/admin/site_settings/${key}`, { type: 'PUT', data });
}
});

View File

@ -1,4 +1,7 @@
class Admin::SiteSettingsController < Admin::AdminController
rescue_from Discourse::InvalidParameters do |e|
render_json_error e.message, status: 422
end
def index
render_json_dump(site_settings: SiteSetting.all_settings, diags: SiteSetting.diags)
@ -9,15 +12,17 @@ class Admin::SiteSettingsController < Admin::AdminController
id = params[:id]
value = params[id]
value.strip! if value.is_a?(String)
begin
# note, as of Ruby 2.3 symbols are GC'd so this is considered safe
if SiteSetting.hidden_settings.include?(id.to_sym)
raise Discourse::InvalidParameters, "You are not allowed to change hidden settings"
end
SiteSetting.set_and_log(id, value, current_user)
render nothing: true
rescue Discourse::InvalidParameters => e
render json: { errors: [e.message] }, status: 422
raise_access_hidden_setting(id)
SiteSetting.set_and_log(id, value, current_user)
render nothing: true
end
private
def raise_access_hidden_setting(id)
# note, as of Ruby 2.3 symbols are GC'd so this is considered safe
if SiteSetting.hidden_settings.include?(id.to_sym)
raise Discourse::InvalidParameters, "You are not allowed to change hidden settings"
end
end

View File

@ -14,11 +14,7 @@ class SiteSetting < ActiveRecord::Base
def self.load_settings(file)
SiteSettings::YamlLoader.new(file).load do |category, name, default, opts|
if opts.delete(:client)
client_setting(name, default, opts.merge(category: category))
else
setting(name, default, opts.merge(category: category))
end
setting(name, default, opts.merge(category: category))
end
end
@ -31,6 +27,11 @@ class SiteSetting < ActiveRecord::Base
end
end
# `current` hash is not populated everytime when load a site setting
# in order to support locale default. Instead, we simply `refresh!` once.
# This should only affects the spec in which you should populate `current`
refresh!
client_settings << :available_locales
def self.available_locales

View File

@ -59,6 +59,9 @@ Discourse::Application.configure do
end
config.after_initialize do
SiteSetting.defaults.set_regardless_of_locale(:port, 3000)
SiteSetting.refresh!
if ENV['BULLET']
Bullet.enable = true
Bullet.rails_logger = true

View File

@ -46,4 +46,22 @@ Discourse::Application.configure do
config.logger = Logger.new(nil)
config.log_level = :fatal
end
config.after_initialize do
SiteSetting.defaults.tap do |s|
s.set_regardless_of_locale(:s3_upload_bucket, 'bucket')
s.set_regardless_of_locale(:min_post_length, 5)
s.set_regardless_of_locale(:min_first_post_length, 5)
s.set_regardless_of_locale(:min_private_message_post_length, 10)
s.set_regardless_of_locale(:crawl_images, false)
s.set_regardless_of_locale(:download_remote_images_to_local, false)
s.set_regardless_of_locale(:unique_posts_mins, 0)
s.set_regardless_of_locale(:queue_jobs, false)
# disable plugins
if ENV['LOAD_PLUGINS'] == '1'
s.set_regardless_of_locale(:discourse_narrative_bot_enabled, false)
end
end
SiteSetting.refresh!
end
end

View File

@ -12,6 +12,9 @@
# shadowed_by_global - "Shadow" a site setting with a GlobalSetting. If the GlobalSetting
# exists it will be used instead of the setting and the setting will be hidden.
# Useful for things like API keys on multisite.
# locale_default - A hash which overrides according to `SiteSetting.default_locale`.
# The key should be as the same as possible value of default_locale.
#
#
# type: email - Must be a valid email address.
# type: username - Must match the username of an existing user.
@ -68,11 +71,6 @@ required:
default: ''
basic:
default_locale:
default: 'en'
enum: 'LocaleSiteSetting'
refresh: true
shadowed_by_global: true
allow_user_locale:
client: true
default: false
@ -432,21 +430,24 @@ posting:
min_post_length:
client: true
min: 1
default:
test: 5
default: 20
default: 20
locale_default:
zh_CN: 8
zh_TW: 8
min_first_post_length:
client: true
min: 1
default:
test: 5
default: 20
default: 20
locale_default:
zh_CN: 8
zh_TW: 8
min_private_message_post_length:
client: true
min: 1
default:
test: 5
default: 10
default: 10
locale_default:
zh_CN: 3
zh_TW: 3
max_post_length:
client: true
default: 32000
@ -454,17 +455,32 @@ posting:
topic_featured_link_enabled:
client: true
default: true
body_min_entropy: 7
body_min_entropy:
default: 7
locale_default:
zh_CN: 3
zh_TW: 3
min_topic_title_length:
client: true
default: 15
locale_default:
zh_CN: 6
zh_TW: 6
max_topic_title_length:
client: true
default: 255
max: 255
title_min_entropy: 10
title_min_entropy:
default: 10
locale_default:
zh_CN: 3
zh_TW: 3
allow_uppercase_posts: false
title_prettify: true
title_prettify:
default: true
locale_default:
zh_CN: false
zh_TW: false
title_fancy_entities: true
min_private_message_title_length:
client: true
@ -478,9 +494,15 @@ posting:
min_title_similar_length:
client: true
default: 10
locale_default:
zh_CN: 4
zh_TW: 4
min_body_similar_length:
client: true
default: 15
locale_default:
zh_CN: 5
zh_TW: 5
enable_private_messages:
default: true
client: true
@ -524,7 +546,11 @@ posting:
newuser_max_attachments:
client: true
default: 0
post_excerpt_maxlength: 300
post_excerpt_maxlength:
default: 300
locale_default:
zh_CN: 120
zh_TW: 120
show_pinned_excerpt_mobile:
client: true
default: true
@ -603,7 +629,11 @@ email:
client: true
private_email_time_window_seconds: 20
email_posts_context: 5
digest_min_excerpt_length: 100
digest_min_excerpt_length:
default: 100
locale_default:
zh_CN: 50
zh_TW: 50
digest_topics:
default: 5
min: 1
@ -726,9 +756,7 @@ files:
refresh: true
type: list
crawl_images:
default:
test: false
default: true
default: true
max_image_width:
client: true
default: 690
@ -736,9 +764,7 @@ files:
client: true
default: 500
download_remote_images_to_local:
default:
test: false
default: true
default: true
download_remote_images_threshold: 10
download_remote_images_max_days_old:
default: 30
@ -908,7 +934,11 @@ security:
onebox:
enable_flash_video_onebox: false
post_onebox_maxlength: 500
post_onebox_maxlength:
default: 500
locale_default:
zh_CN: 200
zh_TW: 200
onebox_domains_blacklist:
default: ''
type: list
@ -954,10 +984,7 @@ spam:
auto_block_first_post_regex: ""
rate_limits:
unique_posts_mins:
default:
test: 0
default: 5
unique_posts_mins: 5
rate_limit_create_topic: 15
rate_limit_create_post: 5
rate_limit_new_user_create_topic: 120
@ -995,28 +1022,20 @@ rate_limits:
developer:
force_hostname:
hidden:
development: false
default: true
hidden: true
default: ''
port:
hidden:
development: false
default: true
default:
development: 3000
default: ''
hidden: true
default: ''
queue_jobs:
hidden:
development: false
default: true
default:
test: false
default: true
hidden: true
default: true
enable_long_polling:
client: true
default: true
long_polling_interval: 25000
long_polling_interval:
default: 25000
max: 25000
long_polling_base_url:
client: true
default: '/'
@ -1144,12 +1163,19 @@ search:
min_search_term_length:
client: true
default: 3
locale_default:
zh_CN: 2
zh_TW: 2
search_tokenize_chinese_japanese_korean: false
search_prefer_recent_posts: false
search_recent_posts_size: 100000
search_recent_posts_size:
default: 100000
max: 100000
log_search_queries: true
search_query_log_max_size: 1000000
search_query_log_max_size:
default: 1000000
max: 1000000
uncategorized:
version_checks:
@ -1166,6 +1192,9 @@ uncategorized:
slug_generation_method:
default: 'ascii'
enum: 'SlugSetting'
locale_default:
zh_CN: 'none'
zh_TW: 'none'
permalink_normalizations:
default: ''
@ -1306,6 +1335,9 @@ uncategorized:
read_time_word_count:
default: 500
client: true
locale_default:
zh_CN: 350
zh_TW: 350
topic_page_title_includes_category: true
@ -1323,7 +1355,7 @@ user_preferences:
default_email_mailing_list_mode: false
default_email_mailing_list_mode_frequency:
enum: 'MailingListModeSiteSetting'
default: 0
default: 1
disable_mailing_list_mode:
default: false
client: true

View File

@ -0,0 +1,8 @@
class CorrectDefaultEmailMailingListModeFrequency < ActiveRecord::Migration
def up
execute "UPDATE site_settings SET value = '1' WHERE value = '0' AND name = 'default_email_mailing_list_mode_frequency';"
end
def down
end
end

View File

@ -84,6 +84,8 @@ module Discourse
# Cross site request forgery
class CSRF < StandardError; end
class Deprecation < StandardError; end
def self.filters
@filters ||= [:latest, :unread, :new, :read, :posted, :bookmarks]
end

View File

@ -1,14 +1,16 @@
require_dependency 'enum'
require_dependency 'site_settings/deprecated_settings'
require_dependency 'site_settings/type_supervisor'
require_dependency 'site_settings/defaults_provider'
require_dependency 'site_settings/db_provider'
require 'site_setting_validations'
module SiteSettingExtension
include SiteSettingValidations
include SiteSettings::DeprecatedSettings
extend Forwardable
# For plugins, so they can tell if a feature is supported
def supported_types
[:email, :username, :list, :enum]
end
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
# part 1 of refactor, centralizing the dependency here
def provider=(val)
@ -20,22 +22,6 @@ module SiteSettingExtension
@provider ||= SiteSettings::DbProvider.new(SiteSetting)
end
def types
@types ||= Enum.new(string: 1,
time: 2,
integer: 3,
float: 4,
bool: 5,
null: 6,
enum: 7,
list: 8,
url_list: 9,
host_list: 10,
category_list: 11,
value_list: 12,
regex: 13)
end
def mutex
@mutex ||= Mutex.new
end
@ -46,25 +32,17 @@ module SiteSettingExtension
end
def defaults
@defaults ||= {}
@defaults ||= SiteSettings::DefaultsProvider.new(self)
end
def type_supervisor
@type_supervisor ||= SiteSettings::TypeSupervisor.new(defaults)
end
def categories
@categories ||= {}
end
def enums
@enums ||= {}
end
def static_types
@static_types ||= {}
end
def choices
@choices ||= {}
end
def shadowed_settings
@shadowed_settings ||= []
end
@ -85,34 +63,14 @@ module SiteSettingExtension
@previews ||= {}
end
def validators
@validators ||= {}
end
def setting(name_arg, default = nil, opts = {})
name = name_arg.to_sym
mutex.synchronize do
self.defaults[name] = default
defaults.load_setting(name,
default,
opts.extract!(*SiteSettings::DefaultsProvider::CONSUMED_OPTS))
categories[name] = opts[:category] || :uncategorized
current_value = current.has_key?(name) ? current[name] : default
if enum = opts[:enum]
enums[name] = enum.is_a?(String) ? enum.constantize : enum
opts[:type] ||= :enum
end
if new_choices = opts[:choices]
new_choices = eval(new_choices) if new_choices.is_a?(String)
choices.has_key?(name) ?
choices[name].concat(new_choices) :
choices[name] = new_choices
end
if type = opts[:type]
static_types[name.to_sym] = type.to_sym
end
if opts[:hidden]
hidden_settings << name
@ -124,7 +82,6 @@ module SiteSettingExtension
unless val.nil? || (val == ''.freeze)
hidden_settings << name
shadowed_settings << name
current_value = val
end
end
@ -132,31 +89,24 @@ module SiteSettingExtension
refresh_settings << name
end
if opts[:client]
client_settings << name.to_sym
end
if opts[:preview]
previews[name] = opts[:preview]
end
opts[:validator] = opts[:validator].try(:constantize)
type = opts[:type] || get_data_type(name, defaults[name])
type_supervisor.load_setting(name,
opts.extract!(*SiteSettings::TypeSupervisor::CONSUMED_OPTS))
if validator_type = opts[:validator] || validator_for(type)
validators[name] = { class: validator_type, opts: opts }
end
current[name] = current_value
setup_methods(name)
end
end
# just like a setting, except that it is available in javascript via DiscourseSession
def client_setting(name, default = nil, opts = {})
setting(name, default, opts)
client_settings << name.to_sym
end
def settings_hash
result = {}
@defaults.each do |s, _|
defaults.each_key do |s|
result[s] = send(s).to_s
end
result
@ -174,32 +124,21 @@ module SiteSettingExtension
# Retrieve all settings
def all_settings(include_hidden = false)
@defaults
.reject { |s, _| hidden_settings.include?(s) && !include_hidden }
defaults
.reject { |s, _| !include_hidden && hidden_settings.include?(s) }
.map do |s, v|
value = send(s)
type = types[get_data_type(s, value)]
opts = {
setting: s,
description: description(s),
default: v.to_s,
type: type.to_s,
value: value.to_s,
category: categories[s],
preview: previews[s]
}
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))
if type == :enum && enum_class(s)
opts.merge!(valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?)
elsif type == :enum
opts.merge!(valid_values: choices[s].map { |c| { name: c, value: c } }, translate_names: false)
end
opts[:textarea] = true if static_types[s] == :textarea
opts[:choices] = choices[s] if choices.has_key? s
opts
end
opts
end.unshift(defaults.locale_setting_hash)
end
def description(setting)
@ -217,22 +156,22 @@ module SiteSettingExtension
def refresh!
mutex.synchronize do
ensure_listen_for_changes
old = current
new_hash = Hash[*(provider.all.map { |s|
[s.name.intern, convert(s.value, s.data_type, s.name)]
new_hash = Hash[*(defaults.db_all.map { |s|
[s.name.to_sym, type_supervisor.to_rb_value(s.name, s.value, s.data_type)]
}.to_a.flatten)]
# add defaults, cause they are cached
new_hash = defaults.merge(new_hash)
defaults_view = defaults.all
# add locale default and defaults based on default_locale, cause they are cached
new_hash = defaults_view.merge(new_hash)
# add shadowed
shadowed_settings.each { |ss| new_hash[ss] = GlobalSetting.send(ss) }
changes, deletions = diff_hash(new_hash, old)
changes, deletions = diff_hash(new_hash, current)
changes.each { |name, val| current[name] = val }
deletions.each { |name, val| current[name] = defaults[name] }
deletions.each { |name, _| current[name] = defaults_view[name] }
clear_cache!
end
@ -282,44 +221,9 @@ module SiteSettingExtension
end
def add_override!(name, val)
type = get_data_type(name, defaults[name.to_sym])
val = val.to_s if type == types[:string]
if type == types[:bool] && val != true && val != false
val = (val == "t" || val == "true") ? 't' : 'f'
end
if type == types[:integer] && !val.is_a?(Integer)
val = val.to_i
end
if type == types[:null] && val != ''
type = get_data_type(name, val)
end
if type == types[:enum]
val = val.to_i if defaults[name.to_sym].is_a?(Integer)
if enum_class(name)
raise Discourse::InvalidParameters.new(:value) unless enum_class(name).valid_value?(val)
else
raise Discourse::InvalidParameters.new(:value) unless choices[name].include?(val)
end
end
if v = validators[name]
validator = v[:class].new(v[:opts])
unless validator.valid_value?(val)
raise Discourse::InvalidParameters.new(validator.error_message)
end
end
if self.respond_to? "validate_#{name}"
send("validate_#{name}", val)
end
val, type = type_supervisor.to_db_value(name, val)
provider.save(name, val, type)
current[name] = convert(val, type, name)
current[name] = type_supervisor.to_rb_value(name, val)
notify_clients!(name) if client_settings.include? name
clear_cache!
end
@ -332,26 +236,10 @@ module SiteSettingExtension
MessageBus.publish('/client_settings', name: name, value: self.send(name))
end
def has_setting?(name)
defaults.has_key?(name.to_sym) || defaults.has_key?("#{name}?".to_sym)
end
def requires_refresh?(name)
refresh_settings.include?(name.to_sym)
end
def is_valid_data?(name, value)
valid = true
type = get_data_type(name, defaults[name.to_sym])
if type == types[:integer]
# validate integer
valid = false unless value.to_i.is_a?(Integer)
end
valid
end
def filter_value(name, value)
if %w[disabled_image_download_domains onebox_domains_blacklist exclude_rel_nofollow_domains email_domains_blacklist email_domains_whitelist white_listed_spam_host_domains].include? name
domain_array = []
@ -362,7 +250,7 @@ module SiteSettingExtension
end
def set(name, value)
if has_setting?(name) && is_valid_data?(name, value)
if has_setting?(name)
value = filter_value(name, value)
self.send("#{name}=", value)
Discourse.request_refresh! if requires_refresh?(name)
@ -399,90 +287,11 @@ module SiteSettingExtension
[changes, deletions]
end
def get_data_type(name, val)
return types[:null] if val.nil?
# Some types are just for validations like email.
# Only consider it valid if includes in `types`
if static_type = static_types[name.to_sym]
return types[static_type] if types.keys.include?(static_type)
end
case val
when String
types[:string]
when Integer
types[:integer]
when Float
types[:float]
when TrueClass, FalseClass
types[:bool]
else
raise ArgumentError.new :val
end
end
def convert(value, type, name)
case type
when types[:float]
value.to_f
when types[:integer]
value.to_i
when types[:bool]
value == true || value == "t" || value == "true"
when types[:null]
nil
when types[:enum]
defaults[name.to_sym].is_a?(Integer) ? value.to_i : value
else
return value if types[type]
# Otherwise it's a type error
raise ArgumentError.new :type
end
end
def validator_for(type_name)
@validator_mapping ||= {
'email' => EmailSettingValidator,
'username' => UsernameSettingValidator,
types[:integer] => IntegerSettingValidator,
types[:string] => StringSettingValidator,
'list' => StringSettingValidator,
'enum' => StringSettingValidator,
'regex' => RegexSettingValidator
}
@validator_mapping[type_name]
end
DEPRECATED_SETTINGS = [
['use_https', 'force_https', '1.7']
]
def setup_deprecated_methods
DEPRECATED_SETTINGS.each do |old_setting, new_setting, version|
define_singleton_method old_setting do
logger.warn("`SiteSetting.#{old_setting}` has been deprecated and will be removed in the #{version} Release. Please use `SiteSetting.#{new_setting}` instead")
self.public_send new_setting
end
define_singleton_method "#{old_setting}?" do
logger.warn("`SiteSetting.#{old_setting}?` has been deprecated and will be removed in the #{version} Release. Please use `SiteSetting.#{new_setting}?` instead")
self.public_send "#{new_setting}?"
end
define_singleton_method "#{old_setting}=" do |val|
logger.warn("`SiteSetting.#{old_setting}=` has been deprecated and will be removed in the #{version} Release. Please use `SiteSetting.#{new_setting}=` instead")
self.public_send "#{new_setting}=", val
end
end
end
def setup_methods(name)
clean_name = name.to_s.sub("?", "").to_sym
define_singleton_method clean_name do
c = @containers[provider.current_site]
if c
if (c = @containers[provider.current_site])
c[name]
else
refresh!
@ -499,10 +308,6 @@ module SiteSettingExtension
end
end
def enum_class(name)
enums[name]
end
def get_hostname(url)
unless (URI.parse(url).scheme rescue nil).nil?
url = "http://#{url}" if URI.parse(url).scheme.nil?

View File

@ -0,0 +1,126 @@
module SiteSettings; end
# A cache for providing default value based on site locale
class SiteSettings::DefaultsProvider
include Enumerable
CONSUMED_OPTS = %i[default locale_default].freeze
DEFAULT_LOCALE_KEY = :default_locale
DEFAULT_LOCALE = 'en'.freeze
DEFAULT_CATEGORY = 'required'.freeze
def initialize(site_setting)
@site_setting = site_setting
@site_setting.refresh_settings << DEFAULT_LOCALE_KEY
@cached = {}
@defaults = {}
@defaults[DEFAULT_LOCALE.to_sym] = {}
@site_locale = nil
refresh_site_locale!
end
def load_setting(name_arg, value, opts = {})
name = name_arg.to_sym
@defaults[DEFAULT_LOCALE.to_sym][name] = value
if (locale_default = opts[:locale_default])
locale_default.each do |locale, v|
locale = locale.to_sym
@defaults[locale] ||= {}
@defaults[locale][name] = v
end
end
refresh_cache!
end
def db_all
@site_setting.provider.all.delete_if { |s| s.name.to_sym == DEFAULT_LOCALE_KEY }
end
def all
@cached
end
def get(name)
@cached[name.to_sym]
end
# Used to override site settings in dev/test env
def set_regardless_of_locale(name, value)
name = name.to_sym
if @site_setting.has_setting?(name)
@defaults.each { |_, hash| hash.delete(name) }
@defaults[DEFAULT_LOCALE.to_sym][name] = value
value, type = @site_setting.type_supervisor.to_db_value(name, value)
@cached[name] = @site_setting.type_supervisor.to_rb_value(name, value, type)
else
raise ArgumentError.new("No setting named '#{name}' exists")
end
end
alias [] get
attr_reader :site_locale
def site_locale=(val)
val = val.to_s
raise Discourse::InvalidParameters.new(:value) unless LocaleSiteSetting.valid_value?(val)
if val != @site_locale
@site_setting.provider.save(DEFAULT_LOCALE_KEY, val, SiteSetting.types[:string])
refresh_site_locale!
@site_setting.refresh!
Discourse.request_refresh!
end
@site_locale
end
def each
@cached.each { |k, v| yield k.to_sym, v }
end
def locale_setting_hash
{
setting: DEFAULT_LOCALE_KEY,
default: DEFAULT_LOCALE,
category: DEFAULT_CATEGORY,
description: @site_setting.description(DEFAULT_LOCALE_KEY),
type: SiteSetting.types[SiteSetting.types[:enum]],
preview: nil,
value: @site_locale,
valid_values: LocaleSiteSetting.values,
translate_names: LocaleSiteSetting.translate_names?
}
end
def refresh_site_locale!
if GlobalSetting.respond_to?(DEFAULT_LOCALE_KEY) &&
(global_val = GlobalSetting.send(DEFAULT_LOCALE_KEY)) &&
!global_val.blank?
@site_locale = global_val
elsif (db_val = @site_setting.provider.find(DEFAULT_LOCALE_KEY))
@site_locale = db_val.value.to_s
else
@site_locale = DEFAULT_LOCALE
end
refresh_cache!
@site_locale
end
def has_setting?(name)
has_key?(name.to_sym) || has_key?("#{name.to_s}?".to_sym)
end
private
def has_key?(key)
@cached.key?(key) || key == DEFAULT_LOCALE_KEY
end
def refresh_cache!
@cached = @defaults[DEFAULT_LOCALE.to_sym].merge(@defaults.fetch(@site_locale.to_sym, {}))
end
end

View File

@ -0,0 +1,26 @@
module SiteSettings; end
module SiteSettings::DeprecatedSettings
DEPRECATED_SETTINGS = [
%w[use_https force_https 1.7]
]
def setup_deprecated_methods
DEPRECATED_SETTINGS.each do |old_setting, new_setting, version|
define_singleton_method old_setting do
logger.warn("`SiteSetting.#{old_setting}` has been deprecated and will be removed in the #{version} Release. Please use `SiteSetting.#{new_setting}` instead")
self.public_send new_setting
end
define_singleton_method "#{old_setting}?" do
logger.warn("`SiteSetting.#{old_setting}?` has been deprecated and will be removed in the #{version} Release. Please use `SiteSetting.#{new_setting}?` instead")
self.public_send "#{new_setting}?"
end
define_singleton_method "#{old_setting}=" do |val|
logger.warn("`SiteSetting.#{old_setting}=` has been deprecated and will be removed in the #{version} Release. Please use `SiteSetting.#{new_setting}=` instead")
self.public_send "#{new_setting}=", val
end
end
end
end

View File

@ -10,7 +10,7 @@ class SiteSettings::LocalProcessProvider
@settings[current_site] ||= {}
end
def initialize()
def initialize
@settings = {}
self.current_site = "test"
end

View File

@ -0,0 +1,210 @@
require_dependency 'site_settings/validations'
require_dependency 'enum'
module SiteSettings; end
class SiteSettings::TypeSupervisor
include SiteSettings::Validations
CONSUMED_OPTS = %i[enum choices type validator min max regex hidden regex_error].freeze
VALIDATOR_OPTS = %i[min max regex hidden regex_error].freeze
# For plugins, so they can tell if a feature is supported
SUPPORTED_TYPES = %i[email username list enum].freeze
def self.types
@types ||= Enum.new(string: 1,
time: 2,
integer: 3,
float: 4,
bool: 5,
null: 6,
enum: 7,
list: 8,
url_list: 9,
host_list: 10,
category_list: 11,
value_list: 12,
regex: 13,
email: 14,
username: 15)
end
def self.parse_value_type(val)
case val
when NilClass
self.types[:null]
when String
self.types[:string]
when Integer
self.types[:integer]
when Float
self.types[:float]
when TrueClass, FalseClass
self.types[:bool]
else
raise ArgumentError.new :val
end
end
def self.supported_types
SUPPORTED_TYPES
end
def initialize(defaults_provider)
@defaults_provider = defaults_provider
@enums = {}
@static_types = {}
@choices = {}
@validators = {}
@types = {}
end
def load_setting(name_arg, opts = {})
name = name_arg.to_sym
if (enum = opts[:enum])
@enums[name] = enum.is_a?(String) ? enum.constantize : enum
opts[:type] ||= :enum
end
if (new_choices = opts[:choices])
new_choices = eval(new_choices) if new_choices.is_a?(String)
if @choices.has_key?(name)
@choices[name].concat(new_choices)
else
@choices[name] = new_choices
end
end
if (type = opts[:type])
@static_types[name] = type.to_sym
end
@types[name] = get_data_type(name, @defaults_provider[name])
opts[:validator] = opts[:validator].try(:constantize)
if (validator_type = (opts[:validator] || validator_for(@types[name])))
@validators[name] = { class: validator_type, opts: opts.slice(*VALIDATOR_OPTS) }
end
end
def to_rb_value(name, value, override_type = nil)
name = name.to_sym
type = @types[name] = (override_type || @types[name] || get_data_type(name, value))
case type
when self.class.types[:float]
value.to_f
when self.class.types[:integer]
value.to_i
when self.class.types[:bool]
value == true || value == 't' || value == 'true'
when self.class.types[:null]
nil
when self.class.types[:enum]
@defaults_provider[name].is_a?(Integer) ? value.to_i : value.to_s
when self.class.types[:string]
value.to_s
else
return value if self.class.types[type]
# Otherwise it's a type error
raise ArgumentError.new :type
end
end
def to_db_value(name, value)
val, type = normalize_input(name, value)
validate_value(name, type, val)
[val, type]
end
def type_hash(name)
name = name.to_sym
type = self.class.types[@types[name]]
result = { type: type.to_s }
if type == :enum
if (klass = enum_class(name))
result.merge!(valid_values: klass.values, translate_names: klass.translate_names?)
else
result.merge!(valid_values: @choices[name].map { |c| { name: c, value: c } }, translate_names: false)
end
end
result[:choices] = @choices[name] if @choices.has_key? name
result
end
private
def normalize_input(name, val)
name = name.to_sym
type = @types[name] || self.class.parse_value_type(val)
if type == self.class.types[:bool]
val = (val == true || val == 't' || val == 'true') ? 't' : 'f'
elsif type == self.class.types[:integer] && !val.is_a?(Integer)
val = val.to_i
elsif type == self.class.types[:null] && val != ''
type = get_data_type(name, val)
elsif type == self.class.types[:enum]
val = @defaults_provider[name].is_a?(Integer) ? val.to_i : val.to_s
end
[val, type]
end
def validate_value(name, type, val)
if type == self.class.types[:enum]
if enum_class(name)
raise Discourse::InvalidParameters.new(:value) unless enum_class(name).valid_value?(val)
else
raise Discourse::InvalidParameters.new(:value) unless @choices[name].include?(val)
end
end
if (v = @validators[name])
validator = v[:class].new(v[:opts])
unless validator.valid_value?(val)
raise Discourse::InvalidParameters, "#{name.to_s}: #{validator.error_message}"
end
end
validate_method = "validate_#{name}"
if self.respond_to? validate_method
send(validate_method, val)
end
end
def get_data_type(name, val)
# Some types are just for validations like email.
# Only consider it valid if includes in `types`
if (static_type = @static_types[name.to_sym])
return self.class.types[static_type] if self.class.types.keys.include?(static_type)
end
self.class.parse_value_type(val)
end
def enum_class(name)
@enums[name]
end
def validator_for(type_name)
case type_name
when self.class.types[:email]
EmailSettingValidator
when self.class.types[:username]
UsernameSettingValidator
when self.class.types[:integer]
IntegerSettingValidator
when self.class.types[:regex]
RegexSettingValidator
when self.class.types[:string], self.class.types[:list], self.class.types[:enum]
StringSettingValidator
else nil
end
end
end

View File

@ -1,6 +1,6 @@
module SiteSettings; end
module SiteSettingValidations
module SiteSettings::Validations
def validate_error(key)
raise Discourse::InvalidParameters.new(I18n.t("errors.site_settings.#{key}"))
end

View File

@ -1,32 +1,26 @@
module SiteSettings; end
class SiteSettings::YamlLoader
def initialize(file)
@file = file
end
def env_val(value)
if value.is_a?(Hash)
value.has_key?(Rails.env) ? value[Rails.env] : value['default']
else
value
end
end
def load
yaml = YAML.load_file(@file)
yaml.each_key do |category|
yaml[category].each do |setting_name, hash|
if hash.is_a?(Hash)
# Get default value for the site setting:
value = env_val(hash.delete('default'))
if hash.key?('hidden')
hash['hidden'] = env_val(hash.delete('hidden'))
value = hash.delete('default')
if value.is_a?(Hash)
raise Discourse::Deprecation, "Site setting per env is no longer supported. Error setting: #{setting_name}"
end
yield category, setting_name, value, hash.symbolize_keys!
if hash['hidden']&.is_a?(Hash)
raise Discourse::Deprecation, "Hidden site setting per env is no longer supported. Error setting: #{setting_name}"
end
yield category, setting_name, value, hash.deep_symbolize_keys!
else
# Simplest case. site_setting_name: 'default value'
yield category, setting_name, hash, {}

View File

@ -1,8 +1,6 @@
plugins:
discourse_narrative_bot_enabled:
default:
default: true
test: false
default: true
client: true
disable_discourse_narrative_bot_welcome_post:
default: false

View File

@ -0,0 +1,48 @@
require 'benchmark/ips'
require File.expand_path('../../../../config/environment', __FILE__)
# Put pre conditions here
# Used db but it's OK in the most cases
# build the cache
SiteSetting.title = SecureRandom.hex
SiteSetting.default_locale = SiteSetting.default_locale == 'en' ? 'zh_CN' : 'en'
SiteSetting.refresh!
tests = [
["current cache", lambda do
SiteSetting.title
SiteSetting.enable_sso
end
],
["change default locale with current cache refreshed", lambda do
SiteSetting.default_locale = SiteSetting.default_locale == 'en' ? 'zh_CN' : 'en'
end
],
["change site setting", lambda do
SiteSetting.title = SecureRandom.hex
end
],
]
Benchmark.ips do |x|
tests.each do |test, proc|
x.report(test, proc)
end
end
# 2017-08-02 - Erick's Site Setting change
# Before
# Calculating -------------------------------------
# current cache 167.518k (±12.1%) i/s - 822.983k in 5.000478s
# change default locale with current cache refreshed
# 174.173 (±16.7%) i/s - 845.000 in 5.015281s
# change site setting 132.956 (±16.5%) i/s - 663.000 in 5.124766s
# After
# Calculating -------------------------------------
# current cache 167.170k (±12.2%) i/s - 824.688k in 5.022784s
# change default locale with current cache refreshed
# 79.876 (±16.3%) i/s - 392.000 in 5.067448s
# change site setting 129.085 (±13.2%) i/s - 636.000 in 5.032536s

View File

@ -0,0 +1,32 @@
require 'ruby-prof'
def profile(&blk)
result = RubyProf.profile(&blk)
printer = RubyProf::GraphHtmlPrinter.new(result)
printer.print(STDOUT)
end
profile { '' } # loading profiler dependency
require File.expand_path('../../../../config/environment', __FILE__)
# warming up
SiteSetting.title
SiteSetting.enable_sso
SiteSetting.default_locale = SiteSetting.default_locale == 'en' ? 'zh_CN' : 'en'
SiteSetting.title = SecureRandom.hex
profile do
SiteSetting.title
end
profile do
SiteSetting.enable_sso
end
profile do
SiteSetting.default_locale = SiteSetting.default_locale == 'en' ? 'zh_CN' : 'en'
end
profile do
SiteSetting.title = SecureRandom.hex
end

View File

@ -64,7 +64,7 @@ describe SiteSettingExtension do
expect(settings.hello).to eq(99)
end
it "Publishes changes cross sites" do
it "publishes changes cross sites" do
settings.setting(:hello, 1)
settings2.setting(:hello, 1)
@ -143,7 +143,7 @@ describe SiteSettingExtension do
it "should publish changes to clients" do
settings.setting("test_setting", 100)
settings.client_setting("test_setting")
settings.setting("test_setting", nil, client: true)
messages = MessageBus.track_publish do
settings.test_setting = 88
@ -155,8 +155,11 @@ describe SiteSettingExtension do
end
describe "remove_override" do
it "correctly nukes overrides" do
before do
settings.setting(:test_override, "test")
settings.refresh!
end
it "correctly nukes overrides" do
settings.test_override = "bla"
settings.remove_override!(:test_override)
expect(settings.test_override).to eq("test")
@ -263,6 +266,7 @@ describe SiteSettingExtension do
settings.setting(:test_int_enum, 1, enum: TestIntEnumClass)
settings.test_int_enum = "2"
settings.refresh!
expect(settings.defaults[:test_int_enum]).to eq(1)
expect(settings.test_int_enum).to eq(2)
end
@ -272,7 +276,7 @@ describe SiteSettingExtension do
class TestEnumClass
def self.valid_value?(v)
true
self.values.include?(v)
end
def self.values
['en']
@ -299,6 +303,10 @@ describe SiteSettingExtension do
expect(settings.all_settings.detect { |s| s[:setting] == :test_enum }).to be_present
end
it 'should report error when being set other values' do
expect { settings.test_enum = 'not_in_enum' }.to raise_error(Discourse::InvalidParameters)
end
context 'when overridden' do
after :each do
settings.remove_override!(:validated_setting)
@ -384,6 +392,14 @@ describe SiteSettingExtension do
end
end
describe ".set_and_log" do
it "raises an error when set for an invalid setting name" do
expect {
settings.set_and_log("provider", "haxxed")
}.to raise_error(ArgumentError)
end
end
describe "filter domain name" do
before do
settings.setting(:white_listed_spam_host_domains, "www.example.com")
@ -503,4 +519,62 @@ describe SiteSettingExtension do
end
end
describe 'locale default overrides are respected' do
before do
settings.setting(:test_override, 'default', locale_default: { zh_CN: 'cn' })
settings.refresh!
end
after do
settings.remove_override!(:test_override)
end
it 'ensures the default cache expired after overriding the default_locale' do
expect(settings.test_override).to eq('default')
settings.default_locale = 'zh_CN'
expect(settings.test_override).to eq('cn')
end
it 'returns the saved setting even locale default exists' do
expect(settings.test_override).to eq('default')
settings.default_locale = 'zh_CN'
settings.test_override = 'saved'
expect(settings.test_override).to eq('saved')
end
end
describe '.requires_refresh?' do
it 'always refresh default_locale always require refresh' do
expect(settings.requires_refresh?(:default_locale)).to be_truthy
end
end
describe '.default_locale' do
it 'is always loaded' do
expect(settings.default_locale).to eq 'en'
end
end
describe '.default_locale=' do
it 'can be changed' do
settings.default_locale = 'zh_CN'
expect(settings.default_locale).to eq 'zh_CN'
end
it 'refresh!' do
settings.expects(:refresh!)
settings.default_locale = 'zh_CN'
end
it 'expires the cache' do
settings.default_locale = 'zh_CN'
expect(Rails.cache.exist?(SiteSettingExtension.client_settings_cache_key)).to be_falsey
end
it 'refreshes the client' do
Discourse.expects(:request_refresh!)
settings.default_locale = 'zh_CN'
end
end
end

View File

@ -0,0 +1,268 @@
require 'rails_helper'
require_dependency 'site_settings/defaults_provider'
describe SiteSettings::DefaultsProvider do
let :provider_local do
SiteSettings::LocalProcessProvider.new
end
def new_settings(provider)
Class.new do
extend SiteSettingExtension
self.provider = provider
end
end
let :settings do
new_settings(provider_local)
end
describe 'inserts default_locale into refresh' do
it 'when initialize' do
expect(settings.refresh_settings.include?(SiteSettings::DefaultsProvider::DEFAULT_LOCALE_KEY)).to be_truthy
end
end
describe '.db_all' do
it 'collects values from db except default locale' do
settings.provider.save(SiteSettings::DefaultsProvider::DEFAULT_LOCALE_KEY,
'en',
SiteSetting.types[:string])
expect(settings.defaults.db_all).to eq([])
end
it 'can collect values from db' do
settings.provider.save('try_a', 1, SiteSetting.types[:integer])
settings.provider.save('try_b', 2, SiteSetting.types[:integer])
expect(settings.defaults.db_all.count).to eq 2
end
end
describe 'expose default cache according to locale' do
before(:each) do
settings.setting(:test_override, 'default', locale_default: { zh_CN: 'cn' })
settings.setting(:test_default, 'test', regex: '^\S+$')
settings.refresh!
end
describe '.all' do
it 'returns all values according to the current locale' do
expect(settings.defaults.all).to eq(test_override: 'default', test_default: 'test')
settings.defaults.site_locale = 'zh_CN'
settings.defaults.refresh_site_locale!
expect(settings.defaults.all).to eq(test_override: 'cn', test_default: 'test')
end
end
describe '.get' do
it 'returns the default value to a site setting' do
expect(settings.defaults.get(:test_override)).to eq 'default'
end
it 'accepts a string as the parameters' do
expect(settings.defaults.get('test_override')).to eq 'default'
end
it 'returns the default value according to current locale' do
expect(settings.defaults.get(:test_override)).to eq 'default'
settings.defaults.site_locale = 'zh_CN'
expect(settings.defaults.get(:test_override)).to eq 'cn'
end
end
describe '.set_regardless_of_locale' do
let(:val) { 'env_overriden' }
it 'sets the default value to a site setting regardless the locale' do
settings.defaults.set_regardless_of_locale(:test_override, val)
expect(settings.defaults.get(:test_override)).to eq val
settings.defaults.site_locale = 'zh_CN'
expect(settings.defaults.get(:test_override)).to eq val
end
it 'handles the string' do
settings.defaults.set_regardless_of_locale('test_override', val)
expect(settings.defaults.get(:test_override)).to eq val
end
it 'converts the data type' do
settings.defaults.set_regardless_of_locale(:test_override, 1)
expect(settings.defaults.get(:test_override)).to eq '1'
end
it 'raises when the setting does not exists' do
expect {
settings.defaults.set_regardless_of_locale(:not_exist, 1)
}.to raise_error(ArgumentError)
end
it 'raises when the value is not valid' do
expect {
settings.defaults.set_regardless_of_locale(:test_default, 'regex will fail')
}.to raise_error(Discourse::InvalidParameters)
end
end
describe '.each' do
it 'yields the pair of site settings' do
expect { |b| settings.defaults.each(&b) }.to yield_successive_args([:test_override, 'default'], [:test_default, 'test'])
settings.defaults.site_locale = 'zh_CN'
expect { |b| settings.defaults.each(&b) }.to yield_successive_args([:test_override, 'cn'], [:test_default, 'test'])
end
end
end
describe '.site_locale' do
it 'returns the current site locale' do
expect(settings.defaults.site_locale).to eq 'en'
end
context 'when locale is set in the db' do
let(:db_val) { 'zr' }
let(:global_val) { 'gr' }
before do
settings.provider.save(SiteSettings::DefaultsProvider::DEFAULT_LOCALE_KEY,
db_val,
SiteSetting.types[:string])
settings.defaults.refresh_site_locale!
end
it 'should load from database' do
expect(settings.defaults.site_locale).to eq db_val
end
it 'prioritizes GlobalSetting than value from db' do
GlobalSetting.stubs(:default_locale).returns(global_val)
settings.defaults.refresh_site_locale!
expect(settings.defaults.site_locale).to eq global_val
end
it 'ignores blank GlobalSetting' do
GlobalSetting.stubs(:default_locale).returns('')
settings.defaults.refresh_site_locale!
expect(settings.defaults.site_locale).to eq db_val
end
end
end
describe '.site_locale=' do
it 'changes and store the current site locale' do
settings.defaults.site_locale = 'zh_CN'
expect(settings.defaults.site_locale).to eq 'zh_CN'
end
it 'changes and store the current site locale' do
expect { settings.defaults.site_locale = 'random' }.to raise_error(Discourse::InvalidParameters)
expect(settings.defaults.site_locale).to eq 'en'
end
it "don't change when it's shadowed" do
GlobalSetting.stubs(:default_locale).returns('shadowed')
settings.defaults.site_locale = 'zh_CN'
expect(settings.defaults.site_locale).to eq 'shadowed'
end
it 'refresh_site_locale! when called' do
settings.defaults.expects(:refresh_site_locale!)
settings.defaults.site_locale = 'zh_CN'
end
it 'refreshes the client when changed' do
Discourse.expects(:request_refresh!).once
settings.defaults.site_locale = 'zh_CN'
end
it "doesn't refresh the client when changed" do
Discourse.expects(:request_refresh!).never
settings.defaults.site_locale = 'en'
end
end
describe '.locale_setting_hash' do
it 'returns the hash for client display' do
result = settings.defaults.locale_setting_hash
expect(result[:setting]).to eq(SiteSettings::DefaultsProvider::DEFAULT_LOCALE_KEY)
expect(result[:default]).to eq(SiteSettings::DefaultsProvider::DEFAULT_LOCALE)
expect(result[:type]).to eq(SiteSetting.types[SiteSetting.types[:enum]])
expect(result[:preview]).to be_nil
expect(result[:value]).to eq(SiteSettings::DefaultsProvider::DEFAULT_LOCALE)
expect(result[:category]).to eq(SiteSettings::DefaultsProvider::DEFAULT_CATEGORY)
expect(result[:valid_values]).to eq(LocaleSiteSetting.values)
expect(result[:translate_names]).to eq(LocaleSiteSetting.translate_names?)
expect(result[:description]).not_to be_nil
end
end
describe '.load_setting' do
it 'adds a setting to the cache' do
settings.defaults.load_setting('new_a', 1)
expect(settings.defaults[:new_a]).to eq 1
end
it 'takes care of locale default' do
settings.defaults.load_setting(:new_b, 1, locale_default: { zh_CN: 2, zh_TW: 2 })
expect(settings.defaults[:new_b]).to eq 1
end
end
describe '.refresh_site_locale!' do
it 'loads the change to locale' do
expect(settings.defaults.site_locale).to eq 'en'
settings.provider.save(SiteSettings::DefaultsProvider::DEFAULT_LOCALE_KEY,
'zh_CN',
SiteSetting.types[:string])
settings.defaults.refresh_site_locale!
expect(settings.defaults.site_locale).to eq 'zh_CN'
end
it 'loads from GlobalSettings' do
expect(settings.defaults.site_locale).to eq 'en'
GlobalSetting.stubs(:default_locale).returns('fr')
settings.defaults.refresh_site_locale!
expect(settings.defaults.site_locale).to eq 'fr'
end
it 'prioritized GlobalSettings than db' do
expect(settings.defaults.site_locale).to eq 'en'
settings.provider.save(SiteSettings::DefaultsProvider::DEFAULT_LOCALE_KEY,
'zh_CN',
SiteSetting.types[:string])
GlobalSetting.stubs(:default_locale).returns('fr')
settings.defaults.refresh_site_locale!
expect(settings.defaults.site_locale).to eq 'fr'
end
end
describe '.has_setting?' do
before do
settings.setting(:r, 1)
settings.setting(:question?, 1)
end
it "returns true when it's present in the cache" do
expect(settings.defaults.has_setting?(:r)).to be_truthy
end
it '"responds when the arg is string' do
expect(settings.defaults.has_setting?('r')).to be_truthy
end
it 'default_locale always exists' do
expect(settings.defaults.has_setting?(SiteSettings::DefaultsProvider::DEFAULT_LOCALE_KEY)).to be_truthy
end
it 'returns false when the key is not exist' do
expect(settings.defaults.has_setting?('no_key')).to be_falsey
end
it 'checks name with question mark' do
expect(settings.defaults.has_setting?(:question)).to be_truthy
expect(settings.defaults.has_setting?('question')).to be_truthy
end
end
end

View File

@ -0,0 +1,341 @@
require 'rails_helper'
require_dependency 'site_settings/type_supervisor'
describe SiteSettings::TypeSupervisor do
let :provider_local do
SiteSettings::LocalProcessProvider.new
end
def new_settings(provider)
Class.new do
extend SiteSettingExtension
self.provider = provider
end
end
let :settings do
new_settings(provider_local)
end
subject { SiteSettings::TypeSupervisor }
describe 'constants' do
it 'validator opts are the subset of consumed opts' do
expect(Set.new(SiteSettings::TypeSupervisor::CONSUMED_OPTS).superset?(
Set.new(SiteSettings::TypeSupervisor::VALIDATOR_OPTS))).to be_truthy
end
end
describe '#types' do
context "verify enum sequence" do
it "'string' should be at 1st position" do
expect(SiteSettings::TypeSupervisor.types[:string]).to eq(1)
end
it "'time' should be at 2nd position" do
expect(SiteSettings::TypeSupervisor.types[:time]).to eq(2)
end
it "'integer' should be at 3rd position" do
expect(SiteSettings::TypeSupervisor.types[:integer]).to eq(3)
end
it "'float' should be at 4th position" do
expect(SiteSettings::TypeSupervisor.types[:float]).to eq(4)
end
it "'bool' should be at 5th position" do
expect(SiteSettings::TypeSupervisor.types[:bool]).to eq(5)
end
it "'null' should be at 6th position" do
expect(SiteSettings::TypeSupervisor.types[:null]).to eq(6)
end
it "'enum' should be at 7th position" do
expect(SiteSettings::TypeSupervisor.types[:enum]).to eq(7)
end
it "'list' should be at 8th position" do
expect(SiteSettings::TypeSupervisor.types[:list]).to eq(8)
end
it "'url_list' should be at 9th position" do
expect(SiteSettings::TypeSupervisor.types[:url_list]).to eq(9)
end
it "'host_list' should be at 10th position" do
expect(SiteSettings::TypeSupervisor.types[:host_list]).to eq(10)
end
it "'category_list' should be at 11th position" do
expect(SiteSettings::TypeSupervisor.types[:category_list]).to eq(11)
end
it "'value_list' should be at 12th position" do
expect(SiteSettings::TypeSupervisor.types[:value_list]).to eq(12)
end
it "'regex' should be at 13th position" do
expect(SiteSettings::TypeSupervisor.types[:regex]).to eq(13)
end
it "'email' should be at 14th position" do
expect(SiteSettings::TypeSupervisor.types[:email]).to eq(14)
end
it "'username' should be at 15th position" do
expect(SiteSettings::TypeSupervisor.types[:username]).to eq(15)
end
end
end
describe '#parse_value_type' do
it 'returns :null type when the value is nil' do
expect(subject.parse_value_type(nil)).to eq(SiteSetting.types[:null])
end
it 'returns :integer type when the value is int' do
expect(subject.parse_value_type(2)).to eq(SiteSetting.types[:integer])
end
it 'returns :integer type when the value is large int' do
expect(subject.parse_value_type(99999999999999999999999999999999999)).to eq(SiteSetting.types[:integer])
end
it 'returns :float type when the value is float' do
expect(subject.parse_value_type(1.23)).to eq(SiteSetting.types[:float])
end
it 'returns :bool type when the value is true' do
expect(subject.parse_value_type(true)).to eq(SiteSetting.types[:bool])
end
it 'returns :bool type when the value is false' do
expect(subject.parse_value_type(false)).to eq(SiteSetting.types[:bool])
end
it 'raises when the value is not listed' do
expect {
subject.parse_value_type(Object.new)
}.to raise_error ArgumentError
end
end
context 'with different data types' do
class TestEnumClass
def self.valid_value?(v)
self.values.include?(v)
end
def self.values
['en']
end
def self.translate_names?
false
end
end
class TestSmallThanTenValidator
def initialize(opts)
end
def valid_value?(v)
v < 10
end
def error_message
''
end
end
before do
settings.setting(:type_null, nil)
settings.setting(:type_int, 1)
settings.setting(:type_true, true)
settings.setting(:type_false, false)
settings.setting(:type_float, 2.3232)
settings.setting(:type_string, 'string')
settings.setting(:type_enum_default_string, '2', type: 'enum', choices: ['2'])
settings.setting(:type_enum_class, 'en', enum: 'TestEnumClass')
settings.setting(:type_validator, 5, validator: 'TestSmallThanTenValidator')
settings.setting(:type_mock_validate_method, 'no_value')
settings.setting(:type_custom, 'custom', type: 'list')
settings.refresh!
end
describe '.to_db_value' do
let(:true_val) { 't' }
let(:false_val) { 'f' }
it 'returns nil value' do
expect(settings.type_supervisor.to_db_value(:type_null, nil)).to eq [nil, SiteSetting.types[:null]]
end
it 'gives a second chance to guess even told :null type' do
expect(settings.type_supervisor.to_db_value(:type_null, 1)).to eq [1, SiteSetting.types[:integer]]
end
it 'writes `t` or `f` given the possible bool value' do
expect(settings.type_supervisor.to_db_value(:type_true, true)).to eq [true_val, SiteSetting.types[:bool]]
expect(settings.type_supervisor.to_db_value(:type_true, 't')).to eq [true_val, SiteSetting.types[:bool]]
expect(settings.type_supervisor.to_db_value(:type_true, 'true')).to eq [true_val, SiteSetting.types[:bool]]
expect(settings.type_supervisor.to_db_value(:type_true, false)).to eq [false_val, SiteSetting.types[:bool]]
end
it 'writes `f` if given not `true` value' do
expect(settings.type_supervisor.to_db_value(:type_true, '')).to eq [false_val, SiteSetting.types[:bool]]
expect(settings.type_supervisor.to_db_value(:type_true, nil)).to eq [false_val, SiteSetting.types[:bool]]
end
it 'returns floats value' do
expect(settings.type_supervisor.to_db_value(:type_float, 1.2)).to eq [1.2, SiteSetting.types[:float]]
expect(settings.type_supervisor.to_db_value(:type_float, 1)).to eq [1.0, SiteSetting.types[:float]]
end
it 'returns string value' do
expect(settings.type_supervisor.to_db_value(:type_string, 'a')).to eq ['a', SiteSetting.types[:string]]
end
it 'returns enum value with string default' do
expect(settings.type_supervisor.to_db_value(:type_enum_default_string, 2)).to eq ['2', SiteSetting.types[:enum]]
expect(settings.type_supervisor.to_db_value(:type_enum_default_string, '2')).to eq ['2', SiteSetting.types[:enum]]
end
it 'raises when it does not in the enum choices' do
expect {
settings.type_supervisor.to_db_value(:type_enum_default_string, 'random')
}.to raise_error Discourse::InvalidParameters
end
it 'returns enum value for the given enum class' do
expect(settings.type_supervisor.to_db_value(:type_enum_class, 'en')).to eq ['en', SiteSetting.types[:enum]]
end
it 'raises when it does not in the enum class' do
expect {
settings.type_supervisor.to_db_value(:type_enum_class, 'random')
}.to raise_error Discourse::InvalidParameters
end
it 'validates value by validator' do
expect(settings.type_supervisor.to_db_value(:type_validator, 1)).to eq [1, SiteSetting.types[:integer]]
end
it 'raises when the validator says so' do
expect {
settings.type_supervisor.to_db_value(:type_validator, 11)
}.to raise_error Discourse::InvalidParameters
end
it 'tries invoke validate methods' do
settings.type_supervisor.expects(:validate_type_mock_validate_method).with('no')
settings.type_supervisor.to_db_value(:type_mock_validate_method, 'no')
end
end
describe '.to_rb_value' do
let(:true_val) { 't' }
let(:false_val) { 'f' }
it 'the type can be overriden by a parameter' do
expect(settings.type_supervisor.to_rb_value(:type_null, '1', SiteSetting.types[:integer])).to eq(1)
end
it 'returns nil value' do
expect(settings.type_supervisor.to_rb_value(:type_null, '1')).to eq nil
expect(settings.type_supervisor.to_rb_value(:type_null, 1)).to eq nil
expect(settings.type_supervisor.to_rb_value(:type_null, 'null')).to eq nil
expect(settings.type_supervisor.to_rb_value(:type_null, 'nil')).to eq nil
end
it 'returns true when it is true or `t` or `true`' do
expect(settings.type_supervisor.to_rb_value(:type_true, true)).to eq true
expect(settings.type_supervisor.to_rb_value(:type_true, 't')).to eq true
expect(settings.type_supervisor.to_rb_value(:type_true, 'true')).to eq true
end
it 'returns false if not one of `true` value' do
expect(settings.type_supervisor.to_rb_value(:type_true, 'tr')).to eq false
expect(settings.type_supervisor.to_rb_value(:type_true, '')).to eq false
expect(settings.type_supervisor.to_rb_value(:type_true, nil)).to eq false
expect(settings.type_supervisor.to_rb_value(:type_true, false)).to eq false
expect(settings.type_supervisor.to_rb_value(:type_true, 'f')).to eq false
expect(settings.type_supervisor.to_rb_value(:type_true, 'false')).to eq false
end
it 'returns float value' do
expect(settings.type_supervisor.to_rb_value(:type_float, 1.2)).to eq 1.2
expect(settings.type_supervisor.to_rb_value(:type_float, 1)).to eq 1.0
expect(settings.type_supervisor.to_rb_value(:type_float, '2.2')).to eq 2.2
expect(settings.type_supervisor.to_rb_value(:type_float, '2')).to eq 2
end
it 'returns string value' do
expect(settings.type_supervisor.to_rb_value(:type_string, 'a')).to eq 'a'
expect(settings.type_supervisor.to_rb_value(:type_string, 2)).to eq '2'
end
it 'returns value with string default' do
expect(settings.type_supervisor.to_rb_value(:type_enum_default_string, 2)).to eq '2'
expect(settings.type_supervisor.to_rb_value(:type_enum_default_string, '2')).to eq '2'
end
it 'returns value with a custom type' do
expect(settings.type_supervisor.to_rb_value(:type_custom, 2)).to eq 2
expect(settings.type_supervisor.to_rb_value(:type_custom, '2|3')).to eq '2|3'
end
end
end
describe '.type_hash' do
class TestEnumClass2
def self.valid_value?(v)
self.values.include?(v)
end
def self.values
['a', 'b']
end
def self.translate_names?
false
end
end
before do
settings.setting(:type_null, nil)
settings.setting(:type_int, 1)
settings.setting(:type_true, true)
settings.setting(:type_float, 2.3232)
settings.setting(:type_string, 'string')
settings.setting(:type_url_list, 'string', type: 'url_list')
settings.setting(:type_enum_choices, '2', type: 'enum', choices: ['1', '2'])
settings.setting(:type_enum_class, 'a', enum: 'TestEnumClass2')
settings.setting(:type_list, 'a', type: 'list', choices: ['a', 'b'])
settings.refresh!
end
it 'returns null type' do
expect(settings.type_supervisor.type_hash(:type_null)[:type]).to eq 'null'
end
it 'returns int type' do
expect(settings.type_supervisor.type_hash(:type_int)[:type]).to eq 'integer'
end
it 'returns bool type' do
expect(settings.type_supervisor.type_hash(:type_true)[:type]).to eq 'bool'
end
it 'returns float type' do
expect(settings.type_supervisor.type_hash(:type_float)[:type]).to eq 'float'
end
it 'returns string type' do
expect(settings.type_supervisor.type_hash(:type_string)[:type]).to eq 'string'
end
it 'returns url_list type' do
expect(settings.type_supervisor.type_hash(:type_url_list)[:type]).to eq 'url_list'
end
it 'returns enum type' do
expect(settings.type_supervisor.type_hash(:type_enum_choices)[:type]).to eq 'enum'
end
it 'returns list choices' do
expect(settings.type_supervisor.type_hash(:type_list)[:choices]).to eq ['a', 'b']
end
it 'returns enum choices' do
hash = settings.type_supervisor.type_hash(:type_enum_choices)
expect(hash[:valid_values]).to eq [{ name: '1', value: '1' }, { name: '2', value: '2' }]
expect(hash[:translate_names]).to eq false
end
it 'returns enum class' do
hash = settings.type_supervisor.type_hash(:type_enum_class)
expect(hash[:valid_values]).to eq ['a', 'b']
expect(hash[:translate_names]).to eq false
end
end
end

View File

@ -8,26 +8,18 @@ describe SiteSettings::YamlLoader do
def load_yaml(file_arg)
SiteSettings::YamlLoader.new(file_arg).load do |category, name, default, opts|
if opts.delete(:client)
client_setting(category, name, default, opts)
else
setting(category, name, default, opts)
end
setting(category, name, default, opts)
end
end
def setting(category, name, default = nil, opts = {})
@settings ||= []
@client_settings ||= []
@settings << name
@categories ||= []
@categories << category
@categories.uniq!
end
def client_setting(category, name, default = nil)
@client_settings ||= []
@client_settings << name
setting(category, name, default)
@client_settings << name if opts.has_key?(:client)
end
end
@ -36,7 +28,9 @@ describe SiteSettings::YamlLoader do
let(:client) { "#{Rails.root}/spec/fixtures/site_settings/client.yml" }
let(:enum) { "#{Rails.root}/spec/fixtures/site_settings/enum.yml" }
let(:enum_client) { "#{Rails.root}/spec/fixtures/site_settings/enum_client.yml" }
let(:env) { "#{Rails.root}/spec/fixtures/site_settings/env.yml" }
let(:deprecated_env) { "#{Rails.root}/spec/fixtures/site_settings/deprecated_env.yml" }
let(:deprecated_hidden) { "#{Rails.root}/spec/fixtures/site_settings/deprecated_hidden.yml" }
let(:locale_default) { "#{Rails.root}/spec/fixtures/site_settings/locale_default.yml" }
it "loads simple settings" do
receiver.expects(:setting).with('category1', 'title', 'My Site', {}).once
@ -57,9 +51,9 @@ describe SiteSettings::YamlLoader do
end
it "can load client settings" do
receiver.expects(:client_setting).with('category1', 'title', 'Discourse', {})
receiver.expects(:client_setting).with('category2', 'tos_url', '', {})
receiver.expects(:client_setting).with('category2', 'must_approve_users', false, {})
receiver.expects(:setting).with('category1', 'title', 'Discourse', client: true)
receiver.expects(:setting).with('category2', 'tos_url', '', client: true)
receiver.expects(:setting).with('category2', 'must_approve_users', false, client: true)
receiver.load_yaml(client)
end
@ -69,15 +63,22 @@ describe SiteSettings::YamlLoader do
end
it "can load enum client settings" do
receiver.expects(:client_setting).with do |category, name, default, opts|
category == ('basics') && name == ('default_locale') && default == ('en') && opts[:enum] == ('LocaleSiteSetting')
receiver.expects(:setting).with do |category, name, default, opts|
category == ('basics') && name == ('default_locale') && default == ('en') && opts[:enum] == ('LocaleSiteSetting') && opts[:client] == true
end
receiver.load_yaml(enum_client)
end
it "can load settings based on environment" do
receiver.expects(:setting).with('misc', 'port', '', {})
receiver.expects(:client_setting).with('misc', 'crawl_images', false, {})
receiver.load_yaml(env)
it "raises deprecation when load settings based on environment" do
expect { receiver.load_yaml(deprecated_env) }.to raise_error(Discourse::Deprecation)
end
it "raises deprecation when hidden property is based on environment" do
expect { receiver.load_yaml(deprecated_hidden) }.to raise_error(Discourse::Deprecation)
end
it "can load settings with locale default" do
receiver.expects(:setting).with('search', 'min_search_term_length', 3, min: 2, client: true, locale_default: { zh_CN: 2, zh_TW: 2 })
receiver.load_yaml(locale_default)
end
end

View File

@ -27,6 +27,7 @@ describe Admin::SiteSettingsController do
before do
SiteSetting.setting(:test_setting, "default")
SiteSetting.refresh!
end
it 'sets the value when the param is present' do
@ -49,6 +50,7 @@ describe Admin::SiteSettingsController do
it 'does not allow changing of hidden settings' do
SiteSetting.setting(:hidden_setting, "hidden", hidden: true)
SiteSetting.refresh!
result = xhr :put, :update, id: 'hidden_setting', hidden_setting: 'not allowed'
expect(SiteSetting.hidden_setting).to eq("hidden")
expect(result.status).to eq(422)

View File

@ -0,0 +1,6 @@
developer:
force_hostname:
hidden:
development: false
default: true
default: ''

View File

@ -0,0 +1,8 @@
search:
min_search_term_length:
client: true
default: 3
locale_default:
zh_CN: 2
zh_TW: 2
min: 2

View File

@ -80,8 +80,8 @@ Spork.prefork do
# and pretend they are default.
# There are a bunch of settings that are seeded, they must be loaded as defaults
SiteSetting.current.each do |k, v|
# skip setting defauls for settings that are in unloaded plugins
SiteSetting.defaults[k] = v if SiteSetting.respond_to? k
# skip setting defaults for settings that are in unloaded plugins
SiteSetting.defaults.set_regardless_of_locale(k, v) if SiteSetting.respond_to? k
end
require_dependency 'site_settings/local_process_provider'
@ -119,6 +119,7 @@ Spork.prefork do
SiteSetting.provider.all.each do |setting|
SiteSetting.remove_override!(setting.name)
end
SiteSetting.defaults.site_locale = SiteSettings::DefaultsProvider::DEFAULT_LOCALE
# very expensive IO operations
SiteSetting.automatically_download_gravatars = false