2021-08-25 12:24:42 +00:00
|
|
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
|
|
|
|
|
|
|
require 'digest/md5'
|
|
|
|
|
|
|
|
class CoreWorkflow::Attributes
|
|
|
|
attr_accessor :user, :payload, :assets
|
|
|
|
|
|
|
|
def initialize(result_object:)
|
|
|
|
@result_object = result_object
|
|
|
|
@user = result_object.user
|
|
|
|
@payload = result_object.payload
|
|
|
|
@assets = result_object.assets
|
|
|
|
end
|
|
|
|
|
|
|
|
def payload_class
|
|
|
|
@payload['class_name'].constantize
|
|
|
|
end
|
|
|
|
|
|
|
|
def selected_only
|
|
|
|
|
|
|
|
# params loading and preparing is very expensive so cache it
|
|
|
|
checksum = Digest::MD5.hexdigest(Marshal.dump(@payload['params']))
|
|
|
|
return @selected_only[checksum] if @selected_only.present? && @selected_only[checksum]
|
|
|
|
|
|
|
|
@selected_only = {}
|
|
|
|
@selected_only[checksum] = begin
|
|
|
|
clean_params = payload_class.association_name_to_id_convert(@payload['params'])
|
|
|
|
clean_params = payload_class.param_cleanup(clean_params, true, false, false)
|
|
|
|
payload_class.new(clean_params)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-29 08:03:04 +00:00
|
|
|
def selectable_field?(key)
|
|
|
|
return if key == 'id'
|
|
|
|
return if !@payload['params'].key?(key)
|
|
|
|
|
|
|
|
# some objects have no attributes like "CoreWorkflow"-object as well.
|
|
|
|
# attributes only exists in the frontend so we skip this check
|
|
|
|
return true if object_elements.blank?
|
|
|
|
|
|
|
|
object_elements_hash.key?(key)
|
|
|
|
end
|
|
|
|
|
2021-08-25 12:24:42 +00:00
|
|
|
def overwrite_selected(result)
|
|
|
|
selected_attributes = selected_only.attributes
|
|
|
|
selected_attributes.each_key do |key|
|
2021-09-29 08:03:04 +00:00
|
|
|
next if !selectable_field?(key)
|
|
|
|
|
|
|
|
# special behaviour for owner id
|
|
|
|
if key == 'owner_id' && selected_attributes[key].nil?
|
|
|
|
selected_attributes[key] = 1
|
|
|
|
end
|
2021-08-25 12:24:42 +00:00
|
|
|
|
|
|
|
result[key.to_sym] = selected_attributes[key]
|
|
|
|
end
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2021-12-13 12:11:32 +00:00
|
|
|
def exists?
|
|
|
|
return if @payload['params']['id'].blank?
|
|
|
|
|
|
|
|
@exists ||= payload_class.exists?(id: @payload['params']['id'])
|
|
|
|
end
|
|
|
|
|
|
|
|
def overwritten
|
|
|
|
|
|
|
|
# params loading and preparing is very expensive so cache it
|
|
|
|
checksum = Digest::MD5.hexdigest(Marshal.dump(@payload['params']))
|
|
|
|
return @overwritten[checksum] if @overwritten.present? && @overwritten[checksum]
|
|
|
|
|
|
|
|
@overwritten = {}
|
|
|
|
@overwritten[checksum] = begin
|
|
|
|
result = saved_only(dump: true)
|
2021-08-25 12:24:42 +00:00
|
|
|
overwrite_selected(result)
|
2021-12-13 12:11:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def selected
|
|
|
|
if exists?
|
|
|
|
overwritten
|
2021-08-25 12:24:42 +00:00
|
|
|
else
|
|
|
|
selected_only
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-12-13 12:11:32 +00:00
|
|
|
def saved_only(dump: false)
|
|
|
|
return if !exists?
|
2021-08-25 12:24:42 +00:00
|
|
|
|
|
|
|
# dont use lookup here because the cache will not
|
|
|
|
# know about new attributes and make crashes
|
|
|
|
@saved_only ||= payload_class.find_by(id: @payload['params']['id'])
|
2021-09-29 08:03:04 +00:00
|
|
|
|
2021-12-13 12:11:32 +00:00
|
|
|
return @saved_only if !dump
|
|
|
|
|
2021-09-29 08:03:04 +00:00
|
|
|
# we use marshal here because clone still uses references and dup can't
|
|
|
|
# detect changes for the rails object
|
|
|
|
Marshal.load(Marshal.dump(@saved_only))
|
2021-08-25 12:24:42 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def saved
|
|
|
|
@saved ||= saved_only || payload_class.new
|
|
|
|
end
|
|
|
|
|
|
|
|
def object_elements
|
|
|
|
@object_elements ||= ObjectManager::Object.new(@payload['class_name']).attributes(@user, saved_only, data_only: false).each_with_object([]) do |element, result|
|
|
|
|
result << element.data.merge(screens: element.screens)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-29 08:03:04 +00:00
|
|
|
def object_elements_hash
|
|
|
|
@object_elements_hash ||= object_elements.index_by { |x| x[:name] }
|
|
|
|
end
|
|
|
|
|
2021-08-25 12:24:42 +00:00
|
|
|
def screen_value(attribute, type)
|
|
|
|
attribute[:screens].dig(@payload['screen'], type)
|
|
|
|
end
|
|
|
|
|
2021-09-20 16:12:33 +00:00
|
|
|
def request_id_default
|
|
|
|
payload['request_id']
|
|
|
|
end
|
|
|
|
|
2021-08-25 12:24:42 +00:00
|
|
|
# dont cache this else the result object will work with references and cache bugs occur
|
2021-09-20 16:12:33 +00:00
|
|
|
def visibility_default
|
2021-08-25 12:24:42 +00:00
|
|
|
object_elements.each_with_object({}) do |attribute, result|
|
2021-09-23 07:52:08 +00:00
|
|
|
result[ attribute[:name] ] = screen_value(attribute, 'shown') == false ? 'hide' : 'show'
|
2021-08-25 12:24:42 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-03 13:16:34 +00:00
|
|
|
def attribute_mandatory?(attribute)
|
|
|
|
return screen_value(attribute, 'required').present? if !screen_value(attribute, 'required').nil?
|
|
|
|
return screen_value(attribute, 'null').blank? if !screen_value(attribute, 'null').nil?
|
|
|
|
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2021-08-25 12:24:42 +00:00
|
|
|
# dont cache this else the result object will work with references and cache bugs occur
|
|
|
|
def mandatory_default
|
|
|
|
object_elements.each_with_object({}) do |attribute, result|
|
2021-09-03 13:16:34 +00:00
|
|
|
result[ attribute[:name] ] = attribute_mandatory?(attribute)
|
2021-08-25 12:24:42 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# dont cache this else the result object will work with references and cache bugs occur
|
|
|
|
def auto_select_default
|
|
|
|
object_elements.each_with_object({}) do |attribute, result|
|
|
|
|
next if !attribute[:only_shown_if_selectable]
|
|
|
|
|
|
|
|
result[ attribute[:name] ] = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-20 16:12:33 +00:00
|
|
|
# dont cache this else the result object will work with references and cache bugs occur
|
|
|
|
def readonly_default
|
|
|
|
object_elements.each_with_object({}) do |attribute, result|
|
|
|
|
result[ attribute[:name] ] = false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def select_default
|
|
|
|
@result_object.result[:select] || {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def fill_in_default
|
|
|
|
@result_object.result[:fill_in] || {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def eval_default
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
|
|
|
|
def matched_workflows_default
|
|
|
|
@result_object.result[:matched_workflows] || []
|
|
|
|
end
|
|
|
|
|
|
|
|
def rerun_count_default
|
|
|
|
@result_object.result[:rerun_count] || 0
|
|
|
|
end
|
|
|
|
|
2021-08-25 12:24:42 +00:00
|
|
|
def options_array(options)
|
|
|
|
result = []
|
|
|
|
|
|
|
|
options.each do |option|
|
|
|
|
result << option['value']
|
|
|
|
if option['children'].present?
|
|
|
|
result += options_array(option['children'])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
|
|
|
def options_hash(options)
|
|
|
|
options.keys
|
|
|
|
end
|
|
|
|
|
|
|
|
def options_relation(attribute)
|
|
|
|
key = "#{attribute[:relation]}_#{attribute[:name]}"
|
|
|
|
@options_relation ||= {}
|
|
|
|
@options_relation[key] ||= "CoreWorkflow::Attributes::#{attribute[:relation]}".constantize.new(attributes: self, attribute: attribute)
|
|
|
|
@options_relation[key].values
|
|
|
|
end
|
|
|
|
|
|
|
|
def attribute_filter?(attribute)
|
|
|
|
screen_value(attribute, 'filter').present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def attribute_options_array?(attribute)
|
|
|
|
attribute[:options].present? && attribute[:options].instance_of?(Array)
|
|
|
|
end
|
|
|
|
|
|
|
|
def attribute_options_hash?(attribute)
|
|
|
|
attribute[:options].present? && attribute[:options].instance_of?(Hash)
|
|
|
|
end
|
|
|
|
|
|
|
|
def attribute_options_relation?(attribute)
|
2022-02-10 15:18:15 +00:00
|
|
|
attribute[:tag] == 'select' && attribute[:relation].present?
|
2021-08-25 12:24:42 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def values(attribute)
|
|
|
|
values = nil
|
|
|
|
if attribute_filter?(attribute)
|
|
|
|
values = screen_value(attribute, 'filter')
|
|
|
|
elsif attribute_options_array?(attribute)
|
|
|
|
values = options_array(attribute[:options])
|
|
|
|
elsif attribute_options_hash?(attribute)
|
|
|
|
values = options_hash(attribute[:options])
|
|
|
|
elsif attribute_options_relation?(attribute)
|
|
|
|
values = options_relation(attribute)
|
|
|
|
end
|
|
|
|
values
|
|
|
|
end
|
|
|
|
|
|
|
|
def values_empty(attribute, values)
|
|
|
|
return values if values == ['']
|
|
|
|
|
|
|
|
saved_value = saved_attribute_value(attribute)
|
|
|
|
if saved_value.present? && values.exclude?(saved_value)
|
|
|
|
values |= Array(saved_value.to_s)
|
|
|
|
end
|
|
|
|
|
|
|
|
if attribute[:nulloption] && values.exclude?('')
|
|
|
|
values.unshift('')
|
|
|
|
end
|
|
|
|
|
|
|
|
values
|
|
|
|
end
|
|
|
|
|
|
|
|
def restrict_values_default
|
|
|
|
result = {}
|
|
|
|
object_elements.each do |attribute|
|
|
|
|
values = values(attribute)
|
|
|
|
next if values.blank?
|
|
|
|
|
|
|
|
values = values_empty(attribute, values)
|
|
|
|
result[ attribute[:name] ] = values.map(&:to_s)
|
|
|
|
end
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
|
|
|
def saved_attribute_value(attribute)
|
|
|
|
|
|
|
|
# special case for owner_id
|
2021-10-21 11:22:06 +00:00
|
|
|
return if saved_only&.class == Ticket && attribute[:name] == 'owner_id'
|
2021-08-25 12:24:42 +00:00
|
|
|
|
2021-10-21 11:22:06 +00:00
|
|
|
saved_only&.try(attribute[:name])
|
2021-08-25 12:24:42 +00:00
|
|
|
end
|
|
|
|
end
|