# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/ class Setting < ApplicationModel store :options store :state_current store :state_initial store :preferences before_create :state_check, :set_initial, :check_broadcast before_update :state_check, :check_broadcast after_create :reset_change_id after_update :reset_change_id attr_accessor :state @@current = {} # rubocop:disable Style/ClassVars @@raw = {} # rubocop:disable Style/ClassVars @@change_id = nil # rubocop:disable Style/ClassVars @@last_changed_at = nil # rubocop:disable Style/ClassVars @@lookup_at = nil # rubocop:disable Style/ClassVars @@lookup_timeout = if ENV['ZAMMAD_SETTING_TTL'] # rubocop:disable Style/ClassVars ENV['ZAMMAD_SETTING_TTL'].to_i.seconds else 15.seconds end =begin set config setting Setting.set('some_config_name', some_value) =end def self.set(name, value) setting = Setting.find_by(name: name) if !setting raise "Can't find config setting '#{name}'" end setting.state_current = { value: value } setting.save! logger.info "Setting.set(#{name}, #{value.inspect})" end =begin get config setting value = Setting.get('some_config_name') =end def self.get(name) load @@current[name] end =begin reset config setting to default Setting.reset('some_config_name') Setting.reset('some_config_name', force) # true|false - force it false per default =end def self.reset(name, force = false) setting = Setting.find_by(name: name) if !setting raise "Can't find config setting '#{name}'" end return true if !force && setting.state_current == setting.state_initial setting.state_current = setting.state_initial setting.save! logger.info "Setting.reset(#{name}, #{setting.state_current.inspect})" end =begin reload config settings Setting.reload =end def self.reload @@last_changed_at = nil # rubocop:disable Style/ClassVars load(true) end private # load values and cache them def self.load(force = false) # check if config is already generated return false if !force && @@current.present? && cache_valid? # read all or only changed since last read latest = Setting.order(updated_at: :desc).limit(1).pluck(:updated_at) settings = if @@last_changed_at && @@current.present? Setting.where('updated_at > ?', @@last_changed_at).order(:id).pluck(:name, :state_current) else Setting.order(:id).pluck(:name, :state_current) end if latest @@last_changed_at = latest[0] # rubocop:disable Style/ClassVars end if settings.present? settings.each { |setting| @@raw[setting[0]] = setting[1]['value'] } @@raw.each { |key, value| if value.class != String @@current[key] = value next end @@current[key] = value.gsub(/\#\{config\.(.+?)\}/) { @@raw[$1].to_s } } end @@change_id = Cache.get('Setting::ChangeId') # rubocop:disable Style/ClassVars @@lookup_at = Time.zone.now # rubocop:disable Style/ClassVars true end private_class_method :load # set initial value in state_initial def set_initial self.state_initial = state_current end def reset_change_id @@current[name] = state_current[:value] change_id = rand(999_999_999).to_s logger.debug "Setting.reset_change_id: set new cache, #{change_id}" Cache.write('Setting::ChangeId', change_id, { expires_in: 24.hours }) @@lookup_at = nil # rubocop:disable Style/ClassVars end # check if cache is still valid def self.cache_valid? if @@lookup_at && @@lookup_at > Time.zone.now - @@lookup_timeout #logger.debug "Setting.cache_valid?: cache_id has been set within last #{@@lookup_timeout} seconds" return true end change_id = Cache.get('Setting::ChangeId') if change_id == @@change_id @@lookup_at = Time.zone.now # rubocop:disable Style/ClassVars #logger.debug "Setting.cache_valid?: cache still valid, #{@@change_id}/#{change_id}" return true end #logger.debug "Setting.cache_valid?: cache has changed, #{@@change_id}/#{change_id}" false end private_class_method :cache_valid? # convert state into hash to be able to store it as store def state_check return if !state return if state && state.respond_to?('has_key?') && state.key?(:value) self.state_current = { value: state } end # notify clients about public config changes def check_broadcast return if frontend != true value = state_current if state_current.key?(:value) value = state_current[:value] end Sessions.broadcast( { event: 'config_update', data: { name: name, value: value } }, 'public' ) end end