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 08c447960..8e40c64f5 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee @@ -7,9 +7,9 @@ class App.UiElement.ticket_selector ticket: name: 'Ticket' model: 'Ticket' - #article: - # name: 'Article' - # model: 'TicketArticle' + article: + name: 'Article' + model: 'TicketArticle' customer: name: 'Customer' model: 'User' diff --git a/app/assets/javascripts/app/controllers/job.coffee b/app/assets/javascripts/app/controllers/job.coffee index 3d29dc0ee..a7fff60bf 100644 --- a/app/assets/javascripts/app/controllers/job.coffee +++ b/app/assets/javascripts/app/controllers/job.coffee @@ -23,7 +23,7 @@ class Index extends App.ControllerContent { name: 'New Scheduler', 'data-type': 'new', class: 'btn--success' } ] container: @el.closest('.content') - #large: true + large: true ) App.Config.set('Job', { prio: 3400, name: 'Scheduler', parent: '#manage', target: '#manage/job', controller: Index, role: ['Admin'] }, 'NavBarAdmin') diff --git a/app/assets/javascripts/app/controllers/trigger.coffee b/app/assets/javascripts/app/controllers/trigger.coffee index 251143ccf..b58841385 100644 --- a/app/assets/javascripts/app/controllers/trigger.coffee +++ b/app/assets/javascripts/app/controllers/trigger.coffee @@ -23,7 +23,7 @@ class Index extends App.ControllerContent { name: 'New Trigger', 'data-type': 'new', class: 'btn--success' } ] container: @el.closest('.content') - #large: true + large: true ) App.Config.set('Trigger', { prio: 3300, name: 'Trigger', parent: '#manage', target: '#manage/trigger', controller: Index, role: ['Admin'] }, 'NavBarAdmin') diff --git a/app/assets/javascripts/app/models/ticket_article.coffee b/app/assets/javascripts/app/models/ticket_article.coffee index 736a2b785..1eb27461f 100644 --- a/app/assets/javascripts/app/models/ticket_article.coffee +++ b/app/assets/javascripts/app/models/ticket_article.coffee @@ -3,19 +3,19 @@ class App.TicketArticle extends App.Model @extend Spine.Model.Ajax @url: @apiPath + '/ticket_articles' @configure_attributes = [ - { name: 'ticket_id', display: 'TicketID', null: false, readonly: 1, }, + { name: 'ticket_id', display: 'TicketID', null: false, readonly: 1, searchable: false }, { name: 'from', display: 'From', tag: 'input', type: 'text', limit: 100, null: false }, { name: 'to', display: 'To', tag: 'input', type: 'text', limit: 100, null: true }, { name: 'cc', display: 'Cc', tag: 'input', type: 'text', limit: 100, null: true }, { name: 'subject', display: 'Subject', tag: 'input', type: 'text', limit: 100, null: true }, - { name: 'body', display: 'Text', tag: 'textarea', rows: 5, limit: 100, null: false }, + { name: 'body', display: 'Text', tag: 'textarea', rows: 5, limit: 100, null: false, searchable: false }, { name: 'type_id', display: 'Type', tag: 'select', multiple: false, null: false, relation: 'TicketArticleType', default: '' }, { name: 'sender_id', display: 'Sender', tag: 'select', multiple: false, null: false, relation: 'TicketArticleSender', default: '' }, { name: 'internal', display: 'Visibility', tag: 'radio', default: false, null: true, options: { true: 'internal', false: 'public' } }, { name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 }, - { name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 }, - { name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 }, - { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, + { name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1, searchable: false }, + { name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1, searchable: false }, + { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1, searchable: false }, ] uiUrl: -> diff --git a/app/models/ticket.rb b/app/models/ticket.rb index 1caf3e1c6..67e297ef9 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -726,6 +726,7 @@ perform changes on ticket } # get subject + value['subject'].gsub!(/\#\{config\.(.+?)\}/, '<%= c "\\1", false %>') value['subject'].gsub!(/\#\{(.+?)\}/, '<%= d "\\1", false %>') subject = NotificationFactory::Mailer.template( templateInline: value['subject'], @@ -734,6 +735,7 @@ perform changes on ticket ) subject = subject_build(subject) + value['body'].gsub!(/\#\{config\.(.+?)\}/, '<%= c "\\1", true %>') value['body'].gsub!(/\#\{(.+?)\}/, '<%= d "\\1", true %>') body = NotificationFactory::Mailer.template( templateInline: value['body'], diff --git a/app/models/transaction/trigger.rb b/app/models/transaction/trigger.rb index d2dfb9f6d..d79f4389a 100644 --- a/app/models/transaction/trigger.rb +++ b/app/models/transaction/trigger.rb @@ -49,16 +49,33 @@ class Transaction::Trigger condition.delete('ticket.action') end + # check if selector is matching condition['ticket.id'] = { operator: 'is', value: ticket.id, } + if article + condition['article.id'] = { + operator: 'is', + value: article.id, + } + end + ticket_count, tickets = Ticket.selectors(condition, 1) next if ticket_count == 0 next if tickets.first.id != ticket.id + # check if min one article attribute is used + article_selector = false + trigger.condition.each do |key, _value| + (object_name, attribute) = key.split('.', 2) + next if object_name != 'article' + next if attribute == 'id' + article_selector = true + end + # check in min one attribute has changed - if @item[:type] == 'update' + if @item[:type] == 'update' && !article_selector match = false trigger.condition.each do |key, _value| (object_name, attribute) = key.split('.', 2) diff --git a/db/seeds.rb b/db/seeds.rb index b4a90f59b..f86b4592a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -3856,6 +3856,81 @@ Scheduler.create_or_update( created_by_id: 1, ) +Trigger.create_or_update( + name: 'auto reply (on new tickets)', + condition: { + 'ticket.action' => { + 'operator' => 'is', + 'value' => 'create', + }, + 'ticket.state_id' => { + 'operator' => 'is not', + 'value' => '4', + }, + 'article.type_id' => { + 'operator' => 'is', + 'value' => [ + Ticket::Article::Type.lookup(name: 'email').id, + Ticket::Article::Type.lookup(name: 'phone').id, + Ticket::Article::Type.lookup(name: 'web').id, + ], + }, + }, + perform: { + 'notification.email' => { + 'body' => '

Your request (#{config.ticket_hook}##{ticket.number}) has been received and will be reviewed by our support staff.

+
+

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' => 'Thanks for your inquiry (#{ticket.title})', + }, + }, + active: true, + created_by_id: 1, + updated_by_id: 1, +) +Trigger.create_or_update( + name: 'auto reply (on follow up of tickets)', + condition: { + 'ticket.action' => { + 'operator' => 'is', + 'value' => 'update', + }, + 'article.sender_id' => { + 'operator' => 'is', + 'value' => Ticket::Article::Sender.lookup(name: 'Customer').id, + }, + 'article.type_id' => { + 'operator' => 'is', + 'value' => [ + Ticket::Article::Type.lookup(name: 'email').id, + Ticket::Article::Type.lookup(name: 'phone').id, + Ticket::Article::Type.lookup(name: 'web').id, + ], + }, + }, + perform: { + 'notification.email' => { + 'body' => '

Your follow up for (#{config.ticket_hook}##{ticket.number}) has been received and will be reviewed by our support staff.

+
+

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' => 'Thanks for your follow up (#{ticket.title})', + }, + }, + active: true, + created_by_id: 1, + updated_by_id: 1, +) + # reset primary key sequences if ActiveRecord::Base.connection_config[:adapter] == 'postgresql' ActiveRecord::Base.connection.tables.each do |t| diff --git a/test/unit/ticket_trigger_test.rb b/test/unit/ticket_trigger_test.rb index 3047073a6..f669ee791 100644 --- a/test/unit/ticket_trigger_test.rb +++ b/test/unit/ticket_trigger_test.rb @@ -369,12 +369,30 @@ class TicketTriggerTest < ActiveSupport::TestCase 'operator' => 'is', 'value' => 'create', }, + 'ticket.state_id' => { + 'operator' => 'is not', + 'value' => '4', + }, + 'article.type_id' => { + 'operator' => 'is', + 'value' => [ + Ticket::Article::Type.lookup(name: 'email').id, + Ticket::Article::Type.lookup(name: 'phone').id, + Ticket::Article::Type.lookup(name: 'web').id, + ], + }, }, perform: { 'notification.email' => { - 'body' => 'some text
#{ticket.customer.lastname}
#{ticket.title}', + 'body' => '

Your request (Ticket##{ticket.number}) has been received and will be reviewed by our support staff.

+
+

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' => 'Thanks for your inquiry (#{ticket.title})!', + 'subject' => 'Thanks for your inquiry (#{ticket.title})', }, }, disable_notification: true, @@ -384,6 +402,44 @@ class TicketTriggerTest < ActiveSupport::TestCase ) trigger2 = Trigger.create_or_update( + name: 'auto reply (on follow up of tickets)', + condition: { + 'ticket.action' => { + 'operator' => 'is', + 'value' => 'update', + }, + 'article.sender_id' => { + 'operator' => 'is', + 'value' => Ticket::Article::Sender.lookup(name: 'Customer').id, + }, + 'article.type_id' => { + 'operator' => 'is', + 'value' => [ + Ticket::Article::Type.lookup(name: 'email').id, + Ticket::Article::Type.lookup(name: 'phone').id, + Ticket::Article::Type.lookup(name: 'web').id, + ], + }, + }, + perform: { + 'notification.email' => { + 'body' => '

Your follow up for (#{config.ticket_hook}##{ticket.number}) has been received and will be reviewed by our support staff.

+
+

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' => 'Thanks for your follow up (#{ticket.title})', + }, + }, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + trigger3 = Trigger.create_or_update( name: 'not matching', condition: { 'ticket.action' => { @@ -408,80 +464,131 @@ class TicketTriggerTest < ActiveSupport::TestCase updated_by_id: 1, ) - # ticket #1 - ticket1 = Ticket.create( - title: 'test auto reply 1', - group: Group.lookup(name: 'Users'), - customer_id: customer1.id, - state: Ticket::State.lookup(name: 'new'), - priority: Ticket::Priority.lookup(name: '2 normal'), + # 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(2, ticket_p.articles.count) + article_p = ticket_p.articles.last + assert_match('Thanks for your inquiry (aaäöüßad asd)', article_p.subject) + assert_match('Zammad ', article_p.from) + 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) + + ticket_p.priority = Ticket::Priority.lookup(name: '2 normal') + 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') + + article_p = Ticket::Article.create( + ticket_id: ticket_p.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message note', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Agent'), + type: Ticket::Article::Type.find_by(name: 'note'), updated_by_id: 1, created_by_id: 1, ) - assert(ticket1, 'ticket1 created') - - assert_equal('test auto reply 1', ticket1.title, 'ticket1.title verify') - assert_equal('Users', ticket1.group.name, 'ticket1.group verify') - assert_equal('new', ticket1.state.name, 'ticket1.state verify') - assert_equal(0, ticket1.articles.count, 'ticket1.articles verify') - 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(3, ticket_p.articles.count, 'ticket_p.articles verify') - ticket1 = Ticket.lookup(id: ticket1.id) - assert_equal('test auto reply 1', ticket1.title, 'ticket1.title verify') - assert_equal('Users', ticket1.group.name, 'ticket1.group verify') - assert_equal('new', ticket1.state.name, 'ticket1.state verify') - assert_equal(0, ticket1.articles.count, 'ticket1.articles verify') - - ticket1.priority = Ticket::Priority.lookup(name: '2 normal') - ticket1.save - - Observer::Transaction.commit - - assert_equal('test auto reply 1', ticket1.title, 'ticket1.title verify') - assert_equal('Users', ticket1.group.name, 'ticket1.group verify') - assert_equal('new', ticket1.state.name, 'ticket1.state verify') - assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify') - assert_equal(0, ticket1.articles.count, 'ticket1.articles verify') - - # ticket #2 - ticket2 = Ticket.create( - title: 'test auto reply 2', - group: Group.lookup(name: 'Users'), - customer_id: customer2.id, - state: Ticket::State.lookup(name: 'new'), - priority: Ticket::Priority.lookup(name: '2 normal'), + article_p = Ticket::Article.create( + ticket_id: ticket_p.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message note', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Agent'), + type: Ticket::Article::Type.find_by(name: 'email'), updated_by_id: 1, created_by_id: 1, ) - assert(ticket2, 'ticket2 created') - - assert_equal('test auto reply 2', ticket2.title, 'ticket2.title verify') - assert_equal('Users', ticket2.group.name, 'ticket2.group verify') - assert_equal('new', ticket2.state.name, 'ticket2.state verify') - assert_equal(0, ticket2.articles.count, 'ticket2.articles verify') - 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(4, ticket_p.articles.count, 'ticket_p.articles verify') - ticket2 = Ticket.lookup(id: ticket2.id) - assert_equal('test auto reply 2', ticket2.title, 'ticket2.title verify') - assert_equal('Users', ticket2.group.name, 'ticket2.group verify') - assert_equal('new', ticket2.state.name, 'ticket2.state verify') - assert_equal(1, ticket2.articles.count, 'ticket2.articles verify') - article1 = ticket2.articles.last - assert_match('Thanks for your inquiry (test auto reply 2)!', article1.subject) - assert_equal('text/html', article1.content_type) - - ticket2.priority = Ticket::Priority.lookup(name: '2 normal') - ticket2.save - + article_p = Ticket::Article.create( + ticket_id: ticket_p.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message note', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Customer'), + type: Ticket::Article::Type.find_by(name: 'email'), + updated_by_id: 1, + created_by_id: 1, + ) 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(6, ticket_p.articles.count, 'ticket_p.articles verify') - assert_equal('test auto reply 2', ticket2.title, 'ticket2.title verify') - assert_equal('Users', ticket2.group.name, 'ticket2.group verify') - assert_equal('new', ticket2.state.name, 'ticket2.state verify') - assert_equal('2 normal', ticket2.priority.name, 'ticket2.priority verify') - assert_equal(1, ticket2.articles.count, 'ticket2.articles verify') + article_p = ticket_p.articles.last + assert_match('Thanks for your follow up (aaäöüßad asd)', article_p.subject) + assert_match('Zammad ', article_p.from) + 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) + + ticket_p.state = Ticket::State.lookup(name: 'open') + ticket_p.save + article_p = Ticket::Article.create( + ticket_id: ticket_p.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message note', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Customer'), + type: Ticket::Article::Type.find_by(name: 'email'), + updated_by_id: 1, + created_by_id: 1, + ) + 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('open', ticket_p.state.name, 'ticket_p.state verify') + assert_equal('2 normal', ticket_p.priority.name, 'ticket_p.priority verify') + assert_equal(8, ticket_p.articles.count, 'ticket_p.articles verify') + + article_p = ticket_p.articles.last + assert_match('Thanks for your follow up (aaäöüßad asd)', article_p.subject) + assert_match('Zammad ', article_p.from) + 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) # process mail without Precedence header content = IO.binread('test/fixtures/ticket_trigger/mail1.box')