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("") + 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("") + 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("") + 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