notification.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class Transaction::Notification
  3. =begin
  4. {
  5. object: 'Ticket',
  6. type: 'update',
  7. object_id: 123,
  8. interface_handle: 'application_server', # application_server|websocket|scheduler
  9. changes: {
  10. 'attribute1' => [before, now],
  11. 'attribute2' => [before, now],
  12. },
  13. created_at: Time.zone.now,
  14. user_id: 123,
  15. },
  16. =end
  17. def initialize(item, params = {})
  18. @item = item
  19. @params = params
  20. end
  21. def perform
  22. # return if we run import mode
  23. return if Setting.get('import_mode')
  24. return if @item[:object] != 'Ticket'
  25. return if @params[:disable_notification]
  26. ticket = Ticket.find_by(id: @item[:object_id])
  27. return if !ticket
  28. if @item[:article_id]
  29. article = Ticket::Article.find(@item[:article_id])
  30. # ignore notifications
  31. sender = Ticket::Article::Sender.lookup(id: article.sender_id)
  32. if sender&.name == 'System'
  33. return if @item[:changes].blank? && article.preferences[:notification] != true
  34. if article.preferences[:notification] != true
  35. article = nil
  36. end
  37. end
  38. end
  39. # find recipients
  40. recipients_and_channels = []
  41. recipients_reason = {}
  42. # loop through all users
  43. possible_recipients = possible_recipients_of_group(ticket.group_id)
  44. # apply owner
  45. if ticket.owner_id != 1
  46. possible_recipients.push ticket.owner
  47. recipients_reason[ticket.owner_id] = 'are assigned'
  48. end
  49. # apply out of office agents
  50. possible_recipients_additions = Set.new
  51. possible_recipients.each do |user|
  52. recursive_ooo_replacements(
  53. user: user,
  54. replacements: possible_recipients_additions,
  55. reasons: recipients_reason,
  56. )
  57. end
  58. if possible_recipients_additions.present?
  59. # join unique entries
  60. possible_recipients = possible_recipients | possible_recipients_additions.to_a
  61. end
  62. already_checked_recipient_ids = {}
  63. possible_recipients.each do |user|
  64. result = NotificationFactory::Mailer.notification_settings(user, ticket, @item[:type])
  65. next if !result
  66. next if already_checked_recipient_ids[user.id]
  67. already_checked_recipient_ids[user.id] = true
  68. recipients_and_channels.push result
  69. next if recipients_reason[user.id]
  70. recipients_reason[user.id] = 'are in group'
  71. end
  72. # send notifications
  73. recipients_and_channels.each do |item|
  74. user = item[:user]
  75. channels = item[:channels]
  76. # ignore user who changed it by him self via web
  77. if @params[:interface_handle] == 'application_server'
  78. next if article&.updated_by_id == user.id
  79. next if !article && @item[:user_id] == user.id
  80. end
  81. # ignore inactive users
  82. next if !user.active?
  83. # ignore if no changes has been done
  84. changes = human_changes(user, ticket)
  85. next if @item[:type] == 'update' && !article && changes.blank?
  86. # check if today already notified
  87. if @item[:type] == 'reminder_reached' || @item[:type] == 'escalation' || @item[:type] == 'escalation_warning'
  88. identifier = user.email
  89. if !identifier || identifier == ''
  90. identifier = user.login
  91. end
  92. already_notified = History.where(
  93. history_type_id: History.type_lookup('notification').id,
  94. history_object_id: History.object_lookup('Ticket').id,
  95. o_id: ticket.id
  96. ).where('created_at > ?', Time.zone.now.beginning_of_day).where('value_to LIKE ?', "%#{identifier}(#{@item[:type]}:%").exists?
  97. next if already_notified
  98. end
  99. # create online notification
  100. used_channels = []
  101. if channels['online']
  102. used_channels.push 'online'
  103. created_by_id = @item[:user_id] || 1
  104. # delete old notifications
  105. if @item[:type] == 'reminder_reached'
  106. seen = false
  107. created_by_id = 1
  108. OnlineNotification.remove_by_type('Ticket', ticket.id, @item[:type], user)
  109. elsif @item[:type] == 'escalation' || @item[:type] == 'escalation_warning'
  110. seen = false
  111. created_by_id = 1
  112. OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation', user)
  113. OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation_warning', user)
  114. # on updates without state changes create unseen messages
  115. elsif @item[:type] != 'create' && (@item[:changes].blank? || @item[:changes]['state_id'].blank?)
  116. seen = false
  117. else
  118. seen = ticket.online_notification_seen_state(user.id)
  119. end
  120. OnlineNotification.add(
  121. type: @item[:type],
  122. object: 'Ticket',
  123. o_id: ticket.id,
  124. seen: seen,
  125. created_by_id: created_by_id,
  126. user_id: user.id,
  127. )
  128. Rails.logger.debug { "sent ticket online notifiaction to agent (#{@item[:type]}/#{ticket.id}/#{user.email})" }
  129. end
  130. # ignore email channel notification and empty emails
  131. if !channels['email'] || !user.email || user.email == ''
  132. add_recipient_list(ticket, user, used_channels, @item[:type])
  133. next
  134. end
  135. used_channels.push 'email'
  136. add_recipient_list(ticket, user, used_channels, @item[:type])
  137. # get user based notification template
  138. # if create, send create message / block update messages
  139. template = nil
  140. if @item[:type] == 'create'
  141. template = 'ticket_create'
  142. elsif @item[:type] == 'update'
  143. template = 'ticket_update'
  144. elsif @item[:type] == 'reminder_reached'
  145. template = 'ticket_reminder_reached'
  146. elsif @item[:type] == 'escalation'
  147. template = 'ticket_escalation'
  148. elsif @item[:type] == 'escalation_warning'
  149. template = 'ticket_escalation_warning'
  150. else
  151. raise "unknown type for notification #{@item[:type]}"
  152. end
  153. current_user = User.lookup(id: @item[:user_id])
  154. if !current_user
  155. current_user = User.lookup(id: 1)
  156. end
  157. attachments = []
  158. if article
  159. attachments = article.attachments_inline
  160. end
  161. NotificationFactory::Mailer.notification(
  162. template: template,
  163. user: user,
  164. objects: {
  165. ticket: ticket,
  166. article: article,
  167. recipient: user,
  168. current_user: current_user,
  169. changes: changes,
  170. reason: recipients_reason[user.id],
  171. },
  172. message_id: "<notification.#{DateTime.current.to_s(:number)}.#{ticket.id}.#{user.id}.#{rand(999_999)}@#{Setting.get('fqdn')}>",
  173. references: ticket.get_references,
  174. main_object: ticket,
  175. attachments: attachments,
  176. )
  177. Rails.logger.debug { "sent ticket email notifiaction to agent (#{@item[:type]}/#{ticket.id}/#{user.email})" }
  178. end
  179. end
  180. def add_recipient_list(ticket, user, channels, type)
  181. return if channels.blank?
  182. identifier = user.email
  183. if !identifier || identifier == ''
  184. identifier = user.login
  185. end
  186. recipient_list = "#{identifier}(#{type}:#{channels.join(',')})"
  187. History.add(
  188. o_id: ticket.id,
  189. history_type: 'notification',
  190. history_object: 'Ticket',
  191. value_to: recipient_list,
  192. created_by_id: @item[:user_id] || 1
  193. )
  194. end
  195. def human_changes(user, record)
  196. return {} if !@item[:changes]
  197. locale = user.locale
  198. # only show allowed attributes
  199. attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
  200. user_related_changes = {}
  201. @item[:changes].each do |key, value|
  202. # if no config exists, use all attributes
  203. if attribute_list.blank?
  204. user_related_changes[key] = value
  205. # if config exists, just use existing attributes for user
  206. elsif attribute_list[key.to_s]
  207. user_related_changes[key] = value
  208. end
  209. end
  210. changes = {}
  211. user_related_changes.each do |key, value|
  212. # get attribute name
  213. attribute_name = key.to_s
  214. object_manager_attribute = attribute_list[attribute_name]
  215. if attribute_name[-3, 3] == '_id'
  216. attribute_name = attribute_name[ 0, attribute_name.length - 3 ].to_s
  217. end
  218. # add item to changes hash
  219. if key.to_s == attribute_name
  220. changes[attribute_name] = value
  221. end
  222. # if changed item is an _id field/reference, look up the real values
  223. value_id = []
  224. value_str = [ value[0], value[1] ]
  225. if key.to_s[-3, 3] == '_id'
  226. value_id[0] = value[0]
  227. value_id[1] = value[1]
  228. if record.respond_to?(attribute_name) && record.send(attribute_name)
  229. relation_class = record.send(attribute_name).class
  230. if relation_class && value_id[0]
  231. relation_model = relation_class.lookup(id: value_id[0])
  232. if relation_model
  233. if relation_model['name']
  234. value_str[0] = relation_model['name']
  235. elsif relation_model.respond_to?('fullname')
  236. value_str[0] = relation_model.send('fullname')
  237. end
  238. end
  239. end
  240. if relation_class && value_id[1]
  241. relation_model = relation_class.lookup(id: value_id[1])
  242. if relation_model
  243. if relation_model['name']
  244. value_str[1] = relation_model['name']
  245. elsif relation_model.respond_to?('fullname')
  246. value_str[1] = relation_model.send('fullname')
  247. end
  248. end
  249. end
  250. end
  251. end
  252. # check if we have a dedicated display name for it
  253. display = attribute_name
  254. if object_manager_attribute && object_manager_attribute[:display]
  255. # delete old key
  256. changes.delete(display)
  257. # set new key
  258. display = object_manager_attribute[:display].to_s
  259. end
  260. changes[display] = if object_manager_attribute && object_manager_attribute[:translate]
  261. from = Translation.translate(locale, value_str[0])
  262. to = Translation.translate(locale, value_str[1])
  263. [from, to]
  264. else
  265. [value_str[0].to_s, value_str[1].to_s]
  266. end
  267. end
  268. changes
  269. end
  270. private
  271. def recursive_ooo_replacements(user:, replacements:, reasons:, level: 0)
  272. if level == 10
  273. Rails.logger.warn("Found more than 10 replacement levels for agent #{user}.")
  274. return
  275. end
  276. replacement = user.out_of_office_agent
  277. return if !replacement
  278. # return for already found, added and checked users
  279. # to prevent re-doing complete lookup paths
  280. return if !replacements.add?(replacement)
  281. reasons[replacement.id] = 'are the out-of-office replacement of the owner'
  282. recursive_ooo_replacements(
  283. user: replacement,
  284. replacements: replacements,
  285. reasons: reasons,
  286. level: level + 1
  287. )
  288. end
  289. def possible_recipients_of_group(group_id)
  290. cache = Cache.get("Transaction::Notification.group_access.full::#{group_id}")
  291. return cache if cache
  292. possible_recipients = User.group_access(group_id, 'full').sort_by(&:login)
  293. Cache.write("Transaction::Notification.group_access.full::#{group_id}", possible_recipients, expires_in: 20.seconds)
  294. possible_recipients
  295. end
  296. end