123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- class Taskbar < ApplicationModel
- include ChecksClientNotification
- include ::Taskbar::HasAttachments
- include Taskbar::Assets
- include Taskbar::TriggersSubscriptions
- include Taskbar::List
- TASKBAR_APPS = %w[desktop mobile].freeze
- TASKBAR_STATIC_ENTITIES = %w[
- Search
- ].freeze
- store :state
- store :params
- store :preferences
- belongs_to :user
- validates :app, inclusion: { in: TASKBAR_APPS }
- validates :key, uniqueness: { scope: %i[user_id app] }
- before_validation :set_user
- before_create :update_last_contact, :update_preferences_infos
- before_update :update_last_contact, :update_preferences_infos
- after_update :notify_clients
- after_destroy :update_preferences_infos, :notify_clients
- association_attributes_ignored :user
- client_notification_events_ignored :create, :update, :touch
- client_notification_send_to :user_id
- attr_accessor :local_update
- default_scope { order(:id) }
- scope :related_taskbars, lambda { |taskbar|
- where(key: taskbar.key)
- .where.not(id: taskbar.id)
- }
- scope :app, ->(app) { where(app:) }
- def self.taskbar_entities
- @taskbar_entities ||= begin
- ApplicationModel.descendants.select { |model| model.included_modules.include?(HasTaskbars) }.each_with_object([]) do |model, result|
- model.taskbar_entities&.each do |entity|
- result << entity
- end
- end | TASKBAR_STATIC_ENTITIES
- end
- end
- def self.taskbar_ignore_state_updates_entities
- @taskbar_ignore_state_updates_entities ||= begin
- ApplicationModel.descendants.select { |model| model.included_modules.include?(HasTaskbars) }.each_with_object([]) do |model, result|
- model.taskbar_ignore_state_updates_entities&.each do |entity|
- result << entity
- end
- end
- end
- end
- def state_changed?
- return false if state.blank?
- state.each do |key, value|
- if value.is_a? Hash
- value.each do |key1, value1|
- next if value1.blank?
- next if key1 == 'form_id'
- return true
- end
- else
- next if value.blank?
- next if key == 'form_id'
- return true
- end
- end
- false
- end
- def attributes_with_association_names(empty_keys: false)
- add_attachments_to_attributes(super)
- end
- def attributes_with_association_ids
- add_attachments_to_attributes(super)
- end
- def as_json(options = {})
- add_attachments_to_attributes(super)
- end
- def preferences_task_info
- output = { user_id:, apps: { app.to_sym => { last_contact: last_contact, changed: state_changed? } } }
- output[:id] = id if persisted?
- output
- end
- def related_taskbars
- self.class.related_taskbars(self)
- end
- def touch_last_contact!
- # Don't inform the current user (only!) about live user and item updates.
- self.skip_live_user_trigger = true
- self.skip_item_trigger = true
- self.last_contact = Time.zone.now
- save!
- end
- def saved_change_to_dirty?
- return false if !saved_change_to_preferences?
- !!preferences[:dirty] != !!preferences_previously_was[:dirty]
- end
- private
- def update_last_contact
- return if local_update
- return if changes.blank?
- return if changed_only_prio?
- if changes['notify']
- count = 0
- changes.each_key do |attribute|
- next if attribute == 'updated_at'
- next if attribute == 'created_at'
- count += 1
- end
- return true if count <= 1
- end
- self.last_contact = Time.zone.now
- end
- def set_user
- return if local_update
- return if !UserInfo.current_user_id
- self.user_id = UserInfo.current_user_id
- end
- def update_preferences_infos
- return if key == 'Search'
- return if local_update
- return if changed_only_prio?
- preferences = self.preferences || {}
- preferences[:tasks] = collect_related_tasks
- update_related_taskbars(preferences)
- # remember preferences for current taskbar
- self.preferences = preferences if !destroyed?
- end
- def collect_related_tasks
- related_taskbars.map(&:preferences_task_info)
- .tap { |arr| arr.push(preferences_task_info) if !destroyed? }
- .each_with_object({}) { |elem, memo| reduce_related_tasks(elem, memo) }
- .values
- .sort_by { |elem| elem[:id] || Float::MAX } # sort by IDs to pass old tests
- end
- def changed_only_prio?
- changed_attribute_names_to_save.to_set == Set.new(%w[updated_at prio])
- end
- def reduce_related_tasks(elem, memo)
- key = elem[:user_id]
- if memo[key]
- memo[key].deep_merge! elem
- return
- end
- memo[key] = elem
- end
- def update_related_taskbars(preferences)
- related_taskbars.each do |taskbar|
- taskbar.with_lock do
- taskbar.preferences = preferences
- taskbar.local_update = true
- taskbar.skip_item_trigger = true
- taskbar.save!
- end
- end
- end
- def notify_clients
- return if !saved_change_to_attribute?('preferences')
- data = {
- event: 'taskbar:preferences',
- data: {
- id: id,
- key: key,
- preferences: preferences,
- },
- }
- PushMessages.send_to(
- user_id,
- data,
- )
- end
- end
|