taskbar.rb 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. TASKBAR_APPS = %w[desktop mobile].freeze
  9. TASKBAR_STATIC_ENTITIES = %w[
  10. Search
  11. ].freeze
  12. store :state
  13. store :params
  14. store :preferences
  15. belongs_to :user
  16. validates :app, inclusion: { in: TASKBAR_APPS }
  17. validates :key, uniqueness: { scope: %i[user_id app] }
  18. before_validation :set_user
  19. before_create :update_last_contact, :update_preferences_infos
  20. before_update :update_last_contact, :update_preferences_infos
  21. after_update :notify_clients
  22. after_destroy :update_preferences_infos, :notify_clients
  23. association_attributes_ignored :user
  24. client_notification_events_ignored :create, :update, :touch
  25. client_notification_send_to :user_id
  26. attr_accessor :local_update
  27. default_scope { order(:id) }
  28. scope :related_taskbars, lambda { |taskbar|
  29. where(key: taskbar.key)
  30. .where.not(id: taskbar.id)
  31. }
  32. def self.taskbar_entities
  33. ApplicationModel.descendants.select { |model| model.included_modules.include?(HasTaskbars) }.each_with_object([]) do |model, result|
  34. model.taskbar_entities&.each do |entity|
  35. result << entity
  36. end
  37. end | TASKBAR_STATIC_ENTITIES
  38. end
  39. def state_changed?
  40. return false if state.blank?
  41. state.each do |key, value|
  42. if value.is_a? Hash
  43. value.each do |key1, value1|
  44. next if value1.blank?
  45. next if key1 == 'form_id'
  46. return true
  47. end
  48. else
  49. next if value.blank?
  50. next if key == 'form_id'
  51. return true
  52. end
  53. end
  54. false
  55. end
  56. def attributes_with_association_names(empty_keys: false)
  57. add_attachments_to_attributes(super)
  58. end
  59. def attributes_with_association_ids
  60. add_attachments_to_attributes(super)
  61. end
  62. def as_json(options = {})
  63. add_attachments_to_attributes(super)
  64. end
  65. def preferences_task_info
  66. output = { user_id:, apps: { app.to_sym => { last_contact: last_contact, changed: state_changed? } } }
  67. output[:id] = id if persisted?
  68. output
  69. end
  70. def related_taskbars
  71. self.class.related_taskbars(self)
  72. end
  73. def touch_last_contact!
  74. # Don't inform the current user (only!) about live user and item updates.
  75. self.skip_live_user_trigger = true
  76. self.skip_item_trigger = true
  77. self.last_contact = Time.zone.now
  78. save!
  79. end
  80. private
  81. def update_last_contact
  82. return true if local_update
  83. return true if changes.blank?
  84. if changes['notify']
  85. count = 0
  86. changes.each_key do |attribute|
  87. next if attribute == 'updated_at'
  88. next if attribute == 'created_at'
  89. count += 1
  90. end
  91. return true if count <= 1
  92. end
  93. self.last_contact = Time.zone.now
  94. end
  95. def set_user
  96. return true if local_update
  97. return true if !UserInfo.current_user_id
  98. self.user_id = UserInfo.current_user_id
  99. end
  100. def update_preferences_infos
  101. return if key == 'Search'
  102. return if local_update
  103. preferences = self.preferences || {}
  104. preferences[:tasks] = collect_related_tasks
  105. update_related_taskbars(preferences)
  106. # remember preferences for current taskbar
  107. self.preferences = preferences if !destroyed?
  108. end
  109. def collect_related_tasks
  110. related_taskbars.map(&:preferences_task_info)
  111. .tap { |arr| arr.push(preferences_task_info) if !destroyed? }
  112. .each_with_object({}) { |elem, memo| reduce_related_tasks(elem, memo) }
  113. .values
  114. .sort_by { |elem| elem[:id] || Float::MAX } # sort by IDs to pass old tests
  115. end
  116. def reduce_related_tasks(elem, memo)
  117. key = elem[:user_id]
  118. if memo[key]
  119. memo[key].deep_merge! elem
  120. return
  121. end
  122. memo[key] = elem
  123. end
  124. def update_related_taskbars(preferences)
  125. related_taskbars.each do |taskbar|
  126. taskbar.with_lock do
  127. taskbar.preferences = preferences
  128. taskbar.local_update = true
  129. taskbar.skip_item_trigger = true
  130. taskbar.save!
  131. end
  132. end
  133. end
  134. def notify_clients
  135. return true if !saved_change_to_attribute?('preferences')
  136. data = {
  137. event: 'taskbar:preferences',
  138. data: {
  139. id: id,
  140. key: key,
  141. preferences: preferences,
  142. },
  143. }
  144. PushMessages.send_to(
  145. user_id,
  146. data,
  147. )
  148. end
  149. end