diff --git a/app/assets/javascripts/admin/components/site-setting.js.es6 b/app/assets/javascripts/admin/components/site-setting.js.es6 index 73e8160a28c..d796e274921 100644 --- a/app/assets/javascripts/admin/components/site-setting.js.es6 +++ b/app/assets/javascripts/admin/components/site-setting.js.es6 @@ -64,16 +64,16 @@ export default Ember.Component.extend(BufferedContent, { }.on("willDestroyElement"), _save() { - const setting = this.get('buffered'), - action = SiteSetting.update(setting.get('setting'), setting.get('value')); - action.then(() => { - this.set('validationMessage', null); - this.commitBuffer(); - }).catch((e) => { + 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) { if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) { - this.set('validationMessage', e.jqXHR.responseJSON.errors[0]); + self.set('validationMessage', e.jqXHR.responseJSON.errors[0]); } else { - this.set('validationMessage', I18n.t('generic_error')); + self.set('validationMessage', I18n.t('generic_error')); } }); }, diff --git a/app/assets/javascripts/admin/models/site-setting.js.es6 b/app/assets/javascripts/admin/models/site-setting.js.es6 index 6ed78787968..51037626634 100644 --- a/app/assets/javascripts/admin/models/site-setting.js.es6 +++ b/app/assets/javascripts/admin/models/site-setting.js.es6 @@ -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 }); } }); diff --git a/app/controllers/admin/site_settings_controller.rb b/app/controllers/admin/site_settings_controller.rb index ed64aa2d4bb..87478597a17 100644 --- a/app/controllers/admin/site_settings_controller.rb +++ b/app/controllers/admin/site_settings_controller.rb @@ -1,7 +1,4 @@ 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) @@ -12,17 +9,15 @@ class Admin::SiteSettingsController < Admin::AdminController id = params[:id] value = params[id] value.strip! if value.is_a?(String) - 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" + 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 end end diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 4159a1cfb5b..2e4fa838f34 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -14,7 +14,11 @@ class SiteSetting < ActiveRecord::Base def self.load_settings(file) SiteSettings::YamlLoader.new(file).load do |category, name, default, opts| - setting(name, default, opts.merge(category: category)) + if opts.delete(:client) + client_setting(name, default, opts.merge(category: category)) + else + setting(name, default, opts.merge(category: category)) + end end end @@ -27,11 +31,6 @@ 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 diff --git a/config/environments/development.rb b/config/environments/development.rb index dc0a0cda2e7..d9c10db2e8a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -59,9 +59,6 @@ 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 diff --git a/config/environments/test.rb b/config/environments/test.rb index c2129f70953..5cd785ce22c 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -46,22 +46,4 @@ 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 diff --git a/config/site_settings.yml b/config/site_settings.yml index 3f85d1e1aa1..b9092e186cf 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -12,9 +12,6 @@ # 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. @@ -71,6 +68,11 @@ required: default: '' basic: + default_locale: + default: 'en' + enum: 'LocaleSiteSetting' + refresh: true + shadowed_by_global: true allow_user_locale: client: true default: false @@ -430,24 +432,21 @@ posting: min_post_length: client: true min: 1 - default: 20 - locale_default: - zh_CN: 8 - zh_TW: 8 + default: + test: 5 + default: 20 min_first_post_length: client: true min: 1 - default: 20 - locale_default: - zh_CN: 8 - zh_TW: 8 + default: + test: 5 + default: 20 min_private_message_post_length: client: true min: 1 - default: 10 - locale_default: - zh_CN: 3 - zh_TW: 3 + default: + test: 5 + default: 10 max_post_length: client: true default: 32000 @@ -455,32 +454,17 @@ posting: topic_featured_link_enabled: client: true default: true - body_min_entropy: - default: 7 - locale_default: - zh_CN: 3 - zh_TW: 3 + body_min_entropy: 7 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: - default: 10 - locale_default: - zh_CN: 3 - zh_TW: 3 + title_min_entropy: 10 allow_uppercase_posts: false - title_prettify: - default: true - locale_default: - zh_CN: false - zh_TW: false + title_prettify: true title_fancy_entities: true min_private_message_title_length: client: true @@ -494,15 +478,9 @@ 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 @@ -546,11 +524,7 @@ posting: newuser_max_attachments: client: true default: 0 - post_excerpt_maxlength: - default: 300 - locale_default: - zh_CN: 120 - zh_TW: 120 + post_excerpt_maxlength: 300 show_pinned_excerpt_mobile: client: true default: true @@ -629,11 +603,7 @@ email: client: true private_email_time_window_seconds: 20 email_posts_context: 5 - digest_min_excerpt_length: - default: 100 - locale_default: - zh_CN: 50 - zh_TW: 50 + digest_min_excerpt_length: 100 digest_topics: default: 5 min: 1 @@ -756,7 +726,9 @@ files: refresh: true type: list crawl_images: - default: true + default: + test: false + default: true max_image_width: client: true default: 690 @@ -764,7 +736,9 @@ files: client: true default: 500 download_remote_images_to_local: - default: true + default: + test: false + default: true download_remote_images_threshold: 10 download_remote_images_max_days_old: default: 30 @@ -934,11 +908,7 @@ security: onebox: enable_flash_video_onebox: false - post_onebox_maxlength: - default: 500 - locale_default: - zh_CN: 200 - zh_TW: 200 + post_onebox_maxlength: 500 onebox_domains_blacklist: default: '' type: list @@ -984,7 +954,10 @@ spam: auto_block_first_post_regex: "" rate_limits: - unique_posts_mins: 5 + unique_posts_mins: + default: + test: 0 + default: 5 rate_limit_create_topic: 15 rate_limit_create_post: 5 rate_limit_new_user_create_topic: 120 @@ -1022,20 +995,28 @@ rate_limits: developer: force_hostname: - hidden: true + hidden: + development: false + default: true default: '' port: - hidden: true - default: '' + hidden: + development: false + default: true + default: + development: 3000 + default: '' queue_jobs: - hidden: true - default: true + hidden: + development: false + default: true + default: + test: false + default: true enable_long_polling: client: true default: true - long_polling_interval: - default: 25000 - max: 25000 + long_polling_interval: 25000 long_polling_base_url: client: true default: '/' @@ -1163,19 +1144,12 @@ 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: - default: 100000 - max: 100000 + search_recent_posts_size: 100000 log_search_queries: true - search_query_log_max_size: - default: 1000000 - max: 1000000 + search_query_log_max_size: 1000000 uncategorized: version_checks: @@ -1192,9 +1166,6 @@ uncategorized: slug_generation_method: default: 'ascii' enum: 'SlugSetting' - locale_default: - zh_CN: 'none' - zh_TW: 'none' permalink_normalizations: default: '' @@ -1335,9 +1306,6 @@ uncategorized: read_time_word_count: default: 500 client: true - locale_default: - zh_CN: 350 - zh_TW: 350 topic_page_title_includes_category: true @@ -1355,7 +1323,7 @@ user_preferences: default_email_mailing_list_mode: false default_email_mailing_list_mode_frequency: enum: 'MailingListModeSiteSetting' - default: 1 + default: 0 disable_mailing_list_mode: default: false client: true diff --git a/db/migrate/20170725075535_correct_default_email_mailing_list_mode_frequency.rb b/db/migrate/20170725075535_correct_default_email_mailing_list_mode_frequency.rb deleted file mode 100644 index d2e4cdd0688..00000000000 --- a/db/migrate/20170725075535_correct_default_email_mailing_list_mode_frequency.rb +++ /dev/null @@ -1,8 +0,0 @@ -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 diff --git a/lib/discourse.rb b/lib/discourse.rb index cd6a27e4401..dd9d9d3fef9 100644 --- a/lib/discourse.rb +++ b/lib/discourse.rb @@ -84,8 +84,6 @@ 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 diff --git a/lib/site_setting_extension.rb b/lib/site_setting_extension.rb index 410f22bc17c..5b9fb8987d6 100644 --- a/lib/site_setting_extension.rb +++ b/lib/site_setting_extension.rb @@ -1,16 +1,14 @@ -require_dependency 'site_settings/deprecated_settings' -require_dependency 'site_settings/type_supervisor' -require_dependency 'site_settings/defaults_provider' +require_dependency 'enum' require_dependency 'site_settings/db_provider' +require 'site_setting_validations' module SiteSettingExtension - include SiteSettings::DeprecatedSettings - extend Forwardable + include SiteSettingValidations - 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 + # For plugins, so they can tell if a feature is supported + def supported_types + [:email, :username, :list, :enum] + end # part 1 of refactor, centralizing the dependency here def provider=(val) @@ -22,6 +20,22 @@ 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 @@ -32,17 +46,25 @@ module SiteSettingExtension end def defaults - @defaults ||= SiteSettings::DefaultsProvider.new(self) - end - - def type_supervisor - @type_supervisor ||= SiteSettings::TypeSupervisor.new(defaults) + @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 @@ -63,14 +85,34 @@ module SiteSettingExtension @previews ||= {} end + def validators + @validators ||= {} + end + def setting(name_arg, default = nil, opts = {}) name = name_arg.to_sym mutex.synchronize do - defaults.load_setting(name, - default, - opts.extract!(*SiteSettings::DefaultsProvider::CONSUMED_OPTS)) - + self.defaults[name] = default 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 @@ -82,6 +124,7 @@ module SiteSettingExtension unless val.nil? || (val == ''.freeze) hidden_settings << name shadowed_settings << name + current_value = val end end @@ -89,24 +132,31 @@ module SiteSettingExtension refresh_settings << name end - if opts[:client] - client_settings << name.to_sym - end - if opts[:preview] previews[name] = opts[:preview] end - type_supervisor.load_setting(name, - opts.extract!(*SiteSettings::TypeSupervisor::CONSUMED_OPTS)) + opts[:validator] = opts[:validator].try(:constantize) + type = opts[:type] || get_data_type(name, defaults[name]) + 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_key do |s| + @defaults.each do |s, _| result[s] = send(s).to_s end result @@ -124,21 +174,32 @@ module SiteSettingExtension # Retrieve all settings def all_settings(include_hidden = false) - defaults - .reject { |s, _| !include_hidden && hidden_settings.include?(s) } + @defaults + .reject { |s, _| hidden_settings.include?(s) && !include_hidden } .map do |s, v| - 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)) + 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] + } - opts - end.unshift(defaults.locale_setting_hash) + 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 end def description(setting) @@ -156,22 +217,22 @@ module SiteSettingExtension def refresh! mutex.synchronize do ensure_listen_for_changes + old = current - new_hash = Hash[*(defaults.db_all.map { |s| - [s.name.to_sym, type_supervisor.to_rb_value(s.name, s.value, s.data_type)] + new_hash = Hash[*(provider.all.map { |s| + [s.name.intern, convert(s.value, s.data_type, s.name)] }.to_a.flatten)] - 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 defaults, cause they are cached + new_hash = defaults.merge(new_hash) # add shadowed shadowed_settings.each { |ss| new_hash[ss] = GlobalSetting.send(ss) } - changes, deletions = diff_hash(new_hash, current) + changes, deletions = diff_hash(new_hash, old) + changes.each { |name, val| current[name] = val } - deletions.each { |name, _| current[name] = defaults_view[name] } + deletions.each { |name, val| current[name] = defaults[name] } clear_cache! end @@ -221,9 +282,44 @@ module SiteSettingExtension end def add_override!(name, val) - val, type = type_supervisor.to_db_value(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 + provider.save(name, val, type) - current[name] = type_supervisor.to_rb_value(name, val) + current[name] = convert(val, type, name) notify_clients!(name) if client_settings.include? name clear_cache! end @@ -236,10 +332,26 @@ 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 = [] @@ -250,7 +362,7 @@ module SiteSettingExtension end def set(name, value) - if has_setting?(name) + if has_setting?(name) && is_valid_data?(name, value) value = filter_value(name, value) self.send("#{name}=", value) Discourse.request_refresh! if requires_refresh?(name) @@ -287,11 +399,90 @@ 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 - if (c = @containers[provider.current_site]) + c = @containers[provider.current_site] + if c c[name] else refresh! @@ -308,6 +499,10 @@ 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? diff --git a/lib/site_settings/validations.rb b/lib/site_setting_validations.rb similarity index 97% rename from lib/site_settings/validations.rb rename to lib/site_setting_validations.rb index 0643ab49b6a..7858c68f0e8 100644 --- a/lib/site_settings/validations.rb +++ b/lib/site_setting_validations.rb @@ -1,6 +1,6 @@ -module SiteSettings; end -module SiteSettings::Validations +module SiteSettingValidations + def validate_error(key) raise Discourse::InvalidParameters.new(I18n.t("errors.site_settings.#{key}")) end diff --git a/lib/site_settings/defaults_provider.rb b/lib/site_settings/defaults_provider.rb deleted file mode 100644 index 4b7b432912b..00000000000 --- a/lib/site_settings/defaults_provider.rb +++ /dev/null @@ -1,126 +0,0 @@ -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 diff --git a/lib/site_settings/deprecated_settings.rb b/lib/site_settings/deprecated_settings.rb deleted file mode 100644 index ca63777b4c3..00000000000 --- a/lib/site_settings/deprecated_settings.rb +++ /dev/null @@ -1,26 +0,0 @@ -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 diff --git a/lib/site_settings/local_process_provider.rb b/lib/site_settings/local_process_provider.rb index e3974b0ea4d..a9451505459 100644 --- a/lib/site_settings/local_process_provider.rb +++ b/lib/site_settings/local_process_provider.rb @@ -10,7 +10,7 @@ class SiteSettings::LocalProcessProvider @settings[current_site] ||= {} end - def initialize + def initialize() @settings = {} self.current_site = "test" end diff --git a/lib/site_settings/type_supervisor.rb b/lib/site_settings/type_supervisor.rb deleted file mode 100644 index 0cd9f272bc9..00000000000 --- a/lib/site_settings/type_supervisor.rb +++ /dev/null @@ -1,210 +0,0 @@ -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 diff --git a/lib/site_settings/yaml_loader.rb b/lib/site_settings/yaml_loader.rb index 787be9848e2..5142f5aab1a 100644 --- a/lib/site_settings/yaml_loader.rb +++ b/lib/site_settings/yaml_loader.rb @@ -1,26 +1,32 @@ 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 = hash.delete('default') - if value.is_a?(Hash) - raise Discourse::Deprecation, "Site setting per env is no longer supported. Error setting: #{setting_name}" + value = env_val(hash.delete('default')) + + if hash.key?('hidden') + hash['hidden'] = env_val(hash.delete('hidden')) end - 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! + yield category, setting_name, value, hash.symbolize_keys! else # Simplest case. site_setting_name: 'default value' yield category, setting_name, hash, {} diff --git a/plugins/discourse-narrative-bot/config/settings.yml b/plugins/discourse-narrative-bot/config/settings.yml index c33d382ced0..e36e06f8a4d 100644 --- a/plugins/discourse-narrative-bot/config/settings.yml +++ b/plugins/discourse-narrative-bot/config/settings.yml @@ -1,6 +1,8 @@ plugins: discourse_narrative_bot_enabled: - default: true + default: + default: true + test: false client: true disable_discourse_narrative_bot_welcome_post: default: false diff --git a/script/benchmarks/site_setting/bench.rb b/script/benchmarks/site_setting/bench.rb deleted file mode 100644 index 1ac30060130..00000000000 --- a/script/benchmarks/site_setting/bench.rb +++ /dev/null @@ -1,48 +0,0 @@ -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 diff --git a/script/benchmarks/site_setting/profile.rb b/script/benchmarks/site_setting/profile.rb deleted file mode 100644 index 7b7237055d9..00000000000 --- a/script/benchmarks/site_setting/profile.rb +++ /dev/null @@ -1,32 +0,0 @@ -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 diff --git a/spec/components/site_setting_extension_spec.rb b/spec/components/site_setting_extension_spec.rb index 5d87e8f9838..851ceec026e 100644 --- a/spec/components/site_setting_extension_spec.rb +++ b/spec/components/site_setting_extension_spec.rb @@ -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.setting("test_setting", nil, client: true) + settings.client_setting("test_setting") messages = MessageBus.track_publish do settings.test_setting = 88 @@ -155,11 +155,8 @@ describe SiteSettingExtension do end describe "remove_override" do - before do - settings.setting(:test_override, "test") - settings.refresh! - end it "correctly nukes overrides" do + settings.setting(:test_override, "test") settings.test_override = "bla" settings.remove_override!(:test_override) expect(settings.test_override).to eq("test") @@ -266,7 +263,6 @@ 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 @@ -276,7 +272,7 @@ describe SiteSettingExtension do class TestEnumClass def self.valid_value?(v) - self.values.include?(v) + true end def self.values ['en'] @@ -303,10 +299,6 @@ 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) @@ -392,14 +384,6 @@ 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") @@ -519,62 +503,4 @@ 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 diff --git a/spec/components/site_settings/defaults_provider_spec.rb b/spec/components/site_settings/defaults_provider_spec.rb deleted file mode 100644 index 01239fbff9b..00000000000 --- a/spec/components/site_settings/defaults_provider_spec.rb +++ /dev/null @@ -1,268 +0,0 @@ -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 diff --git a/spec/components/site_settings/type_supervisor_spec.rb b/spec/components/site_settings/type_supervisor_spec.rb deleted file mode 100644 index 3e4fbae1cd9..00000000000 --- a/spec/components/site_settings/type_supervisor_spec.rb +++ /dev/null @@ -1,341 +0,0 @@ -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 diff --git a/spec/components/site_settings/yaml_loader_spec.rb b/spec/components/site_settings/yaml_loader_spec.rb index 52e1952999f..7354cb04fd4 100644 --- a/spec/components/site_settings/yaml_loader_spec.rb +++ b/spec/components/site_settings/yaml_loader_spec.rb @@ -8,18 +8,26 @@ describe SiteSettings::YamlLoader do def load_yaml(file_arg) SiteSettings::YamlLoader.new(file_arg).load do |category, name, default, opts| - setting(category, name, default, opts) + if opts.delete(:client) + client_setting(category, name, default, opts) + else + setting(category, name, default, opts) + end end end def setting(category, name, default = nil, opts = {}) @settings ||= [] - @client_settings ||= [] @settings << name @categories ||= [] @categories << category @categories.uniq! - @client_settings << name if opts.has_key?(:client) + end + + def client_setting(category, name, default = nil) + @client_settings ||= [] + @client_settings << name + setting(category, name, default) end end @@ -28,9 +36,7 @@ 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(: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" } + let(:env) { "#{Rails.root}/spec/fixtures/site_settings/env.yml" } it "loads simple settings" do receiver.expects(:setting).with('category1', 'title', 'My Site', {}).once @@ -51,9 +57,9 @@ describe SiteSettings::YamlLoader do end it "can load client settings" do - 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.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.load_yaml(client) end @@ -63,22 +69,15 @@ describe SiteSettings::YamlLoader do end it "can load enum client settings" do - receiver.expects(:setting).with do |category, name, default, opts| - category == ('basics') && name == ('default_locale') && default == ('en') && opts[:enum] == ('LocaleSiteSetting') && opts[:client] == true + receiver.expects(:client_setting).with do |category, name, default, opts| + category == ('basics') && name == ('default_locale') && default == ('en') && opts[:enum] == ('LocaleSiteSetting') end receiver.load_yaml(enum_client) end - 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) + 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) end end diff --git a/spec/controllers/admin/site_settings_controller_spec.rb b/spec/controllers/admin/site_settings_controller_spec.rb index 6193572987e..7a2de2d6d0f 100644 --- a/spec/controllers/admin/site_settings_controller_spec.rb +++ b/spec/controllers/admin/site_settings_controller_spec.rb @@ -27,7 +27,6 @@ describe Admin::SiteSettingsController do before do SiteSetting.setting(:test_setting, "default") - SiteSetting.refresh! end it 'sets the value when the param is present' do @@ -50,7 +49,6 @@ 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) diff --git a/spec/fixtures/site_settings/deprecated_hidden.yml b/spec/fixtures/site_settings/deprecated_hidden.yml deleted file mode 100644 index ba483cbb0db..00000000000 --- a/spec/fixtures/site_settings/deprecated_hidden.yml +++ /dev/null @@ -1,6 +0,0 @@ -developer: - force_hostname: - hidden: - development: false - default: true - default: '' diff --git a/spec/fixtures/site_settings/deprecated_env.yml b/spec/fixtures/site_settings/env.yml similarity index 100% rename from spec/fixtures/site_settings/deprecated_env.yml rename to spec/fixtures/site_settings/env.yml diff --git a/spec/fixtures/site_settings/locale_default.yml b/spec/fixtures/site_settings/locale_default.yml deleted file mode 100644 index 9da10c8f39d..00000000000 --- a/spec/fixtures/site_settings/locale_default.yml +++ /dev/null @@ -1,8 +0,0 @@ -search: - min_search_term_length: - client: true - default: 3 - locale_default: - zh_CN: 2 - zh_TW: 2 - min: 2 diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 0a983d995bd..986bde936c9 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -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 defaults for settings that are in unloaded plugins - SiteSetting.defaults.set_regardless_of_locale(k, v) if SiteSetting.respond_to? k + # skip setting defauls for settings that are in unloaded plugins + SiteSetting.defaults[k] = v if SiteSetting.respond_to? k end require_dependency 'site_settings/local_process_provider' @@ -119,7 +119,6 @@ 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