mailer.rb 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class NotificationFactory::Mailer
  3. =begin
  4. get notification settings for user and notification type
  5. result = NotificationFactory::Mailer.notification_settings(user, ticket, type)
  6. type: create | update | reminder_reached | escalation (escalation_warning)
  7. returns
  8. {
  9. user: user,
  10. channels: {
  11. online: true,
  12. email: true,
  13. },
  14. }
  15. =end
  16. def self.notification_settings(user, ticket, type)
  17. # map types if needed
  18. map = {
  19. 'escalation_warning' => 'escalation'
  20. }
  21. type = type.split('.').first # pick parent type of a subtype. Eg. update vs update.merged_into
  22. if map[type]
  23. type = map[type]
  24. end
  25. # this cache will optimize the preference catch performance
  26. # because of the yaml deserialization its pretty slow
  27. # on many tickets you we cache it.
  28. user_preferences = Rails.cache.read("NotificationFactory::Mailer.notification_settings::#{user.id}")
  29. if user_preferences.blank?
  30. user_preferences = user.preferences
  31. Rails.cache.write("NotificationFactory::Mailer.notification_settings::#{user.id}", user_preferences, expires_in: 20.seconds)
  32. end
  33. return if !user_preferences
  34. return if !user_preferences['notification_config']
  35. matrix = user_preferences['notification_config']['matrix']
  36. return if !matrix
  37. owned_by_nobody = false
  38. owned_by_me = false
  39. subscribed = false
  40. case ticket.owner_id
  41. when 1
  42. owned_by_nobody = true
  43. when user.id
  44. owned_by_me = true
  45. else
  46. # check the replacement chain of max 10
  47. # if the current user is in it
  48. check_for = ticket.owner
  49. 10.times do
  50. replacement = check_for.out_of_office_agent
  51. break if !replacement
  52. check_for = replacement
  53. next if replacement.id != user.id
  54. owned_by_me = true
  55. break
  56. end
  57. end
  58. # always trigger notifications for user if he is subscribed
  59. if owned_by_me == false && ticket.mentions.exists?(user: user)
  60. subscribed = true
  61. end
  62. # check if group is in selected groups
  63. if !owned_by_me && !subscribed
  64. selected_group_ids = user_preferences['notification_config']['group_ids']
  65. if selected_group_ids.is_a?(Array)
  66. hit = nil
  67. if selected_group_ids.blank? || (selected_group_ids[0] == '-' && selected_group_ids.count == 1)
  68. hit = true
  69. else
  70. hit = false
  71. selected_group_ids.each do |selected_group_id|
  72. if selected_group_id.to_s == ticket.group_id.to_s
  73. hit = true
  74. break
  75. end
  76. end
  77. end
  78. return if !hit # no group access
  79. end
  80. end
  81. return if !matrix[type]
  82. data = matrix[type]
  83. return if !data
  84. return if !data['criteria']
  85. channels = data['channel']
  86. return if !channels
  87. if data['criteria']['owned_by_me'] && owned_by_me
  88. return {
  89. user: user,
  90. channels: channels
  91. }
  92. end
  93. if data['criteria']['owned_by_nobody'] && owned_by_nobody
  94. return {
  95. user: user,
  96. channels: channels
  97. }
  98. end
  99. if data['criteria']['subscribed'] && subscribed
  100. return {
  101. user: user,
  102. channels: channels
  103. }
  104. end
  105. # 'no' actually means 'no criteria' or 'for all tickets'.
  106. return if !data['criteria']['no']
  107. {
  108. user: user,
  109. channels: channels
  110. }
  111. end
  112. =begin
  113. success = NotificationFactory::Mailer.deliver(
  114. recipient: User.find(123),
  115. subject: 'some subject',
  116. body: 'some body',
  117. content_type: '', # optional, e. g. 'text/html'
  118. message_id: '<some_message_id@fqdn>', # optional
  119. references: ['message-id123', 'message-id456'], # optional
  120. attachments: [attachments...], # optional
  121. )
  122. =end
  123. def self.deliver(data)
  124. raise Exceptions::UnprocessableEntity, "Unable to send mail to user with id #{data[:recipient][:id]} because there is no email available." if data[:recipient][:email].blank?
  125. sender = Setting.get('notification_sender')
  126. Rails.logger.debug { "Send notification to: #{data[:recipient][:email]} (from:#{sender}/subject:#{data[:subject]})" }
  127. content_type = 'text/plain'
  128. if data[:content_type]
  129. content_type = data[:content_type]
  130. end
  131. # get active Email::Outbound Channel and send
  132. channel = Channel.find_by(area: 'Email::Notification', active: true)
  133. if channel.blank?
  134. Rails.logger.info "Can't find an active 'Email::Notification' channel. Canceling notification sending."
  135. return false
  136. end
  137. channel.deliver(
  138. {
  139. # in_reply_to: in_reply_to,
  140. from: sender,
  141. to: data[:recipient][:email],
  142. subject: data[:subject],
  143. message_id: data[:message_id],
  144. references: data[:references],
  145. body: data[:body],
  146. content_type: content_type,
  147. attachments: data[:attachments],
  148. },
  149. true
  150. )
  151. end
  152. =begin
  153. NotificationFactory::Mailer.notification(
  154. template: 'password_reset',
  155. user: User.find(2),
  156. objects: {
  157. recipient: User.find(2),
  158. },
  159. main_object: ticket.find(123), # optional
  160. message_id: '<some_message_id@fqdn>', # optional
  161. references: ['message-id123', 'message-id456'], # optional
  162. standalone: true, # default: false - will send header & footer
  163. attachments: [attachments...], # optional
  164. )
  165. =end
  166. def self.notification(data)
  167. # get subject
  168. result = NotificationFactory::Mailer.template(
  169. template: data[:template],
  170. locale: data[:user][:preferences][:locale],
  171. objects: data[:objects],
  172. standalone: data[:standalone],
  173. )
  174. # rebuild subject
  175. if data[:main_object].respond_to?(:subject_build)
  176. result[:subject] = data[:main_object].subject_build(result[:subject])
  177. end
  178. # prepare scaling of images
  179. if result[:body]
  180. result[:body] = HtmlSanitizer.adjust_inline_image_size(result[:body])
  181. result[:body] = HtmlSanitizer.dynamic_image_size(result[:body])
  182. end
  183. NotificationFactory::Mailer.deliver(
  184. recipient: data[:user],
  185. subject: result[:subject],
  186. body: result[:body],
  187. content_type: 'text/html',
  188. message_id: data[:message_id],
  189. references: data[:references],
  190. attachments: data[:attachments],
  191. )
  192. end
  193. =begin
  194. get count of already sent notifications
  195. count = NotificationFactory::Mailer.already_sent?(ticket, recipient_user, type)
  196. retunes
  197. 8
  198. =end
  199. def self.already_sent?(ticket, recipient, type)
  200. result = ticket.history_get
  201. count = 0
  202. result.each do |item|
  203. next if item['type'] != 'notification'
  204. next if item['object'] != 'Ticket'
  205. next if !item['value_to'].match?(%r{#{recipient.email}}i)
  206. next if !item['value_to'].match?(%r{#{type}}i)
  207. count += 1
  208. end
  209. count
  210. end
  211. =begin
  212. result = NotificationFactory::Mailer.template(
  213. template: 'password_reset',
  214. locale: 'en-us',
  215. timezone: 'America/Santiago',
  216. objects: {
  217. recipient: User.find(2),
  218. },
  219. )
  220. result = NotificationFactory::Mailer.template(
  221. templateInline: "Invitation to \#{config.product_name} at \#{config.fqdn}",
  222. locale: 'en-us',
  223. timezone: 'America/Santiago',
  224. objects: {
  225. recipient: User.find(2),
  226. },
  227. quote: true, # html quoting
  228. )
  229. only raw subject/body
  230. result = NotificationFactory::Mailer.template(
  231. template: 'password_reset',
  232. locale: 'en-us',
  233. timezone: 'America/Santiago',
  234. objects: {
  235. recipient: User.find(2),
  236. },
  237. raw: true, # will not add application template
  238. standalone: true, # default: false - will send header & footer
  239. )
  240. returns
  241. {
  242. subject: 'some subject',
  243. body: 'some body',
  244. }
  245. =end
  246. def self.template(data)
  247. if data[:templateInline]
  248. return NotificationFactory::Renderer.new(
  249. objects: data[:objects],
  250. locale: data[:locale],
  251. timezone: data[:timezone],
  252. template: data[:templateInline],
  253. escape: data[:quote]
  254. ).render
  255. end
  256. template = NotificationFactory.template_read(
  257. locale: data[:locale] || Locale.default,
  258. template: data[:template],
  259. format: data[:format] || 'html',
  260. type: 'mailer',
  261. )
  262. message_subject = NotificationFactory::Renderer.new(
  263. objects: data[:objects],
  264. locale: data[:locale],
  265. timezone: data[:timezone],
  266. template: template[:subject],
  267. escape: false,
  268. trusted: true,
  269. ).render
  270. # strip off the extra newline at the end of the subject to avoid =0A suffixes (see #2726)
  271. message_subject.chomp!
  272. message_body = NotificationFactory::Renderer.new(
  273. objects: data[:objects],
  274. locale: data[:locale],
  275. timezone: data[:timezone],
  276. template: template[:body],
  277. trusted: true,
  278. ).render
  279. if !data[:raw]
  280. application_template = NotificationFactory.application_template_read(
  281. format: 'html',
  282. type: 'mailer',
  283. )
  284. data[:objects][:message] = message_body
  285. data[:objects][:standalone] = data[:standalone]
  286. message_body = NotificationFactory::Renderer.new(
  287. objects: data[:objects],
  288. locale: data[:locale],
  289. timezone: data[:timezone],
  290. template: application_template,
  291. trusted: true,
  292. ).render
  293. end
  294. {
  295. subject: message_subject,
  296. body: message_body,
  297. }
  298. end
  299. end