diff --git a/app/assets/javascripts/app/controllers/_ui_element/postmaster_match.js.coffee b/app/assets/javascripts/app/controllers/_ui_element/postmaster_match.js.coffee
index 2065ffb86..42ec88b75 100644
--- a/app/assets/javascripts/app/controllers/_ui_element/postmaster_match.js.coffee
+++ b/app/assets/javascripts/app/controllers/_ui_element/postmaster_match.js.coffee
@@ -1,176 +1,242 @@
class App.UiElement.postmaster_match
- @render: (attribute, params, form_controller) ->
- addItem = (key, displayName, el, defaultValue = '') =>
- add = { name: key, display: displayName, tag: 'input', null: false, default: defaultValue }
- itemInput = $( form_controller.formGenItem( add ).append(' ' ) )
+ @defaults: ->
+ groups =
+ general:
+ name: 'Basic Settings'
+ options: [
+ {
+ value: 'from'
+ name: 'From'
+ },
+ {
+ value: 'to'
+ name: 'To'
+ },
+ {
+ value: 'cc'
+ name: 'Cc'
+ },
+ {
+ value: 'x-any-recipient'
+ name: 'Any Recipient'
+ },
+ {
+ value: 'subject'
+ name: 'Subject'
+ },
+ {
+ value: 'body'
+ name: 'Body'
+ },
+ ]
+ expert:
+ name: 'Expert Settings'
+ options: [
+ {
+ value: 'x-spam-flag'
+ name: 'X-Spam-Flag'
+ },
+ {
+ value: 'x-spam-level'
+ name: 'X-Spam-Level'
+ },
+ {
+ value: 'x-spam-score'
+ name: 'X-Spam-Score'
+ },
+ {
+ value: 'x-spam-status'
+ name: 'X-Spam-Status'
+ },
+ {
+ value: 'importance'
+ name: 'Importance'
+ },
+ {
+ value: 'x-priority'
+ name: 'X-Priority'
+ },
- # remove on click
- itemInput.find('.remove').bind('click', (e) ->
- e.preventDefault()
- key = $(e.target).closest('.form-group').find('select, input').attr('name')
- return if !key
- $(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').show()
- $(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').prop('disabled', false)
- $(e.target).closest('.form-group').remove()
- )
+ {
+ value: 'organization'
+ name: 'Organization'
+ },
- # add new item
- control = el.closest('.postmaster_match')
- control.find('.list').append(itemInput)
- control.find('.addSelection select').val('')
- control.find('.addSelection select option[value="' + key + '"]').prop('disabled', true)
- control.find('.addSelection select option[value="' + key + '"]').hide()
+ {
+ value: 'x-original-to'
+ name: 'X-Original-To'
+ },
+ {
+ value: 'delivered-to'
+ name: 'Delivered-To'
+ },
+ {
+ value: 'envelope-to'
+ name: 'Envelope-To'
+ },
+ {
+ value: 'return-path'
+ name: 'Return-Path'
+ },
+ {
+ value: 'mailing-list'
+ name: 'Mailing-List'
+ },
+ {
+ value: 'list-id'
+ name: 'List-Id'
+ },
+ {
+ value: 'list-archive'
+ name: 'List-Archive'
+ },
+ {
+ value: 'mailing-list'
+ name: 'Mailing-List'
+ },
+ {
+ value: 'auto-submitted'
+ name: 'Auto-Submitted'
+ },
+ {
+ value: 'x-loop'
+ name: 'X-Loop'
+ },
+ ]
+
+ groups
+
+ @render: (attribute, params = {}) ->
+
+ groups = @defaults()
+
+ selector = @buildAttributeSelector(groups, attribute)
# scaffold of match elements
- item = $('
-
')
+ item = $( App.view('generic/postmaster_match')( attribute: attribute ) )
+ item.find('.js-attributeSelector').prepend(selector)
- # select shown attributes
- loopData = [
- {
- value: 'from'
- name: 'From'
- },
- {
- value: 'to'
- name: 'To'
- },
- {
- value: 'cc'
- name: 'Cc'
- },
- {
- value: 'subject'
- name: 'Subject'
- },
- {
- value: 'body'
- name: 'Body'
- },
- {
- value: ''
- name: '-'
- disable: true
- },
- {
- value: 'x-any-recipient'
- name: 'Any Recipient'
- },
- {
- value: ''
- name: '-'
- disable: true
- },
- {
- value: ''
- name: '- ' + App.i18n.translateInline('expert settings') + ' -'
- disable: true
- },
- {
- value: ''
- name: '-'
- disable: true
- },
- {
- value: 'x-spam-flag'
- name: 'X-Spam-Flag'
- },
- {
- value: 'x-spam-level'
- name: 'X-Spam-Level'
- },
- {
- value: 'x-spam-score'
- name: 'X-Spam-Score'
- },
- {
- value: 'x-spam-status'
- name: 'X-Spam-Status'
- },
- {
- value: 'importance'
- name: 'Importance'
- },
- {
- value: 'x-priority'
- name: 'X-Priority'
- },
-
- {
- value: 'organization'
- name: 'Organization'
- },
-
- {
- value: 'x-original-to'
- name: 'X-Original-To'
- },
- {
- value: 'delivered-to'
- name: 'Delivered-To'
- },
- {
- value: 'envelope-to'
- name: 'Envelope-To'
- },
- {
- value: 'return-path'
- name: 'Return-Path'
- },
- {
- value: 'mailing-list'
- name: 'Mailing-List'
- },
- {
- value: 'list-id'
- name: 'List-Id'
- },
- {
- value: 'list-archive'
- name: 'List-Archive'
- },
- {
- value: 'mailing-list'
- name: 'Mailing-List'
- },
- {
- value: 'auto-submitted'
- name: 'Auto-Submitted'
- },
- {
- value: 'x-loop'
- name: 'X-Loop'
- },
- ]
- for listItem in loopData
- listItem.value = "#{ attribute.name }::#{listItem.value}"
- add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true, required: false }
- item.find('.addSelection').append( form_controller.formGenItem( add ) )
-
- # bind add click
- item.find('.add').bind('click', (e) ->
- e.preventDefault()
- name = $(@).closest('.controls').find('.addSelection').find('select').val()
- displayName = $(@).closest('.controls').find('.addSelection').find('select option:selected').html()
- return if !name
- addItem( name, displayName, $(@) )
+ # add filter
+ item.find('.js-add').bind('click', (e) =>
+ element = $(e.target).closest('.js-filterElement')
+ elementClone = element.clone(true)
+ element.after(elementClone)
+ elementClone.find('.js-attributeSelector select').trigger('change')
)
- # show default values
- loopDataValue = {}
- if attribute.value
- for key, value of attribute.value
- displayName = key
- for listItem in loopData
- if listItem.value is "#{ attribute.name }::#{key}"
- addItem( "#{ attribute.name }::#{key}", listItem.name, item.find('.add'), value )
+ # remove filter
+ item.find('.js-remove').bind('click', (e) =>
+ $(e.target).closest('.js-filterElement').remove()
+ @rebuildAttributeSelectors(item)
+ )
+
+ # change attribute selector
+ item.find('.js-attributeSelector select').bind('change', (e) =>
+ key = $(e.target).find('option:selected').attr('value')
+ elementRow = $(e.target).closest('.js-filterElement')
+
+ @rebuildAttributeSelectors(item, elementRow, key, attribute)
+ @rebuildOperater(item, elementRow, key, groups, undefined, attribute)
+ @buildValue(item, elementRow, key, groups, undefined, undefined, attribute)
+ )
+
+ # change operator
+ item.find('.js-operator select').bind('change', (e) =>
+ key = $(e.target).find('.js-attributeSelector option:selected').attr('value')
+ operator = $(e.target).find('option:selected').attr('value')
+ elementRow = $(e.target).closest('.js-filterElement')
+ @buildValue(item, elementRow, key, groups, undefined, operator, attribute)
+ )
+
+ # build inital params
+ if !_.isEmpty(params[attribute.name])
+
+ selectorExists = false
+ for key, meta of params[attribute.name]
+ selectorExists = true
+ operator = meta.operator
+ value = meta.value
+
+ # get selector rows
+ elementFirst = item.find('.js-filterElement').first()
+ elementLast = item.find('.js-filterElement').last()
+
+ # clone, rebuild and append
+ elementClone = elementFirst.clone(true)
+ @rebuildAttributeSelectors(item, elementClone, key, attribute)
+ @rebuildOperater(item, elementClone, key, groups, operator, attribute)
+ @buildValue(item, elementClone, key, groups, value, operator, attribute)
+ elementLast.after(elementClone)
+
+ # remove first dummy row
+ if selectorExists
+ item.find('.js-filterElement').first().remove()
item
+
+ @buildValue: (elementFull, elementRow, key, groups, value, operator, attribute) ->
+
+ # do nothing if item already exists
+ name = "#{attribute.name}::#{key}::value"
+ return if elementRow.find("[name=\"#{name}\"]").get(0)
+ config =
+ name: name
+ tag: 'input'
+ type: 'text'
+ value: value
+ item = App.UiElement[config.tag].render(config, {})
+ elementRow.find('.js-value').html(item)
+
+ @buildAttributeSelector: (groups, attribute) ->
+ selection = $(' ')
+ for groupKey, groupMeta of groups
+ displayName = App.i18n.translateInline(groupMeta.name)
+ selection.closest('select').append(" ")
+ optgroup = selection.find("optgroup.js-#{groupKey}")
+ for entry in groupMeta.options
+ displayName = App.i18n.translateInline(entry.name)
+ optgroup.append("#{displayName} ")
+ selection
+
+ @rebuildAttributeSelectors: (elementFull, elementRow, key, attribute) ->
+
+ # enable all
+ elementFull.find('.js-attributeSelector select option').removeAttr('disabled')
+
+ # disable all used attributes
+ elementFull.find('.js-attributeSelector select').each(->
+ keyLocal = $(@).val()
+ elementFull.find('.js-attributeSelector select option[value="' + keyLocal + '"]').attr('disabled', true)
+ )
+
+ # disable - if we only have one attribute
+ if elementFull.find('.js-attributeSelector select').length > 1
+ elementFull.find('.js-remove').removeClass('is-disabled')
+ else
+ elementFull.find('.js-remove').addClass('is-disabled')
+
+ # set attribute
+ if key
+ elementRow.find('.js-attributeSelector select').val(key)
+
+ @buildOperator: (elementFull, elementRow, key, groups, current_operator, attribute) ->
+ selection = $(" ")
+
+ for operator in ['contains', 'contains not']
+ operatorName = App.i18n.translateInline(operator)
+ selected = ''
+ if current_operator is operator
+ selected = 'selected="selected"'
+ selection.append("#{operatorName} ")
+ selection
+
+ @rebuildOperater: (elementFull, elementRow, key, groups, current_operator, attribute) ->
+ return if !key
+
+ # do nothing if item already exists
+ name = "#{attribute.name}::#{key}::operator"
+ return if elementRow.find("[name=\"#{name}\"]").get(0)
+
+ # render new operator
+ operator = @buildOperator(elementFull, elementRow, key, groups, current_operator, attribute)
+ elementRow.find('.js-operator select').replaceWith(operator)
diff --git a/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.js.coffee b/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.js.coffee
index 9c4170950..464330b3e 100644
--- a/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.js.coffee
+++ b/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.js.coffee
@@ -1,124 +1,171 @@
class App.UiElement.postmaster_set
- @render: (attribute, params, form_controller) ->
- addItem = (key, displayName, el, defaultValue = '') =>
- 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 = $( form_controller.formGenItem( add ).append(' ' ) )
+ @defaults: ->
+ groups =
+ general:
+ name: 'Ticket'
+ options: [
+ {
+ value: 'x-zammad-ticket-priority_id'
+ name: 'Priority'
+ relation: 'TicketPriority'
+ },
+ {
+ value: 'x-zammad-ticket-state_id'
+ name: 'State'
+ relation: 'TicketState'
+ },
+ {
+ value: 'x-zammad-ticket-customer'
+ name: 'Customer'
+ },
+ {
- # remove on click
- itemInput.find('.remove').bind('click', (e) ->
- e.preventDefault()
- key = $(e.target).closest('.form-group').find('select, input').attr('name')
- return if !key
- $(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').show()
- $(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').prop('disabled', false)
- $(e.target).closest('.form-group').remove()
- )
+ value: 'x-zammad-ticket-group_id'
+ name: 'Group'
+ relation: 'Group'
+ },
+ {
+ value: 'x-zammad-ticket-owner'
+ name: 'Owner'
+ relation: 'User',
+ tag: 'user_autocompletion',
+ },
+ {
+ value: 'x-zammad-ignore'
+ name: 'Ignore Message'
+ options: { true: 'Yes', false: 'No'}
+ },
+ ]
+ expert:
+ name: 'Article'
+ options: [
+ {
+ value: 'x-zammad-article-internal'
+ name: 'Internal'
+ options: { true: 'Yes', false: 'No'}
+ },
+ {
+ value: 'x-zammad-article-type_id'
+ name: 'Type'
+ relation: 'TicketArticleType'
+ },
+ {
+ value: 'x-zammad-article-sender_id'
+ name: 'Sender'
+ relation: 'TicketArticleSender'
+ },
+ ]
- # add new item
- control = el.closest('.perform_set')
- control.find('.list').append(itemInput)
- control.find('.addSelection select').val('')
- control.find('.addSelection select option[value="' + key + '"]').prop('disabled', true)
- control.find('.addSelection select option[value="' + key + '"]').hide()
+ groups
- # scaffold of perform elements
- item = $('
- ')
+ @render: (attribute, params = {}) ->
+ groups = @defaults()
- # select shown attributes
- loopData = [
- {
- value: 'x-zammad-ticket-priority_id'
- name: 'Ticket Priority'
- relation: 'TicketPriority'
- },
- {
- value: 'x-zammad-ticket-state_id'
- name: 'Ticket State'
- relation: 'TicketState'
- },
- {
- value: 'x-zammad-ticket-customer'
- name: 'Ticket Customer'
- },
- {
- value: 'x-zammad-ticket-group_id'
- name: 'Ticket Group'
- relation: 'Group'
- },
- {
- value: 'x-zammad-ticket-owner'
- name: 'Ticket Owner'
- },
- {
- value: ''
- name: '-'
- disable: true
- },
- {
- value: 'x-zammad-article-internal'
- name: 'Article Internal'
- options: { true: 'Yes', false: 'No'}
- },
- {
- value: 'x-zammad-article-type_id'
- name: 'Article Type'
- relation: 'TicketArticleType'
- },
- {
- value: 'x-zammad-article-sender_id'
- name: 'Article Sender'
- relation: 'TicketArticleSender'
- },
- {
- value: ''
- name: '-'
- disable: true
- },
- {
- value: 'x-zammad-ignore'
- name: 'Ignore Message'
- options: { true: 'Yes', false: 'No'}
- },
- ]
- for listItem in loopData
- listItem.value = "#{ attribute.name }::#{listItem.value}"
- add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true, required: false }
- item.find('.addSelection').append( form_controller.formGenItem( add ) )
+ selector = @buildAttributeSelector(groups, attribute)
- item.find('.add').bind('click', (e) ->
- e.preventDefault()
- name = $(@).closest('.controls').find('.addSelection').find('select').val()
- displayName = $(@).closest('.controls').find('.addSelection').find('select option:selected').html()
- return if !name
- addItem( name, displayName, $(@) )
+ # scaffold of match elements
+ item = $( App.view('generic/postmaster_set')( attribute: attribute ) )
+ item.find('.js-attributeSelector').prepend(selector)
+
+ # add filter
+ item.find('.js-add').bind('click', (e) =>
+ element = $(e.target).closest('.js-filterElement')
+ elementClone = element.clone(true)
+ element.after(elementClone)
+ elementClone.find('.js-attributeSelector select').trigger('change')
)
- # show default values
- loopDataValue = {}
- if attribute.value
- for key, value of attribute.value
- displayName = key
- for listItem in loopData
- if listItem.value is "#{ attribute.name }::#{key}"
- addItem( "#{ attribute.name }::#{key}", listItem.name, item.find('.add'), value )
+ # remove filter
+ item.find('.js-remove').bind('click', (e) =>
+ $(e.target).closest('.js-filterElement').remove()
+ @rebuildAttributeSelectors(item)
+ )
+
+ # change attribute selector
+ item.find('.js-attributeSelector select').bind('change', (e) =>
+ key = $(e.target).find('option:selected').attr('value')
+ elementRow = $(e.target).closest('.js-filterElement')
+
+ @rebuildAttributeSelectors(item, elementRow, key, attribute)
+ @buildValue(item, elementRow, key, groups, undefined, undefined, attribute)
+ )
+
+ # build inital params
+ if !_.isEmpty(params[attribute.name])
+
+ selectorExists = false
+ for key, meta of params[attribute.name]
+ selectorExists = true
+ operator = meta.operator
+ value = meta.value
+
+ # get selector rows
+ elementFirst = item.find('.js-filterElement').first()
+ elementLast = item.find('.js-filterElement').last()
+
+ # clone, rebuild and append
+ elementClone = elementFirst.clone(true)
+ @rebuildAttributeSelectors(item, elementClone, key, attribute)
+ @buildValue(item, elementClone, key, groups, value, operator, attribute)
+ elementLast.after(elementClone)
+
+ # remove first dummy row
+ if selectorExists
+ item.find('.js-filterElement').first().remove()
+
+ item
+
+ @buildValue: (elementFull, elementRow, key, groups, value, operator, attribute) ->
+
+ # do nothing if item already exists
+ name = "#{attribute.name}::#{key}::value"
+ return if elementRow.find("[name=\"#{name}\"]").get(0)
+ config = {}
+ for groupName, meta of groups
+ for entry in meta.options
+ if entry.value is key
+ config = entry
+ if !config.tag
+ if config.relation || config.options
+ config['tag'] = 'select'
+ else
+ config['tag'] = 'input'
+ config['type'] = 'text'
+ config['name'] = name
+ config['value'] = value
+ item = App.UiElement[config.tag].render(config, {})
+ elementRow.find('.js-value').html(item)
+
+ @buildAttributeSelector: (groups, attribute) ->
+ selection = $(' ')
+ for groupKey, groupMeta of groups
+ displayName = App.i18n.translateInline(groupMeta.name)
+ selection.closest('select').append(" ")
+ optgroup = selection.find("optgroup.js-#{groupKey}")
+ for entry in groupMeta.options
+ displayName = App.i18n.translateInline(entry.name)
+ optgroup.append("#{displayName} ")
+ selection
+
+ @rebuildAttributeSelectors: (elementFull, elementRow, key, attribute) ->
+
+ # enable all
+ elementFull.find('.js-attributeSelector select option').removeAttr('disabled')
+
+ # disable all used attributes
+ elementFull.find('.js-attributeSelector select').each(->
+ keyLocal = $(@).val()
+ elementFull.find('.js-attributeSelector select option[value="' + keyLocal + '"]').attr('disabled', true)
+ )
+
+ # disable - if we only have one attribute
+ if elementFull.find('.js-attributeSelector select').length > 1
+ elementFull.find('.js-remove').removeClass('is-disabled')
+ else
+ elementFull.find('.js-remove').addClass('is-disabled')
+
+ # set attribute
+ if key
+ elementRow.find('.js-attributeSelector select').val(key)
- item
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/generic/postmaster_match.jst.eco b/app/assets/javascripts/app/views/generic/postmaster_match.jst.eco
new file mode 100644
index 000000000..edc7adf08
--- /dev/null
+++ b/app/assets/javascripts/app/views/generic/postmaster_match.jst.eco
@@ -0,0 +1,26 @@
+
+
+
+
+
+ <%- @Icon('arrow-down', 'dropdown-arrow') %>
+
+
+
+
+
+ <%- @Icon('arrow-down', 'dropdown-arrow') %>
+
+
+
+
+
+
+ <%- @Icon('minus') %>
+
+
+ <%- @Icon('plus') %>
+
+
+
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/generic/postmaster_set.jst.eco b/app/assets/javascripts/app/views/generic/postmaster_set.jst.eco
new file mode 100644
index 000000000..8e1fe93d1
--- /dev/null
+++ b/app/assets/javascripts/app/views/generic/postmaster_set.jst.eco
@@ -0,0 +1,20 @@
+
+
+
+
+
+ <%- @Icon('arrow-down', 'dropdown-arrow') %>
+
+
+
+
+
+
+ <%- @Icon('minus') %>
+
+
+ <%- @Icon('plus') %>
+
+
+
+
\ No newline at end of file
diff --git a/app/models/channel/filter/database.rb b/app/models/channel/filter/database.rb
index adc3d5c6d..02dff0b80 100644
--- a/app/models/channel/filter/database.rb
+++ b/app/models/channel/filter/database.rb
@@ -6,38 +6,44 @@ module Channel::Filter::Database
def self.run( _channel, mail )
# process postmaster filter
- filters = PostmasterFilter.where( active: true, channel: 'email' )
+ filters = PostmasterFilter.where( active: true, channel: 'email' ).order(:name)
filters.each {|filter|
Rails.logger.info " proccess filter #{filter.name} ..."
- match = true
- looped = false
- filter[:match].each {|key, value|
- looped = true
+ all_matches_ok = true
+ min_one_rule_exists = false
+ filter[:match].each {|key, meta|
begin
+ next if !meta || !meta['value'] || meta['value'].empty?
+ min_one_rule_exists = true
scan = []
if mail
- scan = mail[ key.downcase.to_sym ].scan(/#{value}/i)
+ scan = mail[ key.downcase.to_sym ].scan(/#{meta['value']}/i)
end
- if match && scan[0]
- Rails.logger.info " matching #{key.downcase}:'#{mail[ key.downcase.to_sym ]}' on #{value}"
- match = true
+ if scan[0]
+ if meta[:operator] == 'contains not'
+ all_matches_ok = false
+ end
+ Rails.logger.info " matching #{key.downcase}:'#{mail[ key.downcase.to_sym ]}' on #{meta['value']}"
else
- Rails.logger.info " is not matching #{key.downcase}:'#{mail[ key.downcase.to_sym ]}' on #{value}"
- match = false
+ if meta[:operator] == 'contains'
+ all_matches_ok = false
+ end
+ Rails.logger.info " not matching #{key.downcase}:'#{mail[ key.downcase.to_sym ]}' on #{meta['value']}"
end
+ break if !all_matches_ok
rescue => e
- match = false
- Rails.logger.error "can't use match rule #{value} on #{mail[ key.to_sym ]}"
+ all_matches_ok = false
+ Rails.logger.error "can't use match rule #{meta['value']} on #{mail[ key.to_sym ]}"
Rails.logger.error e.inspect
end
}
- next if !looped
- next if !match
+ next if !min_one_rule_exists
+ next if !all_matches_ok
- filter[:perform].each {|key, value|
- Rails.logger.info " perform '#{key.downcase}' = '#{value}'"
- mail[ key.downcase.to_sym ] = value
+ filter[:perform].each {|key, meta|
+ Rails.logger.info " perform '#{key.downcase}' = '#{meta.inspect}'"
+ mail[ key.downcase.to_sym ] = meta['value']
}
}
diff --git a/test/unit/email_postmaster_test.rb b/test/unit/email_postmaster_test.rb
new file mode 100644
index 000000000..0ea668a52
--- /dev/null
+++ b/test/unit/email_postmaster_test.rb
@@ -0,0 +1,205 @@
+# encoding: utf-8
+# rubocop:disable all
+require 'test_helper'
+
+class EmailPostmasterTest < ActiveSupport::TestCase
+ test 'process with postmaster filter' do
+ group1 = Group.create_if_not_exists(
+ name: 'Test Group1',
+ created_by_id: 1,
+ updated_by_id: 1,
+ )
+ group2 = Group.create_if_not_exists(
+ name: 'Test Group2',
+ created_by_id: 1,
+ updated_by_id: 1,
+ )
+ PostmasterFilter.destroy_all
+ PostmasterFilter.create(
+ name: 'not used',
+ match: {
+ from: {
+ operator: 'contains',
+ value: 'nobody@example.com',
+ },
+ },
+ perform: {
+ 'X-Zammad-Ticket-priority' => {
+ value: '3 high',
+ },
+ },
+ channel: 'email',
+ active: true,
+ created_by_id: 1,
+ updated_by_id: 1,
+ )
+ PostmasterFilter.create(
+ name: 'used',
+ match: {
+ from: {
+ operator: 'contains',
+ value: 'me@example.com',
+ },
+ },
+ perform: {
+ 'X-Zammad-Ticket-group_id' => {
+ value: group1.id,
+ },
+ 'x-Zammad-Article-Internal' => {
+ value: true,
+ },
+ },
+ channel: 'email',
+ active: true,
+ created_by_id: 1,
+ updated_by_id: 1,
+ )
+ PostmasterFilter.create(
+ name: 'used x-any-recipient',
+ match: {
+ 'x-any-recipient' => {
+ operator: 'contains',
+ value: 'any@example.com',
+ },
+ },
+ perform: {
+ 'X-Zammad-Ticket-group_id' => {
+ value: group2.id,
+ },
+ 'x-Zammad-Article-Internal' => {
+ value: true,
+ },
+ },
+ channel: 'email',
+ active: true,
+ created_by_id: 1,
+ updated_by_id: 1,
+ )
+
+ data = 'From: me@example.com
+To: customer@example.com
+Subject: some subject
+
+Some Text'
+
+ parser = Channel::EmailParser.new
+ ticket, article, user = parser.process( { trusted: false }, data )
+ assert_equal('Test Group1', ticket.group.name)
+ assert_equal('2 normal', ticket.priority.name)
+ assert_equal('some subject', ticket.title)
+
+ assert_equal('Customer', article.sender.name)
+ assert_equal('email', article.type.name)
+ assert_equal(true, article.internal)
+
+ data = 'From: Some Body
+To: Bob
+Cc: any@example.com
+Subject: some subject
+
+Some Text'
+
+ parser = Channel::EmailParser.new
+ ticket, article, user = parser.process( { trusted: false }, data )
+
+ assert_equal('Test Group2', ticket.group.name)
+ assert_equal('2 normal', ticket.priority.name)
+ assert_equal('some subject', ticket.title)
+
+ assert_equal('Customer', article.sender.name)
+ assert_equal('email', article.type.name)
+ assert_equal(true, article.internal)
+
+
+ PostmasterFilter.create(
+ name: 'used x-any-recipient',
+ match: {
+ 'x-any-recipient' => {
+ operator: 'contains not',
+ value: 'any_not@example.com',
+ },
+ },
+ perform: {
+ 'X-Zammad-Ticket-group_id' => {
+ value: group2.id,
+ },
+ 'X-Zammad-Ticket-priority_id' => {
+ value: '1',
+ },
+ 'x-Zammad-Article-Internal' => {
+ value: 'false',
+ },
+ },
+ channel: 'email',
+ active: true,
+ created_by_id: 1,
+ updated_by_id: 1,
+ )
+
+ data = 'From: Some Body
+To: Bob
+Cc: any@example.com
+Subject: some subject2
+
+Some Text'
+
+ parser = Channel::EmailParser.new
+ ticket, article, user = parser.process( { trusted: false }, data )
+
+ assert_equal('Test Group2', ticket.group.name)
+ assert_equal('1 low', ticket.priority.name)
+ assert_equal('some subject2', ticket.title)
+
+ assert_equal('Customer', article.sender.name)
+ assert_equal('email', article.type.name)
+ assert_equal(false, article.internal)
+
+ PostmasterFilter.destroy_all
+
+ PostmasterFilter.create(
+ name: 'used - empty selector',
+ match: {
+ from: {
+ operator: 'contains',
+ value: '',
+ },
+ },
+ perform: {
+ 'X-Zammad-Ticket-group_id' => {
+ value: group2.id,
+ },
+ 'X-Zammad-Ticket-priority_id' => {
+ value: '1',
+ },
+ 'x-Zammad-Article-Internal' => {
+ value: true,
+ },
+ },
+ channel: 'email',
+ active: true,
+ created_by_id: 1,
+ updated_by_id: 1,
+ )
+
+ data = 'From: Some Body
+To: Bob
+Cc: any@example.com
+Subject: some subject - no selector
+
+Some Text'
+
+ parser = Channel::EmailParser.new
+ ticket, article, user = parser.process( { trusted: false }, data )
+
+ assert_equal('Users', ticket.group.name)
+ assert_equal('2 normal', ticket.priority.name)
+ assert_equal('some subject - no selector', ticket.title)
+
+ assert_equal('Customer', article.sender.name)
+ assert_equal('email', article.type.name)
+ assert_equal(false, article.internal)
+
+ PostmasterFilter.destroy_all
+ end
+
+end