Init version of triggers.

This commit is contained in:
Martin Edenhofer 2016-05-03 02:36:44 +02:00
parent ea4b7c4bd9
commit 0487fa1d12
25 changed files with 1010 additions and 142 deletions

View file

@ -410,8 +410,8 @@ class App.ControllerForm extends App.Controller
for key in array
# check if item is-hidden and should not be used
if lookupForm.find('[name="' + key.name + '"]').hasClass('is-hidden')
param[key.name] = undefined
if lookupForm.find('[name="' + key.name + '"]').hasClass('is-hidden') || lookupForm.find('div[data-name="' + key.name + '"]').hasClass('is-hidden')
delete param[key.name]
continue
# collect all params, push it to an array if already exists

View file

@ -1,6 +1,6 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.ticket_perform_action
@defaults: ->
@defaults: (attribute) ->
defaults = ['ticket.state_id']
groups =
@ -8,31 +8,40 @@ class App.UiElement.ticket_perform_action
name: 'Ticket'
model: 'Ticket'
if attribute.notification
groups.notification =
name: 'Notification'
model: 'Notification'
# megre config
elements = {}
for groupKey, groupMeta of groups
for row in App[groupMeta.model].configure_attributes
if !App[groupMeta.model]
elements["#{groupKey}.email"] = { name: 'email', display: 'Email' }
else
# ignore passwords and relations
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids'
for row in App[groupMeta.model].configure_attributes
# ignore readonly attributes
if !row.readonly
config = _.clone(row)
if config.tag is 'tag'
config.operator = ['add', 'remove']
elements["#{groupKey}.#{config.name}"] = config
# ignore passwords and relations
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids'
# ignore readonly attributes
if !row.readonly
config = _.clone(row)
if config.tag is 'tag'
config.operator = ['add', 'remove']
elements["#{groupKey}.#{config.name}"] = config
[defaults, groups, elements]
@render: (attribute, params = {}) ->
[defaults, groups, elements] = @defaults()
[defaults, groups, elements] = @defaults(attribute)
selector = @buildAttributeSelector(groups, elements)
# return item
item = $( App.view('generic/ticket_perform_action')( attribute: attribute ) )
item = $( App.view('generic/ticket_perform_action/index')( attribute: attribute ) )
item.find('.js-attributeSelector').prepend(selector)
# add filter
@ -65,8 +74,6 @@ class App.UiElement.ticket_perform_action
selectorExists = false
for groupAndAttribute, meta of params[attribute.name]
selectorExists = true
value = meta.value
operator = meta.operator
# get selector rows
elementFirst = item.find('.js-filterElement').first()
@ -149,7 +156,19 @@ class App.UiElement.ticket_perform_action
if groupAndAttribute
elementRow.find('.js-attributeSelector select').val(groupAndAttribute)
@buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
if groupAndAttribute is 'notification.email'
elementRow.find('.js-setAttribute').html('')
@buildRecipientList(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
else
elementRow.find('.js-setNotification').html('')
if !elementRow.find('.js-setAttribute div').get(0)
attributeSelectorElement = $( App.view('generic/ticket_perform_action/attribute_selector')(
attribute: attribute
name: name
meta: meta || {}
))
elementRow.find('.js-setAttribute').html(attributeSelectorElement)
@buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
@buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
currentOperator = elementRow.find('.js-operator option:selected').attr('value')
@ -160,9 +179,7 @@ class App.UiElement.ticket_perform_action
name = "#{attribute.name}::#{groupAndAttribute}::operator"
selection = $("<select class=\"form-control\" name=\"#{name}\"></select>")
attributeConfig = elements[groupAndAttribute]
if !attributeConfig.operator
elementRow.find('.js-operator').addClass('hide')
else
@ -282,7 +299,36 @@ class App.UiElement.ticket_perform_action
elementRow.find('.js-value').removeClass('hide').html(item)
@buildRecipientList: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
return if elementRow.find('.js-setNotification .js-body').get(0)
options =
'ticket_owner': 'Owner'
'ticket_customer': 'Customer'
'ticket_agents': 'All Agents'
name = "#{attribute.name}::notification.email"
selection = $("<select class=\"form-control\" name=\"#{name}::recipient\" ></select>")
for key, value of options
selected = ''
if key is meta.recipient
selected = 'selected="selected"'
selection.append("<option value=\"#{key}\" #{selected}>#{App.i18n.translateInline(value)}</option>")
notificationElement = $( App.view('generic/ticket_perform_action/notification_email')(
attribute: attribute
name: name
meta: meta || {}
))
notificationElement.find('.js-recipient select').replaceWith(selection)
notificationElement.find('.js-body div[contenteditable="true"]').ce(
mode: 'richtext'
placeholder: 'message'
maxlength: 2000
)
elementRow.find('.js-setNotification').html(notificationElement)
@humanText: (condition) ->
none = App.i18n.translateContent('No filter.')

View file

@ -1,12 +1,15 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.ticket_selector
@defaults: ->
@defaults: (attribute) ->
defaults = ['ticket.state_id']
groups =
ticket:
name: 'Ticket'
model: 'Ticket'
#article:
# name: 'Article'
# model: 'TicketArticle'
customer:
name: 'Customer'
model: 'User'
@ -25,13 +28,25 @@ class App.UiElement.ticket_selector
'_id$': ['is', 'is not']
'_ids$': ['is', 'is not']
# megre config
# merge config
elements = {}
if attribute.action
elements['ticket.action'] =
name: 'action'
display: 'Action'
tag: 'select'
null: false
options:
create: 'created'
update: 'updated'
operator: ['is', 'is not']
for groupKey, groupMeta of groups
for row in App[groupMeta.model].configure_attributes
# ignore passwords and relations
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids'
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids' && row.searchable isnt false
config = _.clone(row)
for operatorRegEx, operator of operators_type
myRegExp = new RegExp(operatorRegEx, 'i')
@ -47,15 +62,12 @@ class App.UiElement.ticket_selector
@render: (attribute, params = {}) ->
[defaults, groups, elements] = @defaults()
[defaults, groups, elements] = @defaults(attribute)
selector = @buildAttributeSelector(groups, elements)
search = =>
@preview(item)
# return item
item = $( App.view('generic/ticket_selector')( attribute: attribute ) )
item = $( App.view('generic/ticket_selector')(attribute: attribute) )
item.find('.js-attributeSelector').prepend(selector)
# add filter
@ -65,7 +77,8 @@ class App.UiElement.ticket_selector
element.after(elementClone)
elementClone.find('.js-attributeSelector select').trigger('change')
@updateAttributeSelectors(item)
@preview(item)
if attribute.preview isnt false
@preview(item)
)
# remove filter
@ -73,7 +86,8 @@ class App.UiElement.ticket_selector
return if $(e.currentTarget).hasClass('is-disabled')
$(e.target).closest('.js-filterElement').remove()
@updateAttributeSelectors(item)
@preview(item)
if attribute.preview isnt false
@preview(item)
)
# build inital params
@ -108,23 +122,6 @@ class App.UiElement.ticket_selector
elementLast.after(elementClone)
item.find('.js-filterElement').first().remove()
triggerSearch = ->
item.find('.js-previewCounterContainer').addClass('hide')
item.find('.js-previewLoader').removeClass('hide')
App.Delay.set(
search,
600,
'preview',
)
# bind for preview
item.on('change', 'select.form-control', (e) ->
triggerSearch()
)
item.on('change keyup', 'input.form-control', (e) ->
triggerSearch()
)
# change attribute selector
item.find('.js-attributeSelector select').bind('change', (e) =>
elementRow = $(e.target).closest('.js-filterElement')
@ -140,6 +137,27 @@ class App.UiElement.ticket_selector
@buildOperator(item, elementRow, groupAndAttribute, elements, {}, attribute)
)
# bind for preview
if attribute.preview isnt false
search = =>
@preview(item)
triggerSearch = ->
item.find('.js-previewCounterContainer').addClass('hide')
item.find('.js-previewLoader').removeClass('hide')
App.Delay.set(
search,
600,
'preview',
)
item.on('change', 'select', (e) ->
triggerSearch()
)
item.on('change keyup', 'input', (e) ->
triggerSearch()
)
item
@preview: (item) ->

View file

@ -10,7 +10,6 @@ class Index extends App.ControllerContent
id: @id
genericObject: 'Trigger'
defaultSortBy: 'name'
#groupBy: 'role'
pageData:
title: 'Triggers'
home: 'triggers'

View file

@ -4,9 +4,8 @@ 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 },
{ name: 'perform', display: 'Execute changes on objects', tag: 'ticket_perform_action', null: true },
{ name: 'disable_notiifcation', display: 'Disable Notifications', tag: 'boolean', default: true },
{ name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: false, preview: false, action: 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 },
]
@ -14,8 +13,9 @@ class App.Trigger extends App.Model
@configure_overview = [
'name',
]
###
@description = '''
Trigger are....
'''
'''
###

View file

@ -0,0 +1,15 @@
<div class="flex horizontal">
<div class="controls">
<div class="u-positionOrigin js-operator">
<select></select>
<%- @Icon('arrow-down', 'dropdown-arrow') %>
</div>
</div>
<div class="controls">
<div class="u-positionOrigin js-preCondition">
<select></select>
<%- @Icon('arrow-down', 'dropdown-arrow') %>
</div>
</div>
<div class="controls js-value"></div>
</div>

View file

@ -6,19 +6,8 @@
<%- @Icon('arrow-down', 'dropdown-arrow') %>
</div>
</div>
<div class="controls">
<div class="u-positionOrigin js-operator">
<select></select>
<%- @Icon('arrow-down', 'dropdown-arrow') %>
</div>
</div>
<div class="controls">
<div class="u-positionOrigin js-preCondition">
<select></select>
<%- @Icon('arrow-down', 'dropdown-arrow') %>
</div>
</div>
<div class="controls js-value"></div>
<div class="js-setAttribute"></div>
<div class="js-setNotification flex"></div>
</div>
<div class="filter-controls">
<div class="filter-control filter-control-remove js-remove" title="<%- @Ti('Remote') %>">

View file

@ -0,0 +1,8 @@
<div class="flex">
<div class="controls js-recipient u-positionOrigin">
<select></select>
<%- @Icon('arrow-down', 'dropdown-arrow') %>
</div>
<div class="controls js-subject"><input type="text" name="<%= @name %>::subject" value="<%= @meta.subject %>" style="width: 100%;" placeholder="<%- @T('Subject') %>"></div>
<div class="controls js-body"><div class="richtext form-control"><div contenteditable="true" data-name="<%= @name %>::body"><%- @meta.body %></div></div></div>
</div>

View file

@ -30,7 +30,7 @@
</div>
</div>
</div>
<div class="js-preview">
<div class="js-preview <% if @attribute.preview is false: %>hide<% end %>">
<h3><%- @T('Preview') %><span class="subtitle js-previewCounterContainer hide"> <span class="u-highlight js-previewCounter">?</span> <%- @T('matches') %></span> <span class="tiny loading icon js-previewLoader hide" style="margin-left: 3px;"></span></h3>
<div class="js-previewTable"></div>
</div>

View file

@ -44,21 +44,7 @@ class Job < ApplicationModel
# use transaction
ActiveRecord::Base.transaction do
UserInfo.current_user_id = 1
logger.debug "Perform job #{job.perform.inspect} in Ticket.find(#{ticket.id})"
changed = false
job.perform.each do |key, value|
(object_name, attribute) = key.split('.', 2)
raise "Unable to update object #{object_name}.#{attribute}, only can update tickets!" if object_name != 'ticket'
next if ticket[attribute].to_s == value['value'].to_s
changed = true
ticket[attribute] = value['value']
logger.debug "set #{object_name}.#{attribute} = #{value['value'].inspect}"
end
next if !changed
ticket.save
ticket.perform_changes(job.perform, 'job')
# execute object transaction
Observer::Transaction.commit(

View file

@ -19,6 +19,12 @@ class Observer::Ticket::Article::CommunicateEmail::BackgroundJob
channel = ticket.group.email_address.channel
notification = false
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
if sender['name'] == 'System'
notification = true
end
# get linked channel and send
message = channel.deliver(
{
@ -32,7 +38,8 @@ class Observer::Ticket::Article::CommunicateEmail::BackgroundJob
content_type: record.content_type,
body: record.body,
attachments: record.attachments
}
},
notification
)
# store mail plain

View file

@ -41,7 +41,7 @@ class Observer::Ticket::Article::FillupFromEmail < ActiveRecord::Observer
raise "No email address found for group '#{ticket.group.name}'"
end
system_sender = "#{email_address.realname} <#{email_address.email}>"
if Setting.get('ticket_define_email_from') == 'AgentNameSystemAddressName'
if record.created_by_id != 1 && Setting.get('ticket_define_email_from') == 'AgentNameSystemAddressName'
seperator = Setting.get('ticket_define_email_from_seperator')
sender = User.find(record.created_by_id)
record.from = "#{sender.firstname} #{sender.lastname} #{seperator} #{system_sender}"

View file

@ -26,15 +26,41 @@ class Observer::Transaction < ActiveRecord::Observer
# reset buffer
EventBuffer.reset('transaction')
# get asyn backends
sync_backends = []
Setting.where(area: 'Transaction::Backend::Sync').order(:name).each {|setting|
backend = Setting.get(setting.name)
sync_backends.push Kernel.const_get(backend)
}
# get uniq objects
list_objects = get_uniq_changes(list)
list_objects.each {|_object, objects|
objects.each {|_id, item|
# execute sync backends
sync_backends.each {|backend|
execute_singel_backend(backend, item, params)
}
# execute async backends
Delayed::Job.enqueue(Transaction::BackgroundJob.new(item, params))
}
}
end
def self.execute_singel_backend(backend, item, params)
Rails.logger.error "Execute singel backend #{backend}"
begin
UserInfo.current_user_id = nil
integration = backend.new(item, params)
integration.perform
rescue => e
Rails.logger.error 'ERROR: ' + backend.inspect
Rails.logger.error 'ERROR: ' + e.inspect
end
end
=begin
result = get_uniq_changes(events)

View file

@ -13,12 +13,16 @@ class Tag < ApplicationModel
# lookups
if data[:object]
tag_object_id = tag_object_lookup( data[:object] )
tag_object_id = tag_object_lookup(data[:object])
end
if data[:item]
tag_item_id = tag_item_lookup( data[:item] )
tag_item_id = tag_item_lookup(data[:item].strip)
end
# return in duplicate
current_tags = tag_list(data)
return true if current_tags.include?(data[:item].downcase.strip)
# create history
Tag.create(
tag_object_id: tag_object_id,
@ -33,10 +37,10 @@ class Tag < ApplicationModel
# lookups
if data[:object]
tag_object_id = tag_object_lookup( data[:object] )
tag_object_id = tag_object_lookup(data[:object])
end
if data[:item]
tag_item_id = tag_item_lookup( data[:item] )
tag_item_id = tag_item_lookup(data[:item].strip)
end
# create history
@ -49,80 +53,76 @@ class Tag < ApplicationModel
true
end
def self.tag_list( data )
tag_object_id_requested = tag_object_lookup( data[:object] )
def self.tag_list(data)
tag_object_id_requested = tag_object_lookup(data[:object])
tag_search = Tag.where(
tag_object_id: tag_object_id_requested,
o_id: data[:o_id],
)
tags = []
tag_search.each {|tag|
tags.push tag_item_lookup_id( tag.tag_item_id )
tags.push tag_item_lookup_id(tag.tag_item_id)
}
tags
end
def self.tag_item_lookup_id( id )
def self.tag_item_lookup_id(id)
# use cache
return @@cache_item[ id ] if @@cache_item[ id ]
return @@cache_item[id] if @@cache_item[id]
# lookup
tag_item = Tag::Item.find(id)
@@cache_item[ id ] = tag_item.name
@@cache_item[id] = tag_item.name
tag_item.name
end
def self.tag_item_lookup( name )
def self.tag_item_lookup(name)
name = name.downcase
# use cache
return @@cache_item[ name ] if @@cache_item[ name ]
return @@cache_item[name] if @@cache_item[name]
# lookup
tag_item = Tag::Item.find_by( name: name )
tag_item = Tag::Item.find_by(name: name)
if tag_item
@@cache_item[ name ] = tag_item.id
@@cache_item[name] = tag_item.id
return tag_item.id
end
# create
tag_item = Tag::Item.create(
name: name
)
@@cache_item[ name ] = tag_item.id
tag_item = Tag::Item.create(name: name)
@@cache_item[name] = tag_item.id
tag_item.id
end
def self.tag_object_lookup_id( id )
def self.tag_object_lookup_id(id)
# use cache
return @@cache_object[ id ] if @@cache_object[ id ]
return @@cache_object[id] if @@cache_object[id]
# lookup
tag_object = Tag::Object.find(id)
@@cache_object[ id ] = tag_object.name
@@cache_object[id] = tag_object.name
tag_object.name
end
def self.tag_object_lookup( name )
def self.tag_object_lookup(name)
# use cache
return @@cache_object[ name ] if @@cache_object[ name ]
return @@cache_object[name] if @@cache_object[name]
# lookup
tag_object = Tag::Object.find_by( name: name )
tag_object = Tag::Object.find_by(name: name)
if tag_object
@@cache_object[ name ] = tag_object.id
@@cache_object[name] = tag_object.id
return tag_object.id
end
# create
tag_object = Tag::Object.create(
name: name
)
@@cache_object[ name ] = tag_object.id
tag_object = Tag::Object.create(name: name)
@@cache_object[name] = tag_object.id
tag_object.id
end

View file

@ -457,6 +457,9 @@ condition example
elsif selector[0] == 'owner'
tables += ', users owners'
query += 'tickets.owner_id = owners.id'
elsif selector[0] == 'article'
tables += ', ticket_articles articles'
query += 'tickets.id = articles.ticket_id'
else
raise "invalid selector #{attribute.inspect}->#{selector.inspect}"
end
@ -630,6 +633,129 @@ condition example
=begin
perform changes on ticket
ticket.perform_changes({}, 'trigger')
=end
def perform_changes(perform, log)
logger.debug "Perform #{log} #{perform.inspect} on Ticket.find(#{id})"
changed = false
perform.each do |key, value|
(object_name, attribute) = key.split('.', 2)
raise "Unable to update object #{object_name}.#{attribute}, only can update tickets and send notifications!" if object_name != 'ticket' && object_name != 'notification'
# send notification
if object_name == 'notification'
recipients = []
if value['recipient'] == 'ticket_customer'
recipients.push User.lookup(id: customer_id)
elsif value['recipient'] == 'ticket_owner'
recipients.push User.lookup(id: owner_id)
elsif value['recipient'] == 'ticket_agents'
recipients = recipients.concat(agent_of_group)
else
logger.error "Unknown email notification recipient '#{value['recipient']}'"
next
end
recipient_string = ''
recipient_already = {}
recipients.each {|user|
next if !user.email
next if user.email !~ /@/
email = user.email.downcase.strip
next if recipient_already[email]
recipient_already[email] = true
if recipient_string != ''
recipient_string += ', '
end
recipient_string += email
}
next if recipient_string == ''
group = self.group
next if !group
email_address = group.email_address
next if !email_address
next if !email_address.channel_id
objects = {
ticket: self,
article: articles.last,
#recipient: user,
#changes: changes,
}
# get subject
value['subject'].gsub!(/\#\{(.+?)\}/, '<%= d "\\1", false %>')
subject = NotificationFactory::Mailer.template(
templateInline: value['subject'],
locale: 'en-en',
objects: objects,
)
subject = subject_build(subject)
value['body'].gsub!(/\#\{(.+?)\}/, '<%= d "\\1", true %>')
body = NotificationFactory::Mailer.template(
templateInline: value['body'],
locale: 'en-en',
objects: objects,
)
Ticket::Article.create(
ticket_id: id,
#from: 'some_sender@example.com',
to: recipient_string,
subject: subject,
content_type: 'text/html',
body: body,
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'System'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
next
end
# update tags
if key == 'ticket.tags'
next if value['value'].empty?
tags = value['value'].split(/,/)
if value['operator'] == 'add'
tags.each {|tag|
Tag.tag_add(
object: 'Ticket',
o_id: id,
item: tag,
)
}
elsif value['operator'] == 'remove'
tags.each {|tag|
Tag.tag_remove(
object: 'Ticket',
o_id: id,
item: tag,
)
}
else
logger.error "Unknown #{attribute} operator #{value['operator']}"
end
next
end
# update ticket
next if self[attribute].to_s == value['value'].to_s
changed = true
self[attribute] = value['value']
logger.debug "set #{object_name}.#{attribute} = #{value['value'].inspect}"
end
return if !changed
save
end
=begin
get all email references headers of a ticket, to exclude some, parse it as array into method
references = ticket.get_references

View file

@ -29,7 +29,7 @@ returns
# right position
if Setting.get('ticket_hook_position') == 'right'
return subject + " [#{ticket_hook}#{ticket_hook_divider}#{number}] "
return subject + " [#{ticket_hook}#{ticket_hook_divider}#{number}]"
end
# left position

View file

@ -22,16 +22,9 @@ class Transaction::BackgroundJob
end
def perform
Setting.where(area: 'Transaction::Backend').order(:name).each {|setting|
backend = Setting.get(setting.name)
begin
UserInfo.current_user_id = nil
integration = Kernel.const_get(backend).new(@item, @params)
integration.perform
rescue => e
Rails.logger.error 'ERROR: ' + setting.inspect
Rails.logger.error 'ERROR: ' + e.inspect
end
Setting.where(area: 'Transaction::Backend::Async').order(:name).each {|setting|
backend = Kernel.const_get(Setting.get(setting.name))
Observer::Transaction.execute_singel_backend(backend, @item, @params)
}
end

View file

@ -0,0 +1,77 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Transaction::Trigger
=begin
{
object: 'Ticket',
type: 'update',
object_id: 123,
via_web: true,
changes: {
'attribute1' => [before, now],
'attribute2' => [before, now],
}
user_id: 123,
},
=end
def initialize(item, params = {})
@item = item
@params = params
end
def perform
# return if we run import mode
return if Setting.get('import_mode')
return if @item[:object] != 'Ticket'
triggers = Trigger.where(active: true)
return if triggers.empty?
ticket = Ticket.lookup(id: @item[:object_id])
return if !ticket
if @item[:article_id]
article = Ticket::Article.lookup(id: @item[:article_id])
end
UserInfo.current_user_id = 1
triggers.each {|trigger|
condition = trigger.condition
# check action
if condition['ticket.action']
next if condition['ticket.action']['operator'] == 'is' && condition['ticket.action']['value'] != @item[:type]
next if condition['ticket.action']['operator'] != 'is' && condition['ticket.action']['value'] == @item[:type]
condition.delete('ticket.action')
end
condition['ticket.id'] = {
operator: 'is',
value: ticket.id,
}
ticket_count, tickets = Ticket.selectors(condition, 1)
next if ticket_count == 0
next if tickets.first.id != ticket.id
# check in min one attribute has changed
if @item[:type] == 'update'
match = false
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
next if !match
end
ticket.perform_changes(trigger.perform, 'trigger')
}
end
end

View file

@ -1,4 +1,7 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Trigger < ActiveRecord::Base
class Trigger < ApplicationModel
store :condition
store :perform
validates :name, presence: true
end

View file

@ -0,0 +1,63 @@
class UpdateTransaction < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
Setting.create_or_update(
title: 'Define sync transaction backend.',
name: '0100_trigger',
area: 'Transaction::Backend::Sync',
description: 'Define the transaction backend to execute triggers.',
options: {},
state: 'Transaction::Trigger',
frontend: false
)
Setting.create_or_update(
title: 'Define transaction backend.',
name: '0100_notification',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend to send agent notifications.',
options: {},
state: 'Transaction::Notification',
frontend: false
)
Setting.create_or_update(
title: 'Define transaction backend.',
name: '1000_signature_detection',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend to detect customers signature in email.',
options: {},
state: 'Transaction::SignatureDetection',
frontend: false
)
Setting.create_or_update(
title: 'Define transaction backend.',
name: '6000_slack_webhook',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend which posts messages to (http://www.slack.com).',
options: {},
state: 'Transaction::Slack',
frontend: false
)
Setting.create_or_update(
title: 'Define transaction backend.',
name: '9000_clearbit_enrichment',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend which will enrich customer and organization informations from (http://www.clearbit.com).',
options: {},
state: 'Transaction::ClearbitEnrichment',
frontend: false
)
Setting.create_or_update(
title: 'Define transaction backend.',
name: '9100_cti_caller_id_detection',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend which detects caller ids in objects and store them for cti lookups.',
options: {},
state: 'Transaction::CtiCallerIdDetection',
frontend: false
)
end
end

View file

@ -1769,10 +1769,19 @@ Setting.create_if_not_exists(
preferences: { prio: 4 },
frontend: false
)
Setting.create_if_not_exists(
title: 'Define sync transaction backend.',
name: '0100_trigger',
area: 'Transaction::Backend::Sync',
description: 'Define the transaction backend to execute triggers.',
options: {},
state: 'Transaction::Trigger',
frontend: false
)
Setting.create_if_not_exists(
title: 'Define transaction backend.',
name: '0100_notification',
area: 'Transaction::Backend',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend to send agent notifications.',
options: {},
state: 'Transaction::Notification',
@ -1781,7 +1790,7 @@ Setting.create_if_not_exists(
Setting.create_if_not_exists(
title: 'Define transaction backend.',
name: '1000_signature_detection',
area: 'Transaction::Backend',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend to detect customers signature in email.',
options: {},
state: 'Transaction::SignatureDetection',
@ -1790,7 +1799,7 @@ Setting.create_if_not_exists(
Setting.create_if_not_exists(
title: 'Define transaction backend.',
name: '6000_slack_webhook',
area: 'Transaction::Backend',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend which posts messages to (http://www.slack.com).',
options: {},
state: 'Transaction::Slack',
@ -1900,7 +1909,7 @@ Setting.create_if_not_exists(
Setting.create_if_not_exists(
title: 'Define transaction backend.',
name: '9000_clearbit_enrichment',
area: 'Transaction::Backend',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend which will enrich customer and organization informations from (http://www.clearbit.com).',
options: {},
state: 'Transaction::ClearbitEnrichment',
@ -1909,7 +1918,7 @@ Setting.create_if_not_exists(
Setting.create_if_not_exists(
title: 'Define transaction backend.',
name: '9100_cti_caller_id_detection',
area: 'Transaction::Backend',
area: 'Transaction::Backend::Async',
description: 'Define the transaction backend which detects caller ids in objects and store them for cti lookups.',
options: {},
state: 'Transaction::CtiCallerIdDetection',

View file

@ -474,7 +474,6 @@ test("form dependend fields check", function() {
var test_params = {
input1: "",
input2: "some used default",
input3: undefined,
select1: "false",
select2: "false",
selectmulti2: [ "true", "false" ],
@ -503,7 +502,6 @@ test("form dependend fields check", function() {
params = App.ControllerForm.params(el)
test_params = {
input1: "",
input2: undefined,
input3: "some used default",
select1: "true",
select2: "false",
@ -853,7 +851,6 @@ test("form required_if + shown_if", function() {
input1: "some not used default33",
input2: "some name66",
input3: "some name77",
input4: undefined,
active: true,
};
params = App.ControllerForm.params(el)
@ -868,9 +865,6 @@ test("form required_if + shown_if", function() {
el.find('[name="{boolean}active"]').val('false').trigger('change')
test_params = {
input1: "some not used default33",
input2: undefined,
input3: undefined,
input4: undefined,
active: false,
};
params = App.ControllerForm.params(el)
@ -886,7 +880,6 @@ test("form required_if + shown_if", function() {
input1: "some not used default33",
input2: "some name66",
input3: "some name77",
input4: undefined,
active: true,
};
params = App.ControllerForm.params(el)

View file

@ -268,7 +268,7 @@ test('form checks', function() {
model: {
configure_attributes: [
{ name: 'condition', display: 'Conditions', tag: 'ticket_selector', null: true },
{ name: 'executions', display: 'Executions', tag: 'ticket_perform_action', null: true },
{ name: 'executions', display: 'Executions', tag: 'ticket_perform_action', null: true, notification: true },
]
},
autofocus: true
@ -290,7 +290,7 @@ test('form checks', function() {
deepEqual(params, test_params, 'form param check');
/* with params or defaults */
$('#forms').append('<hr><h1>form time check</h1><form id="form3"></form>')
$('#forms').append('<hr><h1>form 3</h1><form id="form3"></form>')
var el = $('#form3')
var defaults = {
condition: {
@ -342,6 +342,11 @@ test('form checks', function() {
operator: 'remove',
value: 'tag1, tag2',
},
'notification.email': {
recipient: 'ticket_customer',
subject: 'some subject',
body: "some<br>\nbody",
},
},
}
new App.ControllerForm({
@ -349,7 +354,7 @@ test('form checks', function() {
model: {
configure_attributes: [
{ name: 'condition', display: 'Conditions', tag: 'ticket_selector', null: true },
{ name: 'executions', display: 'Executions', tag: 'ticket_perform_action', null: true },
{ name: 'executions', display: 'Executions', tag: 'ticket_perform_action', null: true, notification: true },
]
},
params: defaults,
@ -409,6 +414,11 @@ test('form checks', function() {
operator: 'remove',
value: 'tag1, tag2',
},
'notification.email': {
recipient: 'ticket_customer',
subject: 'some subject',
body: "some<br>\nbody",
},
},
}
deepEqual(params, test_params, 'form param check')
@ -464,12 +474,172 @@ test('form checks', function() {
operator: 'remove',
value: 'tag1, tag2',
},
'notification.email': {
recipient: 'ticket_customer',
subject: 'some subject',
body: "some<br>\nbody",
},
},
}
deepEqual(params, test_params, 'form param check')
//deepEqual(el.find('[name="times::days"]').val(), ['mon', 'wed'], 'check times::days value')
//equal(el.find('[name="times::hours"]').val(), 2, 'check times::hours value')
//equal(el.find('[name="times::minutes"]').val(), null, 'check times::minutes value')
// change selector
el.find('[name="executions::notification.email::subject"]').closest('.js-filterElement').find('.js-remove').click()
var params = App.ControllerForm.params(el)
var test_params = {
condition: {
'ticket.title': {
operator: 'contains',
value: 'some title',
},
'ticket.created_at': {
operator: 'before (absolute)',
value: '2015-09-20T03:41:00.000Z',
},
'ticket.updated_at': {
operator: 'within last (relative)',
range: 'year',
value: '2',
},
'ticket.organization_id': {
operator: 'is not',
pre_condition: 'specific',
value: '12',
},
'ticket.owner_id': {
operator: 'is',
pre_condition: 'specific',
value: '47',
value_completion: 'Bob Smith <bod@example.com>',
},
'ticket.created_by_id': {
operator: 'is',
pre_condition: 'current_user.id',
value: '',
value_completion: ''
},
},
executions: {
'ticket.priority_id': {
value: '3',
},
'ticket.owner_id': {
pre_condition: 'specific',
value: '47',
value_completion: 'Bob Smith <bod@example.com>'
},
'ticket.tags': {
operator: 'remove',
value: 'tag1, tag2',
},
},
}
deepEqual(params, test_params, 'form param check')
// change selector
el.find('.js-attributeSelector').last().find('select').val('notification.email').trigger('change')
el.find('[name="executions::notification.email::subject"]').val('some subject')
el.find('[data-name="executions::notification.email::body"]').html('lala')
var params = App.ControllerForm.params(el)
var test_params = {
condition: {
'ticket.title': {
operator: 'contains',
value: 'some title',
},
'ticket.created_at': {
operator: 'before (absolute)',
value: '2015-09-20T03:41:00.000Z',
},
'ticket.updated_at': {
operator: 'within last (relative)',
range: 'year',
value: '2',
},
'ticket.organization_id': {
operator: 'is not',
pre_condition: 'specific',
value: '12',
},
'ticket.owner_id': {
operator: 'is',
pre_condition: 'specific',
value: '47',
value_completion: 'Bob Smith <bod@example.com>',
},
'ticket.created_by_id': {
operator: 'is',
pre_condition: 'current_user.id',
value: '',
value_completion: ''
},
},
executions: {
'ticket.priority_id': {
value: '3',
},
'ticket.owner_id': {
pre_condition: 'specific',
value: '47',
value_completion: 'Bob Smith <bod@example.com>'
},
'notification.email': {
recipient: 'ticket_owner',
subject: 'some subject',
body: 'lala',
},
},
}
deepEqual(params, test_params, 'form param check')
/* with params or defaults */
$('#forms').append('<hr><h1>form 4</h1><form id="form4"></form>')
var el = $('#form4')
var defaults = {
condition: {
'ticket.title': {
operator: 'contains',
value: 'some title',
},
},
executions: {
'notification.email': {
recipient: 'ticket_customer',
subject: 'some subject',
body: "some<br>\nbody",
},
},
}
new App.ControllerForm({
el: el,
model: {
configure_attributes: [
{ name: 'condition', display: 'Conditions', tag: 'ticket_selector', null: true },
{ name: 'executions', display: 'Executions', tag: 'ticket_perform_action', null: true, notification: true },
]
},
params: defaults,
autofocus: true
})
var params = App.ControllerForm.params(el)
var test_params = {
condition: {
'ticket.title': {
operator: 'contains',
value: 'some title',
},
},
executions: {
'notification.email': {
recipient: 'ticket_customer',
subject: 'some subject',
body: "some<br>\nbody",
},
},
}
deepEqual(params, test_params, 'form param check')
});

View file

@ -1,6 +1,9 @@
# encoding: utf-8
# inital data set as extention to db/seeds.rb
Trigger.destroy_all
Job.destroy_all
# create email address and apply it to all groups
channel_id = nil
channel = Channel.find_by(area: 'Email::Notification', active: true)

View file

@ -0,0 +1,337 @@
# encoding: utf-8
require 'test_helper'
class TicketTriggerTest < ActiveSupport::TestCase
test '1 basic' do
trigger1 = Trigger.create_or_update(
name: 'auto reply',
condition: {
'ticket.state_id' => {
'operator' => 'is',
'value' => Ticket::State.lookup(name: 'new').id.to_s,
}
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquery (#{ticket.title})!',
},
'ticket.priority_id' => {
'value' => Ticket::Priority.lookup(name: '3 high').id.to_s,
},
'ticket.tags' => {
'operator' => 'add',
'value' => 'aa, kk',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
trigger2 = Trigger.create_or_update(
name: 'not matching',
condition: {
'ticket.state_id' => {
'operator' => 'is',
'value' => Ticket::State.lookup(name: 'closed').id.to_s,
}
},
perform: {
'ticket.priority_id' => {
'value' => Ticket::Priority.lookup(name: '3 high').id.to_s,
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket1 = Ticket.create(
title: "some <b>title</b>\n äöüß",
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
assert(ticket1, 'ticket1 created')
assert_equal('some <b>title</b> äöüß', 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')
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
Observer::Transaction.commit
ticket1 = Ticket.lookup(id: ticket1.id)
assert_equal('some <b>title</b> äöüß', 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('3 high', ticket1.priority.name, 'ticket1.priority verify')
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
article1 = ticket1.articles.last
assert_match('Thanks for your inquery (some <b>title</b> äöüß)!', article1.subject)
assert_match('Braun<br>some &lt;b&gt;title&lt;/b&gt;', article1.body)
assert_equal('text/html', article1.content_type)
ticket1.priority = Ticket::Priority.lookup(name: '2 normal')
ticket1.save
Observer::Transaction.commit
assert_equal('some <b>title</b> äöüß', 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(1, ticket1.articles.count, 'ticket1.articles verify')
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
ticket1.state = Ticket::State.lookup(name: 'open')
ticket1.save
Observer::Transaction.commit
assert_equal('some <b>title</b> äöüß', ticket1.title, 'ticket1.title verify')
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
assert_equal('open', ticket1.state.name, 'ticket1.state verify')
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
ticket1.state = Ticket::State.lookup(name: 'new')
ticket1.save
Observer::Transaction.commit
ticket1 = Ticket.lookup(id: ticket1.id)
assert_equal('some <b>title</b> äöüß', 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('3 high', ticket1.priority.name, 'ticket1.priority verify')
assert_equal(2, ticket1.articles.count, 'ticket1.articles verify')
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
ticket2 = Ticket.create(
title: "some title\n äöüß",
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'open'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
assert(ticket2, 'ticket2 created')
assert_equal('some title äöüß', ticket2.title, 'ticket2.title verify')
assert_equal('Users', ticket2.group.name, 'ticket2.group verify')
assert_equal('open', ticket2.state.name, 'ticket2.state verify')
assert_equal('2 normal', ticket2.priority.name, 'ticket2.priority verify')
assert_equal(0, ticket2.articles.count, 'ticket2.articles verify')
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket2.id))
Observer::Transaction.commit
ticket2 = Ticket.lookup(id: ticket2.id)
assert_equal('some title äöüß', ticket2.title, 'ticket2.title verify')
assert_equal('Users', ticket2.group.name, 'ticket2.group verify')
assert_equal('open', ticket2.state.name, 'ticket2.state verify')
assert_equal('2 normal', ticket2.priority.name, 'ticket2.priority verify')
assert_equal(0, ticket2.articles.count, 'ticket2.articles verify')
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket2.id))
Trigger.destroy_all
end
test '2 actions - create' do
trigger1 = Trigger.create_or_update(
name: 'auto reply',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
'ticket.state_id' => {
'operator' => 'is',
'value' => Ticket::State.lookup(name: 'new').id.to_s,
}
},
perform: {
'notification.email' => {
'body' => 'dasdasdasd',
'recipient' => 'ticket_customer',
'subject' => 'asdasdas',
},
'ticket.priority_id' => {
'value' => Ticket::Priority.lookup(name: '3 high').id.to_s,
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket1 = Ticket.create(
title: "some title\n äöüß",
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
assert(ticket1, 'ticket1 created')
assert_equal('some title äöüß', 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')
Observer::Transaction.commit
ticket1 = Ticket.lookup(id: ticket1.id)
assert_equal('some title äöüß', 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('3 high', ticket1.priority.name, 'ticket1.priority verify')
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
ticket1.priority = Ticket::Priority.lookup(name: '2 normal')
ticket1.save
Observer::Transaction.commit
assert_equal('some title äöüß', 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(1, ticket1.articles.count, 'ticket1.articles verify')
ticket1.state = Ticket::State.lookup(name: 'open')
ticket1.save
Observer::Transaction.commit
assert_equal('some title äöüß', ticket1.title, 'ticket1.title verify')
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
assert_equal('open', ticket1.state.name, 'ticket1.state verify')
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
ticket1.state = Ticket::State.lookup(name: 'new')
ticket1.save
Observer::Transaction.commit
ticket1 = Ticket.lookup(id: ticket1.id)
assert_equal('some title äöüß', 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(1, ticket1.articles.count, 'ticket1.articles verify')
Trigger.destroy_all
end
test '2 actions - update' do
trigger1 = Trigger.create_or_update(
name: 'auto reply',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
'ticket.state_id' => {
'operator' => 'is',
'value' => Ticket::State.lookup(name: 'new').id.to_s,
}
},
perform: {
'notification.email' => {
'body' => 'dasdasdasd',
'recipient' => 'ticket_customer',
'subject' => 'asdasdas',
},
'ticket.priority_id' => {
'value' => Ticket::Priority.lookup(name: '3 high').id.to_s,
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket1 = Ticket.create(
title: "some title\n äöüß",
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
assert(ticket1, 'ticket1 created')
assert_equal('some title äöüß', 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')
Observer::Transaction.commit
ticket1 = Ticket.lookup(id: ticket1.id)
assert_equal('some title äöüß', 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')
ticket1.priority = Ticket::Priority.lookup(name: '2 normal')
ticket1.save
Observer::Transaction.commit
assert_equal('some title äöüß', 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')
ticket1.state = Ticket::State.lookup(name: 'open')
ticket1.save
Observer::Transaction.commit
assert_equal('some title äöüß', ticket1.title, 'ticket1.title verify')
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
assert_equal('open', 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')
ticket1.state = Ticket::State.lookup(name: 'new')
ticket1.save
Observer::Transaction.commit
ticket1 = Ticket.lookup(id: ticket1.id)
assert_equal('some title äöüß', 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('3 high', ticket1.priority.name, 'ticket1.priority verify')
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
Trigger.destroy_all
end
end