From e2bb6cce56bd8c008e99be57e89c10f91766b8fa Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Sat, 16 Jul 2016 23:43:08 +0200 Subject: [PATCH] Added has changed filter to triggers. --- .../_ui_element/ticket_selector.coffee | 51 ++- .../app/models/_application_model.coffee | 4 + .../javascripts/app/models/trigger.coffee | 2 +- app/models/ticket/subject.rb | 2 + app/models/transaction/background_job.rb | 3 +- app/models/transaction/clearbit_enrichment.rb | 3 +- .../transaction/cti_caller_id_detection.rb | 2 + app/models/transaction/notification.rb | 3 +- app/models/transaction/signature_detection.rb | 3 +- app/models/transaction/slack.rb | 4 +- app/models/transaction/trigger.rb | 35 +- test/unit/ticket_test.rb | 2 + test/unit/ticket_trigger_test.rb | 316 ++++++++++++++++++ 13 files changed, 402 insertions(+), 28 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee b/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee index 8e40c64f5..344441b06 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee @@ -24,10 +24,23 @@ class App.UiElement.ticket_selector '^input$': ['contains', 'contains not'] '^textarea$': ['contains', 'contains not'] + if attribute.hasChanged + operators_type = + '^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'within next (relative)', 'within last (relative)', 'after (relative)', 'has changed'] + '^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'within next (relative)', 'within last (relative)', 'after (relative)', 'has changed'] + 'boolean$': ['is', 'is not', 'has changed'] + '^input$': ['contains', 'contains not', 'has changed'] + '^textarea$': ['contains', 'contains not', 'has changed'] + operators_name = '_id$': ['is', 'is not'] '_ids$': ['is', 'is not'] + if attribute.hasChanged + operators_name = + '_id$': ['is', 'is not', 'has changed'] + '_ids$': ['is', 'is not', 'has changed'] + # merge config elements = {} @@ -37,9 +50,10 @@ class App.UiElement.ticket_selector display: 'Action' tag: 'select' null: false + translate: true options: - create: 'Created' - update: 'Updated' + create: 'created' + update: 'updated' operator: ['is', 'is not'] for groupKey, groupMeta of groups @@ -226,21 +240,37 @@ class App.UiElement.ticket_selector @buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) -> currentOperator = elementRow.find('.js-operator option:selected').attr('value') - if !meta.operator - meta.operator = currentOperator - name = "#{attribute.name}::#{groupAndAttribute}::operator" + if !meta.operator && currentOperator + meta.operator = currentOperator + selection = $("") attributeConfig = elements[groupAndAttribute] if attributeConfig.operator + + # check if operator exists + operatorExists = false + for operator in attributeConfig.operator + if meta.operator is operator + operatorExists = true + break + + if !operatorExists + for operator in attributeConfig.operator + meta.operator = operator + break + for operator in attributeConfig.operator operatorName = App.i18n.translateInline(operator) selected = '' - if meta.operator is operator - selected = 'selected="selected"' - selection.append("") + if !groupAndAttribute.match(/^ticket/) && operator is 'has changed' + # do nothing, only show "has changed" in ticket attributes + else + if meta.operator is operator + selected = 'selected="selected"' + selection.append("") selection elementRow.find('.js-operator select').replaceWith(selection) @@ -355,6 +385,11 @@ class App.UiElement.ticket_selector item = App.UiElement['time_range'].render(config, {}) elementRow.find('.js-value').removeClass('hide').html(item) + if meta.operator is 'has changed' + elementRow.find('.js-value').addClass('hide') + elementRow.find('.js-preCondition').addClass('hide') + else + elementRow.find('.js-value').removeClass('hide') @humanText: (condition) -> none = App.i18n.translateContent('No filter.') diff --git a/app/assets/javascripts/app/models/_application_model.coffee b/app/assets/javascripts/app/models/_application_model.coffee index 507d524d5..7ccf21ea2 100644 --- a/app/assets/javascripts/app/models/_application_model.coffee +++ b/app/assets/javascripts/app/models/_application_model.coffee @@ -590,6 +590,10 @@ class App.Model extends Spine.Model [ array of objects ] + examples: + + list = App.Model.search({sortBy:'updated_at', order:'DESC'}) + ### @search: (params) -> diff --git a/app/assets/javascripts/app/models/trigger.coffee b/app/assets/javascripts/app/models/trigger.coffee index 94f2d1d48..84d00dc7c 100644 --- a/app/assets/javascripts/app/models/trigger.coffee +++ b/app/assets/javascripts/app/models/trigger.coffee @@ -4,7 +4,7 @@ class App.Trigger extends App.Model @url: @apiPath + '/triggers' @configure_attributes = [ { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, - { name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: false, preview: false, action: true }, + { name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: false, preview: false, action: true, hasChanged: true }, { name: 'perform', display: 'Execute changes on objects', tag: 'ticket_perform_action', null: true, notification: true }, { name: 'active', display: 'Active', tag: 'active', default: true }, { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, diff --git a/app/models/ticket/subject.rb b/app/models/ticket/subject.rb index 805d30bf3..a24c8c144 100644 --- a/app/models/ticket/subject.rb +++ b/app/models/ticket/subject.rb @@ -76,6 +76,8 @@ returns subject = subject[ 0, ticket_subject_size.to_i ] + '[...]' end + subject.gsub!(/^[[:space:]]+/, '') + subject.gsub!(/[[:space:]]+$/, '') subject end end diff --git a/app/models/transaction/background_job.rb b/app/models/transaction/background_job.rb index 838e9c839..a70c2e874 100644 --- a/app/models/transaction/background_job.rb +++ b/app/models/transaction/background_job.rb @@ -12,7 +12,8 @@ class Transaction::BackgroundJob changes: { 'attribute1' => [before,now], 'attribute2' => [before,now], - } + }, + created_at: Time.zone.now, user_id: 123, }, =end diff --git a/app/models/transaction/clearbit_enrichment.rb b/app/models/transaction/clearbit_enrichment.rb index e3fcffae9..1ddb7a5e9 100644 --- a/app/models/transaction/clearbit_enrichment.rb +++ b/app/models/transaction/clearbit_enrichment.rb @@ -10,7 +10,8 @@ class Transaction::ClearbitEnrichment changes: { 'attribute1' => [before, now], 'attribute2' => [before, now], - } + }, + created_at: Time.zone.now, user_id: 123, }, =end diff --git a/app/models/transaction/cti_caller_id_detection.rb b/app/models/transaction/cti_caller_id_detection.rb index 5b7e08974..692485dab 100644 --- a/app/models/transaction/cti_caller_id_detection.rb +++ b/app/models/transaction/cti_caller_id_detection.rb @@ -10,6 +10,7 @@ class Transaction::CtiCallerIdDetection object_id: 123, via_web: true, user_id: 123, + created_at: Time.zone.now, }, { object: 'User', @@ -20,6 +21,7 @@ class Transaction::CtiCallerIdDetection 'attribute1' => [before, now], 'attribute2' => [before, now], } + created_at: Time.zone.now, user_id: 123, }, =end diff --git a/app/models/transaction/notification.rb b/app/models/transaction/notification.rb index 4a980f9af..e7ee25062 100644 --- a/app/models/transaction/notification.rb +++ b/app/models/transaction/notification.rb @@ -11,7 +11,8 @@ class Transaction::Notification changes: { 'attribute1' => [before, now], 'attribute2' => [before, now], - } + }, + created_at: Time.zone.now, user_id: 123, }, =end diff --git a/app/models/transaction/signature_detection.rb b/app/models/transaction/signature_detection.rb index f44a5a230..2e362b35f 100644 --- a/app/models/transaction/signature_detection.rb +++ b/app/models/transaction/signature_detection.rb @@ -12,7 +12,8 @@ class Transaction::SignatureDetection changes: { 'attribute1' => [before, now], 'attribute2' => [before, now], - } + }, + created_at: Time.zone.now, user_id: 123, }, =end diff --git a/app/models/transaction/slack.rb b/app/models/transaction/slack.rb index 40e449b28..27e28ef47 100644 --- a/app/models/transaction/slack.rb +++ b/app/models/transaction/slack.rb @@ -8,6 +8,7 @@ backend = Transaction::Slack.new( type: 'create', object_id: 1, user_id: 123, + created_at: Time.zone.now, ) backend.perform @@ -19,7 +20,8 @@ backend.perform changes: { 'attribute1' => [before, now], 'attribute2' => [before, now], - } + }, + created_at: Time.zone.now, user_id: 123, }, =end diff --git a/app/models/transaction/trigger.rb b/app/models/transaction/trigger.rb index 8dbee34f8..7b71ba22d 100644 --- a/app/models/transaction/trigger.rb +++ b/app/models/transaction/trigger.rb @@ -11,7 +11,8 @@ class Transaction::Trigger changes: { 'attribute1' => [before, now], 'attribute2' => [before, now], - } + }, + created_at: Time.zone.now, user_id: 123, }, =end @@ -49,28 +50,30 @@ class Transaction::Trigger next if condition['ticket.action']['operator'] != 'is' && condition['ticket.action']['value'] == @item[:type] condition.delete('ticket.action') end -=begin + # check "has changed" options - has_changed = true - trigger.condition.each do |key, value| + has_changed_condition_exists = false + has_changed = false + condition.each do |key, value| next if !value next if !value['operator'] next if !value['operator']['has changed'] + has_changed_condition_exists = true # next if has changed? && !@item[:changes][attribute] (object_name, attribute) = key.split('.', 2) # remove condition item, because it has changed if @item[:changes][attribute] - #condition.delete(key) + has_changed = true + condition.delete(key) next end - has_changed = false break - #{"ticket.state_id"=>{"operator"=>"has changed" end - next if !has_changed -=end + + next if has_changed_condition_exists && !has_changed + # check if selector is matching condition['ticket.id'] = { operator: 'is', @@ -99,12 +102,16 @@ class Transaction::Trigger # check in min one attribute has changed if @item[:type] == 'update' && !article_selector match = false - trigger.condition.each do |key, _value| - (object_name, attribute) = key.split('.', 2) - next if object_name != 'ticket' - next if !@item[:changes][attribute] + if has_changed_condition_exists && has_changed match = true - break + else + trigger.condition.each do |key, _value| + (object_name, attribute) = key.split('.', 2) + next if object_name != 'ticket' + next if !@item[:changes][attribute] + match = true + break + end end next if !match end diff --git a/test/unit/ticket_test.rb b/test/unit/ticket_test.rb index 57f1c830a..27247b253 100644 --- a/test/unit/ticket_test.rb +++ b/test/unit/ticket_test.rb @@ -278,6 +278,8 @@ class TicketTest < ActiveSupport::TestCase assert_equal('subject test 1', ticket1.title) assert_equal("ABC subject test 1 [Ticket##{ticket1.number}]", ticket1.subject_build('ABC subject test 1')) assert_equal("RE: ABC subject test 1 [Ticket##{ticket1.number}]", ticket1.subject_build('ABC subject test 1', true)) + assert_equal("RE: ABC subject test 1 [Ticket##{ticket1.number}]", ticket1.subject_build(' ABC subject test 1', true)) + assert_equal("RE: ABC subject test 1 [Ticket##{ticket1.number}]", ticket1.subject_build('ABC subject test 1 ', true)) ticket1.destroy end diff --git a/test/unit/ticket_trigger_test.rb b/test/unit/ticket_trigger_test.rb index 595ae77ba..6a08086d3 100644 --- a/test/unit/ticket_trigger_test.rb +++ b/test/unit/ticket_trigger_test.rb @@ -614,4 +614,320 @@ class TicketTriggerTest < ActiveSupport::TestCase Trigger.destroy_all end + test '4 has changed' do + roles = Role.where(name: 'Customer') + customer1 = User.create_or_update( + login: 'postmaster@example.com', + firstname: 'Notification', + lastname: 'Customer1', + email: 'postmaster@example.com', + password: 'customerpw', + active: true, + roles: roles, + updated_at: '2015-02-05 16:37:00', + updated_by_id: 1, + created_by_id: 1, + ) + customer2 = User.create_or_update( + login: 'ticket-has-changed-customer2@example.com', + firstname: 'Notification', + lastname: 'Customer2', + email: 'ticket-has-changed-customer2@example.com', + password: 'customerpw', + active: true, + organization_id: nil, + roles: roles, + updated_at: '2015-02-05 16:37:00', + updated_by_id: 1, + created_by_id: 1, + ) + roles = Role.where(name: 'Agent') + agent1 = User.create_or_update( + login: 'agent-has-changed@example.com', + firstname: 'Has Changed', + lastname: 'Agent1', + email: 'agent-has-changed@example.com', + password: 'agentpw', + active: true, + roles: roles, + updated_at: '2015-02-05 16:37:00', + updated_by_id: 1, + created_by_id: 1, + ) + trigger1 = Trigger.create_or_update( + name: 'owner update - to customer', + condition: { + 'ticket.owner_id' => { + 'operator' => 'has changed', + 'pre_condition' => 'current_user.id', + 'value' => '', + 'value_completion' => '', + } + }, + perform: { + 'notification.email' => { + 'body' => '

The owner of ticket (Ticket##{ticket.number}) has changed.

+
+

To provide additional information, please reply to this email or click on the following link: +#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id} +

+
+

Zammad, your customer support system

', + 'recipient' => 'ticket_customer', + 'subject' => 'Owner has changed (#{ticket.title})', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + # process mail without Precedence header + content = IO.binread('test/fixtures/ticket_trigger/mail1.box') + ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, content) + + assert_equal('aaäöüßad asd', ticket_p.title) + assert_equal('Users', ticket_p.group.name) + assert_equal('new', ticket_p.state.name) + assert_equal(1, ticket_p.articles.count) + article_p = ticket_p.articles.last + + Observer::Transaction.commit + + ticket_p.owner = agent1 + ticket_p.save + Observer::Transaction.commit + assert_equal('aaäöüßad asd', ticket_p.title, 'ticket_p.title verify') + assert_equal('Users', ticket_p.group.name, 'ticket_p.group verify') + assert_equal('new', ticket_p.state.name, 'ticket_p.state verify') + assert_equal('2 normal', ticket_p.priority.name, 'ticket_p.priority verify') + assert_equal(2, ticket_p.articles.count, 'ticket_p.articles verify') + + p ticket_p.articles.last.inspect + article_p = ticket_p.articles.last + assert_match('Owner has changed', article_p.subject) + assert_match('Zammad ', article_p.from) + assert_match('martin@example.com', article_p.to) + assert_no_match('config\.', article_p.body) + assert_match('http://zammad.example.com', article_p.body) + assert_no_match('ticket.', article_p.body) + assert_match(ticket_p.number, article_p.body) + assert_equal('text/html', article_p.content_type) + + trigger1 = Trigger.create_or_update( + name: 'owner update - to customer', + condition: { + 'ticket.owner_id' => { + 'operator' => 'has changed', + 'pre_condition' => 'current_user.id', + 'value' => '', + 'value_completion' => '', + }, + 'ticket.priority_id' => { + 'operator' => 'is', + 'value' => Ticket::Priority.lookup(name: '3 high').id.to_s, + }, + }, + perform: { + 'notification.email' => { + 'body' => '

The owner of ticket (Ticket##{ticket.number}) has changed.

+
+

To provide additional information, please reply to this email or click on the following link: +#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id} +

+
+

Zammad, your customer support system

', + 'recipient' => 'ticket_customer', + 'subject' => 'Owner has changed (#{ticket.title})', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + # process mail without Precedence header + content = IO.binread('test/fixtures/ticket_trigger/mail1.box') + ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, content) + + assert_equal('aaäöüßad asd', ticket_p.title) + assert_equal('Users', ticket_p.group.name) + assert_equal('new', ticket_p.state.name) + assert_equal(1, ticket_p.articles.count) + article_p = ticket_p.articles.last + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + ticket_p.priority = Ticket::Priority.lookup(name: '1 low') + ticket_p.save + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + ticket_p.priority = Ticket::Priority.lookup(name: '3 high') + ticket_p.save + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + ticket_p.owner = agent1 + ticket_p.save + + Observer::Transaction.commit + assert_equal('aaäöüßad asd', ticket_p.title, 'ticket_p.title verify') + assert_equal('Users', ticket_p.group.name, 'ticket_p.group verify') + assert_equal('new', ticket_p.state.name, 'ticket_p.state verify') + assert_equal('3 high', ticket_p.priority.name, 'ticket_p.priority verify') + assert_equal(2, ticket_p.articles.count, 'ticket_p.articles verify') + + p ticket_p.articles.last.inspect + article_p = ticket_p.articles.last + assert_match('Owner has changed', article_p.subject) + assert_match('Zammad ', article_p.from) + assert_match('martin@example.com', article_p.to) + assert_no_match('config\.', article_p.body) + assert_match('http://zammad.example.com', article_p.body) + assert_no_match('ticket.', article_p.body) + assert_match(ticket_p.number, article_p.body) + assert_equal('text/html', article_p.content_type) + + # should trigger + trigger1 = Trigger.create_or_update( + name: 'owner update - to customer', + condition: { + 'ticket.owner_id' => { + 'operator' => 'has changed', + 'pre_condition' => 'current_user.id', + 'value' => '', + 'value_completion' => '', + }, + 'ticket.priority_id' => { + 'operator' => 'is', + 'value' => Ticket::Priority.lookup(name: '3 high').id.to_s, + }, + 'ticket.action' => { + 'operator' => 'is not', + 'value' => 'create', + }, + }, + perform: { + 'notification.email' => { + 'body' => '

The owner of ticket (Ticket##{ticket.number}) has changed.

+
+

To provide additional information, please reply to this email or click on the following link: +#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id} +

+
+

Zammad, your customer support system

', + 'recipient' => 'ticket_customer', + 'subject' => 'Owner has changed (#{ticket.title})', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + # process mail without Precedence header + content = IO.binread('test/fixtures/ticket_trigger/mail1.box') + ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, content) + + assert_equal('aaäöüßad asd', ticket_p.title) + assert_equal('Users', ticket_p.group.name) + assert_equal('new', ticket_p.state.name) + assert_equal(1, ticket_p.articles.count) + article_p = ticket_p.articles.last + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + ticket_p.priority = Ticket::Priority.lookup(name: '1 low') + ticket_p.save + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + ticket_p.priority = Ticket::Priority.lookup(name: '3 high') + ticket_p.save + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + ticket_p.owner = agent1 + ticket_p.save + + Observer::Transaction.commit + assert_equal('aaäöüßad asd', ticket_p.title, 'ticket_p.title verify') + assert_equal('Users', ticket_p.group.name, 'ticket_p.group verify') + assert_equal('new', ticket_p.state.name, 'ticket_p.state verify') + assert_equal('3 high', ticket_p.priority.name, 'ticket_p.priority verify') + assert_equal(2, ticket_p.articles.count, 'ticket_p.articles verify') + + p ticket_p.articles.last.inspect + article_p = ticket_p.articles.last + assert_match('Owner has changed', article_p.subject) + assert_match('Zammad ', article_p.from) + assert_match('martin@example.com', article_p.to) + assert_no_match('config\.', article_p.body) + assert_match('http://zammad.example.com', article_p.body) + assert_no_match('ticket.', article_p.body) + assert_match(ticket_p.number, article_p.body) + assert_equal('text/html', article_p.content_type) + + # should not trigger + trigger1 = Trigger.create_or_update( + name: 'owner update - to customer', + condition: { + 'ticket.owner_id' => { + 'operator' => 'has changed', + 'pre_condition' => 'current_user.id', + 'value' => '', + 'value_completion' => '', + }, + 'ticket.action' => { + 'operator' => 'is', + 'value' => 'create', + }, + }, + perform: { + 'notification.email' => { + 'body' => '

The owner of ticket (Ticket##{ticket.number}) has changed.

+
+

To provide additional information, please reply to this email or click on the following link: +#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id} +

+
+

Zammad, your customer support system

', + 'recipient' => 'ticket_customer', + 'subject' => 'Owner has changed (#{ticket.title})', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + # process mail without Precedence header + content = IO.binread('test/fixtures/ticket_trigger/mail1.box') + ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, content) + + assert_equal(1, ticket_p.articles.count) + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + ticket_p.owner = agent1 + ticket_p.save + + Observer::Transaction.commit + assert_equal(1, ticket_p.articles.count) + + Trigger.destroy_all + end + end