123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- class NotificationFactory::Mailer
- =begin
- get notification settings for user and notification type
- result = NotificationFactory::Mailer.notification_settings(user, ticket, type)
- type: create | update | reminder_reached | escalation (escalation_warning)
- returns
- {
- user: user,
- channels: {
- online: true,
- email: true,
- },
- }
- =end
- def self.notification_settings(user, ticket, type)
- # map types if needed
- map = {
- 'escalation_warning' => 'escalation'
- }
- type = type.split('.').first # pick parent type of a subtype. Eg. update vs update.merged_into
- if map[type]
- type = map[type]
- end
- # this cache will optimize the preference catch performance
- # because of the yaml deserialization its pretty slow
- # on many tickets you we cache it.
- user_preferences = Rails.cache.read("NotificationFactory::Mailer.notification_settings::#{user.id}")
- if user_preferences.blank?
- user_preferences = user.preferences
- Rails.cache.write("NotificationFactory::Mailer.notification_settings::#{user.id}", user_preferences, expires_in: 20.seconds)
- end
- return if !user_preferences
- return if !user_preferences['notification_config']
- matrix = user_preferences['notification_config']['matrix']
- return if !matrix
- owned_by_nobody = false
- owned_by_me = false
- subscribed = false
- case ticket.owner_id
- when 1
- owned_by_nobody = true
- when user.id
- owned_by_me = true
- else
- # check the replacement chain of max 10
- # if the current user is in it
- check_for = ticket.owner
- 10.times do
- replacement = check_for.out_of_office_agent
- break if !replacement
- check_for = replacement
- next if replacement.id != user.id
- owned_by_me = true
- break
- end
- end
- # always trigger notifications for user if he is subscribed
- if owned_by_me == false && ticket.mentions.exists?(user: user)
- subscribed = true
- end
- # check if group is in selected groups
- if !owned_by_me && !subscribed
- selected_group_ids = user_preferences['notification_config']['group_ids']
- if selected_group_ids.is_a?(Array)
- hit = nil
- if selected_group_ids.blank? || (selected_group_ids[0] == '-' && selected_group_ids.count == 1)
- hit = true
- else
- hit = false
- selected_group_ids.each do |selected_group_id|
- if selected_group_id.to_s == ticket.group_id.to_s
- hit = true
- break
- end
- end
- end
- return if !hit # no group access
- end
- end
- return if !matrix[type]
- data = matrix[type]
- return if !data
- return if !data['criteria']
- channels = data['channel']
- return if !channels
- if data['criteria']['owned_by_me'] && owned_by_me
- return {
- user: user,
- channels: channels
- }
- end
- if data['criteria']['owned_by_nobody'] && owned_by_nobody
- return {
- user: user,
- channels: channels
- }
- end
- if data['criteria']['subscribed'] && subscribed
- return {
- user: user,
- channels: channels
- }
- end
- # 'no' actually means 'no criteria' or 'for all tickets'.
- return if !data['criteria']['no']
- {
- user: user,
- channels: channels
- }
- end
- =begin
- success = NotificationFactory::Mailer.deliver(
- recipient: User.find(123),
- subject: 'some subject',
- body: 'some body',
- content_type: '', # optional, e. g. 'text/html'
- message_id: '<some_message_id@fqdn>', # optional
- references: ['message-id123', 'message-id456'], # optional
- attachments: [attachments...], # optional
- )
- =end
- def self.deliver(data)
- 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?
- sender = Setting.get('notification_sender')
- Rails.logger.debug { "Send notification to: #{data[:recipient][:email]} (from:#{sender}/subject:#{data[:subject]})" }
- content_type = 'text/plain'
- if data[:content_type]
- content_type = data[:content_type]
- end
- # get active Email::Outbound Channel and send
- channel = Channel.find_by(area: 'Email::Notification', active: true)
- if channel.blank?
- Rails.logger.info "Can't find an active 'Email::Notification' channel. Canceling notification sending."
- return false
- end
- channel.deliver(
- {
- # in_reply_to: in_reply_to,
- from: sender,
- to: data[:recipient][:email],
- subject: data[:subject],
- message_id: data[:message_id],
- references: data[:references],
- body: data[:body],
- content_type: content_type,
- attachments: data[:attachments],
- },
- true
- )
- end
- =begin
- NotificationFactory::Mailer.notification(
- template: 'password_reset',
- user: User.find(2),
- objects: {
- recipient: User.find(2),
- },
- main_object: ticket.find(123), # optional
- message_id: '<some_message_id@fqdn>', # optional
- references: ['message-id123', 'message-id456'], # optional
- standalone: true, # default: false - will send header & footer
- attachments: [attachments...], # optional
- )
- =end
- def self.notification(data)
- # get subject
- result = NotificationFactory::Mailer.template(
- template: data[:template],
- locale: data[:user][:preferences][:locale],
- objects: data[:objects],
- standalone: data[:standalone],
- )
- # rebuild subject
- if data[:main_object].respond_to?(:subject_build)
- result[:subject] = data[:main_object].subject_build(result[:subject])
- end
- # prepare scaling of images
- if result[:body]
- result[:body] = HtmlSanitizer.adjust_inline_image_size(result[:body])
- result[:body] = HtmlSanitizer.dynamic_image_size(result[:body])
- end
- NotificationFactory::Mailer.deliver(
- recipient: data[:user],
- subject: result[:subject],
- body: result[:body],
- content_type: 'text/html',
- message_id: data[:message_id],
- references: data[:references],
- attachments: data[:attachments],
- )
- end
- =begin
- get count of already sent notifications
- count = NotificationFactory::Mailer.already_sent?(ticket, recipient_user, type)
- retunes
- 8
- =end
- def self.already_sent?(ticket, recipient, type)
- result = ticket.history_get
- count = 0
- result.each do |item|
- next if item['type'] != 'notification'
- next if item['object'] != 'Ticket'
- next if !item['value_to'].match?(%r{#{recipient.email}}i)
- next if !item['value_to'].match?(%r{#{type}}i)
- count += 1
- end
- count
- end
- =begin
- result = NotificationFactory::Mailer.template(
- template: 'password_reset',
- locale: 'en-us',
- timezone: 'America/Santiago',
- objects: {
- recipient: User.find(2),
- },
- )
- result = NotificationFactory::Mailer.template(
- templateInline: "Invitation to \#{config.product_name} at \#{config.fqdn}",
- locale: 'en-us',
- timezone: 'America/Santiago',
- objects: {
- recipient: User.find(2),
- },
- quote: true, # html quoting
- )
- only raw subject/body
- result = NotificationFactory::Mailer.template(
- template: 'password_reset',
- locale: 'en-us',
- timezone: 'America/Santiago',
- objects: {
- recipient: User.find(2),
- },
- raw: true, # will not add application template
- standalone: true, # default: false - will send header & footer
- )
- returns
- {
- subject: 'some subject',
- body: 'some body',
- }
- =end
- def self.template(data)
- if data[:templateInline]
- return NotificationFactory::Renderer.new(
- objects: data[:objects],
- locale: data[:locale],
- timezone: data[:timezone],
- template: data[:templateInline],
- escape: data[:quote]
- ).render
- end
- template = NotificationFactory.template_read(
- locale: data[:locale] || Locale.default,
- template: data[:template],
- format: data[:format] || 'html',
- type: 'mailer',
- )
- message_subject = NotificationFactory::Renderer.new(
- objects: data[:objects],
- locale: data[:locale],
- timezone: data[:timezone],
- template: template[:subject],
- escape: false,
- trusted: true,
- ).render
- # strip off the extra newline at the end of the subject to avoid =0A suffixes (see #2726)
- message_subject.chomp!
- message_body = NotificationFactory::Renderer.new(
- objects: data[:objects],
- locale: data[:locale],
- timezone: data[:timezone],
- template: template[:body],
- trusted: true,
- ).render
- if !data[:raw]
- application_template = NotificationFactory.application_template_read(
- format: 'html',
- type: 'mailer',
- )
- data[:objects][:message] = message_body
- data[:objects][:standalone] = data[:standalone]
- message_body = NotificationFactory::Renderer.new(
- objects: data[:objects],
- locale: data[:locale],
- timezone: data[:timezone],
- template: application_template,
- trusted: true,
- ).render
- end
- {
- subject: message_subject,
- body: message_body,
- }
- end
- end
|