can_assets.rb 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. # Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. module ApplicationModel::CanAssets
  3. extend ActiveSupport::Concern
  4. =begin
  5. get all assets / related models for this user
  6. user = User.find(123)
  7. result = user.assets(assets_if_exists)
  8. returns
  9. result = {
  10. :User => {
  11. 123 => user_model_123,
  12. 1234 => user_model_1234,
  13. }
  14. }
  15. =end
  16. def assets(data = {})
  17. return data if !authorized_asset?
  18. app_model = self.class.to_app_model
  19. if !data[ app_model ]
  20. data[ app_model ] = {}
  21. end
  22. if !data[ app_model ][ id ]
  23. data[ app_model ][ id ] = attributes_with_association_ids
  24. end
  25. return data if !self['created_by_id'] && !self['updated_by_id']
  26. app_model_user = User.to_app_model
  27. %w[created_by_id updated_by_id].each do |local_user_id|
  28. next if !self[ local_user_id ]
  29. next if data[ app_model_user ] && data[ app_model_user ][ self[ local_user_id ] ]
  30. user = User.lookup(id: self[ local_user_id ])
  31. next if !user
  32. data = user.assets(data)
  33. end
  34. data
  35. end
  36. def authorized_asset?
  37. true
  38. end
  39. =begin
  40. get assets and record_ids of selector
  41. model = Model.find(123)
  42. assets = model.assets_of_selector('attribute_name_of_selector', assets)
  43. =end
  44. def assets_of_selector(selector, assets = {})
  45. value = send(selector)
  46. return assets if value.blank?
  47. assets_of_selector_deep(Selector::Base.migrate_selector(value), assets)
  48. assets
  49. end
  50. def assets_of_selector_deep(value, assets = {})
  51. value[:conditions].each do |item|
  52. if item[:conditions].nil?
  53. assets_of_single_selector(item, assets)
  54. else
  55. assets_of_selector_deep(item, assets)
  56. end
  57. end
  58. end
  59. def assets_added_to?(data)
  60. data.dig(self.class.to_app_model, id).present?
  61. end
  62. private
  63. def assets_of_single_selector(item, assets = {})
  64. area, key = item[:name].split('.')
  65. return if !key
  66. return if %w[ticket_customer ticket_owner].include?(area)
  67. area = 'user' if %w[customer session].include? area
  68. attribute_ref_class, item_ids = if area == 'notification'
  69. notifications_assets_data(item)
  70. else
  71. non_notifications_assets_data(area, key, item)
  72. end
  73. return if !attribute_ref_class
  74. items = item_ids
  75. .compact_blank
  76. .filter_map { |elem| attribute_ref_class.lookup(id: elem) }
  77. ApplicationModel::CanAssets.reduce items, assets
  78. end
  79. def notifications_assets_data(content)
  80. return if content['recipient'].blank?
  81. item_ids = Array(content['recipient'])
  82. .filter_map do |elem|
  83. match = elem.match %r{\Auserid_(?<id>\d+)\z}
  84. match[:id] if match
  85. end
  86. [::User, item_ids]
  87. end
  88. def non_notifications_assets_data(area, key, content)
  89. return if %w[article execution_time].include? area
  90. begin
  91. attribute_class = area.to_classname.constantize
  92. rescue => e
  93. logger.error "Unable to get asset for '#{area}': #{e.inspect}"
  94. return
  95. end
  96. reflection = key.delete_suffix '_id'
  97. klass = Models.all.dig(attribute_class, :reflections, reflection)&.klass
  98. return if !klass
  99. [klass, Array(content['value'])]
  100. end
  101. # methods defined here are going to extend the class, not the instance of it
  102. class_methods do
  103. =begin
  104. return object and assets
  105. data = Model.full(123)
  106. data = {
  107. id: 123,
  108. assets: assets,
  109. }
  110. =end
  111. def full(id)
  112. object = find(id)
  113. assets = object.assets({})
  114. {
  115. id: object.id,
  116. assets: assets,
  117. }
  118. end
  119. =begin
  120. get assets of object list
  121. list = [
  122. {
  123. object => 'Ticket',
  124. o_id => 1,
  125. },
  126. {
  127. object => 'User',
  128. o_id => 121,
  129. },
  130. ]
  131. assets = Model.assets_of_object_list(list, assets)
  132. =end
  133. def assets_of_object_list(list, assets = {})
  134. list.each do |item|
  135. record = item['object'].constantize.lookup(id: item['o_id'])
  136. next if record.blank?
  137. assets = record.assets(assets)
  138. if item['created_by_id'].present?
  139. user = User.find(item['created_by_id'])
  140. assets = user.assets(assets)
  141. end
  142. if item['updated_by_id'].present?
  143. user = User.find(item['updated_by_id'])
  144. assets = user.assets(assets)
  145. end
  146. end
  147. assets
  148. end
  149. end
  150. class << self
  151. =begin
  152. Compiles an assets hash for given items
  153. @param items [Array<CanAssets>] list of items responding to @see #assets
  154. @param data [Hash] given collection. Empty {} or assets collection in progress
  155. @param suffix [String] try to use non-default assets method
  156. @return [Hash] collection including assets of items
  157. @example
  158. list = Ticket.all
  159. ApplicationModel::CanAssets.reduce(list, {})
  160. =end
  161. def reduce(items, data = {}, suffix = nil)
  162. items.reduce(data) do |memo, elem|
  163. method_name = if suffix.present? && elem.respond_to?(:"assets_#{suffix}")
  164. "assets_#{suffix}"
  165. else
  166. :assets
  167. end
  168. elem.send method_name, memo
  169. end
  170. end
  171. end
  172. end