can_assets.rb 4.7 KB

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