result.rb 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class CoreWorkflow::Result
  3. include ::Mixin::HasBackends
  4. MAX_RERUN = 25
  5. attr_accessor :payload, :payload_backup, :user, :assets, :assets_in_result, :result, :rerun, :rerun_history, :form_updater, :restricted_fields
  6. def initialize(payload:, user:, assets: {}, assets_in_result: true, result: {}, form_updater: false)
  7. if payload.respond_to?(:permit!)
  8. payload = payload.permit!.to_h
  9. end
  10. raise ArgumentError, __("The required parameter 'payload->class_name' is missing.") if !payload['class_name']
  11. raise ArgumentError, __("The required parameter 'payload->screen' is missing.") if !payload['screen']
  12. @restricted_fields = {}
  13. @payload = payload
  14. @payload_backup = Marshal.load(Marshal.dump(payload))
  15. @user = user
  16. @assets = assets
  17. @assets_in_result = assets_in_result
  18. @result = result
  19. @form_updater = form_updater
  20. @rerun = false
  21. @rerun_history = []
  22. end
  23. def attributes
  24. @attributes ||= CoreWorkflow::Attributes.new(result_object: self)
  25. end
  26. def workflows
  27. CoreWorkflow.active.object(payload['class_name'])
  28. end
  29. def set_default
  30. @rerun = false
  31. set_payload_body
  32. set_payload_customer_id_default
  33. @result[:restrict_values] = {}
  34. %i[request_id visibility mandatory readonly select fill_in eval matched_workflows rerun_count].each do |group|
  35. @result[group] = attributes.send(:"#{group}_default")
  36. end
  37. set_form_updater_default
  38. # restrict init defaults to make sure param values to removed if not allowed
  39. attributes.restrict_values_default.each do |field, values|
  40. # skip initial rerun to improve performance
  41. # priority e.g. would trigger a rerun because its not set yet
  42. # but we skip rerun here because the initial values have no logic which
  43. # are dependent on form changes
  44. run_backend_value('set_fixed_to', field, values, skip_rerun: true, skip_mark_restricted: true)
  45. end
  46. set_default_only_shown_if_selectable
  47. end
  48. def set_payload_body
  49. @payload['params']['body'] = @payload.dig('params', 'article', 'body')
  50. end
  51. def set_payload_customer_id_default
  52. return if !@payload['params']['customer_id'].nil?
  53. return if !@user
  54. return if !@user.permissions?('ticket.customer')
  55. return if @user.permissions?('ticket.agent')
  56. @payload['params']['customer_id'] = @user.id.to_s
  57. end
  58. def set_form_updater_default
  59. return if !form_updater
  60. @result[:all_options] = attributes.all_options_default
  61. @result[:historical_options] = attributes.historical_options_default
  62. end
  63. def set_default_only_shown_if_selectable
  64. # only_shown_if_selectable should not work on bulk feature
  65. return if @payload['screen'] == 'overview_bulk'
  66. auto_hide = {}
  67. attributes.auto_select_default.each do |field, state|
  68. result = run_backend_value('auto_select', field, state)
  69. next if result.compact.blank?
  70. auto_hide[field] = true
  71. end
  72. auto_hide.each do |field, state|
  73. run_backend_value('hide', field, state)
  74. end
  75. end
  76. def run
  77. set_default
  78. workflows.each do |workflow|
  79. condition = CoreWorkflow::Condition.new(result_object: self, workflow: workflow)
  80. next if !condition.match_all?
  81. run_workflow(workflow)
  82. run_custom(workflow, condition)
  83. match_workflow(workflow)
  84. break if workflow.stop_after_match
  85. end
  86. consider_rerun
  87. end
  88. def matches_selector?(selector:, check:)
  89. condition_object = CoreWorkflow::Condition.new(result_object: self)
  90. condition_object.check = check
  91. condition_object.condition_selector_match?(selector)
  92. end
  93. def run_workflow(workflow)
  94. Array(workflow.perform).each do |field, config|
  95. run_backend(field, config)
  96. end
  97. end
  98. def run_custom(workflow, condition)
  99. Array(workflow.perform.dig('custom.module', 'execute')).each do |module_path|
  100. custom_module = module_path.constantize.new(condition_object: condition, result_object: self)
  101. custom_module.perform
  102. end
  103. end
  104. def run_backend(field, perform_config, skip_rerun: false, skip_mark_restricted: false)
  105. Array(perform_config['operator']).map do |backend|
  106. "CoreWorkflow::Result::#{backend.classify}"
  107. .constantize
  108. .new(result_object: self, field: field, perform_config: perform_config, skip_rerun: skip_rerun, skip_mark_restricted: skip_mark_restricted)
  109. .run
  110. end
  111. end
  112. def run_backend_value(backend, field, value, skip_rerun: false, skip_mark_restricted: false)
  113. perform_config = {
  114. 'operator' => backend,
  115. backend => value,
  116. }
  117. run_backend(field, perform_config, skip_rerun: skip_rerun, skip_mark_restricted: skip_mark_restricted)
  118. end
  119. def change_flags(flags)
  120. @result[:flags] ||= {}
  121. @result[:flags] = @result[:flags].merge(flags)
  122. end
  123. def match_workflow(workflow)
  124. @result[:matched_workflows] |= Array(workflow.id)
  125. end
  126. def assets_in_result?
  127. return false if assets == false
  128. return false if !@assets_in_result
  129. @result[:assets] = assets
  130. true
  131. end
  132. def workflow_restricted_fields
  133. @workflow_restricted_fields ||= begin
  134. result = []
  135. workflows.each do |workflow|
  136. fields = workflow.perform.each_with_object([]) do |(key, value), result_inner|
  137. next if %w[select remove_option set_fixed_to add_option].exclude?(value['operator'])
  138. result_inner << key.split('.')[-1]
  139. end
  140. result |= fields
  141. end
  142. result
  143. end
  144. end
  145. def filter_restrict_values
  146. @result[:restrict_values].select! do |field, _values|
  147. attribute = attributes.object_elements_hash[field]
  148. next if attribute && workflow_restricted_fields.exclude?(field) && !@restricted_fields[field] && !attributes.attribute_options_relation?(attribute) && !attributes.attribute_filter?(attribute)
  149. true
  150. end
  151. end
  152. def rerun_loop?
  153. return false if rerun_history.size < 3
  154. rerun_history.last(3).uniq.size != 3
  155. end
  156. def consider_rerun
  157. @rerun_history << Marshal.load(Marshal.dump(@result.except(:rerun_count)))
  158. if @rerun && @result[:rerun_count] < MAX_RERUN && !rerun_loop?
  159. @result[:rerun_count] += 1
  160. return run
  161. end
  162. filter_restrict_values if !@form_updater
  163. assets_in_result?
  164. @result
  165. end
  166. end