setting.rb
276 lines
| 7.7 KiB
| text/x-ruby
|
RubyLexer
|
r5708 | # Redmine - project management software | ||
|
r13490 | # Copyright (C) 2006-2015 Jean-Philippe Lang | ||
|
r164 | # | ||
# 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. | ||||
|
r5708 | # | ||
|
r164 | # 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. | ||||
|
r5708 | # | ||
|
r164 | # 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. | ||||
class Setting < ActiveRecord::Base | ||||
|
r892 | DATE_FORMATS = [ | ||
|
r11191 | '%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' | ||||
|
r892 | ] | ||
|
r5708 | |||
|
r892 | TIME_FORMATS = [ | ||
'%H:%M', | ||||
'%I:%M %p' | ||||
] | ||||
|
r5708 | |||
|
r1766 | ENCODINGS = %w(US-ASCII | ||
windows-1250 | ||||
windows-1251 | ||||
windows-1252 | ||||
windows-1253 | ||||
windows-1254 | ||||
windows-1255 | ||||
windows-1256 | ||||
windows-1257 | ||||
windows-1258 | ||||
windows-31j | ||||
ISO-2022-JP | ||||
ISO-2022-KR | ||||
ISO-8859-1 | ||||
ISO-8859-2 | ||||
ISO-8859-3 | ||||
ISO-8859-4 | ||||
ISO-8859-5 | ||||
ISO-8859-6 | ||||
ISO-8859-7 | ||||
ISO-8859-8 | ||||
ISO-8859-9 | ||||
ISO-8859-13 | ||||
ISO-8859-15 | ||||
KOI8-R | ||||
UTF-8 | ||||
UTF-16 | ||||
UTF-16BE | ||||
UTF-16LE | ||||
EUC-JP | ||||
Shift_JIS | ||||
|
r4779 | CP932 | ||
|
r1766 | GB18030 | ||
GBK | ||||
ISCII91 | ||||
EUC-KR | ||||
Big5 | ||||
Big5-HKSCS | ||||
TIS-620) | ||||
|
r5708 | |||
|
r164 | cattr_accessor :available_settings | ||
|
r13337 | self.available_settings ||= {} | ||
|
r5708 | |||
|
r13338 | validates_uniqueness_of :name, :if => Proc.new {|setting| setting.new_record? || setting.name_changed?} | ||
|
r13337 | validates_inclusion_of :name, :in => Proc.new {available_settings.keys} | ||
|
r12097 | validates_numericality_of :value, :only_integer => true, :if => Proc.new { |setting| | ||
|
r13337 | (s = available_settings[setting.name]) && s['format'] == 'int' | ||
|
r12097 | } | ||
|
r13100 | attr_protected :id | ||
|
r674 | |||
# Hash used to cache setting values | ||||
@cached_settings = {} | ||||
@cached_cleared_on = Time.now | ||||
|
r5708 | |||
|
r717 | def value | ||
v = read_attribute(:value) | ||||
# Unserialize serialized settings | ||||
|
r13730 | if available_settings[name]['serialized'] && v.is_a?(String) | ||
v = YAML::load(v) | ||||
v = force_utf8_strings(v) | ||||
end | ||||
|
r13337 | v = v.to_sym if available_settings[name]['format'] == 'symbol' && !v.blank? | ||
|
r717 | v | ||
end | ||||
|
r5708 | |||
|
r731 | def value=(v) | ||
|
r13337 | v = v.to_yaml if v && available_settings[name] && available_settings[name]['serialized'] | ||
|
r1089 | write_attribute(:value, v.to_s) | ||
|
r731 | end | ||
|
r5708 | |||
|
r674 | # Returns the value of the setting named name | ||
|
r164 | def self.[](name) | ||
|
r717 | v = @cached_settings[name] | ||
v ? v : (@cached_settings[name] = find_or_default(name).value) | ||||
|
r164 | end | ||
|
r5708 | |||
|
r717 | def self.[]=(name, v) | ||
|
r674 | setting = find_or_default(name) | ||
|
r717 | setting.value = (v ? v : "") | ||
|
r674 | @cached_settings[name] = nil | ||
|
r164 | setting.save | ||
setting.value | ||||
end | ||||
|
r5708 | |||
|
r11967 | # Sets a setting value from params | ||
def self.set_from_params(name, params) | ||||
params = params.dup | ||||
params.delete_if {|v| v.blank? } if params.is_a?(Array) | ||||
|
r13100 | params.symbolize_keys! if params.is_a?(Hash) | ||
|
r11967 | |||
m = "#{name}_from_params" | ||||
if respond_to? m | ||||
self[name.to_sym] = send m, params | ||||
else | ||||
self[name.to_sym] = params | ||||
end | ||||
end | ||||
# Returns a hash suitable for commit_update_keywords setting | ||||
# | ||||
# Example: | ||||
# params = {:keywords => ['fixes', 'closes'], :status_id => ["3", "5"], :done_ratio => ["", "100"]} | ||||
# Setting.commit_update_keywords_from_params(params) | ||||
|
r11978 | # # => [{'keywords => 'fixes', 'status_id' => "3"}, {'keywords => 'closes', 'status_id' => "5", 'done_ratio' => "100"}] | ||
|
r11967 | def self.commit_update_keywords_from_params(params) | ||
|
r11978 | s = [] | ||
|
r11967 | if params.is_a?(Hash) && params.key?(:keywords) && params.values.all? {|v| v.is_a? Array} | ||
attributes = params.except(:keywords).keys | ||||
params[:keywords].each_with_index do |keywords, i| | ||||
next if keywords.blank? | ||||
|
r11978 | s << attributes.inject({}) {|h, a| | ||
|
r11967 | value = params[a][i].to_s | ||
h[a.to_s] = value if value.present? | ||||
h | ||||
|
r11978 | }.merge('keywords' => keywords) | ||
|
r11967 | end | ||
end | ||||
s | ||||
end | ||||
|
r1013 | # Helper that returns an array based on per_page_options setting | ||
def self.per_page_options_array | ||||
per_page_options.split(%r{[\s,]}).collect(&:to_i).select {|n| n > 0}.sort | ||||
end | ||||
|
r5708 | |||
|
r11967 | # Helper that returns a Hash with single update keywords as keys | ||
|
r11978 | def self.commit_update_keywords_array | ||
a = [] | ||||
if commit_update_keywords.is_a?(Array) | ||||
commit_update_keywords.each do |rule| | ||||
next unless rule.is_a?(Hash) | ||||
rule = rule.dup | ||||
rule.delete_if {|k, v| v.blank?} | ||||
keywords = rule['keywords'].to_s.downcase.split(",").map(&:strip).reject(&:blank?) | ||||
next if keywords.empty? | ||||
a << rule.merge('keywords' => keywords) | ||||
|
r11967 | end | ||
end | ||||
|
r11978 | a | ||
|
r11967 | end | ||
|
r2397 | def self.openid? | ||
|
r2419 | Object.const_defined?(:OpenID) && self[:openid].to_i > 0 | ||
|
r2397 | end | ||
|
r5708 | |||
|
r674 | # Checks if settings have changed since the values were read | ||
# and clears the cache hash if it's the case | ||||
# Called once per request | ||||
def self.check_cache | ||||
settings_updated_on = Setting.maximum(:updated_on) | ||||
if settings_updated_on && @cached_cleared_on <= settings_updated_on | ||||
|
r7684 | clear_cache | ||
|
r674 | end | ||
end | ||||
|
r11458 | |||
|
r7684 | # Clears the settings cache | ||
def self.clear_cache | ||||
@cached_settings.clear | ||||
@cached_cleared_on = Time.now | ||||
logger.info "Settings cache cleared." if logger | ||||
end | ||||
|
r5708 | |||
|
r13337 | def self.define_plugin_setting(plugin) | ||
if plugin.settings | ||||
name = "plugin_#{plugin.id}" | ||||
define_setting name, {'default' => plugin.settings[:default], 'serialized' => true} | ||||
end | ||||
end | ||||
# Defines getter and setter for each setting | ||||
# Then setting values can be read using: Setting.some_setting_name | ||||
# or set using Setting.some_setting_name = "some value" | ||||
def self.define_setting(name, options={}) | ||||
available_settings[name.to_s] = options | ||||
src = <<-END_SRC | ||||
def self.#{name} | ||||
self[:#{name}] | ||||
end | ||||
def self.#{name}? | ||||
self[:#{name}].to_i > 0 | ||||
end | ||||
def self.#{name}=(value) | ||||
self[:#{name}] = value | ||||
end | ||||
END_SRC | ||||
class_eval src, __FILE__, __LINE__ | ||||
end | ||||
def self.load_available_settings | ||||
YAML::load(File.open("#{Rails.root}/config/settings.yml")).each do |name, options| | ||||
define_setting name, options | ||||
end | ||||
end | ||||
def self.load_plugin_settings | ||||
Redmine::Plugin.all.each do |plugin| | ||||
define_plugin_setting(plugin) | ||||
end | ||||
end | ||||
load_available_settings | ||||
load_plugin_settings | ||||
|
r674 | private | ||
|
r13730 | |||
def force_utf8_strings(arg) | ||||
|
r13735 | if arg.is_a?(String) | ||
|
r13730 | arg.dup.force_encoding('UTF-8') | ||
elsif arg.is_a?(Array) | ||||
arg.map do |a| | ||||
force_utf8_strings(a) | ||||
end | ||||
elsif arg.is_a?(Hash) | ||||
arg = arg.dup | ||||
arg.each do |k,v| | ||||
arg[k] = force_utf8_strings(v) | ||||
end | ||||
arg | ||||
else | ||||
arg | ||||
end | ||||
end | ||||
|
r674 | # Returns the Setting instance for the setting named name | ||
# (record found in database or new record with default value) | ||||
def self.find_or_default(name) | ||||
name = name.to_s | ||||
|
r13337 | raise "There's no setting named #{name}" unless available_settings.has_key?(name) | ||
|
r13339 | setting = where(:name => name).order(:id => :desc).first | ||
|
r8789 | unless setting | ||
|
r12148 | setting = new | ||
setting.name = name | ||||
|
r13337 | setting.value = available_settings[name]['default'] | ||
|
r8789 | end | ||
setting | ||||
|
r674 | end | ||
|
r164 | end | ||