taskbar.rb 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Taskbar < ApplicationModel
  3. include ChecksClientNotification
  4. include ::Taskbar::HasAttachments
  5. include Taskbar::Assets
  6. include Taskbar::TriggersSubscriptions
  7. include Taskbar::List
  8. include Taskbar::Validator
  9. TASKBAR_APPS = %w[desktop mobile].freeze
  10. TASKBAR_ENTITIES = %w[
  11. OrganizationProfile
  12. Search
  13. TicketCreate
  14. TicketZoom
  15. UserProfile
  16. ].freeze
  17. store :state
  18. store :params
  19. store :preferences
  20. belongs_to :user
  21. validates :app, inclusion: { in: TASKBAR_APPS }
  22. before_create :update_last_contact, :set_user, :update_preferences_infos
  23. before_update :update_last_contact, :set_user, :update_preferences_infos
  24. after_update :notify_clients
  25. after_destroy :update_preferences_infos, :notify_clients
  26. association_attributes_ignored :user
  27. client_notification_events_ignored :create, :update, :touch
  28. client_notification_send_to :user_id
  29. attr_accessor :local_update
  30. default_scope { order(:id) }
  31. scope :related_taskbars, lambda { |taskbar|
  32. where(key: taskbar.key)
  33. .where.not(id: taskbar.id)
  34. }
  35. # Returns IDs of objects referenced by the taskbars.
  36. # Works on scopes, relations etc.
  37. #
  38. # @return [Hash{Symbol=>Array<Integer>}] of arrays of object IDs
  39. #
  40. # @example
  41. #
  42. # user.taskbars.to_object_ids # => { user_ids: [1, 2, 3], organization_ids: [1, 2, 3], ticket_ids: [1, 2, 3] }
  43. #
  44. def self.to_object_ids
  45. all.each_with_object({ user_ids: [], organization_ids: [], ticket_ids: [] }) do |elem, memo|
  46. case elem.params
  47. in { user_id: }
  48. memo[:user_ids] << user_id.to_i
  49. in { organization_id: }
  50. memo[:organization_ids] << organization_id.to_i
  51. in { ticket_id: }
  52. memo[:ticket_ids] << ticket_id.to_i
  53. else
  54. end
  55. end
  56. end
  57. def state_changed?
  58. return false if state.blank?
  59. state.each_value do |value|
  60. if value.is_a? Hash
  61. value.each do |key1, value1|
  62. next if value1.blank?
  63. next if key1 == 'form_id'
  64. return true
  65. end
  66. else
  67. next if value.blank?
  68. return true
  69. end
  70. end
  71. false
  72. end
  73. def attributes_with_association_names(empty_keys: false)
  74. add_attachments_to_attributes(super)
  75. end
  76. def attributes_with_association_ids
  77. add_attachments_to_attributes(super)
  78. end
  79. def as_json(options = {})
  80. add_attachments_to_attributes(super)
  81. end
  82. def preferences_task_info
  83. output = { user_id:, apps: { app.to_sym => { last_contact: last_contact, changed: state_changed? } } }
  84. output[:id] = id if persisted?
  85. output
  86. end
  87. def related_taskbars
  88. self.class.related_taskbars(self)
  89. end
  90. def touch_last_contact!
  91. # Don't inform the current user (only!) about live user and item updates.
  92. self.skip_live_user_trigger = true
  93. self.skip_item_trigger = true
  94. self.last_contact = Time.zone.now
  95. save!
  96. end
  97. private
  98. def update_last_contact
  99. return true if local_update
  100. return true if changes.blank?
  101. if changes['notify']
  102. count = 0
  103. changes.each_key do |attribute|
  104. next if attribute == 'updated_at'
  105. next if attribute == 'created_at'
  106. count += 1
  107. end
  108. return true if count <= 1
  109. end
  110. self.last_contact = Time.zone.now
  111. end
  112. def set_user
  113. return true if local_update
  114. return true if !UserInfo.current_user_id
  115. self.user_id = UserInfo.current_user_id
  116. end
  117. def update_preferences_infos
  118. return if key == 'Search'
  119. return if local_update
  120. preferences = self.preferences || {}
  121. preferences[:tasks] = collect_related_tasks
  122. update_related_taskbars(preferences)
  123. # remember preferences for current taskbar
  124. self.preferences = preferences if !destroyed?
  125. end
  126. def collect_related_tasks
  127. related_taskbars.map(&:preferences_task_info)
  128. .tap { |arr| arr.push(preferences_task_info) if !destroyed? }
  129. .each_with_object({}) { |elem, memo| reduce_related_tasks(elem, memo) }
  130. .values
  131. .sort_by { |elem| elem[:id] || Float::MAX } # sort by IDs to pass old tests
  132. end
  133. def reduce_related_tasks(elem, memo)
  134. key = elem[:user_id]
  135. if memo[key]
  136. memo[key].deep_merge! elem
  137. return
  138. end
  139. memo[key] = elem
  140. end
  141. def update_related_taskbars(preferences)
  142. related_taskbars.each do |taskbar|
  143. taskbar.with_lock do
  144. taskbar.preferences = preferences
  145. taskbar.local_update = true
  146. taskbar.skip_item_trigger = true
  147. taskbar.save!
  148. end
  149. end
  150. end
  151. def notify_clients
  152. return true if !saved_change_to_attribute?('preferences')
  153. data = {
  154. event: 'taskbar:preferences',
  155. data: {
  156. id: id,
  157. key: key,
  158. preferences: preferences,
  159. },
  160. }
  161. PushMessages.send_to(
  162. user_id,
  163. data,
  164. )
  165. end
  166. end