Implemented reply_to header as sender/customer feature.
This commit is contained in:
parent
e9efae4b83
commit
79c398becb
5 changed files with 254 additions and 26 deletions
|
@ -117,32 +117,7 @@ class Channel::EmailParser
|
||||||
}
|
}
|
||||||
|
|
||||||
# set extra headers
|
# set extra headers
|
||||||
begin
|
data = data.merge(Channel::EmailParser.sender_properties(from))
|
||||||
data[:from_email] = Mail::Address.new(from).address
|
|
||||||
data[:from_local] = Mail::Address.new(from).local
|
|
||||||
data[:from_domain] = Mail::Address.new(from).domain
|
|
||||||
data[:from_display_name] = Mail::Address.new(from).display_name ||
|
|
||||||
(Mail::Address.new(from).comments && Mail::Address.new(from).comments[0])
|
|
||||||
rescue
|
|
||||||
from.strip!
|
|
||||||
if from =~ /^(.+?)<(.+?)@(.+?)>$/
|
|
||||||
data[:from_email] = "#{$2}@#{$3}"
|
|
||||||
data[:from_local] = $2
|
|
||||||
data[:from_domain] = $3
|
|
||||||
data[:from_display_name] = $1
|
|
||||||
else
|
|
||||||
data[:from_email] = from
|
|
||||||
data[:from_local] = from
|
|
||||||
data[:from_domain] = from
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# do extra decoding because we needed to use field.value
|
|
||||||
data[:from_display_name] = Mail::Field.new('X-From', data[:from_display_name]).to_s
|
|
||||||
data[:from_display_name].delete!('"')
|
|
||||||
data[:from_display_name].strip!
|
|
||||||
data[:from_display_name].gsub!(/^'/, '')
|
|
||||||
data[:from_display_name].gsub!(/'$/, '')
|
|
||||||
|
|
||||||
# do extra encoding (see issue#1045)
|
# do extra encoding (see issue#1045)
|
||||||
if data[:subject].present?
|
if data[:subject].present?
|
||||||
|
@ -638,6 +613,39 @@ returns
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.sender_properties(from)
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
begin
|
||||||
|
data[:from_email] = Mail::Address.new(from).address
|
||||||
|
data[:from_local] = Mail::Address.new(from).local
|
||||||
|
data[:from_domain] = Mail::Address.new(from).domain
|
||||||
|
data[:from_display_name] = Mail::Address.new(from).display_name ||
|
||||||
|
(Mail::Address.new(from).comments && Mail::Address.new(from).comments[0])
|
||||||
|
rescue
|
||||||
|
from.strip!
|
||||||
|
if from =~ /^(.+?)<(.+?)@(.+?)>$/
|
||||||
|
data[:from_email] = "#{$2}@#{$3}"
|
||||||
|
data[:from_local] = $2
|
||||||
|
data[:from_domain] = $3
|
||||||
|
data[:from_display_name] = $1
|
||||||
|
else
|
||||||
|
data[:from_email] = from
|
||||||
|
data[:from_local] = from
|
||||||
|
data[:from_domain] = from
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# do extra decoding because we needed to use field.value
|
||||||
|
data[:from_display_name] = Mail::Field.new('X-From', data[:from_display_name]).to_s
|
||||||
|
data[:from_display_name].delete!('"')
|
||||||
|
data[:from_display_name].strip!
|
||||||
|
data[:from_display_name].gsub!(/^'/, '')
|
||||||
|
data[:from_display_name].gsub!(/'$/, '')
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
def set_attributes_by_x_headers(item_object, header_name, mail, suffix = false)
|
def set_attributes_by_x_headers(item_object, header_name, mail, suffix = false)
|
||||||
|
|
||||||
# loop all x-zammad-hedaer-* headers
|
# loop all x-zammad-hedaer-* headers
|
||||||
|
|
36
app/models/channel/filter/reply_to_based_sender.rb
Normal file
36
app/models/channel/filter/reply_to_based_sender.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
module Channel::Filter::ReplyToBasedSender
|
||||||
|
|
||||||
|
def self.run(_channel, mail)
|
||||||
|
|
||||||
|
reply_to = mail['reply-to'.to_sym]
|
||||||
|
return if reply_to.blank?
|
||||||
|
|
||||||
|
setting = Setting.get('postmaster_sender_based_on_reply_to')
|
||||||
|
return if setting.blank?
|
||||||
|
|
||||||
|
# get properties of reply-to header
|
||||||
|
result = Channel::EmailParser.sender_properties(reply_to)
|
||||||
|
|
||||||
|
if setting == 'as_sender_of_email'
|
||||||
|
mail[:from] = reply_to
|
||||||
|
mail[:from_email] = result[:from_email]
|
||||||
|
mail[:from_local] = result[:from_local]
|
||||||
|
mail[:from_domain] = result[:from_domain]
|
||||||
|
mail[:from_display_name] = result[:from_display_name]
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if setting == 'as_sender_of_email_use_from_realname'
|
||||||
|
mail[:from] = reply_to
|
||||||
|
mail[:from_email] = result[:from_email]
|
||||||
|
mail[:from_local] = result[:from_local]
|
||||||
|
mail[:from_domain] = result[:from_domain]
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails.logger.error "Invalid setting value for 'postmaster_sender_based_on_reply_to' -> #{setting.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
45
db/migrate/20170525000001_reply_to_sender_feature.rb
Normal file
45
db/migrate/20170525000001_reply_to_sender_feature.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
class ReplyToSenderFeature < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Sender based on Reply-To header',
|
||||||
|
name: 'postmaster_sender_based_on_reply_to',
|
||||||
|
area: 'Email::Base',
|
||||||
|
description: 'Set/overwrite sender/from of email based on reply-to header. Useful to set correct customer if email is received from a third party system on behalf of a customer.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'postmaster_sender_based_on_reply_to',
|
||||||
|
tag: 'select',
|
||||||
|
options: {
|
||||||
|
'' => '-',
|
||||||
|
'as_sender_of_email' => 'Take reply-to header as sender/from of email.',
|
||||||
|
'as_sender_of_email_use_from_realname' => 'Take reply-to header as sender/from of email and use realname of origin from.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: [],
|
||||||
|
preferences: {
|
||||||
|
permission: ['admin.channel_email'],
|
||||||
|
},
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Defines postmaster filter.',
|
||||||
|
name: '0011_postmaster_sender_based_on_reply_to',
|
||||||
|
area: 'Postmaster::PreFilter',
|
||||||
|
description: 'Defines postmaster filter to set the sender/from of emails based on reply-to header.',
|
||||||
|
options: {},
|
||||||
|
state: 'Channel::Filter::ReplyToBasedSender',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1657,6 +1657,33 @@ Setting.create_if_not_exists(
|
||||||
frontend: false
|
frontend: false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Sender based on Reply-To header',
|
||||||
|
name: 'postmaster_sender_based_on_reply_to',
|
||||||
|
area: 'Email::Base',
|
||||||
|
description: 'Set/overwrite sender/from of email based on reply-to header. Useful to set correct customer if email is received from a third party system on behalf of a customer.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'postmaster_sender_based_on_reply_to',
|
||||||
|
tag: 'select',
|
||||||
|
options: {
|
||||||
|
'' => '-',
|
||||||
|
'as_sender_of_email' => 'Take reply-to header as sender/from of email.',
|
||||||
|
'as_sender_of_email_use_from_realname' => 'Take reply-to header as sender/from of email and use realname of origin from.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: [],
|
||||||
|
preferences: {
|
||||||
|
permission: ['admin.channel_email'],
|
||||||
|
},
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Notification Sender',
|
title: 'Notification Sender',
|
||||||
name: 'notification_sender',
|
name: 'notification_sender',
|
||||||
|
@ -2217,6 +2244,15 @@ Setting.create_if_not_exists(
|
||||||
state: 'Channel::Filter::Trusted',
|
state: 'Channel::Filter::Trusted',
|
||||||
frontend: false
|
frontend: false
|
||||||
)
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Defines postmaster filter.',
|
||||||
|
name: '0011_postmaster_sender_based_on_reply_to',
|
||||||
|
area: 'Postmaster::PreFilter',
|
||||||
|
description: 'Defines postmaster filter to set the sender/from of emails based on reply-to header.',
|
||||||
|
options: {},
|
||||||
|
state: 'Channel::Filter::ReplyToBasedSender',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Defines postmaster filter.',
|
title: 'Defines postmaster filter.',
|
||||||
name: '0012_postmaster_filter_sender_is_system_address',
|
name: '0012_postmaster_filter_sender_is_system_address',
|
||||||
|
|
103
test/unit/email_process_reply_to_test.rb
Normal file
103
test/unit/email_process_reply_to_test.rb
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class EmailProcessReplyToTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
|
test 'normal processing' do
|
||||||
|
|
||||||
|
setting_orig = Setting.get('postmaster_sender_based_on_reply_to')
|
||||||
|
Setting.set('postmaster_sender_based_on_reply_to', '')
|
||||||
|
|
||||||
|
email = "From: Bob Smith <marketing_tool@example.com>
|
||||||
|
To: zammad@example.com
|
||||||
|
Subject: some new subject
|
||||||
|
Reply-To: replay_to_customer_process1@example.com
|
||||||
|
|
||||||
|
Some Text"
|
||||||
|
|
||||||
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email)
|
||||||
|
assert_equal('Bob Smith <marketing_tool@example.com>', article_p.from)
|
||||||
|
assert_equal('replay_to_customer_process1@example.com', article_p.reply_to)
|
||||||
|
assert_equal('marketing_tool@example.com', ticket_p.customer.email)
|
||||||
|
assert_equal('Bob', ticket_p.customer.firstname)
|
||||||
|
assert_equal('Smith', ticket_p.customer.lastname)
|
||||||
|
|
||||||
|
Setting.set('postmaster_sender_based_on_reply_to', setting_orig)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'normal processing - take reply to as customer' do
|
||||||
|
|
||||||
|
setting_orig = Setting.get('postmaster_sender_based_on_reply_to')
|
||||||
|
Setting.set('postmaster_sender_based_on_reply_to', 'as_sender_of_email')
|
||||||
|
|
||||||
|
email = "From: Bob Smith <marketing_tool@example.com>
|
||||||
|
To: zammad@example.com
|
||||||
|
Subject: some new subject
|
||||||
|
Reply-To: replay_to_customer_process2@example.com
|
||||||
|
|
||||||
|
Some Text"
|
||||||
|
|
||||||
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email)
|
||||||
|
assert_equal('replay_to_customer_process2@example.com', article_p.from)
|
||||||
|
assert_equal('replay_to_customer_process2@example.com', article_p.reply_to)
|
||||||
|
assert_equal('replay_to_customer_process2@example.com', ticket_p.customer.email)
|
||||||
|
assert_equal('', ticket_p.customer.firstname)
|
||||||
|
assert_equal('', ticket_p.customer.lastname)
|
||||||
|
|
||||||
|
email = "From: Bob Smith <marketing_tool@example.com>
|
||||||
|
To: zammad@example.com
|
||||||
|
Subject: some new subject
|
||||||
|
Reply-To: Some Name <replay_to_customer_process2-1@example.com>
|
||||||
|
|
||||||
|
Some Text"
|
||||||
|
|
||||||
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email)
|
||||||
|
assert_equal('Some Name <replay_to_customer_process2-1@example.com>', article_p.from)
|
||||||
|
assert_equal('Some Name <replay_to_customer_process2-1@example.com>', article_p.reply_to)
|
||||||
|
assert_equal('replay_to_customer_process2-1@example.com', ticket_p.customer.email)
|
||||||
|
assert_equal('Some', ticket_p.customer.firstname)
|
||||||
|
assert_equal('Name', ticket_p.customer.lastname)
|
||||||
|
|
||||||
|
Setting.set('postmaster_sender_based_on_reply_to', setting_orig)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'normal processing - take reply to as customer and use from as realname' do
|
||||||
|
|
||||||
|
setting_orig = Setting.get('postmaster_sender_based_on_reply_to')
|
||||||
|
Setting.set('postmaster_sender_based_on_reply_to', 'as_sender_of_email_use_from_realname')
|
||||||
|
|
||||||
|
email = "From: Bob Smith <marketing_tool@example.com>
|
||||||
|
To: zammad@example.com
|
||||||
|
Subject: some new subject
|
||||||
|
Reply-To: replay_to_customer_process3@example.com
|
||||||
|
|
||||||
|
Some Text"
|
||||||
|
|
||||||
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email)
|
||||||
|
assert_equal('replay_to_customer_process3@example.com', article_p.from)
|
||||||
|
assert_equal('replay_to_customer_process3@example.com', article_p.reply_to)
|
||||||
|
assert_equal('replay_to_customer_process3@example.com', ticket_p.customer.email)
|
||||||
|
assert_equal('Bob', ticket_p.customer.firstname)
|
||||||
|
assert_equal('Smith', ticket_p.customer.lastname)
|
||||||
|
|
||||||
|
email = "From: Bob Smith <marketing_tool@example.com>
|
||||||
|
To: zammad@example.com
|
||||||
|
Subject: some new subject
|
||||||
|
Reply-To: Some Name <replay_to_customer_process3-1@example.com>
|
||||||
|
|
||||||
|
Some Text"
|
||||||
|
|
||||||
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email)
|
||||||
|
assert_equal('Some Name <replay_to_customer_process3-1@example.com>', article_p.from)
|
||||||
|
assert_equal('Some Name <replay_to_customer_process3-1@example.com>', article_p.reply_to)
|
||||||
|
assert_equal('replay_to_customer_process3-1@example.com', ticket_p.customer.email)
|
||||||
|
assert_equal('Bob', ticket_p.customer.firstname)
|
||||||
|
assert_equal('Smith', ticket_p.customer.lastname)
|
||||||
|
|
||||||
|
Setting.set('postmaster_sender_based_on_reply_to', setting_orig)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue