diff --git a/.rubocop/todo.rspec.yml b/.rubocop/todo.rspec.yml
index 9333c5f17..7d30bfb28 100644
--- a/.rubocop/todo.rspec.yml
+++ b/.rubocop/todo.rspec.yml
@@ -695,6 +695,7 @@ RSpec/NestedGroups:
- 'spec/system/manage/organizations_spec.rb'
- 'spec/system/ticket/create_spec.rb'
- 'spec/system/ticket/zoom_spec.rb'
+ - 'spec/models/channel/filter/import_archive_spec.rb'
RSpec/RepeatedDescription:
Exclude:
diff --git a/app/assets/javascripts/app/controllers/_channel/email.coffee b/app/assets/javascripts/app/controllers/_channel/email.coffee
index b6718e81c..a592a6bfb 100644
--- a/app/assets/javascripts/app/controllers/_channel/email.coffee
+++ b/app/assets/javascripts/app/controllers/_channel/email.coffee
@@ -656,15 +656,8 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
@account[key] = value
if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true)
- message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages)
- @$('.js-inbound-acknowledge .js-message').html(message)
- @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-intro')
- @$('.js-inbound-acknowledge .js-next').attr('data-slide', '')
- @$('.js-inbound-acknowledge .js-next').unbind('click.verify').bind('click.verify', (e) =>
- e.preventDefault()
- @verify(@account)
- )
- @showSlide('js-inbound-acknowledge')
+ @probeInboundMessagesFound(data, true)
+ @probeInboundArchive(data)
else
@verify(@account)
@@ -713,11 +706,8 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
@account.inbound = params
if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true)
- message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages)
- @$('.js-inbound-acknowledge .js-message').html(message)
- @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-inbound')
- @$('.js-inbound-acknowledge .js-next').unbind('click.verify')
- @showSlide('js-inbound-acknowledge')
+ @probeInboundMessagesFound(data)
+ @probeInboundArchive(data)
else
@showSlide('js-outbound')
@@ -744,6 +734,65 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
@enable(e)
)
+ probeInboundMessagesFound: (data, verify) =>
+ message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages)
+ @$('.js-inbound-acknowledge .js-messageFound').html(message)
+
+ if !verify
+ @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-inbound')
+ @$('.js-inbound-acknowledge .js-next').unbind('click.verify')
+ else
+ @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-intro')
+ @$('.js-inbound-acknowledge .js-next').attr('data-slide', '')
+ @$('.js-inbound-acknowledge .js-next').unbind('click.verify').bind('click.verify', (e) =>
+ e.preventDefault()
+ @verify(@account)
+ )
+ @showSlide('js-inbound-acknowledge')
+
+ probeInboundArchive: (data) =>
+ if data.archive_possible isnt true
+ @$('.js-archiveMessage').addClass('hide')
+ return
+
+ @$('.js-archiveMessage').removeClass('hide')
+ message = App.i18n.translateContent('In addition, we have found emails in your mailbox that are older than %s weeks. You can import such emails as an "archive", which means that no notifications are sent and the tickets have the status "closed". However, you can find them in Zammad anytime using the search function.', data.archive_week_range)
+ @$('.js-inbound-acknowledge .js-archiveMessageCount').html(message)
+
+ configureAttributesAcknowledge = [
+ {
+ name: 'archive'
+ tag: 'boolean'
+ null: true
+ default: no
+ options: {
+ true: 'archive'
+ false: 'regular'
+ }
+ translate: true
+ },
+ ]
+
+ new App.ControllerForm(
+ elReplace: @$('.js-importTypeSelect'),
+ model:
+ configure_attributes: configureAttributesAcknowledge
+ className: ''
+ noFieldset: true
+ )
+ @$('.js-importTypeSelect select[name=archive]').on('change', (e) =>
+ value = $(e.target).val()
+ @account.inbound ||= {}
+ @account.inbound.options ||= {}
+ if value is 'true'
+ @account.inbound.options.archive = true
+ @account.inbound.options.archive_before = (new Date()).toISOString()
+ else
+ delete @account.inbound.options.archive
+ delete @account.inbound.options.archive_before
+ )
+ @$('.js-importTypeSelect select[name=archive]').trigger('change')
+
probleOutbound: (e) =>
e.preventDefault()
diff --git a/app/assets/javascripts/app/controllers/getting_started.coffee b/app/assets/javascripts/app/controllers/getting_started.coffee
index 45ced174c..2f8fde786 100644
--- a/app/assets/javascripts/app/controllers/getting_started.coffee
+++ b/app/assets/javascripts/app/controllers/getting_started.coffee
@@ -769,15 +769,8 @@ class ChannelEmail extends App.WizardFullScreen
@account[key] = value
if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true)
- message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages)
- @$('.js-inbound-acknowledge .js-message').html(message)
- @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-intro')
- @$('.js-inbound-acknowledge .js-next').attr('data-slide', '')
- @$('.js-inbound-acknowledge .js-next').unbind('click.verify').bind('click.verify', (e) =>
- e.preventDefault()
- @verify(@account)
- )
- @showSlide('js-inbound-acknowledge')
+ @probeInboundMessagesFound(data, true)
+ @probeInboundArchive(data)
else
@verify(@account)
@@ -818,11 +811,8 @@ class ChannelEmail extends App.WizardFullScreen
@account.inbound = params
if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true)
- message = App.i18n.translateContent('We have already found %s emails in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages)
- @$('.js-inbound-acknowledge .js-message').html(message)
- @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-inbound')
- @$('.js-inbound-acknowledge .js-next').unbind('click.verify')
- @showSlide('js-inbound-acknowledge')
+ @probeInboundMessagesFound(data, true)
+ @probeInboundArchive(data)
else
@showSlide('js-outbound')
@@ -848,6 +838,65 @@ class ChannelEmail extends App.WizardFullScreen
@enable(e)
)
+ probeInboundMessagesFound: (data, verify) =>
+ message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages)
+ @$('.js-inbound-acknowledge .js-messageFound').html(message)
+
+ if !verify
+ @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-inbound')
+ @$('.js-inbound-acknowledge .js-next').unbind('click.verify')
+ else
+ @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-intro')
+ @$('.js-inbound-acknowledge .js-next').attr('data-slide', '')
+ @$('.js-inbound-acknowledge .js-next').unbind('click.verify').bind('click.verify', (e) =>
+ e.preventDefault()
+ @verify(@account)
+ )
+ @showSlide('js-inbound-acknowledge')
+
+ probeInboundArchive: (data) =>
+ if data.archive_possible isnt true
+ @$('.js-archiveMessage').addClass('hide')
+ return
+
+ @$('.js-archiveMessage').removeClass('hide')
+ message = App.i18n.translateContent('In addition, we have found emails in your mailbox that are older than %s weeks. You can import such emails as an "archive", which means that no notifications are sent and the tickets have the status "closed". However, you can find them in Zammad anytime using the search function.', data.archive_week_range)
+ @$('.js-inbound-acknowledge .js-archiveMessageCount').html(message)
+
+ configureAttributesAcknowledge = [
+ {
+ name: 'archive'
+ tag: 'boolean'
+ null: true
+ default: no
+ options: {
+ true: 'archive'
+ false: 'regular'
+ }
+ translate: true
+ },
+ ]
+
+ new App.ControllerForm(
+ elReplace: @$('.js-importTypeSelect'),
+ model:
+ configure_attributes: configureAttributesAcknowledge
+ className: ''
+ noFieldset: true
+ )
+ @$('.js-importTypeSelect select[name=archive]').on('change', (e) =>
+ value = $(e.target).val()
+ @account.inbound ||= {}
+ @account.inbound.options ||= {}
+ if value is 'true'
+ @account.inbound.options.archive = true
+ @account.inbound.options.archive_before = (new Date()).toISOString()
+ else
+ delete @account.inbound.options.archive
+ delete @account.inbound.options.archive_before
+ )
+ @$('.js-importTypeSelect select[name=archive]').trigger('change')
+
probleOutbound: (e) =>
e.preventDefault()
diff --git a/app/assets/javascripts/app/views/channel/email_account_wizard.jst.eco b/app/assets/javascripts/app/views/channel/email_account_wizard.jst.eco
index d14be080c..26bd95548 100644
--- a/app/assets/javascripts/app/views/channel/email_account_wizard.jst.eco
+++ b/app/assets/javascripts/app/views/channel/email_account_wizard.jst.eco
@@ -108,7 +108,23 @@
-
<%- @T('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', 'x') %>
+
<%- @T('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', 'x') %>
+
+
+
<%- @T('In addition, we have found emails in your mailbox that are older than %s weeks. You can import such emails as an "archive", which means that no notifications are sent and the tickets have the status "closed". However, you can find them in Zammad anytime using the search function. ', 'x') %>
+
+
<%- @T('Should the emails from this mailbox be imported as an archive or as regular emails?') %>
+
+
+ - <%- @T('Import as archive: |No notifications are sent|, the |tickets are closed| and timestamps are removed. You can still find them in Zammad using the search.') %>
+ - <%- @T('Import as regular: |Notifications are sent| and the |tickets are open| - you can find the tickets in the overview of open tickets.') %>
+
+
+
+ Import as:
+
+
+
\ No newline at end of file
+
diff --git a/app/models/channel/driver/imap.rb b/app/models/channel/driver/imap.rb
index ab0afbe6c..6abe3d23e 100644
--- a/app/models/channel/driver/imap.rb
+++ b/app/models/channel/driver/imap.rb
@@ -174,10 +174,45 @@ example
if content_messages >= content_max_check
content_messages = message_ids.count
end
+
+ archive_possible = false
+ archive_check = 0
+ archive_max_check = 500
+ archive_days_range = 14
+ archive_week_range = archive_days_range / 7
+ message_ids.reverse_each do |message_id|
+ message_meta = nil
+ timeout(1.minute) do
+ message_meta = @imap.fetch(message_id, ['RFC822.HEADER'])[0]
+ end
+
+ headers = self.class.extract_rfc822_headers(message_meta)
+ next if messages_is_verify_message?(headers)
+ next if messages_is_ignore_message?(headers)
+ next if headers['Date'].blank?
+
+ archive_check += 1
+ break if archive_check >= archive_max_check
+
+ begin
+ date = Time.zone.parse(headers['Date'])
+ rescue => e
+ Rails.logger.error e
+ next
+ end
+ break if date >= Time.zone.now - archive_days_range.days
+
+ archive_possible = true
+
+ break
+ end
+
disconnect
return {
- result: 'ok',
- content_messages: content_messages,
+ result: 'ok',
+ content_messages: content_messages,
+ archive_possible: archive_possible,
+ archive_week_range: archive_week_range,
}
end
diff --git a/app/models/channel/email_parser.rb b/app/models/channel/email_parser.rb
index 803c3ce31..9721d29dd 100644
--- a/app/models/channel/email_parser.rb
+++ b/app/models/channel/email_parser.rb
@@ -142,6 +142,11 @@ returns
# run postmaster pre filter
UserInfo.current_user_id = 1
+
+ # set interface handle
+ original_interface_handle = ApplicationHandleInfo.current
+ transaction_params = { interface_handle: "#{original_interface_handle}.postmaster", disable: [] }
+
filters = {}
Setting.where(area: 'Postmaster::PreFilter').order(:name).each do |setting|
filters[setting.name] = Setting.get(setting.name).constantize
@@ -149,7 +154,7 @@ returns
filters.each do |key, backend|
Rails.logger.debug { "run postmaster pre filter #{key}: #{backend}" }
begin
- backend.run(channel, mail)
+ backend.run(channel, mail, transaction_params)
rescue => e
Rails.logger.error "can't run postmaster pre filter #{key}: #{backend}"
Rails.logger.error e.inspect
@@ -163,15 +168,12 @@ returns
return
end
- # set interface handle
- original_interface_handle = ApplicationHandleInfo.current
-
ticket = nil
article = nil
session_user = nil
# use transaction
- Transaction.execute(interface_handle: "#{original_interface_handle}.postmaster") do
+ Transaction.execute(transaction_params) do
# get sender user
session_user_id = mail[:'x-zammad-session-user-id']
diff --git a/app/models/channel/filter/auto_response_check.rb b/app/models/channel/filter/auto_response_check.rb
index ad04e049a..b22b9b80b 100644
--- a/app/models/channel/filter/auto_response_check.rb
+++ b/app/models/channel/filter/auto_response_check.rb
@@ -2,7 +2,7 @@
module Channel::Filter::AutoResponseCheck
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
# if header is available, do not generate auto response
mail[ :'x-zammad-send-auto-response' ] = false
diff --git a/app/models/channel/filter/bounce_delivery_permanent_failed.rb b/app/models/channel/filter/bounce_delivery_permanent_failed.rb
index 1c3c66dde..b35addda2 100644
--- a/app/models/channel/filter/bounce_delivery_permanent_failed.rb
+++ b/app/models/channel/filter/bounce_delivery_permanent_failed.rb
@@ -2,7 +2,7 @@
module Channel::Filter::BounceDeliveryPermanentFailed
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
return if !mail[:mail_instance]
return if !mail[:mail_instance].bounced?
diff --git a/app/models/channel/filter/bounce_delivery_temporary_failed.rb b/app/models/channel/filter/bounce_delivery_temporary_failed.rb
index 5a74b0fcd..6d595adc0 100644
--- a/app/models/channel/filter/bounce_delivery_temporary_failed.rb
+++ b/app/models/channel/filter/bounce_delivery_temporary_failed.rb
@@ -2,7 +2,7 @@
module Channel::Filter::BounceDeliveryTemporaryFailed
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
return if !mail[:mail_instance]
return if !mail[:attachments]
return if mail[:mail_instance].action != 'delayed'
diff --git a/app/models/channel/filter/bounce_follow_up_check.rb b/app/models/channel/filter/bounce_follow_up_check.rb
index 1ee32d27d..40d98361c 100644
--- a/app/models/channel/filter/bounce_follow_up_check.rb
+++ b/app/models/channel/filter/bounce_follow_up_check.rb
@@ -2,7 +2,7 @@
module Channel::Filter::BounceFollowUpCheck
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
return if !mail[:mail_instance]
return if !mail[:mail_instance].bounced?
diff --git a/app/models/channel/filter/database.rb b/app/models/channel/filter/database.rb
index e00651782..71315028b 100644
--- a/app/models/channel/filter/database.rb
+++ b/app/models/channel/filter/database.rb
@@ -3,7 +3,7 @@
# process all database filter
module Channel::Filter::Database
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
# process postmaster filter
filters = PostmasterFilter.where(active: true, channel: 'email').order(:name, :created_at)
diff --git a/app/models/channel/filter/follow_up_check.rb b/app/models/channel/filter/follow_up_check.rb
index 3d5db3408..3a5353afd 100644
--- a/app/models/channel/filter/follow_up_check.rb
+++ b/app/models/channel/filter/follow_up_check.rb
@@ -2,7 +2,7 @@
module Channel::Filter::FollowUpCheck
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
return if mail[:'x-zammad-ticket-id']
@@ -55,32 +55,7 @@ module Channel::Filter::FollowUpCheck
end
# get ticket# from references
- if setting.include?('references') || (mail[:'x-zammad-is-auto-response'] == true || Setting.get('ticket_hook_position') == 'none')
-
- # get all references 'References' + 'In-Reply-To'
- references = ''
- if mail[:references]
- references += mail[:references]
- end
- if mail[:'in-reply-to']
- if references != ''
- references += ' '
- end
- references += mail[:'in-reply-to']
- end
- if references != ''
- message_ids = references.split(/\s+/)
- message_ids.each do |message_id|
- message_id_md5 = Digest::MD5.hexdigest(message_id)
- article = Ticket::Article.where(message_id_md5: message_id_md5).order('created_at DESC, id DESC').limit(1).first
- next if !article
-
- Rails.logger.debug { "Follow-up for '##{article.ticket.number}' in references." }
- mail[:'x-zammad-ticket-id'] = article.ticket_id
- return true
- end
- end
- end
+ return true if ( setting.include?('references') || (mail[:'x-zammad-is-auto-response'] == true || Setting.get('ticket_hook_position') == 'none') ) && follow_up_by_md5(mail)
# get ticket# from references current email has same subject as initial article
if mail[:subject].present?
@@ -125,4 +100,32 @@ module Channel::Filter::FollowUpCheck
true
end
+
+ def self.mail_references(mail)
+ references = []
+ %i[references in-reply-to].each do |key|
+ next if mail[key].blank?
+
+ references.push(mail[key])
+ end
+ references.join(' ')
+ end
+
+ def self.message_id_article(message_id)
+ message_id_md5 = Digest::MD5.hexdigest(message_id)
+ Ticket::Article.where(message_id_md5: message_id_md5).order('created_at DESC, id DESC').limit(1).first
+ end
+
+ def self.follow_up_by_md5(mail)
+ return if mail[:'x-zammad-ticket-id']
+
+ mail_references(mail).split(/\s+/).each do |message_id|
+ article = message_id_article(message_id)
+ next if article.blank?
+
+ Rails.logger.debug "Follow up for '##{article.ticket.number}' in references."
+ mail[:'x-zammad-ticket-id'] = article.ticket_id
+ return true
+ end
+ end
end
diff --git a/app/models/channel/filter/follow_up_merged.rb b/app/models/channel/filter/follow_up_merged.rb
index 105db0529..6de61a77a 100644
--- a/app/models/channel/filter/follow_up_merged.rb
+++ b/app/models/channel/filter/follow_up_merged.rb
@@ -2,7 +2,7 @@
module Channel::Filter::FollowUpMerged
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
return if mail[:'x-zammad-ticket-id'].blank?
ticket = Ticket.find_by(id: mail[:'x-zammad-ticket-id'])
diff --git a/app/models/channel/filter/follow_up_possible_check.rb b/app/models/channel/filter/follow_up_possible_check.rb
index b9376f72c..a1b1a6cd0 100644
--- a/app/models/channel/filter/follow_up_possible_check.rb
+++ b/app/models/channel/filter/follow_up_possible_check.rb
@@ -2,7 +2,7 @@
module Channel::Filter::FollowUpPossibleCheck
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
ticket_id = mail[:'x-zammad-ticket-id']
return true if !ticket_id
diff --git a/app/models/channel/filter/identify_sender.rb b/app/models/channel/filter/identify_sender.rb
index 1cdbcb855..7ec42123f 100644
--- a/app/models/channel/filter/identify_sender.rb
+++ b/app/models/channel/filter/identify_sender.rb
@@ -2,7 +2,7 @@
module Channel::Filter::IdentifySender
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
customer_user_id = mail[ :'x-zammad-ticket-customer_id' ]
customer_user = nil
diff --git a/app/models/channel/filter/import_archive.rb b/app/models/channel/filter/import_archive.rb
new file mode 100644
index 000000000..8076ecdff
--- /dev/null
+++ b/app/models/channel/filter/import_archive.rb
@@ -0,0 +1,95 @@
+# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
+
+module Channel::Filter::ImportArchive
+
+ def self.run(channel, mail, transaction_params)
+ return if !import_channel?(channel, mail)
+
+ # set ignore if already imported
+ message_id = mail[:'message-id']
+ return if !message_id
+
+ # check if we already have imported this message
+ message_id_md5 = Digest::MD5.hexdigest(message_id)
+ if Ticket::Article.exists?(message_id_md5: message_id_md5)
+ mail[:'x-zammad-ignore'] = true
+ return true
+ end
+
+ # set create time if given in email
+ overwrite_created_at(mail)
+
+ # do not send auto responses
+ skip_auto_response(mail)
+
+ # set ticket to closed
+ ticket_closed(mail)
+
+ # disable notifications and trigger
+ disable_notifications(transaction_params)
+
+ # find possible follow up ticket by mail references
+ # we need this check here because in the follow up filter
+ # this check is based on settings and we want to make sure
+ # that we always check the ticket id based on the mail headers.
+ Channel::Filter::FollowUpCheck.follow_up_by_md5(mail)
+
+ true
+ end
+
+ def self.import_channel?(channel, mail)
+ return false if !mail[:date]
+
+ options = channel_options(channel)
+ return false if options[:archive] != true
+ return false if !import_channel_date_range?(channel, mail)
+
+ true
+ end
+
+ def self.import_channel_date_range?(channel, mail)
+ options = channel_options(channel)
+ return false if options[:archive_before].present? && options[:archive_before].to_date < mail[:date]
+ return false if options[:archive_till].present? && options[:archive_till].to_date < Time.now.utc
+
+ true
+ end
+
+ def self.message_id?(mail)
+ return if !mail[:'message-id']
+
+ true
+ end
+
+ def self.overwrite_created_at(mail)
+ mail[:'x-zammad-ticket-created_at'] = mail[:date]
+ mail[:'x-zammad-article-created_at'] = mail[:date]
+ end
+
+ def self.skip_auto_response(mail)
+ mail[:'x-zammad-is-auto-response'] = true
+ end
+
+ def self.ticket_closed(mail)
+ closed_state = Ticket::State.by_category(:closed).first
+ mail[:'x-zammad-ticket-state_id'] = closed_state.id
+ mail[:'x-zammad-ticket-followup-state_id'] = closed_state.id
+ end
+
+ def self.disable_notifications(transaction_params)
+ transaction_params[:disable] += %w[
+ Transaction::Notification
+ Transaction::Slack
+ Transaction::Trigger
+ ]
+ end
+
+ def self.channel_options(channel)
+ if channel.instance_of?(Channel)
+ return channel.options.dig(:inbound, :options) || {}
+ end
+
+ channel.dig(:options, :inbound, :options) || {}
+ end
+
+end
diff --git a/app/models/channel/filter/monitoring_base.rb b/app/models/channel/filter/monitoring_base.rb
index 3c20460cf..20be1d1cf 100644
--- a/app/models/channel/filter/monitoring_base.rb
+++ b/app/models/channel/filter/monitoring_base.rb
@@ -11,7 +11,7 @@ class Channel::Filter::MonitoringBase
# Nagios
# https://github.com/NagiosEnterprises/nagioscore/blob/754218e67653929a58938b99ef6b6039b6474fe4/sample-config/template-object/commands.cfg.in#L35
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
integration = integration_name
return if !Setting.get("#{integration}_integration")
diff --git a/app/models/channel/filter/out_of_office_check.rb b/app/models/channel/filter/out_of_office_check.rb
index 505bdab00..76f6f3334 100644
--- a/app/models/channel/filter/out_of_office_check.rb
+++ b/app/models/channel/filter/out_of_office_check.rb
@@ -2,7 +2,7 @@
module Channel::Filter::OutOfOfficeCheck
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
mail[ :'x-zammad-out-of-office' ] = false
diff --git a/app/models/channel/filter/own_notification_loop_detection.rb b/app/models/channel/filter/own_notification_loop_detection.rb
index 8bdc71669..086fe04c4 100644
--- a/app/models/channel/filter/own_notification_loop_detection.rb
+++ b/app/models/channel/filter/own_notification_loop_detection.rb
@@ -2,7 +2,7 @@
module Channel::Filter::OwnNotificationLoopDetection
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
message_id = mail[:'message-id']
return if !message_id
diff --git a/app/models/channel/filter/reply_to_based_sender.rb b/app/models/channel/filter/reply_to_based_sender.rb
index 78e2bb0c4..49c317239 100644
--- a/app/models/channel/filter/reply_to_based_sender.rb
+++ b/app/models/channel/filter/reply_to_based_sender.rb
@@ -2,7 +2,7 @@
module Channel::Filter::ReplyToBasedSender
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
reply_to = mail[:'reply-to']
return if reply_to.blank?
diff --git a/app/models/channel/filter/secure_mailing.rb b/app/models/channel/filter/secure_mailing.rb
index 84be2af01..e8cf77151 100644
--- a/app/models/channel/filter/secure_mailing.rb
+++ b/app/models/channel/filter/secure_mailing.rb
@@ -2,7 +2,7 @@
module Channel::Filter::SecureMailing
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
::SecureMailing.incoming(mail)
end
end
diff --git a/app/models/channel/filter/sender_is_system_address.rb b/app/models/channel/filter/sender_is_system_address.rb
index ef2592532..bd0ac357a 100644
--- a/app/models/channel/filter/sender_is_system_address.rb
+++ b/app/models/channel/filter/sender_is_system_address.rb
@@ -2,7 +2,7 @@
module Channel::Filter::SenderIsSystemAddress
- def self.run(_channel, mail)
+ def self.run(_channel, mail, _transaction_params)
# if attributes already set by header
return if mail[:'x-zammad-ticket-create-article-sender']
diff --git a/app/models/channel/filter/service_now_check.rb b/app/models/channel/filter/service_now_check.rb
index 3945e0739..6ead40c3f 100644
--- a/app/models/channel/filter/service_now_check.rb
+++ b/app/models/channel/filter/service_now_check.rb
@@ -3,7 +3,7 @@
module Channel::Filter::ServiceNowCheck
# This filter will run pre and post
- def self.run(_channel, mail, ticket = nil, _article = nil, _session_user = nil)
+ def self.run(_channel, mail, ticket_or_transaction_params = nil, _article = nil, _session_user = nil)
return if mail['x-servicenow-generated'].blank?
source_id = self.source_id(subject: mail[:subject])
@@ -13,7 +13,7 @@ module Channel::Filter::ServiceNowCheck
return if source_name.blank?
# check if we can followup by existing service now relation
- if ticket.blank?
+ if ticket_or_transaction_params.blank? || ticket_or_transaction_params.is_a?(Hash)
from_sync_entry(
mail: mail,
source_name: source_name,
@@ -22,7 +22,7 @@ module Channel::Filter::ServiceNowCheck
return
end
- ExternalSync.create_with(source_id: source_id).find_or_create_by(source: source_name, object: 'Ticket', o_id: ticket.id)
+ ExternalSync.create_with(source_id: source_id).find_or_create_by(source: source_name, object: 'Ticket', o_id: ticket_or_transaction_params.id)
end
=begin
diff --git a/app/models/channel/filter/trusted.rb b/app/models/channel/filter/trusted.rb
index d36e36a3c..a5bf57a20 100644
--- a/app/models/channel/filter/trusted.rb
+++ b/app/models/channel/filter/trusted.rb
@@ -3,7 +3,7 @@
# delete all X-Zammad header if channel is not trusted
module Channel::Filter::Trusted
- def self.run(channel, mail)
+ def self.run(channel, mail, _transaction_params)
# check if trust x-headers
if !trusted(channel)
@@ -29,7 +29,7 @@ module Channel::Filter::Trusted
def self.trusted(channel)
return true if channel[:trusted]
- return true if channel.instance_of?(Channel) && channel.options[:inbound][:trusted]
+ return true if channel.instance_of?(Channel) && channel.options[:inbound] && channel.options[:inbound][:trusted]
false
end
diff --git a/db/migrate/20190419000001_setting_add_import_archive.rb b/db/migrate/20190419000001_setting_add_import_archive.rb
new file mode 100644
index 000000000..fdf4ab40c
--- /dev/null
+++ b/db/migrate/20190419000001_setting_add_import_archive.rb
@@ -0,0 +1,17 @@
+class SettingAddImportArchive < ActiveRecord::Migration[5.1]
+ def change
+
+ # return if it's a new setup
+ return if !Setting.exists?(name: 'system_init_done')
+
+ Setting.create_if_not_exists(
+ title: 'Define postmaster filter.',
+ name: '0018_postmaster_import_archive',
+ area: 'Postmaster::PreFilter',
+ description: 'Define postmaster filter to import archive mailboxes.',
+ options: {},
+ state: 'Channel::Filter::ImportArchive',
+ frontend: false
+ )
+ end
+end
diff --git a/db/seeds/settings.rb b/db/seeds/settings.rb
index 57eefad55..81264c158 100644
--- a/db/seeds/settings.rb
+++ b/db/seeds/settings.rb
@@ -3374,6 +3374,15 @@ Setting.create_if_not_exists(
state: 'Channel::Filter::ReplyToBasedSender',
frontend: false
)
+Setting.create_if_not_exists(
+ title: 'Define postmaster filter.',
+ name: '0018_postmaster_import_archive',
+ area: 'Postmaster::PreFilter',
+ description: 'Define postmaster filter to import archive mailboxes.',
+ options: {},
+ state: 'Channel::Filter::ImportArchive',
+ frontend: false
+)
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '0012_postmaster_filter_sender_is_system_address',
diff --git a/lib/email_helper/probe.rb b/lib/email_helper/probe.rb
index 9fa0a1737..2119da811 100644
--- a/lib/email_helper/probe.rb
+++ b/lib/email_helper/probe.rb
@@ -91,9 +91,11 @@ returns on fail
next if result_outbound[:result] != 'ok'
return {
- result: 'ok',
- content_messages: result_inbound[:content_messages],
- setting: settings,
+ result: 'ok',
+ content_messages: result_inbound[:content_messages],
+ archive_possible: result_inbound[:archive_possible],
+ archive_week_range: result_inbound[:archive_week_range],
+ setting: settings,
}
end
end
@@ -122,9 +124,11 @@ returns on fail
next if result_inbound[:result] != 'ok'
- success = true
- result[:setting][:inbound] = config
- result[:content_messages] = result_inbound[:content_messages]
+ success = true
+ result[:setting][:inbound] = config
+ result[:content_messages] = result_inbound[:content_messages]
+ result[:archive_possible] = result_inbound[:archive_possible]
+ result[:archive_week_range] = result_inbound[:archive_week_range]
break
end
diff --git a/spec/models/channel/email_parser_spec.rb b/spec/models/channel/email_parser_spec.rb
index 2eef079db..7709c6bb4 100644
--- a/spec/models/channel/email_parser_spec.rb
+++ b/spec/models/channel/email_parser_spec.rb
@@ -1242,7 +1242,7 @@ RSpec.describe Channel::EmailParser, type: :model do
it 'applies the OutOfOfficeCheck filter to given message' do
expect(Channel::Filter::OutOfOfficeCheck)
.to receive(:run)
- .with(kind_of(Hash), hash_including(subject: subject_line))
+ .with(kind_of(Hash), hash_including(subject: subject_line), kind_of(Hash))
described_class.new.process({}, raw_mail)
end
diff --git a/spec/models/channel/filter/import_archive_spec.rb b/spec/models/channel/filter/import_archive_spec.rb
new file mode 100644
index 000000000..ba53b882d
--- /dev/null
+++ b/spec/models/channel/filter/import_archive_spec.rb
@@ -0,0 +1,247 @@
+require 'rails_helper'
+
+RSpec.describe Channel::Filter::ImportArchive do
+
+ let!(:agent1) { create(:agent, groups: Group.all) }
+
+ let(:channel_as_model) do
+ Channel.new(options: { inbound: { options: { archive: true } } })
+ end
+
+ let(:channel_as_hash) do
+ { options: { inbound: { options: { archive: true } } } }
+ end
+
+ let(:mail001) do
+ email_file_path = Rails.root.join('test/data/mail/mail001.box')
+ File.read(email_file_path)
+ end
+
+ let(:email_parse_mail001) do
+ email_raw_string = mail001
+ Channel::EmailParser.new.process(channel_as_model, email_raw_string)
+ end
+
+ let(:email_parse_mail001_hash) do
+ email_raw_string = mail001
+ Channel::EmailParser.new.process(channel_as_hash, email_raw_string)
+ end
+
+ let(:email_parse_mail001_answer) do
+ email_raw_string = mail001
+ email_raw_string.gsub!('Date: Thu, 3 May 2012 11:36:43 +0200', 'Date: Thu, 3 May 2014 11:36:43 +0200')
+ email_raw_string.gsub!('Message-Id: <053EA3703574649ABDAF24D43A05604F327A130@MEMASFRK004.example.com>', "In-Reply-To: <053EA3703574649ABDAF24D43A05604F327A130@MEMASFRK004.example.com>\nMessage-Id: <053EA3703574649ABDAF24D43A05604F327A130-1@MEMASFRK004.example.com>")
+
+ Channel::EmailParser.new.process(channel_as_model, email_raw_string)
+ end
+
+ shared_examples 'import archive base checks' do |ticket_create_date, article_create_date, article_count|
+ it 'checks if the state is closed' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001
+ expect(ticket1_p.state.name).to eq('closed')
+ end
+
+ it 'checks if the article got created' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001
+ expect(ticket1_p.articles.count).to eq(article_count)
+ end
+
+ it 'checks if the ticket create date is correct' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001
+ expect(ticket1_p.created_at).to eq(Time.zone.parse(ticket_create_date))
+ end
+
+ it 'checks if the article create date is correct' do
+ _ticket1_p, article1_p, _user1_p = email_parse_mail001
+ expect(article1_p.created_at).to eq(Time.zone.parse(article_create_date))
+ end
+ end
+
+ shared_examples 'import archive answer checks' do |ticket_create_date, article_create_date, article_count|
+ it 'checks if the state is closed' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001_answer
+ expect(ticket1_p.state.name).to eq('closed')
+ end
+
+ it 'checks if the article got created' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001_answer
+ expect(ticket1_p.articles.count).to eq(article_count)
+ end
+
+ it 'checks if the ticket create date is correct' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001_answer
+ expect(ticket1_p.created_at).to eq(Time.zone.parse(ticket_create_date))
+ end
+
+ it 'checks if the article create date is correct' do
+ _ticket1_p, article1_p, _user1_p = email_parse_mail001_answer
+ expect(article1_p.created_at).to eq(Time.zone.parse(article_create_date))
+ end
+ end
+
+ shared_examples 'notification sent checks' do |notification_count, parse_hash = false|
+ def email_hash(parse_hash)
+ if parse_hash
+ email_parse_mail001_hash
+ else
+ email_parse_mail001
+ end
+ end
+
+ before do
+ ticket1_p, article1_p, _user1_p = email_hash(parse_hash)
+
+ Scheduler.worker(true)
+ ticket1_p.reload
+ article1_p.reload
+ end
+
+ it 'verifies if notifications are sent' do
+ ticket1_p, _article1_p, _user1_p = email_hash(parse_hash)
+ expect(NotificationFactory::Mailer.already_sent?(ticket1_p, agent1, 'email')).to eq(notification_count)
+ end
+ end
+
+ describe '.run' do
+
+ context 'when initial ticket (import before outdated)' do
+ let(:channel_as_model) do
+ Channel.new(options: { inbound: { options: { archive: true, archive_before: '2012-03-04 00:00:00' } } })
+ end
+
+ include_examples 'notification sent checks', 1
+ end
+
+ context 'when initial ticket (import before matched)' do
+ let(:channel_as_model) do
+ Channel.new(options: { inbound: { options: { archive: true, archive_before: '2012-05-04 00:00:00' } } })
+ end
+
+ include_examples 'notification sent checks', 0
+ end
+
+ context 'when initial ticket (import till outdated)' do
+ let(:channel_as_model) do
+ Channel.new(options: { inbound: { options: { archive: true, archive_till: (Time.zone.now - 1.day).to_s } } })
+ end
+
+ include_examples 'notification sent checks', 1
+ end
+
+ context 'when initial ticket (import till matched)' do
+ let(:channel_as_model) do
+ Channel.new(options: { inbound: { options: { archive: true, archive_till: (Time.zone.now + 1.day).to_s } } })
+ end
+
+ include_examples 'notification sent checks', 0
+ end
+
+ context 'when initial ticket (import before outdated) with channel hash' do
+ let(:channel_as_hash) do
+ { options: { inbound: { options: { archive: true, archive_before: '2012-03-04 00:00:00' } } } }
+ end
+
+ include_examples 'notification sent checks', 1, true
+ end
+
+ context 'when initial ticket (import before matched) with channel hash' do
+ let(:channel_as_hash) do
+ { options: { inbound: { options: { archive: true, archive_before: '2012-05-04 00:00:00' } } } }
+ end
+
+ include_examples 'notification sent checks', 0, true
+ end
+
+ context 'when initial ticket (import till outdated) with channel hash' do
+ let(:channel_as_hash) do
+ { options: { inbound: { options: { archive: true, archive_till: (Time.zone.now - 1.day).to_s } } } }
+ end
+
+ include_examples 'notification sent checks', 1, true
+ end
+
+ context 'when initial ticket (import till matched) with channel hash' do
+ let(:channel_as_hash) do
+ { options: { inbound: { options: { archive: true, archive_till: (Time.zone.now + 1.day).to_s } } } }
+ end
+
+ include_examples 'notification sent checks', 0, true
+ end
+
+ context 'when initial ticket' do
+
+ include_examples 'import archive base checks', 'Thu, 03 May 2012 09:36:43 UTC +00:00', 'Thu, 03 May 2012 09:36:43 UTC +00:00', 1
+
+ context 'with scheduler run' do
+ before do
+ ticket1_p, article1_p, _user1_p = email_parse_mail001
+ Scheduler.worker(true)
+ ticket1_p.reload
+ article1_p.reload
+ end
+
+ include_examples 'import archive base checks', 'Thu, 03 May 2012 09:36:43 UTC +00:00', 'Thu, 03 May 2012 09:36:43 UTC +00:00', 1
+
+ it 'verifies if notifications are sent' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001
+ expect(NotificationFactory::Mailer.already_sent?(ticket1_p, agent1, 'email')).to eq(0)
+ end
+
+ context 'when follow up check (mail answer)' do
+
+ include_examples 'import archive answer checks', 'Thu, 03 May 2012 09:36:43 UTC +00:00', 'Thu, 03 May 2014 09:36:43 UTC +00:00', 2
+
+ it 'checks if the article is different to the first one' do
+ _ticket1_p, article1_p, _user1_p = email_parse_mail001
+ _ticket2_p, article2_p, _user2_p = email_parse_mail001_answer
+ expect(article2_p.id).not_to eq(article1_p.id)
+ end
+
+ it 'checks if the article is a followup for the existing ticket' do
+ ticket1_p, _article1_p, _user1_p = email_parse_mail001
+ ticket2_p, _article2_p, _user2_p = email_parse_mail001_answer
+ expect(ticket2_p.id).to eq(ticket1_p.id)
+ end
+
+ context 'with scheduler run' do
+ before do
+ ticket2_p, article2_p, _user2_p = email_parse_mail001_answer
+ Scheduler.worker(true)
+ ticket2_p.reload
+ article2_p.reload
+ end
+
+ include_examples 'import archive answer checks', 'Thu, 03 May 2012 09:36:43 UTC +00:00', 'Thu, 03 May 2014 09:36:43 UTC +00:00', 2
+
+ it 'verifies if notifications are sent' do
+ ticket2_p, _article2_p, _user2_p = email_parse_mail001_answer
+ expect(NotificationFactory::Mailer.already_sent?(ticket2_p, agent1, 'email')).to eq(0)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when duplicate check with channel as model' do
+ before do
+ Channel::EmailParser.new.process(channel_as_model, mail001)
+ end
+
+ it 'checks that the ticket count does not change on duplicates' do
+ expect { Channel::EmailParser.new.process(channel_as_model, mail001) }
+ .not_to change(Ticket, :count)
+ end
+ end
+
+ context 'when duplicate check with channel as hash' do
+ before do
+ Channel::EmailParser.new.process(channel_as_hash, mail001)
+ end
+
+ it 'checks that the ticket count does not change on duplicates' do
+ expect { Channel::EmailParser.new.process(channel_as_hash, mail001) }
+ .not_to change(Ticket, :count)
+ end
+ end
+ end
+end
diff --git a/spec/models/channel/filter/out_of_office_check_spec.rb b/spec/models/channel/filter/out_of_office_check_spec.rb
index 7cac01cd3..2f9d68a8f 100644
--- a/spec/models/channel/filter/out_of_office_check_spec.rb
+++ b/spec/models/channel/filter/out_of_office_check_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Channel::Filter::OutOfOfficeCheck do
+RSpec.describe Channel::Filter::OutOfOfficeCheck, type: :channel_filter do
describe '.run' do
let(:mail_hash) { Channel::EmailParser.new.parse(<<~RAW.chomp) }
From: me@example.com
@@ -15,14 +15,14 @@ RSpec.describe Channel::Filter::OutOfOfficeCheck do
shared_examples 'regular message' do
it 'sets x-zammad-out-of-office header to false' do
- expect { described_class.run({}, mail_hash) }
+ expect { filter(mail_hash) }
.to change { mail_hash[:'x-zammad-out-of-office'] }.to(false)
end
end
shared_examples 'auto-response' do
it 'sets x-zammad-out-of-office header to true' do
- expect { described_class.run({}, mail_hash) }
+ expect { filter(mail_hash) }
.to change { mail_hash[:'x-zammad-out-of-office'] }.to(true)
end
end
diff --git a/spec/support/channel_filter.rb b/spec/support/channel_filter.rb
index a8a149104..5d8c1a76d 100644
--- a/spec/support/channel_filter.rb
+++ b/spec/support/channel_filter.rb
@@ -10,8 +10,8 @@ module ChannelFilterHelper
# filter({:'x-zammad-ticket-id' => 1234, ...})
#
# @return [nil]
- def filter(mail_hash, channel: {})
- described_class.run(channel, mail_hash)
+ def filter(mail_hash, channel: {}, transaction_params: {})
+ described_class.run(channel, mail_hash, transaction_params)
end
# Provides a helper method to parse a mail String and run the current class Channel::Filter.