can_assets.rb 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. # Copyright (C) 2012-2024 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. send(selector)
  46. .each_with_object(assets) do |(item, content), memo|
  47. assets_of_single_selector(item, content, memo)
  48. end
  49. end
  50. def assets_added_to?(data)
  51. data.dig(self.class.to_app_model, id).present?
  52. end
  53. private
  54. def assets_of_single_selector(item, content, assets = {})
  55. area, key = item.split('.')
  56. return if !key
  57. return if %w[ticket_customer ticket_owner].include?(area)
  58. area = 'user' if %w[customer session].include? area
  59. attribute_ref_class, item_ids = if area == 'notification'
  60. notifications_assets_data(content)
  61. else
  62. non_notifications_assets_data(area, key, content)
  63. end
  64. return if !attribute_ref_class
  65. items = item_ids
  66. .compact_blank
  67. .filter_map { |elem| attribute_ref_class.lookup(id: elem) }
  68. ApplicationModel::CanAssets.reduce items, assets
  69. end
  70. def notifications_assets_data(content)
  71. return if content['recipient'].blank?
  72. item_ids = Array(content['recipient'])
  73. .filter_map do |elem|
  74. match = elem.match %r{\Auserid_(?<id>\d+)\z}
  75. match[:id] if match
  76. end
  77. [::User, item_ids]
  78. end
  79. def non_notifications_assets_data(area, key, content)
  80. return if %w[article execution_time].include? area
  81. begin
  82. attribute_class = area.to_classname.constantize
  83. rescue => e
  84. logger.error "Unable to get asset for '#{area}': #{e.inspect}"
  85. return
  86. end
  87. reflection = key.delete_suffix '_id'
  88. klass = Models.all.dig(attribute_class, :reflections, reflection)&.klass
  89. return if !klass
  90. [klass, Array(content['value'])]
  91. end
  92. # methods defined here are going to extend the class, not the instance of it
  93. class_methods do
  94. =begin
  95. return object and assets
  96. data = Model.full(123)
  97. data = {
  98. id: 123,
  99. assets: assets,
  100. }
  101. =end
  102. def full(id)
  103. object = find(id)
  104. assets = object.assets({})
  105. {
  106. id: object.id,
  107. assets: assets,
  108. }
  109. end
  110. =begin
  111. get assets of object list
  112. list = [
  113. {
  114. object => 'Ticket',
  115. o_id => 1,
  116. },
  117. {
  118. object => 'User',
  119. o_id => 121,
  120. },
  121. ]
  122. assets = Model.assets_of_object_list(list, assets)
  123. =end
  124. def assets_of_object_list(list, assets = {})
  125. list.each do |item|
  126. record = item['object'].constantize.lookup(id: item['o_id'])
  127. next if record.blank?
  128. assets = record.assets(assets)
  129. if item['created_by_id'].present?
  130. user = User.find(item['created_by_id'])
  131. assets = user.assets(assets)
  132. end
  133. if item['updated_by_id'].present?
  134. user = User.find(item['updated_by_id'])
  135. assets = user.assets(assets)
  136. end
  137. end
  138. assets
  139. end
  140. end
  141. class << self
  142. =begin
  143. Compiles an assets hash for given items
  144. @param items [Array<CanAssets>] list of items responding to @see #assets
  145. @param data [Hash] given collection. Empty {} or assets collection in progress
  146. @param suffix [String] try to use non-default assets method
  147. @return [Hash] collection including assets of items
  148. @example
  149. list = Ticket.all
  150. ApplicationModel::CanAssets.reduce(list, {})
  151. =end
  152. def reduce(items, data = {}, suffix = nil)
  153. items.reduce(data) do |memo, elem|
  154. method_name = if suffix.present? && elem.respond_to?(:"assets_#{suffix}")
  155. "assets_#{suffix}"
  156. else
  157. :assets
  158. end
  159. elem.send method_name, memo
  160. end
  161. end
  162. end
  163. end