They are for now still available as separate entities but that is more due to existing references to them both. Under the hood, they now depend on the same structure `Settings::Definition` which just as well could have been named `Configuration::Definition`, that defines: * the name * the default value * the type (which might be deferred from the default value) * the array of allowed values Both Setting and Configuration can now be overwritten using the same mechanisms: * Default value * Database value * configuration.yml (settings.yml is removed) * ENV varspull/10296/head
parent
a33524ef6d
commit
0b5575aa64
@ -0,0 +1,318 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Settings |
||||
class Definition |
||||
ENV_PREFIX = 'OPENPROJECT_'.freeze |
||||
|
||||
attr_accessor :name, |
||||
:format |
||||
|
||||
attr_writer :value, |
||||
:allowed |
||||
|
||||
def initialize(name, |
||||
value:, |
||||
format: nil, |
||||
writable: true, |
||||
allowed: nil) |
||||
self.name = name.to_s |
||||
self.format = format ? format.to_sym : deduce_format(value) |
||||
self.value = value |
||||
self.writable = writable |
||||
self.allowed = allowed |
||||
end |
||||
|
||||
def value |
||||
return nil if @value.nil? |
||||
|
||||
case format |
||||
when :integer |
||||
@value.to_i |
||||
when :float |
||||
@value.to_f |
||||
when :boolean |
||||
@value.is_a?(Integer) ? ActiveRecord::Type::Boolean.new.cast(@value) : @value |
||||
when :symbol |
||||
@value.to_sym |
||||
else |
||||
if @value.respond_to?(:call) |
||||
@value.call |
||||
else |
||||
@value |
||||
end |
||||
end |
||||
end |
||||
|
||||
def serialized? |
||||
%i[array hash].include?(format) |
||||
end |
||||
|
||||
def writable? |
||||
if writable.respond_to?(:call) |
||||
writable.call |
||||
else |
||||
!!writable |
||||
end |
||||
end |
||||
|
||||
def override_value(other_value) |
||||
if format == :hash |
||||
value.deep_merge! other_value |
||||
else |
||||
self.value = other_value |
||||
end |
||||
|
||||
raise ArgumentError, "Value for #{name} must be one of #{allowed.join(', ')} but is #{value}" unless valid? |
||||
|
||||
self.writable = false |
||||
end |
||||
|
||||
def valid? |
||||
!allowed || |
||||
(format == :array && (value - allowed).empty?) || |
||||
allowed.include?(value) |
||||
end |
||||
|
||||
def allowed |
||||
if @allowed.respond_to?(:call) |
||||
@allowed.call |
||||
else |
||||
@allowed |
||||
end |
||||
end |
||||
|
||||
class << self |
||||
# Adds a setting definition to the set of configured definitions. A definition will define a name and a default value. |
||||
# However, that value can be overwritten by (lower tops higher): |
||||
# * a value stored in the database (`settings` table) |
||||
# * a value in the config/configuration.yml file |
||||
# * a value provided by an ENV var |
||||
# |
||||
# @param [Object] name The name of the definition |
||||
# @param [Object] value The default value the setting has if not overwritten. |
||||
# @param [nil] format The format the value is in e.g. symbol, array, hash, string. If a value is present, |
||||
# the format is deferred. |
||||
# @param [TrueClass] writable Whether the value can be set in the UI. In case the value is set via file or ENV var, |
||||
# this will be set to false later on and UI elements that refer to the definition will be disabled. |
||||
# @param [nil] allowed The array of allowed values that can be assigned to the definition. |
||||
# Will serve to be validated against. A lambda can be provided returning an array in case |
||||
# the array needs to be evaluated dynamically. In case of e.g. boolean format, setting |
||||
# an allowed array is not necessary. |
||||
def add(name, |
||||
value:, |
||||
format: nil, |
||||
writable: true, |
||||
allowed: nil) |
||||
return if @by_name.present? && @by_name[name.to_s].present? |
||||
|
||||
@by_name = nil |
||||
|
||||
all << new(name, |
||||
format: format, |
||||
value: value, |
||||
writable: writable, |
||||
allowed: allowed) |
||||
end |
||||
|
||||
def define(&block) |
||||
instance_exec(&block) |
||||
end |
||||
|
||||
def [](name) |
||||
by_name ||= all.group_by(&:name).transform_values(&:first) |
||||
|
||||
by_name[name.to_s] |
||||
end |
||||
|
||||
def exists?(name) |
||||
by_name.keys.include?(name.to_s) |
||||
end |
||||
|
||||
def all_of_prefix(prefix) |
||||
all.select { |definition| definition.name.start_with?(prefix) } |
||||
end |
||||
|
||||
def all |
||||
@all ||= [] |
||||
|
||||
unless loaded |
||||
self.loaded = true |
||||
load './config/constants/settings/definitions.rb' |
||||
|
||||
# The test setup should govern the configuration |
||||
load_config_from_file unless Rails.env.test? |
||||
override_config |
||||
end |
||||
|
||||
@all |
||||
end |
||||
|
||||
private |
||||
|
||||
# Currently only required for testing |
||||
def reset |
||||
@all = nil |
||||
@loaded = false |
||||
@by_name = nil |
||||
end |
||||
|
||||
def by_name |
||||
@by_name ||= all.group_by(&:name).transform_values(&:first) |
||||
end |
||||
|
||||
def load_config_from_file |
||||
filename = Rails.root.join('config/configuration.yml') |
||||
|
||||
if File.file?(filename) |
||||
file_config = YAML.load_file(filename) |
||||
|
||||
if file_config.is_a? Hash |
||||
load_env_from_config(file_config, Rails.env) |
||||
else |
||||
warn "#{filename} is not a valid OpenProject configuration file, ignoring." |
||||
end |
||||
end |
||||
end |
||||
|
||||
def load_env_from_config(config, env) |
||||
config['default']&.each do |name, value| |
||||
self[name]&.override_value(value) |
||||
end |
||||
config[env]&.each do |name, value| |
||||
self[name]&.override_value(value) |
||||
end |
||||
end |
||||
|
||||
# Replace config values for which an environment variable with the same key in upper case |
||||
# exists. |
||||
# Also merges the existing values that are hashes with values from ENV if they follow the naming |
||||
# schema. |
||||
def override_config(source = default_override_source) |
||||
override_config_values(source) |
||||
merge_hash_config(source) |
||||
end |
||||
|
||||
def override_config_values(source) |
||||
all |
||||
.map(&:name) |
||||
.select { |key| source.include? key.upcase } |
||||
.each { |key| self[key].override_value(extract_value(key, source[key.upcase])) } |
||||
end |
||||
|
||||
def merge_hash_config(source, prefix: ENV_PREFIX) |
||||
source.select { |k, _| k =~ /^#{prefix}/i }.each do |k, raw_value| |
||||
name, value = path_to_hash(*path(prefix, k), |
||||
extract_value(k, raw_value)) |
||||
.first |
||||
|
||||
# There might be ENV vars that match the OPENPROJECT_ prefix but are no OP instance |
||||
# settings, e.g. OPENPROJECT_DISABLE_DEV_ASSET_PROXY |
||||
self[name]&.override_value(value) |
||||
end |
||||
end |
||||
|
||||
def path(prefix, env_var_name) |
||||
env_var_name |
||||
.sub(/^#{prefix}/, '') |
||||
.gsub(/([a-zA-Z0-9]|(__))+/) |
||||
.map do |seg| |
||||
unescape_underscores(seg.downcase) |
||||
end |
||||
end |
||||
|
||||
# takes the path provided and transforms it into a deeply nested hash |
||||
# where the last parameter becomes the value. |
||||
# |
||||
# e.g. path_to_hash(:a, :b, :c, :d) => { a: { b: { c: :d } } } |
||||
|
||||
def path_to_hash(*path) |
||||
value = path.pop |
||||
|
||||
path.reverse.inject(value) do |path_hash, key| |
||||
{ key => path_hash } |
||||
end |
||||
end |
||||
|
||||
def unescape_underscores(path_segment) |
||||
path_segment.gsub '__', '_' |
||||
end |
||||
|
||||
## |
||||
# Extract the configuration value from the given input |
||||
# using YAML. |
||||
# |
||||
# @param key [String] The key of the input within the source hash. |
||||
# @param original_value [String] The string from which to extract the actual value. |
||||
# @return A ruby object (e.g. Integer, Float, String, Hash, Boolean, etc.) |
||||
# @raise [ArgumentError] If the string could not be parsed. |
||||
def extract_value(key, original_value) |
||||
# YAML parses '' as false, but empty ENV variables will be passed as that. |
||||
# To specify specific values, one can use !!str (-> '') or !!null (-> nil) |
||||
return original_value if original_value == '' |
||||
|
||||
parsed = YAML.safe_load(original_value) |
||||
|
||||
if parsed.is_a?(String) |
||||
original_value |
||||
else |
||||
parsed |
||||
end |
||||
rescue StandardError => e |
||||
raise ArgumentError, "Configuration value for '#{key}' is invalid: #{e.message}" |
||||
end |
||||
|
||||
## |
||||
# The default source for overriding configuration values |
||||
# is ENV, but may be changed for testing purposes |
||||
def default_override_source |
||||
ENV |
||||
end |
||||
|
||||
attr_accessor :loaded |
||||
end |
||||
|
||||
private |
||||
|
||||
attr_accessor :serialized, |
||||
:writable |
||||
|
||||
def deduce_format(value) |
||||
case value |
||||
when TrueClass, FalseClass |
||||
:boolean |
||||
when Integer, Date, DateTime, String, Hash, Array, Float, Symbol |
||||
value.class.name.underscore.to_sym |
||||
when ActiveSupport::Duration |
||||
:duration |
||||
else |
||||
raise ArgumentError, "Cannot deduce the format for the setting definition #{name}" |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,935 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# The list of all defined settings that however might get extended e.g. in modules/plugins |
||||
# Contains what was formerly defined in both the settings.yml as well as in configuration.rb. |
||||
# Values from the later can typically be identified by having |
||||
# writable: false |
||||
# set. That would not be strictly necessary since they currently don't have a UI anyway and |
||||
# that is where the value is actually used. |
||||
|
||||
Settings::Definition.define do |
||||
add :activity_days_default, |
||||
value: 30 |
||||
|
||||
add :additional_footer_content, |
||||
format: :string, |
||||
value: nil |
||||
|
||||
add :after_first_login_redirect_url, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :after_login_default_redirect_url, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :apiv3_cors_enabled, |
||||
value: false |
||||
|
||||
add :apiv3_cors_origins, |
||||
value: [] |
||||
|
||||
add :apiv3_docs_enabled, |
||||
value: true |
||||
|
||||
add :apiv3_enable_basic_auth, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :apiv3_max_page_size, |
||||
value: 1000 |
||||
|
||||
add :app_title, |
||||
value: 'OpenProject' |
||||
|
||||
add :attachment_max_size, |
||||
value: 5120 |
||||
|
||||
# Existing setting |
||||
add :attachment_whitelist, |
||||
value: [] |
||||
|
||||
## |
||||
# Carrierwave storage type. Possible values are, among others, :file and :fog. |
||||
# The latter requires further configuration. |
||||
add :attachments_storage, |
||||
value: :file, |
||||
format: :symbol, |
||||
allowed: %i[file fog], |
||||
writable: false |
||||
|
||||
add :attachments_storage_path, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :attachments_grace_period, |
||||
value: 180, |
||||
writable: false |
||||
|
||||
add :auth_source_sso, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :autofetch_changesets, |
||||
value: true |
||||
|
||||
# autologin duration in days |
||||
# 0 means autologin is disabled |
||||
add :autologin, |
||||
value: 0 |
||||
|
||||
add :autologin_cookie_name, |
||||
value: 'autologin', |
||||
writable: false |
||||
|
||||
add :autologin_cookie_path, |
||||
value: '/', |
||||
writable: false |
||||
|
||||
add :autologin_cookie_secure, |
||||
value: false, |
||||
writable: false |
||||
|
||||
add :available_languages, |
||||
format: :hash, |
||||
value: %w[en de fr es pt pt-BR it zh-CN ko ru].freeze, |
||||
allowed: -> { Redmine::I18n.all_languages } |
||||
|
||||
add :avatar_link_expiry_seconds, |
||||
value: 24.hours.to_i, |
||||
writable: false |
||||
|
||||
# Allow users with the required permissions to create backups via the web interface or API. |
||||
add :backup_enabled, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :backup_daily_limit, |
||||
value: 3, |
||||
writable: false |
||||
|
||||
add :backup_initial_waiting_period, |
||||
value: 24.hours, |
||||
format: :integer, |
||||
writable: false |
||||
|
||||
add :backup_include_attachments, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :backup_attachment_size_max_sum_mb, |
||||
value: 1024, |
||||
writable: false |
||||
|
||||
add :blacklisted_routes, |
||||
value: [], |
||||
writable: false |
||||
|
||||
add :bcc_recipients, |
||||
value: true |
||||
|
||||
add :boards_demo_data_available, |
||||
value: false |
||||
|
||||
add :brute_force_block_minutes, |
||||
value: 30 |
||||
|
||||
add :brute_force_block_after_failed_logins, |
||||
value: 20 |
||||
|
||||
add :cache_expires_in_seconds, |
||||
format: :integer, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :cache_formatted_text, |
||||
value: true |
||||
|
||||
# use dalli defaults for memcache |
||||
add :cache_memcache_server, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :cache_namespace, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :commit_fix_done_ratio, |
||||
value: 100 |
||||
|
||||
add :commit_fix_keywords, |
||||
value: 'fixes,closes' |
||||
|
||||
add :commit_fix_status_id, |
||||
format: :integer, |
||||
value: nil, |
||||
allowed: -> { Status.pluck(:id) + [nil] } |
||||
|
||||
# encoding used to convert commit logs to UTF-8 |
||||
add :commit_logs_encoding, |
||||
value: 'UTF-8' |
||||
|
||||
add :commit_logtime_activity_id, |
||||
format: :integer, |
||||
value: nil, |
||||
allowed: -> { TimeEntryActivity.pluck(:id) + [nil] } |
||||
|
||||
add :commit_logtime_enabled, |
||||
value: false |
||||
|
||||
add :commit_ref_keywords, |
||||
value: 'refs,references,IssueID' |
||||
|
||||
add :consent_decline_mail, |
||||
format: :string, |
||||
value: nil |
||||
|
||||
# Time after which users have to have consented to what ever they need to consent |
||||
# to (depending on other settings) such as a privacy policy. |
||||
add :consent_time, |
||||
value: nil, |
||||
format: :datetime |
||||
|
||||
# Additional info about what the user is consenting to (optional). |
||||
add :consent_info, |
||||
value: { |
||||
en: "## Consent\n\nYou need to agree to the [privacy and security policy]" + |
||||
"(https://www.openproject.org/data-privacy-and-security/) of this OpenProject instance." |
||||
} |
||||
|
||||
# Indicates whether or not users need to consent to something such as privacy policy. |
||||
add :consent_required, |
||||
value: false |
||||
|
||||
add :cross_project_work_package_relations, |
||||
value: true |
||||
|
||||
# Allow in-context translations to be loaded with CSP |
||||
add :crowdin_in_context_translations, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :database_cipher_key, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :date_format, |
||||
format: :string, |
||||
value: nil, |
||||
allowed: [ |
||||
'%Y-%m-%d', |
||||
'%d/%m/%Y', |
||||
'%d.%m.%Y', |
||||
'%d-%m-%Y', |
||||
'%m/%d/%Y', |
||||
'%d %b %Y', |
||||
'%d %B %Y', |
||||
'%b %d, %Y', |
||||
'%B %d, %Y' |
||||
].freeze |
||||
|
||||
add :default_auto_hide_popups, |
||||
value: true |
||||
|
||||
# user configuration |
||||
add :default_comment_sort_order, |
||||
value: 'asc', |
||||
writable: false |
||||
|
||||
add :default_language, |
||||
value: 'en' |
||||
|
||||
add :default_projects_modules, |
||||
value: %w[calendar board_view work_package_tracking news costs wiki], |
||||
allowed: -> { OpenProject::AccessControl.available_project_modules.map(&:to_s) } |
||||
|
||||
add :default_projects_public, |
||||
value: false |
||||
|
||||
add :demo_projects_available, |
||||
value: false |
||||
|
||||
add :diff_max_lines_displayed, |
||||
value: 1500 |
||||
|
||||
# only applicable in conjunction with fog (effectively S3) attachments |
||||
# which will be uploaded directly to the cloud storage rather than via OpenProject's |
||||
# server process. |
||||
add :direct_uploads, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :disable_browser_cache, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# allow to disable default modules |
||||
add :disabled_modules, |
||||
value: [], |
||||
writable: false |
||||
|
||||
add :disable_password_choice, |
||||
value: false, |
||||
writable: false |
||||
|
||||
add :disable_password_login, |
||||
value: false, |
||||
writable: false |
||||
|
||||
add :display_subprojects_work_packages, |
||||
value: true |
||||
|
||||
# Destroy all sessions for current_user on logout |
||||
add :drop_old_sessions_on_logout, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# Destroy all sessions for current_user on login |
||||
add :drop_old_sessions_on_login, |
||||
value: false, |
||||
writable: false |
||||
|
||||
add :edition, |
||||
format: :string, |
||||
value: 'standard', |
||||
writable: false, |
||||
allowed: %w[standard bim] |
||||
|
||||
add :ee_manager_visible, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# Enable internal asset server |
||||
add :enable_internal_assets_server, |
||||
value: false, |
||||
writable: false |
||||
|
||||
# email configuration |
||||
add :email_delivery_configuration, |
||||
value: 'inapp', |
||||
allowed: %w[inapp legacy], |
||||
writable: false |
||||
|
||||
add :email_delivery_method, |
||||
format: :symbol, |
||||
value: nil |
||||
|
||||
add :emails_footer, |
||||
value: { |
||||
'en' => '' |
||||
} |
||||
|
||||
add :emails_header, |
||||
value: { |
||||
'en' => '' |
||||
} |
||||
|
||||
# use email address as login, hide login in registration form |
||||
add :email_login, |
||||
value: false |
||||
|
||||
add :enabled_projects_columns, |
||||
value: %w[project_status public created_at latest_activity_at required_disk_space], |
||||
allowed: -> { Projects::TableCell.new(nil, current_user: User.admin.first).all_columns.map(&:first).map(&:to_s) } |
||||
|
||||
add :enabled_scm, |
||||
value: %w[subversion git] |
||||
|
||||
# Allow connections for trial creation and booking |
||||
add :enterprise_trial_creation_host, |
||||
value: 'https://augur.openproject.com', |
||||
writable: false |
||||
|
||||
add :enterprise_chargebee_site, |
||||
value: 'openproject-enterprise', |
||||
writable: false |
||||
|
||||
add :enterprise_plan, |
||||
value: 'enterprise-on-premises---euro---1-year', |
||||
writable: false |
||||
|
||||
add :feeds_enabled, |
||||
value: true |
||||
|
||||
add :feeds_limit, |
||||
value: 15 |
||||
|
||||
# Maximum size of files that can be displayed |
||||
# inline through the file viewer (in KB) |
||||
add :file_max_size_displayed, |
||||
value: 512 |
||||
|
||||
add :first_week_of_year, |
||||
value: nil, |
||||
format: :integer, |
||||
allowed: [1, 4] |
||||
|
||||
# Configure fog, e.g. when using an S3 uploader |
||||
add :fog, |
||||
value: {} |
||||
|
||||
add :fog_download_url_expires_in, |
||||
value: 21600, # 6h by default as 6 hours is max in S3 when using IAM roles |
||||
writable: false |
||||
|
||||
# Additional / overridden help links |
||||
add :force_help_link, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :force_formatting_help_link, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :forced_single_page_size, |
||||
value: 250 |
||||
|
||||
add :host_name, |
||||
value: "localhost:3000" |
||||
|
||||
# Health check configuration |
||||
add :health_checks_authentication_password, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
# Maximum number of backed up jobs (that are not yet executed) |
||||
# before health check fails |
||||
add :health_checks_jobs_queue_count_threshold, |
||||
format: :integer, |
||||
value: 50, |
||||
writable: false |
||||
|
||||
## Maximum number of minutes that jobs have not yet run after their designated 'run_at' time |
||||
add :health_checks_jobs_never_ran_minutes_ago, |
||||
format: :integer, |
||||
value: 5, |
||||
writable: false |
||||
|
||||
## Maximum number of unprocessed requests in puma's backlog. |
||||
add :health_checks_backlog_threshold, |
||||
format: :integer, |
||||
value: 20, |
||||
writable: false |
||||
|
||||
# Default gravatar image, set to something other than 404 |
||||
# to ensure a default is returned |
||||
add :gravatar_fallback_image, |
||||
value: '404', |
||||
writable: false |
||||
|
||||
add :hidden_menu_items, |
||||
value: {}, |
||||
writable: false |
||||
|
||||
# Impressum link to be set, nil by default (= hidden) |
||||
add :impressum_link, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :installation_type, |
||||
value: 'manual', |
||||
writable: false |
||||
|
||||
add :installation_uuid, |
||||
format: :string, |
||||
value: nil |
||||
|
||||
add :internal_password_confirmation, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :invitation_expiration_days, |
||||
value: 7 |
||||
|
||||
add :journal_aggregation_time_minutes, |
||||
value: 5 |
||||
|
||||
# Allow override of LDAP options |
||||
add :ldap_force_no_page, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :ldap_auth_source_tls_options, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
# Allow users to manually sync groups in a different way |
||||
# than the provided job using their own cron |
||||
add :ldap_groups_disable_sync_job, |
||||
value: false, |
||||
writable: false |
||||
|
||||
add :log_level, |
||||
value: 'info', |
||||
writable: false |
||||
|
||||
add :log_requesting_user, |
||||
value: false |
||||
|
||||
# Use lograge to format logs, off by default |
||||
add :lograge_formatter, |
||||
value: nil, |
||||
format: :string, |
||||
writable: false |
||||
|
||||
add :login_required, |
||||
value: false |
||||
|
||||
add :lost_password, |
||||
value: true |
||||
|
||||
add :mail_from, |
||||
value: 'openproject@example.net' |
||||
|
||||
add :mail_handler_api_key, |
||||
format: :string, |
||||
value: nil |
||||
|
||||
add :mail_handler_body_delimiters, |
||||
value: '' |
||||
|
||||
add :mail_handler_body_delimiter_regex, |
||||
value: '' |
||||
|
||||
add :mail_handler_ignore_filenames, |
||||
value: 'signature.asc' |
||||
|
||||
add :mail_suffix_separators, |
||||
value: '+' |
||||
|
||||
add :main_content_language, |
||||
value: 'english', |
||||
writable: false |
||||
|
||||
# Check for missing migrations in internal errors |
||||
add :migration_check_on_exceptions, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# Role given to a non-admin user who creates a project |
||||
add :new_project_user_role_id, |
||||
format: :integer, |
||||
value: nil, |
||||
allowed: -> { Role.pluck(:id) } |
||||
|
||||
add :oauth_allow_remapping_of_existing_users, |
||||
value: false |
||||
|
||||
add :omniauth_direct_login_provider, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :override_bcrypt_cost_factor, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :notification_retention_period_days, |
||||
value: 30 |
||||
|
||||
add :notification_email_delay_minutes, |
||||
value: 15 |
||||
|
||||
add :notification_email_digest_time, |
||||
value: '08:00' |
||||
|
||||
add :onboarding_video_url, |
||||
value: 'https://player.vimeo.com/video/163426858?autoplay=1', |
||||
writable: false |
||||
|
||||
add :onboarding_enabled, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :password_active_rules, |
||||
value: %w[lowercase uppercase numeric special], |
||||
allowed: %w[lowercase uppercase numeric special] |
||||
|
||||
add :password_count_former_banned, |
||||
value: 0 |
||||
|
||||
add :password_days_valid, |
||||
value: 0 |
||||
|
||||
add :password_min_length, |
||||
value: 10 |
||||
|
||||
add :password_min_adhered_rules, |
||||
value: 0 |
||||
|
||||
# TODO: turn into array of ints |
||||
# Requires a migration to be written |
||||
# replace Setting#per_page_options_array |
||||
add :per_page_options, |
||||
value: '20, 100' |
||||
|
||||
add :plain_text_mail, |
||||
value: false |
||||
|
||||
add :protocol, |
||||
value: "http", |
||||
allowed: %w[http https] |
||||
|
||||
add :project_gantt_query, |
||||
value: nil, |
||||
format: :string |
||||
|
||||
add :rails_asset_host, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :rails_cache_store, |
||||
format: :symbol, |
||||
value: :file_store, |
||||
writable: false, |
||||
allowed: %i[file_store memcache] |
||||
|
||||
# url-path prefix |
||||
add :rails_relative_url_root, |
||||
value: '', |
||||
writable: false |
||||
|
||||
add :rails_force_ssl, |
||||
value: false, |
||||
writable: false |
||||
|
||||
add :registration_footer, |
||||
value: { |
||||
'en' => '' |
||||
}, |
||||
writable: false |
||||
|
||||
add :repositories_automatic_managed_vendor, |
||||
value: nil, |
||||
format: :string, |
||||
allowed: -> { OpenProject::SCM::Manager.registered.keys.map(&:to_s) } |
||||
|
||||
# encodings used to convert repository files content to UTF-8 |
||||
# multiple values accepted, comma separated |
||||
add :repositories_encodings, |
||||
value: nil, |
||||
format: :string |
||||
|
||||
add :repository_authentication_caching_enabled, |
||||
value: true |
||||
|
||||
add :repository_checkout_data, |
||||
value: { |
||||
"git" => { "enabled" => 0 }, |
||||
"subversion" => { "enabled" => 0 } |
||||
} |
||||
|
||||
add :repository_log_display_limit, |
||||
value: 100 |
||||
|
||||
add :repository_storage_cache_minutes, |
||||
value: 720 |
||||
|
||||
add :repository_truncate_at, |
||||
value: 500 |
||||
|
||||
add :rest_api_enabled, |
||||
value: true |
||||
|
||||
add :scm, |
||||
format: :hash, |
||||
value: {}, |
||||
writable: false |
||||
|
||||
add :scm_git_command, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :scm_local_checkout_path, |
||||
value: 'repositories', # relative to OpenProject directory |
||||
writable: false |
||||
|
||||
add :scm_subversion_command, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
# Display update / security badge, enabled by default |
||||
add :security_badge_displayed, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :security_badge_url, |
||||
value: "https://releases.openproject.com/v1/check.svg", |
||||
writable: false |
||||
|
||||
add :self_registration, |
||||
value: 2 |
||||
|
||||
add :sendmail_arguments, |
||||
format: :string, |
||||
value: "-i", |
||||
writable: false |
||||
|
||||
add :sendmail_location, |
||||
format: :string, |
||||
value: "/usr/sbin/sendmail", |
||||
writable: false |
||||
|
||||
# Which breadcrumb loggers to enable |
||||
add :sentry_breadcrumb_loggers, |
||||
value: ['active_support_logger'], |
||||
writable: false |
||||
|
||||
# Log errors to sentry instance |
||||
add :sentry_dsn, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
# Allow separate error reporting for frontend errors |
||||
add :sentry_frontend_dsn, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
add :sentry_host, |
||||
format: :string, |
||||
value: nil, |
||||
writable: false |
||||
|
||||
# Allow sentry to collect tracing samples |
||||
# set to 1 to enable default tracing samples (see sentry initializer) |
||||
# set to n >= 1 to enable n times the default tracing |
||||
add :sentry_trace_factor, |
||||
value: 0, |
||||
writable: false |
||||
|
||||
# Allow sentry to collect tracing samples on frontend |
||||
# set to n >= 1 to enable n times the default tracing |
||||
add :sentry_frontend_trace_factor, |
||||
value: 0, |
||||
writable: false |
||||
|
||||
add :session_cookie_name, |
||||
value: '_open_project_session', |
||||
writable: false |
||||
|
||||
# where to store session data |
||||
add :session_store, |
||||
value: :active_record_store, |
||||
writable: false |
||||
|
||||
add :session_ttl_enabled, |
||||
value: false |
||||
|
||||
add :session_ttl, |
||||
value: 120 |
||||
|
||||
add :show_community_links, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# Show pending migrations as warning bar |
||||
add :show_pending_migrations_warning, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# Show mismatched protocol/hostname warning |
||||
# in settings where they must differ this can be disabled |
||||
add :show_setting_mismatch_warning, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# Render storage information |
||||
add :show_storage_information, |
||||
value: true, |
||||
writable: false |
||||
|
||||
# Render warning bars (pending migrations, deprecation, unsupported browsers) |
||||
# Set to false to globally disable this for all users! |
||||
add :show_warning_bars, |
||||
value: true, |
||||
writable: false |
||||
|
||||
add :smtp_enable_starttls_auto, |
||||
format: :boolean, |
||||
value: false |
||||
|
||||
add :smtp_openssl_verify_mode, |
||||
format: :string, |
||||
value: "none", |
||||
allowed: %w[none peer client_once fail_if_no_peer_cert], |
||||
writable: false |
||||
|
||||
add :smtp_ssl, |
||||
format: :boolean, |
||||
value: false |
||||
|
||||
add :smtp_address, |
||||
format: :string, |
||||
value: '' |
||||
|
||||
add :smtp_domain, |
||||
format: :string, |
||||
value: 'your.domain.com' |
||||
|
||||
add :smtp_user_name, |
||||
format: :string, |
||||
value: '' |
||||
|
||||
add :smtp_port, |
||||
format: :integer, |
||||
value: 587 |
||||
|
||||
add :smtp_password, |
||||
format: :string, |
||||
value: '' |
||||
|
||||
add :smtp_authentication, |
||||
format: :string, |
||||
value: 'plain', |
||||
writable: false |
||||
|
||||
add :software_name, |
||||
value: 'OpenProject' |
||||
|
||||
add :software_url, |
||||
value: 'https://www.openproject.org/' |
||||
|
||||
# Slow query logging threshold in ms |
||||
add :sql_slow_query_threshold, |
||||
value: 2000, |
||||
writable: false |
||||
|
||||
add :start_of_week, |
||||
value: nil, |
||||
format: :integer, |
||||
allowed: [1, 6, 7] |
||||
|
||||
# enable statsd metrics (currently puma only) by configuring host |
||||
add :statsd, |
||||
value: { |
||||
'host' => nil, |
||||
'port' => 8125 |
||||
}, |
||||
writable: false |
||||
|
||||
add :sys_api_enabled, |
||||
value: false |
||||
|
||||
add :sys_api_key, |
||||
value: nil, |
||||
format: :string |
||||
|
||||
add :time_format, |
||||
format: :string, |
||||
value: nil, |
||||
allowed: [ |
||||
'%H:%M', |
||||
'%I:%M %p' |
||||
].freeze |
||||
|
||||
add :user_default_timezone, |
||||
value: nil, |
||||
format: :string, |
||||
allowed: ActiveSupport::TimeZone.all + [nil] |
||||
|
||||
add :users_deletable_by_admins, |
||||
value: false |
||||
|
||||
add :users_deletable_by_self, |
||||
value: false |
||||
|
||||
add :user_format, |
||||
value: :firstname_lastname, |
||||
allowed: -> { User::USER_FORMATS_STRUCTURE.keys } |
||||
|
||||
add :web, |
||||
value: { |
||||
'workers' => 2, |
||||
'timeout' => 120, |
||||
'wait_timeout' => 10, |
||||
'min_threads' => 4, |
||||
'max_threads' => 16 |
||||
}, |
||||
writable: false |
||||
|
||||
add :welcome_text, |
||||
format: :string, |
||||
value: nil |
||||
|
||||
add :welcome_title, |
||||
format: :string, |
||||
value: nil |
||||
|
||||
add :welcome_on_homescreen, |
||||
value: false |
||||
|
||||
add :work_package_done_ratio, |
||||
value: 'field', |
||||
allowed: %w[field status disabled] |
||||
|
||||
add :work_packages_export_limit, |
||||
value: 500 |
||||
|
||||
add :work_package_list_default_highlighted_attributes, |
||||
value: [], |
||||
allowed: -> { |
||||
Query.available_columns(nil).select(&:highlightable).map(&:name).map(&:to_s) |
||||
} |
||||
|
||||
add :work_package_list_default_highlighting_mode, |
||||
format: :string, |
||||
value: -> { EnterpriseToken.allows_to?(:conditional_highlighting) ? 'inline' : 'none' }, |
||||
allowed: -> { Query::QUERY_HIGHLIGHTING_MODES }, |
||||
writable: -> { EnterpriseToken.allows_to?(:conditional_highlighting) } |
||||
|
||||
add :work_package_list_default_columns, |
||||
value: %w[id subject type status assigned_to priority], |
||||
allowed: -> { Query.new.available_columns.map(&:name).map(&:to_s) } |
||||
|
||||
add :work_package_startdate_is_adddate, |
||||
value: false |
||||
|
||||
add :youtube_channel, |
||||
value: 'https://www.youtube.com/c/OpenProjectCommunity', |
||||
writable: false |
||||
end |
@ -1,373 +0,0 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2022 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See COPYRIGHT and LICENSE files for more details. |
||||
#++ |
||||
|
||||
# DO NOT MODIFY THIS FILE !!! |
||||
# Settings can be defined through the application in Admin -> Settings |
||||
|
||||
email_delivery_method: |
||||
default: |
||||
format: 'symbol' |
||||
sendmail_location: |
||||
default: "/usr/sbin/sendmail" |
||||
sendmail_arguments: |
||||
default: "-i" |
||||
smtp_openssl_verify_mode: |
||||
default: "none" |
||||
smtp_enable_starttls_auto: |
||||
default: 0 |
||||
format: boolean |
||||
smtp_ssl: |
||||
default: 0 |
||||
format: boolean |
||||
smtp_address: |
||||
default: "" |
||||
smtp_port: |
||||
default: 587 |
||||
format: int |
||||
smtp_domain: |
||||
default: "your.domain.com" |
||||
smtp_authentication: |
||||
default: "plain" |
||||
smtp_user_name: |
||||
default: "" |
||||
smtp_password: |
||||
default: "" |
||||
additional_footer_content: |
||||
default: '' |
||||
# The instance name |
||||
app_title: |
||||
default: OpenProject |
||||
app_subtitle: |
||||
default: Project management |
||||
brute_force_block_minutes: |
||||
default: 30 |
||||
format: int |
||||
brute_force_block_after_failed_logins: |
||||
default: 20 |
||||
format: int |
||||
# Time after which users have to have consented to what ever they need to consent |
||||
# to (depending on other settings) such as a privacy policy. |
||||
consent_time: |
||||
format: datetime |
||||
# Additional info about what the user is consenting to (optional). |
||||
consent_info: |
||||
serialized: true |
||||
default: |
||||
en: "## Consent\n\nYou need to agree to the [privacy and security policy](https://www.openproject.org/data-privacy-and-security/) of this OpenProject instance." |
||||
# Indicates whether or not users need to consent to something such as privacy policy. |
||||
consent_required: |
||||
default: 0 |
||||
format: boolean |
||||
consent_decline_mail: |
||||
default: |
||||
welcome_title: |
||||
default: |
||||
welcome_text: |
||||
default: |
||||
welcome_on_homescreen: |
||||
default: 0 |
||||
format: boolean |
||||
log_requesting_user: |
||||
default: 0 |
||||
format: int |
||||
login_required: |
||||
default: 0 |
||||
self_registration: |
||||
default: '2' |
||||
lost_password: |
||||
default: 1 |
||||
format: int |
||||
password_min_length: |
||||
format: int |
||||
default: 10 |
||||
password_active_rules: |
||||
serialized: true |
||||
default: |
||||
- lowercase |
||||
- uppercase |
||||
- numeric |
||||
- special |
||||
password_min_adhered_rules: |
||||
format: int |
||||
default: 0 |
||||
password_count_former_banned: |
||||
format: int |
||||
default: 0 |
||||
password_days_valid: |
||||
format: int |
||||
default: 0 |
||||
software_name: |
||||
default: OpenProject |
||||
software_url: |
||||
default: 'https://www.openproject.org/' |
||||
attachment_max_size: |
||||
format: int |
||||
default: 5120 |
||||
attachment_whitelist: |
||||
serialized: true |
||||
default: [] |
||||
work_packages_export_limit: |
||||
format: int |
||||
default: 500 |
||||
activity_days_default: |
||||
format: int |
||||
default: 30 |
||||
per_page_options: |
||||
default: '20, 100' |
||||
forced_single_page_size: |
||||
default: 250 |
||||
mail_from: |
||||
default: openproject@example.net |
||||
bcc_recipients: |
||||
default: 1 |
||||
plain_text_mail: |
||||
default: 0 |
||||
cache_formatted_text: |
||||
default: 0 |
||||
wiki_compression: |
||||
default: "" |
||||
available_languages: |
||||
serialized: true |
||||
default: |
||||
- en |
||||
- de |
||||
- fr |
||||
- es |
||||
- pt |
||||
- 'pt-BR' |
||||
- it |
||||
- 'zh-CN' |
||||
- ko |
||||
- ru |
||||
default_language: |
||||
default: en |
||||
default_auto_hide_popups: |
||||
default: 1 |
||||
format: boolean |
||||
email_login: # use email address as login, hide login in registration form |
||||
default: 0 |
||||
host_name: |
||||
default: localhost:3000 |
||||
protocol: |
||||
default: http |
||||
feeds_enabled: |
||||
default: 1 |
||||
feeds_limit: |
||||
format: int |
||||
default: 15 |
||||
# Maximum size of files that can be displayed |
||||
# inline through the file viewer (in KB) |
||||
file_max_size_displayed: |
||||
format: int |
||||
default: 512 |
||||
diff_max_lines_displayed: |
||||
format: int |
||||
default: 1500 |
||||
enabled_scm: |
||||
serialized: true |
||||
default: |
||||
- subversion |
||||
- git |
||||
autofetch_changesets: |
||||
default: 1 |
||||
sys_api_enabled: |
||||
default: 0 |
||||
sys_api_key: |
||||
default: '' |
||||
repository_authentication_caching_enabled: |
||||
default: 1 |
||||
repositories_automatic_managed_vendor: |
||||
default: '' |
||||
commit_ref_keywords: |
||||
default: 'refs,references,IssueID' |
||||
commit_fix_keywords: |
||||
default: 'fixes,closes' |
||||
commit_fix_status_id: |
||||
format: int |
||||
default: 0 |
||||
commit_fix_done_ratio: |
||||
default: 100 |
||||
commit_logtime_enabled: |
||||
default: 0 |
||||
commit_logtime_activity_id: |
||||
format: int |
||||
default: 0 |
||||
# autologin duration in days |
||||
# 0 means autologin is disabled |
||||
autologin: |
||||
format: int |
||||
default: 0 |
||||
# date format |
||||
date_format: |
||||
default: '' |
||||
time_format: |
||||
default: '' |
||||
user_format: |
||||
default: :firstname_lastname |
||||
format: symbol |
||||
cross_project_work_package_relations: |
||||
default: 1 |
||||
format: boolean |
||||
mail_handler_body_delimiters: |
||||
default: '' |
||||
mail_handler_ignore_filenames: |
||||
default: 'signature.asc' |
||||
mail_handler_body_delimiter_regex: |
||||
default: '' |
||||
mail_handler_api_key: |
||||
default: |
||||
mail_suffix_separators: |
||||
default: '+' |
||||
work_package_list_default_columns: |
||||
serialized: true |
||||
default: |
||||
- id |
||||
- subject |
||||
- type |
||||
- status |
||||
- assigned_to |
||||
- priority |
||||
work_package_list_default_highlighting_mode: |
||||
default: 'inline' |
||||
work_package_list_default_highlighted_attributes: |
||||
serialized: true |
||||
default: [] |
||||
display_subprojects_work_packages: |
||||
default: 1 |
||||
work_package_done_ratio: |
||||
default: 'field' |
||||
default_projects_public: |
||||
default: 0 |
||||
default_projects_modules: |
||||
serialized: true |
||||
default: |
||||
- calendar |
||||
- board_view |
||||
- work_package_tracking |
||||
- news |
||||
- costs |
||||
- wiki |
||||
enabled_projects_columns: |
||||
serialized: true |
||||
default: |
||||
- project_status |
||||
- public |
||||
- created_at |
||||
- latest_activity_at |
||||
- required_disk_space |
||||
project_gantt_query: |
||||
default: '' |
||||
# Role given to a non-admin user who creates a project |
||||
new_project_user_role_id: |
||||
format: int |
||||
default: '' |
||||
# encodings used to convert repository files content to UTF-8 |
||||
# multiple values accepted, comma separated |
||||
repositories_encodings: |
||||
default: '' |
||||
# encoding used to convert commit logs to UTF-8 |
||||
commit_logs_encoding: |
||||
default: 'UTF-8' |
||||
repository_log_display_limit: |
||||
format: int |
||||
default: 100 |
||||
emails_footer: |
||||
serialized: true |
||||
default: {} |
||||
start_of_week: |
||||
default: '' |
||||
first_week_of_year: |
||||
default: '' |
||||
rest_api_enabled: |
||||
default: 1 |
||||
session_ttl_enabled: |
||||
default: 0 |
||||
session_ttl: |
||||
format: int |
||||
default: 120 |
||||
emails_header: |
||||
serialized: true |
||||
default: |
||||
en: '' |
||||
work_package_startdate_is_adddate: |
||||
default: 0 |
||||
format: boolean |
||||
user_default_timezone: |
||||
default: "" |
||||
users_deletable_by_admins: |
||||
default: 0 |
||||
users_deletable_by_self: |
||||
default: 0 |
||||
invitation_expiration_days: |
||||
default: 7 |
||||
format: int |
||||
journal_aggregation_time_minutes: |
||||
default: 5 |
||||
format: int |
||||
registration_footer: |
||||
serialized: true |
||||
default: |
||||
en: |
||||
repository_storage_cache_minutes: |
||||
default: 720 |
||||
format: int |
||||
repository_truncate_at: |
||||
default: 500 |
||||
format: int |
||||
repository_checkout_data: |
||||
serialized: true |
||||
default: |
||||
git: |
||||
enabled: 0 |
||||
subversion: |
||||
enabled: 0 |
||||
demo_projects_available: |
||||
default: false |
||||
boards_demo_data_available: |
||||
default: false |
||||
security_badge_displayed: |
||||
default: true |
||||
installation_uuid: |
||||
default: null |
||||
oauth_allow_remapping_of_existing_users: |
||||
default: false |
||||
format: boolean |
||||
apiv3_cors_enabled: |
||||
default: false |
||||
format: boolean |
||||
apiv3_cors_origins: |
||||
serialized: true |
||||
default: [] |
||||
apiv3_docs_enabled: |
||||
default: true |
||||
format: boolean |
||||
apiv3_max_page_size: |
||||
default: 1000 |
||||
notification_retention_period_days: |
||||
default: 30 |
||||
format: int |
@ -1,53 +0,0 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2022 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See COPYRIGHT and LICENSE files for more details. |
||||
#++ |
||||
|
||||
module OpenProject::Reporting::Patches |
||||
module OpenProject::ConfigurationPatch |
||||
def self.included(base) |
||||
base.class_eval do |
||||
extend ModuleMethods |
||||
|
||||
@defaults['cost_reporting_cache_filter_classes'] = true |
||||
|
||||
if config_loaded_before_patch? |
||||
@config['cost_reporting_cache_filter_classes'] = true |
||||
end |
||||
end |
||||
end |
||||
|
||||
module ModuleMethods |
||||
def config_loaded_before_patch? |
||||
@config.present? && !@config.has_key?('cost_reporting_cache_filter_classes') |
||||
end |
||||
|
||||
def cost_reporting_cache_filter_classes |
||||
@config['cost_reporting_cache_filter_classes'] |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,630 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Settings::Definition do |
||||
shared_context 'with clean definitions' do |
||||
let!(:definitions_before) { described_class.all.dup } |
||||
|
||||
before do |
||||
described_class.send(:reset) |
||||
end |
||||
|
||||
after do |
||||
described_class.send(:reset) |
||||
described_class.instance_variable_set(:@all, definitions_before) |
||||
end |
||||
end |
||||
|
||||
describe '.all' do |
||||
subject(:all) { described_class.all } |
||||
|
||||
it "is a list of setting definitions" do |
||||
expect(all) |
||||
.to(be_all { |d| d.is_a?(described_class) }) |
||||
end |
||||
|
||||
it 'contains a definition from settings' do |
||||
expect(all) |
||||
.to(be_any { |d| d.name == 'smtp_address' }) |
||||
end |
||||
|
||||
it 'contains a definition from configuration' do |
||||
expect(all) |
||||
.to(be_any { |d| d.name == 'edition' }) |
||||
end |
||||
|
||||
it 'contains a definition from settings.yml' do |
||||
expect(all) |
||||
.to(be_any { |d| d.name == 'sendmail_location' }) |
||||
end |
||||
|
||||
it 'casts the value from settings.yml' do |
||||
expect(all.detect { |d| d.name == 'brute_force_block_after_failed_logins' }.value) |
||||
.to eq 20 |
||||
end |
||||
|
||||
context 'when overriding from ENV' do |
||||
include_context 'with clean definitions' |
||||
|
||||
it 'allows overriding configuration from ENV' do |
||||
stub_const('ENV', { 'OPENPROJECT_EDITION' => 'bim' }) |
||||
|
||||
expect(all.detect { |d| d.name == 'edition' }.value) |
||||
.to eql 'bim' |
||||
end |
||||
|
||||
it 'overriding boolean configuration from ENV will cast the value' do |
||||
stub_const('ENV', { 'OPENPROJECT_REST__API__ENABLED' => '0' }) |
||||
|
||||
expect(all.detect { |d| d.name == 'rest_api_enabled' }.value) |
||||
.to be false |
||||
end |
||||
|
||||
it 'overriding configuration from ENV will set it to non writable' do |
||||
stub_const('ENV', { 'OPENPROJECT_EDITION' => 'bim' }) |
||||
|
||||
expect(all.detect { |d| d.name == 'edition' }) |
||||
.not_to be_writable |
||||
end |
||||
|
||||
it 'allows overriding settings array from ENV' do |
||||
stub_const('ENV', { 'OPENPROJECT_PASSWORD__ACTIVE__RULES' => YAML.dump(['lowercase']) }) |
||||
|
||||
expect(all.detect { |d| d.name == 'password_active_rules' }.value) |
||||
.to eql ['lowercase'] |
||||
end |
||||
|
||||
it 'overriding settings from ENV will set it to non writable' do |
||||
stub_const('ENV', { 'OPENPROJECT_WELCOME__TITLE' => 'Some title' }) |
||||
|
||||
expect(all.detect { |d| d.name == 'welcome_title' }) |
||||
.not_to be_writable |
||||
end |
||||
|
||||
it 'allows overriding settings hash partially from ENV' do |
||||
stub_const('ENV', { 'OPENPROJECT_REPOSITORY__CHECKOUT__DATA_GIT_ENABLED' => '1' }) |
||||
|
||||
expect(all.detect { |d| d.name == 'repository_checkout_data' }.value) |
||||
.to eql({ |
||||
'git' => { 'enabled' => 1 }, |
||||
'subversion' => { 'enabled' => 0 } |
||||
}) |
||||
end |
||||
|
||||
it 'ENV vars for which no definition exists will not be handled' do |
||||
stub_const('ENV', { 'OPENPROJECT_BOGUS' => '1' }) |
||||
|
||||
expect(all.detect { |d| d.name == 'bogus' }) |
||||
.to be_nil |
||||
end |
||||
end |
||||
|
||||
context 'when overriding from file' do |
||||
include_context 'with clean definitions' |
||||
|
||||
let(:file_contents) do |
||||
{ |
||||
'default' => { |
||||
'edition' => 'bim', |
||||
'sendmail_location' => 'default location' |
||||
}, |
||||
'test' => { |
||||
'smtp_address' => 'test address', |
||||
'sendmail_location' => 'test location', |
||||
'bogus' => 'bogusvalue' |
||||
} |
||||
} |
||||
end |
||||
|
||||
before do |
||||
allow(YAML) |
||||
.to receive(:load_file) |
||||
.with(Rails.root.join('config/configuration.yml')) |
||||
.and_return(file_contents) |
||||
|
||||
allow(YAML) |
||||
.to receive(:load_file) |
||||
.with(Rails.root.join('config/settings.yml')) |
||||
.and_return({}) |
||||
|
||||
allow(File) |
||||
.to receive(:file?) |
||||
.with(Rails.root.join('config/configuration.yml')) |
||||
.and_return(true) |
||||
|
||||
# Loading of the config file is disabled in test env normally. |
||||
allow(Rails.env) |
||||
.to receive(:test?) |
||||
.and_return(false) |
||||
end |
||||
|
||||
it 'overrides from file default' do |
||||
expect(all.detect { |d| d.name == 'edition' }.value) |
||||
.to eql 'bim' |
||||
end |
||||
|
||||
it 'marks the value overwritten from file default unwritable' do |
||||
expect(all.detect { |d| d.name == 'edition' }) |
||||
.not_to be_writable |
||||
end |
||||
|
||||
it 'overrides from file default path but once again from current env' do |
||||
expect(all.detect { |d| d.name == 'sendmail_location' }.value) |
||||
.to eql 'test location' |
||||
end |
||||
|
||||
it 'marks the value overwritten from file default and again from current unwritable' do |
||||
expect(all.detect { |d| d.name == 'sendmail_location' }) |
||||
.not_to be_writable |
||||
end |
||||
|
||||
it 'overrides from file current env' do |
||||
expect(all.detect { |d| d.name == 'smtp_address' }.value) |
||||
.to eql 'test address' |
||||
end |
||||
|
||||
it 'marks the value overwritten from file current unwritable' do |
||||
expect(all.detect { |d| d.name == 'smtp_address' }) |
||||
.not_to be_writable |
||||
end |
||||
|
||||
it 'does not accept undefined settings' do |
||||
expect(all.detect { |d| d.name == 'bogus' }) |
||||
.to be_nil |
||||
end |
||||
|
||||
context 'when having invalid values in the file' do |
||||
let(:file_contents) do |
||||
{ |
||||
'default' => { |
||||
'smtp_openssl_verify_mode' => 'bogus' |
||||
} |
||||
} |
||||
end |
||||
|
||||
it 'is invalid' do |
||||
expect { all } |
||||
.to raise_error ArgumentError |
||||
end |
||||
end |
||||
|
||||
context 'when overwritten from ENV' do |
||||
before do |
||||
stub_const('ENV', { 'OPENPROJECT_SENDMAIL__LOCATION' => 'env location' }) |
||||
end |
||||
|
||||
it 'overrides from ENV' do |
||||
expect(all.detect { |d| d.name == 'sendmail_location' }.value) |
||||
.to eql 'env location' |
||||
end |
||||
|
||||
it 'marks the overwritten value unwritable' do |
||||
expect(all.detect { |d| d.name == 'sendmail_location' }) |
||||
.not_to be_writable |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'when adding an additional setting' do |
||||
include_context 'with clean definitions' |
||||
|
||||
it 'includes the setting' do |
||||
all |
||||
|
||||
described_class.add 'bogus', |
||||
value: 1, |
||||
format: :integer |
||||
|
||||
expect(all.detect { |d| d.name == 'bogus' }.value) |
||||
.to eq(1) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe ".[name]" do |
||||
subject(:definition) { described_class[key] } |
||||
|
||||
context 'with a string' do |
||||
let(:key) { 'smtp_address' } |
||||
|
||||
it 'returns the value' do |
||||
expect(definition.name) |
||||
.to eql key |
||||
end |
||||
end |
||||
|
||||
context 'with a symbol' do |
||||
let(:key) { :smtp_address } |
||||
|
||||
it 'returns the value' do |
||||
expect(definition.name) |
||||
.to eql key.to_s |
||||
end |
||||
end |
||||
|
||||
context 'with a non existing key' do |
||||
let(:key) { 'bogus' } |
||||
|
||||
it 'returns the value' do |
||||
expect(definition) |
||||
.to be_nil |
||||
end |
||||
end |
||||
|
||||
context 'when adding a setting late' do |
||||
include_context 'with clean definitions' |
||||
let(:key) { 'bogus' } |
||||
|
||||
before do |
||||
described_class[key] |
||||
|
||||
described_class.add 'bogus', |
||||
value: 1, |
||||
format: :integer |
||||
end |
||||
|
||||
it 'has the setting' do |
||||
expect(definition.name) |
||||
.to eql key.to_s |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#override_value' do |
||||
let(:format) { :string } |
||||
let(:value) { 'abc' } |
||||
|
||||
let(:instance) do |
||||
described_class |
||||
.new 'bogus', |
||||
format: format, |
||||
value: value |
||||
end |
||||
|
||||
context 'with string format' do |
||||
before do |
||||
instance.override_value('xyz') |
||||
end |
||||
|
||||
it 'overwrites' do |
||||
expect(instance.value) |
||||
.to eql 'xyz' |
||||
end |
||||
|
||||
it 'turns the definition unwritable' do |
||||
expect(instance) |
||||
.not_to be_writable |
||||
end |
||||
end |
||||
|
||||
context 'with hash format' do |
||||
let(:format) { :hash } |
||||
let(:value) do |
||||
{ |
||||
abc: { |
||||
a: 1, |
||||
b: 2 |
||||
}, |
||||
cde: 1 |
||||
} |
||||
end |
||||
|
||||
before do |
||||
instance.override_value({ abc: { a: 5 }, xyz: 2 }) |
||||
end |
||||
|
||||
it 'deep merges' do |
||||
expect(instance.value) |
||||
.to eql({ |
||||
abc: { |
||||
a: 5, |
||||
b: 2 |
||||
}, |
||||
cde: 1, |
||||
xyz: 2 |
||||
}) |
||||
end |
||||
|
||||
it 'turns the definition unwritable' do |
||||
expect(instance) |
||||
.not_to be_writable |
||||
end |
||||
end |
||||
|
||||
context 'with array format' do |
||||
let(:format) { :array } |
||||
let(:value) { [1, 2, 3] } |
||||
|
||||
before do |
||||
instance.override_value([4, 5, 6]) |
||||
end |
||||
|
||||
it 'overwrites' do |
||||
expect(instance.value) |
||||
.to eql [4, 5, 6] |
||||
end |
||||
|
||||
it 'turns the definition unwritable' do |
||||
expect(instance) |
||||
.not_to be_writable |
||||
end |
||||
end |
||||
|
||||
context 'with an invalid value' do |
||||
let(:instance) do |
||||
described_class |
||||
.new 'bogus', |
||||
format: format, |
||||
value: 'foo', |
||||
allowed: %w[foo bar] |
||||
end |
||||
|
||||
it 'raises an error' do |
||||
expect { instance.override_value('invalid') } |
||||
.to raise_error ArgumentError |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '.exists?' do |
||||
context 'with an existing setting' do |
||||
it 'is truthy' do |
||||
expect(described_class) |
||||
.to exist('smtp_address') |
||||
end |
||||
end |
||||
|
||||
context 'with a non existing setting' do |
||||
it 'is truthy' do |
||||
expect(described_class) |
||||
.not_to exist('foobar') |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '.new' do |
||||
context 'with all the attributes' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
format: :integer, |
||||
value: 1, |
||||
writable: false, |
||||
allowed: [1, 2, 3] |
||||
end |
||||
|
||||
it 'has the name' do |
||||
expect(instance.name) |
||||
.to eql 'bogus' |
||||
end |
||||
|
||||
it 'has the format (in symbol)' do |
||||
expect(instance.format) |
||||
.to eq :integer |
||||
end |
||||
|
||||
it 'has the value' do |
||||
expect(instance.value) |
||||
.to eq 1 |
||||
end |
||||
|
||||
it 'is not serialized' do |
||||
expect(instance) |
||||
.not_to be_serialized |
||||
end |
||||
|
||||
it 'has the writable value' do |
||||
expect(instance) |
||||
.not_to be_writable |
||||
end |
||||
|
||||
it 'has the allowed value' do |
||||
expect(instance.allowed) |
||||
.to eql [1, 2, 3] |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (integer value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: 1 |
||||
end |
||||
|
||||
it 'has the name' do |
||||
expect(instance.name) |
||||
.to eql 'bogus' |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :integer |
||||
end |
||||
|
||||
it 'has the value' do |
||||
expect(instance.value) |
||||
.to eq 1 |
||||
end |
||||
|
||||
it 'is not serialized' do |
||||
expect(instance) |
||||
.not_to be_serialized |
||||
end |
||||
|
||||
it 'has the writable value' do |
||||
expect(instance) |
||||
.to be_writable |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (hash value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: { a: 'b' } |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :hash |
||||
end |
||||
|
||||
it 'is serialized' do |
||||
expect(instance) |
||||
.to be_serialized |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (array value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: %i[a b] |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :array |
||||
end |
||||
|
||||
it 'is serialized' do |
||||
expect(instance) |
||||
.to be_serialized |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (true value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: true |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :boolean |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (false value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: false |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :boolean |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (date value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: Time.zone.today |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :date |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (datetime value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: DateTime.now |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :date_time |
||||
end |
||||
end |
||||
|
||||
context 'with the minimal attributes (string value)' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
value: 'abc' |
||||
end |
||||
|
||||
it 'has the format (in symbol) deduced' do |
||||
expect(instance.format) |
||||
.to eq :string |
||||
end |
||||
end |
||||
|
||||
context 'with procs for value, writable and allowed' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
format: 'string', |
||||
value: -> { 'some value' }, |
||||
writable: -> { false }, |
||||
allowed: -> { %w[a b c] } |
||||
end |
||||
|
||||
it 'returns the procs return value for value' do |
||||
expect(instance.value) |
||||
.to eql 'some value' |
||||
end |
||||
|
||||
it 'returns the procs return value for writable' do |
||||
expect(instance.writable?) |
||||
.to be false |
||||
end |
||||
|
||||
it 'returns the procs return value for allowed' do |
||||
expect(instance.allowed) |
||||
.to eql %w[a b c] |
||||
end |
||||
end |
||||
|
||||
context 'with an integer provided as a string' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
format: :integer, |
||||
value: '5' |
||||
end |
||||
|
||||
it 'returns value as an int' do |
||||
expect(instance.value) |
||||
.to eq 5 |
||||
end |
||||
end |
||||
|
||||
context 'with a float provided as a string' do |
||||
let(:instance) do |
||||
described_class.new 'bogus', |
||||
format: :float, |
||||
value: '0.5' |
||||
end |
||||
|
||||
it 'returns value as a float' do |
||||
expect(instance.value) |
||||
.to eq 0.5 |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue