Added generic use of all X-headers. Improved ui for postmaster filter.

This commit is contained in:
Martin Edenhofer 2014-06-08 05:11:21 +02:00
parent eb48ac0e4f
commit d436872bc7
4 changed files with 187 additions and 54 deletions

View file

@ -258,25 +258,29 @@ class App.ControllerForm extends App.Controller
# postmaster_match # postmaster_match
else if attribute.tag is 'postmaster_match' else if attribute.tag is 'postmaster_match'
addItem = (key, displayName, el, defaultValue = '') => addItem = (key, displayName, el, defaultValue = '') =>
itemInput = $("<div>#{ App.i18n.translateInline(displayName) }: <input name=\"#{ key }\" type=\"input\" value=\"#{ defaultValue }\" class=\"form-control\" required/><a href=\"#\" class=\"glyphicon glyphicon-minus remove\"></a></div>") add = { name: key, display: displayName, tag: 'input', null: false, default: defaultValue }
itemInput = $( @formGenItem( add ).append('<a href=\"#\" class=\"glyphicon glyphicon-minus remove\"></a>' ) )
# remove on click # remove on click
itemInput.find('.remove').bind('click', (e) -> itemInput.find('.remove').bind('click', (e) ->
e.preventDefault() e.preventDefault()
key = $(e.target).prev().attr('name') key = $(e.target).parent().find('select, input').attr('name')
return if !key return if !key
$(e.target).parent().parent().parent().find('.addSelection select option[value="' + key + '"]').show() $(e.target).parent().parent().parent().find('.addSelection select option[value="' + key + '"]').show()
$(e.target).parent().parent().parent().find('.list [name="' + key + '"]').parent().remove() $(e.target).parent().parent().parent().find('.addSelection select option[value="' + key + '"]').prop('disabled', false)
$(e.target).parent().parent().parent().find('.list [name="' + key + '"]').parent().parent().remove()
) )
# add new item # add new item
el.parent().parent().parent().find('.list').append(itemInput) el.parent().parent().parent().find('.list').append(itemInput)
el.parent().parent().parent().find('.addSelection select').val('') el.parent().parent().parent().find('.addSelection select').val('')
el.parent().parent().parent().find('.addSelection select option[value="' + key + '"]').prop('disabled', true)
el.parent().parent().parent().find('.addSelection select option[value="' + key + '"]').hide() el.parent().parent().parent().find('.addSelection select option[value="' + key + '"]').hide()
# scaffold of match elements # scaffold of match elements
item = $(' item = $('
<div class="postmaster_match" style="margin-left: 40px;"> <div class="postmaster_match">
<hr>
<div class="list"></div> <div class="list"></div>
<hr> <hr>
<div> <div>
@ -408,7 +412,7 @@ class App.ControllerForm extends App.Controller
] ]
for listItem in loopData for listItem in loopData
listItem.value = "#{ attribute.name }::#{listItem.value}" listItem.value = "#{ attribute.name }::#{listItem.value}"
add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true } add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true, required: false }
item.find('.addSelection').append( @formGenItem( add ) ) item.find('.addSelection').append( @formGenItem( add ) )
# bind add click # bind add click
@ -432,25 +436,39 @@ class App.ControllerForm extends App.Controller
# postmaster_set # postmaster_set
else if attribute.tag is 'postmaster_set' else if attribute.tag is 'postmaster_set'
addItem = (key, displayName, el, defaultValue = '') => addItem = (key, displayName, el, defaultValue = '') =>
itemInput = $("<div>#{ App.i18n.translateInline(displayName) }: <input name=\"#{ key }\" type=\"input\" value=\"#{ defaultValue }\" class=\"form-control\"/><a href=\"#\" class=\"glyphicon glyphicon-minus remove\"></a></div>") collection = undefined
for listItem in loopData
if listItem.value is key
collection = listItem
if collection.relation
add = { name: key, display: displayName, tag: 'select', multiple: false, null: false, nulloption: true, relation: collection.relation, translate: true, default: defaultValue }
else if collection.options
add = { name: key, display: displayName, tag: 'select', multiple: false, null: false, nulloption: true, options: collection.options, translate: true, default: defaultValue }
else
add = { name: key, display: displayName, tag: 'input', null: false, default: defaultValue }
itemInput = $( @formGenItem( add ).append('<a href=\"#\" class=\"glyphicon glyphicon-minus remove\"></a>' ) )
# remove on click # remove on click
itemInput.find('.remove').bind('click', (e) -> itemInput.find('.remove').bind('click', (e) ->
e.preventDefault() e.preventDefault()
key = $(e.target).prev().attr('name') key = $(e.target).parent().find('select, input').attr('name')
return if !key return if !key
$(e.target).parent().parent().parent().find('.addSelection select option[value="' + key + '"]').show() $(e.target).parent().parent().parent().find('.addSelection select option[value="' + key + '"]').show()
$(e.target).parent().parent().parent().find('.list [name="' + key + '"]').parent().remove() $(e.target).parent().parent().parent().find('.addSelection select option[value="' + key + '"]').prop('disabled', false)
$(e.target).parent().parent().parent().find('.list [name="' + key + '"]').parent().parent().remove()
) )
# add new item # add new item
console.log(1111222, key, el, el.parent().parent().parent().find('.addSelection select'))
el.parent().parent().parent().find('.list').append(itemInput) el.parent().parent().parent().find('.list').append(itemInput)
el.parent().parent().parent().find('.addSelection select').val('') el.parent().parent().parent().find('.addSelection select').val('')
el.parent().parent().parent().find('.addSelection select option[value="' + key + '"]').prop('disabled', true)
el.parent().parent().parent().find('.addSelection select option[value="' + key + '"]').hide() el.parent().parent().parent().find('.addSelection select option[value="' + key + '"]').hide()
# scaffold of perform elements # scaffold of perform elements
item = $(' item = $('
<div class="perform_set" style="margin-left: 40px;"> <div class="perform_set">
<hr>
<div class="list"></div> <div class="list"></div>
<hr> <hr>
<div> <div>
@ -463,20 +481,23 @@ class App.ControllerForm extends App.Controller
# select shown attributes # select shown attributes
loopData = [ loopData = [
{ {
value: 'x-zammad-ticket-priority' value: 'x-zammad-ticket-ticket_priority_id'
name: 'Ticket Priority' name: 'Ticket Priority'
relation: 'TicketPriority'
}, },
{ {
value: 'x-zammad-ticket-state' value: 'x-zammad-ticket-ticket_state_id'
name: 'Ticket State' name: 'Ticket State'
relation: 'TicketState'
}, },
{ {
value: 'x-zammad-ticket-customer' value: 'x-zammad-ticket-customer'
name: 'Ticket Customer' name: 'Ticket Customer'
}, },
{ {
value: 'x-zammad-ticket-group' value: 'x-zammad-ticket-group_id'
name: 'Ticket Group' name: 'Ticket Group'
relation: 'Group'
}, },
{ {
value: 'x-zammad-ticket-owner' value: 'x-zammad-ticket-owner'
@ -488,16 +509,18 @@ class App.ControllerForm extends App.Controller
disable: true disable: true
}, },
{ {
value: 'x-zammad-article-visibility' value: 'x-zammad-article-ticket_article_internal'
name: 'Article Visibility' name: 'Article Internal'
}, },
{ {
value: 'x-zammad-article-type' value: 'x-zammad-article-ticket_article_type_id'
name: 'Article Type' name: 'Article Type'
relation: 'TicketArticleType'
}, },
{ {
value: 'x-zammad-article-sender' value: 'x-zammad-article-ticket_article_sender_id'
name: 'Article Sender' name: 'Article Sender'
relation: 'TicketArticleSender'
}, },
{ {
value: '' value: ''
@ -507,11 +530,12 @@ class App.ControllerForm extends App.Controller
{ {
value: 'x-zammad-ignore' value: 'x-zammad-ignore'
name: 'Ignore Message' name: 'Ignore Message'
options: { true: 'Yes', false: 'No'}
}, },
] ]
for listItem in loopData for listItem in loopData
listItem.value = "#{ attribute.name }::#{listItem.value}" listItem.value = "#{ attribute.name }::#{listItem.value}"
add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true } add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true, required: false }
item.find('.addSelection').append( @formGenItem( add ) ) item.find('.addSelection').append( @formGenItem( add ) )
item.find('.add').bind('click', (e) -> item.find('.add').bind('click', (e) ->

View file

@ -371,25 +371,18 @@ class Channel::EmailParser
if !ticket if !ticket
# set attributes # set attributes
ticket_attributes = { ticket = Ticket.new(
:group_id => channel[:group_id] || 1, :group_id => channel[:group_id] || 1,
:customer_id => user.id, :customer_id => user.id,
:title => mail[:subject] || '', :title => mail[:subject] || '',
:ticket_state_id => Ticket::State.where( :name => 'new' ).first.id, :ticket_state_id => Ticket::State.where( :name => 'new' ).first.id,
:ticket_priority_id => Ticket::Priority.where( :name => '2 normal' ).first.id, :ticket_priority_id => Ticket::Priority.where( :name => '2 normal' ).first.id,
} )
# x-headers lookup object_lookup( ticket, 'ticket', mail )
map = [
[ 'x-zammad-ticket-group', Group, 'group_id', 'name' ],
[ 'x-zammad-ticket-state', Ticket::State, 'ticket_state_id', 'name' ],
[ 'x-zammad-ticket-priority', Ticket::Priority, 'ticket_priority_id', 'name' ],
[ 'x-zammad-ticket-owner', User, 'owner_id', 'login' ],
]
object_lookup( ticket_attributes, map, mail )
# create ticket # create ticket
ticket = Ticket.create( ticket_attributes ) ticket.save
end end
# import mail # import mail
@ -399,7 +392,7 @@ class Channel::EmailParser
if mail[ 'X-Zammad-Article-Visibility'.to_sym ] && mail[ 'X-Zammad-Article-Visibility'.to_sym ] == 'internal' if mail[ 'X-Zammad-Article-Visibility'.to_sym ] && mail[ 'X-Zammad-Article-Visibility'.to_sym ] == 'internal'
internal = true internal = true
end end
article_attributes = { article = Ticket::Article.new(
:ticket_id => ticket.id, :ticket_id => ticket.id,
:ticket_article_type_id => Ticket::Article::Type.where( :name => 'email' ).first.id, :ticket_article_type_id => Ticket::Article::Type.where( :name => 'email' ).first.id,
:ticket_article_sender_id => Ticket::Article::Sender.where( :name => 'Customer' ).first.id, :ticket_article_sender_id => Ticket::Article::Sender.where( :name => 'Customer' ).first.id,
@ -410,17 +403,13 @@ class Channel::EmailParser
:subject => mail[:subject], :subject => mail[:subject],
:message_id => mail[:message_id], :message_id => mail[:message_id],
:internal => internal, :internal => internal,
} )
# x-headers lookup # x-headers lookup
map = [ object_lookup( article, 'article', mail )
[ 'x-zammad-article-type', Ticket::Article::Type, 'ticket_article_type_id', 'name' ],
[ 'x-zammad-article-sender', Ticket::Article::Sender, 'ticket_article_sender_id', 'name' ],
]
object_lookup( article_attributes, map, mail )
# create article # create article
article = Ticket::Article.create(article_attributes) article.save
# store mail plain # store mail plain
Store.add( Store.add(
@ -467,15 +456,51 @@ class Channel::EmailParser
return ticket, article, user return ticket, article, user
end end
def object_lookup( attributes, map, mail ) def object_lookup( item_object, header_name, mail )
map.each { |item|
if mail[ item[0].to_sym ] # loop all x-zammad-hedaer-* headers
new_object = item[1].where( "lower(#{item[3]}) = ?", mail[ item[0].to_sym ].downcase ).first item_object.attributes.each{|key,value|
if new_object
attributes[ item[2].to_sym ] = new_object.id # ignore read only attributes
next if key == 'updated_at'
next if key == 'created_at'
next if key == 'updated_by_id'
next if key == 'created_by_id'
# check if id exists
key_short = key[ key.length-3 , key.length ]
if key_short == '_id'
key_short = key[ 0, key.length-3 ]
header = "x-zammad-#{header_name}-#{key_short}"
if mail[ header.to_sym ]
puts "NOTICE: header #{header} found #{mail[ header.to_sym ]}"
item_object.class.reflect_on_all_associations.map { |assoc|
if assoc.name.to_s == key_short
puts "NOTICE: ASSOC found #{assoc.class_name} lookup #{mail[ header.to_sym ]}"
item = assoc.class_name.constantize
if item.respond_to?(:name)
if item.lookup( :name => mail[ header.to_sym ] )
item_object[key] = item.lookup( :name => mail[ header.to_sym ] ).id
end
elsif item.respond_to?(:login)
if item.lookup( :login => mail[ header.to_sym ] )
item_object[key] = item.lookup( :login => mail[ header.to_sym ] ).id
end
end
end
}
end end
end end
# check if attribute exists
header = "x-zammad-#{header_name}-#{key}"
if mail[ header.to_sym ]
puts "NOTICE: header #{header} found #{mail[ header.to_sym ]}"
item_object[key] = mail[ header.to_sym ]
end
} }
end end
def html2ascii(string) def html2ascii(string)

View file

@ -376,6 +376,26 @@ test( "form postmaster filter", function() {
// add match rule // add match rule
// add set rule // add set rule
App.TicketPriority.refresh( [
{
id: 1,
name: 'prio 1',
},
{
id: 2,
name: 'prio 2',
},
] )
App.Group.refresh( [
{
id: 1,
name: 'group 1',
},
{
id: 2,
name: 'group 2',
},
] )
$('#forms').append('<hr><h1>form postmaster filter</h1><form id="form5"></form>') $('#forms').append('<hr><h1>form postmaster filter</h1><form id="form5"></form>')
var el = $('#form5') var el = $('#form5')
@ -386,8 +406,10 @@ test( "form postmaster filter", function() {
subject: 'some subject', subject: 'some subject',
}, },
set: { set: {
'x-zammad-ticket-priority': '3 high', 'x-zammad-ticket-owner': 'owner',
'x-zammad-ticket-group': 'some group', 'x-zammad-ticket-customer': 'customer',
'x-zammad-ticket-ticket_priority_id': 2,
'x-zammad-ticket-group_id': 1,
}, },
} }
new App.ControllerForm({ new App.ControllerForm({
@ -411,12 +433,15 @@ test( "form postmaster filter", function() {
subject: 'some subject', subject: 'some subject',
}, },
set: { set: {
'x-zammad-ticket-priority': '3 high', 'x-zammad-ticket-owner': 'owner',
'x-zammad-ticket-group': 'some group', 'x-zammad-ticket-customer': 'customer',
'x-zammad-ticket-ticket_priority_id': "2",
'x-zammad-ticket-group_id': "1",
}, },
}; };
deepEqual( params, test_params, 'form param check' ); deepEqual( params, test_params, 'form param check' );
el.find('[name="set::x-zammad-ticket-priority"]').next().click() el.find('[name="set::x-zammad-ticket-ticket_priority_id"]').parent().next().click()
el.find('[name="set::x-zammad-ticket-customer"]').parent().next().click()
App.Delay.set( function() { App.Delay.set( function() {
test( "form param check after remove click", function() { test( "form param check after remove click", function() {
params = App.ControllerForm.params( el ) params = App.ControllerForm.params( el )
@ -428,7 +453,8 @@ test( "form postmaster filter", function() {
subject: 'some subject', subject: 'some subject',
}, },
set: { set: {
'x-zammad-ticket-group': 'some group', 'x-zammad-ticket-owner': 'owner',
'x-zammad-ticket-group_id': "1",
}, },
}; };
deepEqual( params, test_params, 'form param check' ); deepEqual( params, test_params, 'form param check' );

View file

@ -1,8 +1,8 @@
# encoding: utf-8 # encoding: utf-8
require 'test_helper' require 'test_helper'
class EmailProcessTest < ActiveSupport::TestCase class EmailProcessTest < ActiveSupport::TestCase
test 'process' do test 'process trusted' do
files = [ files = [
{ {
:data => 'From: me@example.com :data => 'From: me@example.com
@ -25,9 +25,10 @@ Some Text',
:data => 'From: me@example.com :data => 'From: me@example.com
To: customer@example.com To: customer@example.com
Subject: some subject Subject: some subject
X-Zammad-Ticket-Priority: 3 high X-Zammad-Ticket-Ticket_Priority: 3 high
X-Zammad-Article-Sender: system X-Zammad-Article-Ticket_Article_Sender: System
x-Zammad-Article-Type: phone x-Zammad-Article-Ticket_Article_Type: phone
x-Zammad-Article-Internal: true
Some Text', Some Text',
:success => true, :success => true,
@ -39,6 +40,7 @@ Some Text',
1 => { 1 => {
:ticket_article_sender => 'System', :ticket_article_sender => 'System',
:ticket_article_type => 'phone', :ticket_article_type => 'phone',
:internal => true,
}, },
}, },
}, },
@ -58,6 +60,7 @@ Some Textäöü",
:body => 'Some Textäöü', :body => 'Some Textäöü',
:ticket_article_sender => 'Customer', :ticket_article_sender => 'Customer',
:ticket_article_type => 'email', :ticket_article_type => 'email',
:internal => false,
}, },
}, },
}, },
@ -77,6 +80,7 @@ Some Textäöü".encode("ISO-8859-1"),
:body => 'Some Textäöü', :body => 'Some Textäöü',
:ticket_article_sender => 'Customer', :ticket_article_sender => 'Customer',
:ticket_article_type => 'email', :ticket_article_type => 'email',
:internal => false,
}, },
}, },
}, },
@ -168,7 +172,61 @@ Some Text",
if result[level].send(key).respond_to?('name') if result[level].send(key).respond_to?('name')
assert_equal( result[level].send(key).name, value.to_s) assert_equal( result[level].send(key).name, value.to_s)
else else
assert_equal( result[level].send(key), value.to_s) assert_equal( result[level].send(key), value)
end
}
end
}
end
elsif !file[:success] && result == true
assert( true )
elsif !file[:success] && result[1]
assert( false, 'ticket should not be created' )
else
assert( false, 'UNKNOWN!' )
end
}
end
test 'process not trusted' do
files = [
{
:data => 'From: me@example.com
To: customer@example.com
Subject: some subject
X-Zammad-Ticket-Ticket_Priority: 3 high
X-Zammad-Article-Ticket_Article_Sender: System
x-Zammad-Article-Ticket_Article_Type: phone
x-Zammad-Article-Internal: true
Some Text',
:success => true,
:result => {
0 => {
:ticket_priority => '2 normal',
:title => 'some subject',
},
1 => {
:ticket_article_sender => 'Customer',
:ticket_article_type => 'email',
:internal => false,
},
},
},
]
files.each { |file|
parser = Channel::EmailParser.new
result = parser.process( { :trusted => false }, file[:data] )
if file[:success] && result[1]
assert( true )
if file[:result]
[ 0, 1, 2 ].each { |level|
if file[:result][level]
file[:result][level].each { |key, value|
if result[level].send(key).respond_to?('name')
assert_equal( result[level].send(key).name, value.to_s)
else
assert_equal( result[level].send(key), value)
end end
} }
end end