attributes.rb 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. # Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
  2. require 'digest/md5'
  3. class CoreWorkflow::Attributes
  4. attr_accessor :user, :payload, :assets
  5. def initialize(result_object:)
  6. @result_object = result_object
  7. @user = result_object.user
  8. @payload = result_object.payload
  9. @assets = result_object.assets
  10. end
  11. def payload_class
  12. @payload['class_name'].constantize
  13. end
  14. def selected_only
  15. # params loading and preparing is very expensive so cache it
  16. checksum = Digest::MD5.hexdigest(Marshal.dump(@payload['params']))
  17. return @selected_only[checksum] if @selected_only.present? && @selected_only[checksum]
  18. @selected_only = {}
  19. @selected_only[checksum] = begin
  20. clean_params = payload_class.association_name_to_id_convert(@payload['params'])
  21. clean_params = payload_class.param_cleanup(clean_params, true, false, false)
  22. payload_class.new(clean_params)
  23. end
  24. end
  25. def selectable_field?(key)
  26. return if key == 'id'
  27. return if !@payload['params'].key?(key)
  28. # some objects have no attributes like "CoreWorkflow"-object as well.
  29. # attributes only exists in the frontend so we skip this check
  30. return true if object_elements.blank?
  31. object_elements_hash.key?(key)
  32. end
  33. def overwrite_selected(result)
  34. selected_attributes = selected_only.attributes
  35. selected_attributes.each_key do |key|
  36. next if !selectable_field?(key)
  37. # special behaviour for owner id
  38. if key == 'owner_id' && selected_attributes[key].nil?
  39. selected_attributes[key] = 1
  40. end
  41. result[key.to_sym] = selected_attributes[key]
  42. end
  43. result
  44. end
  45. def selected
  46. if @payload['params']['id'] && payload_class.exists?(id: @payload['params']['id'])
  47. result = saved_only
  48. overwrite_selected(result)
  49. else
  50. selected_only
  51. end
  52. end
  53. def saved_only
  54. return if @payload['params']['id'].blank?
  55. # dont use lookup here because the cache will not
  56. # know about new attributes and make crashes
  57. @saved_only ||= payload_class.find_by(id: @payload['params']['id'])
  58. # we use marshal here because clone still uses references and dup can't
  59. # detect changes for the rails object
  60. Marshal.load(Marshal.dump(@saved_only))
  61. end
  62. def saved
  63. @saved ||= saved_only || payload_class.new
  64. end
  65. def object_elements
  66. @object_elements ||= ObjectManager::Object.new(@payload['class_name']).attributes(@user, saved_only, data_only: false).each_with_object([]) do |element, result|
  67. result << element.data.merge(screens: element.screens)
  68. end
  69. end
  70. def object_elements_hash
  71. @object_elements_hash ||= object_elements.index_by { |x| x[:name] }
  72. end
  73. def screen_value(attribute, type)
  74. attribute[:screens].dig(@payload['screen'], type)
  75. end
  76. def request_id_default
  77. payload['request_id']
  78. end
  79. # dont cache this else the result object will work with references and cache bugs occur
  80. def visibility_default
  81. object_elements.each_with_object({}) do |attribute, result|
  82. result[ attribute[:name] ] = screen_value(attribute, 'shown') == false ? 'hide' : 'show'
  83. end
  84. end
  85. def attribute_mandatory?(attribute)
  86. return screen_value(attribute, 'required').present? if !screen_value(attribute, 'required').nil?
  87. return screen_value(attribute, 'null').blank? if !screen_value(attribute, 'null').nil?
  88. false
  89. end
  90. # dont cache this else the result object will work with references and cache bugs occur
  91. def mandatory_default
  92. object_elements.each_with_object({}) do |attribute, result|
  93. result[ attribute[:name] ] = attribute_mandatory?(attribute)
  94. end
  95. end
  96. # dont cache this else the result object will work with references and cache bugs occur
  97. def auto_select_default
  98. object_elements.each_with_object({}) do |attribute, result|
  99. next if !attribute[:only_shown_if_selectable]
  100. result[ attribute[:name] ] = true
  101. end
  102. end
  103. # dont cache this else the result object will work with references and cache bugs occur
  104. def readonly_default
  105. object_elements.each_with_object({}) do |attribute, result|
  106. result[ attribute[:name] ] = false
  107. end
  108. end
  109. def select_default
  110. @result_object.result[:select] || {}
  111. end
  112. def fill_in_default
  113. @result_object.result[:fill_in] || {}
  114. end
  115. def eval_default
  116. []
  117. end
  118. def matched_workflows_default
  119. @result_object.result[:matched_workflows] || []
  120. end
  121. def rerun_count_default
  122. @result_object.result[:rerun_count] || 0
  123. end
  124. def options_array(options)
  125. result = []
  126. options.each do |option|
  127. result << option['value']
  128. if option['children'].present?
  129. result += options_array(option['children'])
  130. end
  131. end
  132. result
  133. end
  134. def options_hash(options)
  135. options.keys
  136. end
  137. def options_relation(attribute)
  138. key = "#{attribute[:relation]}_#{attribute[:name]}"
  139. @options_relation ||= {}
  140. @options_relation[key] ||= "CoreWorkflow::Attributes::#{attribute[:relation]}".constantize.new(attributes: self, attribute: attribute)
  141. @options_relation[key].values
  142. end
  143. def attribute_filter?(attribute)
  144. screen_value(attribute, 'filter').present?
  145. end
  146. def attribute_options_array?(attribute)
  147. attribute[:options].present? && attribute[:options].instance_of?(Array)
  148. end
  149. def attribute_options_hash?(attribute)
  150. attribute[:options].present? && attribute[:options].instance_of?(Hash)
  151. end
  152. def attribute_options_relation?(attribute)
  153. attribute[:relation].present?
  154. end
  155. def values(attribute)
  156. values = nil
  157. if attribute_filter?(attribute)
  158. values = screen_value(attribute, 'filter')
  159. elsif attribute_options_array?(attribute)
  160. values = options_array(attribute[:options])
  161. elsif attribute_options_hash?(attribute)
  162. values = options_hash(attribute[:options])
  163. elsif attribute_options_relation?(attribute)
  164. values = options_relation(attribute)
  165. end
  166. values
  167. end
  168. def values_empty(attribute, values)
  169. return values if values == ['']
  170. saved_value = saved_attribute_value(attribute)
  171. if saved_value.present? && values.exclude?(saved_value)
  172. values |= Array(saved_value.to_s)
  173. end
  174. if attribute[:nulloption] && values.exclude?('')
  175. values.unshift('')
  176. end
  177. values
  178. end
  179. def restrict_values_default
  180. result = {}
  181. object_elements.each do |attribute|
  182. values = values(attribute)
  183. next if values.blank?
  184. values = values_empty(attribute, values)
  185. result[ attribute[:name] ] = values.map(&:to_s)
  186. end
  187. result
  188. end
  189. def saved_attribute_value(attribute)
  190. # special case for owner_id
  191. return if saved_only&.class == Ticket && attribute[:name] == 'owner_id'
  192. saved_only&.try(attribute[:name])
  193. end
  194. end