Added slack integration.
This commit is contained in:
parent
74d47deeea
commit
a102057506
48 changed files with 1403 additions and 309 deletions
3
Gemfile
3
Gemfile
|
@ -88,6 +88,9 @@ gem 'writeexcel'
|
||||||
gem 'icalendar'
|
gem 'icalendar'
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
|
|
||||||
|
# integrations
|
||||||
|
gem 'slack-notifier'
|
||||||
|
|
||||||
# event machine
|
# event machine
|
||||||
gem 'eventmachine'
|
gem 'eventmachine'
|
||||||
gem 'em-websocket'
|
gem 'em-websocket'
|
||||||
|
|
|
@ -267,6 +267,7 @@ GEM
|
||||||
simplecov-html (0.10.0)
|
simplecov-html (0.10.0)
|
||||||
simplecov-rcov (0.2.3)
|
simplecov-rcov (0.2.3)
|
||||||
simplecov (>= 0.4.1)
|
simplecov (>= 0.4.1)
|
||||||
|
slack-notifier (1.5.1)
|
||||||
slop (3.6.0)
|
slop (3.6.0)
|
||||||
spring (1.6.4)
|
spring (1.6.4)
|
||||||
sprockets (3.5.2)
|
sprockets (3.5.2)
|
||||||
|
@ -361,6 +362,7 @@ DEPENDENCIES
|
||||||
simple-rss
|
simple-rss
|
||||||
simplecov
|
simplecov
|
||||||
simplecov-rcov
|
simplecov-rcov
|
||||||
|
slack-notifier
|
||||||
spring
|
spring
|
||||||
sprockets
|
sprockets
|
||||||
sqlite3
|
sqlite3
|
||||||
|
|
|
@ -1,12 +1,30 @@
|
||||||
class Icinga extends App.ControllerTabs
|
class Index extends App.ControllerIntegrationBase
|
||||||
header: 'Icinga'
|
featureIntegration: 'icinga_integration'
|
||||||
constructor: ->
|
featureName: 'Icinga'
|
||||||
super
|
featureConfig: 'icinga_config'
|
||||||
return if !@authenticate(false, 'Admin')
|
description: [
|
||||||
@title 'Icinga', true
|
['This service receives emails from %s and creates tickets with host and service.', 'Icinga']
|
||||||
@tabs = [
|
['If the host and service is recovered again, the ticket will be closed automatically.']
|
||||||
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Icinga' } }
|
]
|
||||||
]
|
|
||||||
@render()
|
|
||||||
|
|
||||||
App.Config.set('IntegrationIcinga', { prio: 1100, parent: '#integration', name: 'Icinga', target: '#integration/icinga', controller: Icinga, role: ['Admin'] }, 'NavBarIntegration')
|
form: (localeEl) ->
|
||||||
|
new App.SettingsForm(
|
||||||
|
area: 'Integration::Icinga'
|
||||||
|
el: localeEl.find('.js-form')
|
||||||
|
)
|
||||||
|
|
||||||
|
class State
|
||||||
|
@current: ->
|
||||||
|
App.Setting.get('icinga_integration')
|
||||||
|
|
||||||
|
App.Config.set(
|
||||||
|
'IntegrationIcinga'
|
||||||
|
{
|
||||||
|
name: 'Icinga'
|
||||||
|
target: '#system/integration/icinga'
|
||||||
|
description: 'A open source monitoring tool.'
|
||||||
|
controller: Index
|
||||||
|
state: State
|
||||||
|
}
|
||||||
|
'NavBarIntegrations'
|
||||||
|
)
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
class Mattermost extends App.ControllerTabs
|
|
||||||
header: 'Mattermost'
|
|
||||||
constructor: ->
|
|
||||||
super
|
|
||||||
return if !@authenticate(false, 'Admin')
|
|
||||||
@title 'Mattermost', true
|
|
||||||
@tabs = [
|
|
||||||
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Mattermost' } }
|
|
||||||
]
|
|
||||||
@render()
|
|
||||||
|
|
||||||
App.Config.set('IntegrationMattermost', { prio: 1000, parent: '#integration', name: 'Mattermost', target: '#integration/mattermost', controller: Mattermost, role: ['Admin'] }, 'NavBarIntegration')
|
|
|
@ -1,12 +1,30 @@
|
||||||
class Nagios extends App.ControllerTabs
|
class Index extends App.ControllerIntegrationBase
|
||||||
header: 'Nagios'
|
featureIntegration: 'nagios_integration'
|
||||||
constructor: ->
|
featureName: 'Nagios'
|
||||||
super
|
featureConfig: 'nagios_config'
|
||||||
return if !@authenticate(false, 'Admin')
|
description: [
|
||||||
@title 'Nagios', true
|
['This service receives emails from %s and creates tickets with host and service.', 'Nagios']
|
||||||
@tabs = [
|
['If the host and service is recovered again, the ticket will be closed automatically.']
|
||||||
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Nagios' } }
|
]
|
||||||
]
|
|
||||||
@render()
|
|
||||||
|
|
||||||
App.Config.set('IntegrationNagios', { prio: 1200, parent: '#integration', name: 'Nagios', target: '#integration/nagios', controller: Nagios, role: ['Admin'] }, 'NavBarIntegration')
|
form: (localeEl) ->
|
||||||
|
new App.SettingsForm(
|
||||||
|
area: 'Integration::Nagios'
|
||||||
|
el: localeEl.find('.js-form')
|
||||||
|
)
|
||||||
|
|
||||||
|
class State
|
||||||
|
@current: ->
|
||||||
|
App.Setting.get('nagios_integration')
|
||||||
|
|
||||||
|
App.Config.set(
|
||||||
|
'IntegrationNagios'
|
||||||
|
{
|
||||||
|
name: 'Nagios'
|
||||||
|
target: '#system/integration/nagios'
|
||||||
|
description: 'A open source monitoring tool.'
|
||||||
|
controller: Index
|
||||||
|
state: State
|
||||||
|
}
|
||||||
|
'NavBarIntegrations'
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
class Index extends App.ControllerIntegrationBase
|
||||||
|
featureIntegration: 'slack_integration'
|
||||||
|
featureName: 'Slack'
|
||||||
|
featureConfig: 'slack_config'
|
||||||
|
description: [
|
||||||
|
['This service sends notifications to your %s channel.', 'Slack']
|
||||||
|
['To setup this Service you need to create a new |"Incoming webhook"| in your %s integration panel, and enter the Webhook URL below.', 'Slack']
|
||||||
|
]
|
||||||
|
|
||||||
|
form: (localEl) =>
|
||||||
|
|
||||||
|
params = App.Setting.get(@featureConfig)
|
||||||
|
if params && params.items
|
||||||
|
params = params.items[0] || {}
|
||||||
|
|
||||||
|
options =
|
||||||
|
create: '1. Ticket Create'
|
||||||
|
update: '2. Ticket Update'
|
||||||
|
reminder_reached: '3. Ticket Reminder Reached'
|
||||||
|
escalation: '4. Ticket Escalation'
|
||||||
|
escalation_warning: '5. Ticket Escalation Warning'
|
||||||
|
|
||||||
|
configureAttributes = [
|
||||||
|
{ name: 'types', display: 'Trigger', tag: 'checkbox', options: options, 'null': false, class: 'vertical', note: 'Where notification is sent.' },
|
||||||
|
{ name: 'group_id', display: 'Group', tag: 'select', relation: 'Group', multiple: true, 'null': false, note: 'Only for this groups.' },
|
||||||
|
{ name: 'webhook', display: 'Webhook', tag: 'input', type: 'text', limit: 200, 'null': false, placeholder: 'https://hooks.slack.com/services/...' },
|
||||||
|
{ name: 'username', display: 'username', tag: 'input', type: 'text', limit: 100, 'null': false, placeholder: 'username' },
|
||||||
|
{ name: 'channel', display: 'channel', tag: 'input', type: 'text', limit: 100, 'null': true, placeholder: '#channel' },
|
||||||
|
]
|
||||||
|
console.log('p', params)
|
||||||
|
settings = []
|
||||||
|
for item in configureAttributes
|
||||||
|
setting =
|
||||||
|
options:
|
||||||
|
form: [item]
|
||||||
|
name: item.name
|
||||||
|
description: item.note || ''
|
||||||
|
title: item.display
|
||||||
|
settings.push setting
|
||||||
|
|
||||||
|
formEl = $( App.view('settings/form')(
|
||||||
|
settings: settings
|
||||||
|
))
|
||||||
|
|
||||||
|
for setting in settings
|
||||||
|
configure_attribute = setting.options['form']
|
||||||
|
configure_attribute[0].display = ''
|
||||||
|
value = params[setting.name]
|
||||||
|
localParams = {}
|
||||||
|
localParams[setting.name] = value
|
||||||
|
new App.ControllerForm(
|
||||||
|
el: formEl.find("[data-name=#{setting.name}]")
|
||||||
|
model: { configure_attributes: configure_attribute, className: '' }
|
||||||
|
params: localParams
|
||||||
|
)
|
||||||
|
|
||||||
|
localEl.find('.js-form').html(formEl)
|
||||||
|
|
||||||
|
class State
|
||||||
|
@current: ->
|
||||||
|
App.Setting.get('slack_integration')
|
||||||
|
|
||||||
|
App.Config.set(
|
||||||
|
'IntegrationSlack'
|
||||||
|
{
|
||||||
|
name: 'Slack'
|
||||||
|
target: '#system/integration/slack'
|
||||||
|
description: 'A team communication tool for the 21st century.'
|
||||||
|
controller: Index
|
||||||
|
state: State
|
||||||
|
}
|
||||||
|
'NavBarIntegrations'
|
||||||
|
)
|
|
@ -37,7 +37,7 @@ class Index extends App.Controller
|
||||||
}
|
}
|
||||||
auth_providers = []
|
auth_providers = []
|
||||||
for key, provider of auth_provider_all
|
for key, provider of auth_provider_all
|
||||||
if @Config.get( provider.config ) is true || @Config.get( provider.config ) is 'true'
|
if @Config.get(provider.config) is true || @Config.get(provider.config) is 'true'
|
||||||
auth_providers.push provider
|
auth_providers.push provider
|
||||||
|
|
||||||
@html App.view('profile/linked_accounts')(
|
@html App.view('profile/linked_accounts')(
|
||||||
|
@ -52,19 +52,19 @@ class Index extends App.Controller
|
||||||
|
|
||||||
# get data
|
# get data
|
||||||
@ajax(
|
@ajax(
|
||||||
id: 'account'
|
id: 'account'
|
||||||
type: 'DELETE'
|
type: 'DELETE'
|
||||||
url: @apiPath + '/users/account'
|
url: @apiPath + '/users/account'
|
||||||
data: JSON.stringify({ provider: provider, uid: uid })
|
data: JSON.stringify(provider: provider, uid: uid)
|
||||||
processData: true
|
processData: true
|
||||||
success: @success
|
success: @success
|
||||||
error: @error
|
error: @error
|
||||||
)
|
)
|
||||||
|
|
||||||
success: (data, status, xhr) =>
|
success: (data, status, xhr) =>
|
||||||
@notify(
|
@notify(
|
||||||
type: 'success'
|
type: 'success'
|
||||||
msg: App.i18n.translateContent( 'Successfully!' )
|
msg: App.i18n.translateContent('Successfully!')
|
||||||
)
|
)
|
||||||
update = =>
|
update = =>
|
||||||
@render()
|
@render()
|
||||||
|
@ -72,10 +72,10 @@ class Index extends App.Controller
|
||||||
|
|
||||||
error: (xhr, status, error) =>
|
error: (xhr, status, error) =>
|
||||||
@render()
|
@render()
|
||||||
data = JSON.parse( xhr.responseText )
|
data = JSON.parse(xhr.responseText)
|
||||||
@notify(
|
@notify(
|
||||||
type: 'error'
|
type: 'error'
|
||||||
msg: App.i18n.translateContent( data.message )
|
msg: App.i18n.translateContent(data.message)
|
||||||
)
|
)
|
||||||
|
|
||||||
App.Config.set( 'LinkedAccounts', { prio: 4000, name: 'Linked Accounts', parent: '#profile', target: '#profile/linked', controller: Index }, 'NavBarProfile' )
|
App.Config.set('LinkedAccounts', { prio: 4000, name: 'Linked Accounts', parent: '#profile', target: '#profile/linked', controller: Index }, 'NavBarProfile')
|
||||||
|
|
|
@ -1,3 +1,93 @@
|
||||||
|
class App.SettingsForm extends App.Controller
|
||||||
|
events:
|
||||||
|
'submit form': 'update'
|
||||||
|
|
||||||
|
constructor: ->
|
||||||
|
super
|
||||||
|
|
||||||
|
# check authentication
|
||||||
|
return if !@authenticate()
|
||||||
|
|
||||||
|
@subscribeId = App.Setting.subscribe(@render, initFetch: true, clear: false)
|
||||||
|
|
||||||
|
render: =>
|
||||||
|
|
||||||
|
# serach area settings
|
||||||
|
settings = App.Setting.search(
|
||||||
|
filter:
|
||||||
|
area: @area
|
||||||
|
)
|
||||||
|
|
||||||
|
# filter online service settings
|
||||||
|
if App.Config.get('system_online_service')
|
||||||
|
settings = _.filter(settings, (setting) ->
|
||||||
|
return if setting.online_service
|
||||||
|
return if setting.preferences && setting.preferences.online_service_disable
|
||||||
|
setting
|
||||||
|
)
|
||||||
|
return if _.isEmpty(settings)
|
||||||
|
|
||||||
|
# sort by prio
|
||||||
|
settings = _.sortBy( settings, (setting) ->
|
||||||
|
return if !setting.preferences
|
||||||
|
setting.preferences.prio
|
||||||
|
)
|
||||||
|
|
||||||
|
localEl = $( App.view('settings/form')(
|
||||||
|
settings: settings
|
||||||
|
))
|
||||||
|
|
||||||
|
for setting in settings
|
||||||
|
configure_attributes = setting.options['form']
|
||||||
|
value = App.Setting.get(setting.name)
|
||||||
|
params = {}
|
||||||
|
params[setting.name] = value
|
||||||
|
new App.ControllerForm(
|
||||||
|
el: localEl.find("[data-name=#{setting.name}]")
|
||||||
|
model: { configure_attributes: configure_attributes, className: '' }
|
||||||
|
params: params
|
||||||
|
)
|
||||||
|
@html localEl
|
||||||
|
|
||||||
|
update: (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@formDisable(e)
|
||||||
|
params = @formParam(e.target)
|
||||||
|
|
||||||
|
ui = @
|
||||||
|
count = 0
|
||||||
|
for name, value of params
|
||||||
|
if App.Setting.findByAttribute('name', name)
|
||||||
|
count += 1
|
||||||
|
App.Setting.set(
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
done: ->
|
||||||
|
ui.formEnable(e)
|
||||||
|
count -= 1
|
||||||
|
if count == 0
|
||||||
|
App.Event.trigger 'notify', {
|
||||||
|
type: 'success'
|
||||||
|
msg: App.i18n.translateContent('Update successful!')
|
||||||
|
timeout: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
# rerender ui || get new collections and session data
|
||||||
|
if @preferences
|
||||||
|
if @preferences.render
|
||||||
|
App.Event.trigger( 'ui:rerender' )
|
||||||
|
|
||||||
|
if @preferences.session_check
|
||||||
|
App.Auth.loginCheck()
|
||||||
|
|
||||||
|
fail: (settings, details) ->
|
||||||
|
App.Event.trigger 'notify', {
|
||||||
|
type: 'error'
|
||||||
|
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
|
||||||
|
timeout: 2000
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
class App.SettingsArea extends App.Controller
|
class App.SettingsArea extends App.Controller
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
@ -115,11 +205,11 @@ class App.SettingsAreaItem extends App.Controller
|
||||||
|
|
||||||
if @setting.preferences.session_check
|
if @setting.preferences.session_check
|
||||||
App.Auth.loginCheck()
|
App.Auth.loginCheck()
|
||||||
fail: ->
|
fail: (settings, details) ->
|
||||||
ui.formEnable(e)
|
ui.formEnable(e)
|
||||||
App.Event.trigger 'notify', {
|
App.Event.trigger 'notify', {
|
||||||
type: 'error'
|
type: 'error'
|
||||||
msg: App.i18n.translateContent('Can\'t update item!')
|
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
|
||||||
timeout: 2000
|
timeout: 2000
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,41 @@
|
||||||
class IndexRouter extends App.ControllerNavSidbar
|
class Index extends App.ControllerContent
|
||||||
authenticateRequired: true
|
constructor: ->
|
||||||
configKey: 'NavBarIntegration'
|
super
|
||||||
|
|
||||||
App.Config.set('integration', IndexRouter, 'Routes')
|
# check authentication
|
||||||
App.Config.set('integration/:target', IndexRouter, 'Routes')
|
return if !@authenticate(false, 'Admin')
|
||||||
|
|
||||||
App.Config.set('Integration', { prio: 1000, name: 'Integration', target: '#integration', role: ['Admin'] }, 'NavBarIntegration')
|
@title 'Integrations', true
|
||||||
|
|
||||||
|
@integrationItems = App.Config.get('NavBarIntegrations')
|
||||||
|
|
||||||
|
if !@integration
|
||||||
|
@subscribeId = App.Setting.subscribe(@render, initFetch: true, clear: false)
|
||||||
|
return
|
||||||
|
|
||||||
|
for key, value of @integrationItems
|
||||||
|
if value.target is "#system/#{@target}/#{@integration}"
|
||||||
|
config = value
|
||||||
|
break
|
||||||
|
|
||||||
|
new config.controller(
|
||||||
|
el: @el.closest('.main')
|
||||||
|
)
|
||||||
|
|
||||||
|
render: =>
|
||||||
|
integrations = []
|
||||||
|
for key, value of @integrationItems
|
||||||
|
value.key = key
|
||||||
|
integrations.push value
|
||||||
|
integrations = _.sortBy(integrations, (item) -> return item.name)
|
||||||
|
|
||||||
|
@html App.view('integration/index')(
|
||||||
|
head: 'Integrations'
|
||||||
|
integrations: integrations
|
||||||
|
)
|
||||||
|
|
||||||
|
release: =>
|
||||||
|
if @subscribeId
|
||||||
|
App.Setting.unsubscribe(@subscribeId)
|
||||||
|
|
||||||
|
App.Config.set('Integration', { prio: 1000, name: 'Integrations', parent: '#system', target: '#system/integration', controller: Index, role: ['Admin'] }, 'NavBarAdmin')
|
||||||
|
|
|
@ -8,6 +8,7 @@ App.Config.set('settings/:target', IndexRouter, 'Routes')
|
||||||
App.Config.set('channels/:target', IndexRouter, 'Routes')
|
App.Config.set('channels/:target', IndexRouter, 'Routes')
|
||||||
App.Config.set('channels/:target/:channel_id', IndexRouter, 'Routes')
|
App.Config.set('channels/:target/:channel_id', IndexRouter, 'Routes')
|
||||||
App.Config.set('system/:target', IndexRouter, 'Routes')
|
App.Config.set('system/:target', IndexRouter, 'Routes')
|
||||||
|
App.Config.set('system/:target/:integration', IndexRouter, 'Routes')
|
||||||
|
|
||||||
App.Config.set('Manage', { prio: 1000, name: 'Manage', target: '#manage', role: ['Admin'] }, 'NavBarAdmin')
|
App.Config.set('Manage', { prio: 1000, name: 'Manage', target: '#manage', role: ['Admin'] }, 'NavBarAdmin')
|
||||||
App.Config.set('Channels', { prio: 2500, name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBarAdmin')
|
App.Config.set('Channels', { prio: 2500, name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBarAdmin')
|
||||||
|
|
|
@ -58,4 +58,4 @@ class Index extends App.ControllerContent
|
||||||
@load()
|
@load()
|
||||||
)
|
)
|
||||||
|
|
||||||
App.Config.set( 'Packages', { prio: 1000, name: 'Packages', parent: '#system', target: '#system/package', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )
|
App.Config.set('Packages', { prio: 1000, name: 'Packages', parent: '#system', target: '#system/package', controller: Index, role: ['Admin'] }, 'NavBarAdmin')
|
||||||
|
|
|
@ -2,3 +2,13 @@ class App.Setting extends App.Model
|
||||||
@configure 'Setting', 'name', 'state_current'
|
@configure 'Setting', 'name', 'state_current'
|
||||||
@extend Spine.Model.Ajax
|
@extend Spine.Model.Ajax
|
||||||
@url: @apiPath + '/settings'
|
@url: @apiPath + '/settings'
|
||||||
|
|
||||||
|
@get: (name) ->
|
||||||
|
setting = App.Setting.findByAttribute('name', name)
|
||||||
|
setting.state_current.value
|
||||||
|
|
||||||
|
@set: (name, value, options = {}) ->
|
||||||
|
setting = App.Setting.findByAttribute('name', name)
|
||||||
|
setting.state_current.value = value
|
||||||
|
setting.save(options)
|
||||||
|
App.Config.set(name, value)
|
||||||
|
|
17
app/assets/javascripts/app/views/integration/base.jst.eco
Normal file
17
app/assets/javascripts/app/views/integration/base.jst.eco
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="page-header-title">
|
||||||
|
<div class="zammad-switch zammad-switch--small js-switch">
|
||||||
|
<input name="<%- @feature %>" type="checkbox" id="setting-switch" <% if @featureEnabled: %>checked<% end %>>
|
||||||
|
<label for="setting-switch"></label>
|
||||||
|
</div>
|
||||||
|
<h1><%- @T(@header) %></h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-content">
|
||||||
|
<% if @description: %>
|
||||||
|
<% for item in @description: %>
|
||||||
|
<p><%- @T(item[0], item[1], item[2]) %></p>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<div class="js-form"></div>
|
||||||
|
</div>
|
29
app/assets/javascripts/app/views/integration/index.jst.eco
Normal file
29
app/assets/javascripts/app/views/integration/index.jst.eco
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="page-header-title">
|
||||||
|
<h1><%- @T(@head) %></h1>
|
||||||
|
</div>
|
||||||
|
<div class="page-content">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 30px;"></th>
|
||||||
|
<th style="width: 40%;"><%- @T('Service') %></th>
|
||||||
|
<th><%- @T('Description') %></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for integration in @integrations: %>
|
||||||
|
<tr data-key="<%= integration.key %>">
|
||||||
|
<td>
|
||||||
|
<% if !integration.state.current(): %>
|
||||||
|
<%- @Icon('status', 'inactive inline') %>
|
||||||
|
<% else: %>
|
||||||
|
<%- @Icon('status', 'ok inline') %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td><a href="<%- integration.target %>"><%= integration.name %></a></td>
|
||||||
|
<td><%= integration.description %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
21
app/assets/javascripts/app/views/settings/form.jst.eco
Normal file
21
app/assets/javascripts/app/views/settings/form.jst.eco
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<form>
|
||||||
|
<div class="settings-entry">
|
||||||
|
<table class="settings-list" style="width: 100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"><%- @T('Title') %>
|
||||||
|
<th width="50%"><%- @T('Value') %>
|
||||||
|
<th width="35%"><%- @T('Description') %>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for setting in @settings: %>
|
||||||
|
<tr>
|
||||||
|
<td><%- @T(setting.title) %>
|
||||||
|
<td data-name="<%- setting.name %>">
|
||||||
|
<td><p class="help-text"><%- @RichText(setting.description) %></p>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn--primary"><%- @T('Submit') %></button>
|
||||||
|
</form>
|
|
@ -1,8 +1,8 @@
|
||||||
<form class="settings-entry" id="<%= @setting.name %>">
|
<form class="settings-entry" id="<%= @setting.name %>">
|
||||||
<h2><%- @T( @setting.title ) %></h2>
|
<h2><%- @T(@setting.title) %></h2>
|
||||||
<p class="help-text"><%- @RichText( @setting.description ) %></p>
|
<p class="help-text"><%- @RichText(@setting.description) %></p>
|
||||||
<div class="horizontal end">
|
<div class="horizontal end">
|
||||||
<div class="form-item flex"></div>
|
<div class="form-item flex"></div>
|
||||||
<button type="submit" class="btn btn--primary"><%- @T( 'Submit' ) %></button>
|
<button type="submit" class="btn btn--primary"><%- @T('Submit') %></button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
|
@ -6962,6 +6962,7 @@ output {
|
||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
|
vertical-align: top;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid hsl(198,18%,86%);
|
border: 1px solid hsl(198,18%,86%);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,180 +0,0 @@
|
||||||
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
require 'event_buffer'
|
|
||||||
require 'notification_factory'
|
|
||||||
|
|
||||||
class Observer::Ticket::Notification < ActiveRecord::Observer
|
|
||||||
observe :ticket, 'ticket::_article'
|
|
||||||
|
|
||||||
def self.transaction(params)
|
|
||||||
|
|
||||||
return if params[:disable_notification]
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# get buffer
|
|
||||||
list = EventBuffer.list('notification')
|
|
||||||
|
|
||||||
# reset buffer
|
|
||||||
EventBuffer.reset('notification')
|
|
||||||
|
|
||||||
via_web = false
|
|
||||||
if ENV['RACK_ENV'] || Rails.configuration.webserver_is_active
|
|
||||||
via_web = true
|
|
||||||
end
|
|
||||||
|
|
||||||
# get uniq objects
|
|
||||||
list_objects = get_uniq_changes(list)
|
|
||||||
list_objects.each {|_ticket_id, item|
|
|
||||||
|
|
||||||
# send background job
|
|
||||||
Delayed::Job.enqueue(Observer::Ticket::Notification::BackgroundJob.new(item, via_web))
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
=begin
|
|
||||||
|
|
||||||
result = get_uniq_changes(events)
|
|
||||||
|
|
||||||
result = {
|
|
||||||
1 => {
|
|
||||||
type: 'create',
|
|
||||||
ticket_id: 123,
|
|
||||||
article_id: 123,
|
|
||||||
},
|
|
||||||
9 => {
|
|
||||||
type: 'update',
|
|
||||||
ticket_id: 123,
|
|
||||||
changes: {
|
|
||||||
attribute1: [before, now],
|
|
||||||
attribute2: [before, now],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result = {
|
|
||||||
9 => {
|
|
||||||
type: 'update',
|
|
||||||
ticket_id: 123,
|
|
||||||
article_id: 123,
|
|
||||||
changes: {
|
|
||||||
attribute1: [before, now],
|
|
||||||
attribute2: [before, now],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
=end
|
|
||||||
|
|
||||||
def self.get_uniq_changes(events)
|
|
||||||
list_objects = {}
|
|
||||||
events.each { |event|
|
|
||||||
|
|
||||||
# get current state of objects
|
|
||||||
if event[:name] == 'Ticket::Article'
|
|
||||||
article = Ticket::Article.lookup(id: event[:id])
|
|
||||||
|
|
||||||
# next if article is already deleted
|
|
||||||
next if !article
|
|
||||||
|
|
||||||
ticket = article.ticket
|
|
||||||
if !list_objects[ticket.id]
|
|
||||||
list_objects[ticket.id] = {}
|
|
||||||
end
|
|
||||||
list_objects[ticket.id][:article_id] = article.id
|
|
||||||
list_objects[ticket.id][:ticket_id] = ticket.id
|
|
||||||
|
|
||||||
if !list_objects[ticket.id][:type]
|
|
||||||
list_objects[ticket.id][:type] = 'update'
|
|
||||||
end
|
|
||||||
|
|
||||||
elsif event[:name] == 'Ticket'
|
|
||||||
ticket = Ticket.lookup(id: event[:id])
|
|
||||||
|
|
||||||
# next if ticket is already deleted
|
|
||||||
next if !ticket
|
|
||||||
|
|
||||||
if !list_objects[ticket.id]
|
|
||||||
list_objects[ticket.id] = {}
|
|
||||||
end
|
|
||||||
list_objects[ticket.id][:ticket_id] = ticket.id
|
|
||||||
|
|
||||||
if !list_objects[ticket.id][:type] || list_objects[ticket.id][:type] == 'update'
|
|
||||||
list_objects[ticket.id][:type] = event[:type]
|
|
||||||
end
|
|
||||||
|
|
||||||
# merge changes
|
|
||||||
if event[:changes]
|
|
||||||
if !list_objects[ticket.id][:changes]
|
|
||||||
list_objects[ticket.id][:changes] = event[:changes]
|
|
||||||
else
|
|
||||||
event[:changes].each {|key, value|
|
|
||||||
if !list_objects[ticket.id][:changes][key]
|
|
||||||
list_objects[ticket.id][:changes][key] = value
|
|
||||||
else
|
|
||||||
list_objects[ticket.id][:changes][key][1] = value[1]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise "unknown object for notification #{event[:name]}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
list_objects
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# Rails.logger.info 'CREATED!!!!'
|
|
||||||
# Rails.logger.info record.inspect
|
|
||||||
e = {
|
|
||||||
name: record.class.name,
|
|
||||||
type: 'create',
|
|
||||||
data: record,
|
|
||||||
id: record.id,
|
|
||||||
}
|
|
||||||
EventBuffer.add('notification', e)
|
|
||||||
end
|
|
||||||
|
|
||||||
def before_update(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# ignore updates on articles / we just want send notifications on ticket updates
|
|
||||||
return if record.class.name == 'Ticket::Article'
|
|
||||||
|
|
||||||
# ignore certain attributes
|
|
||||||
real_changes = {}
|
|
||||||
record.changes.each {|key, value|
|
|
||||||
next if key == 'updated_at'
|
|
||||||
next if key == 'first_response'
|
|
||||||
next if key == 'close_time'
|
|
||||||
next if key == 'last_contact_agent'
|
|
||||||
next if key == 'last_contact_customer'
|
|
||||||
next if key == 'last_contact'
|
|
||||||
next if key == 'article_count'
|
|
||||||
next if key == 'create_article_type_id'
|
|
||||||
next if key == 'create_article_sender_id'
|
|
||||||
real_changes[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
# do not send anything if nothing has changed
|
|
||||||
return if real_changes.empty?
|
|
||||||
|
|
||||||
e = {
|
|
||||||
name: record.class.name,
|
|
||||||
type: 'update',
|
|
||||||
data: record,
|
|
||||||
changes: real_changes,
|
|
||||||
id: record.id,
|
|
||||||
}
|
|
||||||
EventBuffer.add('notification', e)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,10 +1,185 @@
|
||||||
class Observer::Transaction
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Observer::Transaction < ActiveRecord::Observer
|
||||||
|
observe :ticket, 'ticket::_article'
|
||||||
|
|
||||||
def self.commit(params = {})
|
def self.commit(params = {})
|
||||||
|
|
||||||
# execute ticket transactions
|
# add attribute if execution is via web
|
||||||
Observer::Ticket::Notification.transaction(params)
|
params[:via_web] = false
|
||||||
|
if ENV['RACK_ENV'] || Rails.configuration.webserver_is_active
|
||||||
|
params[:via_web] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# execute object transactions
|
||||||
|
Observer::Transaction.perform(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.perform(params)
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# get buffer
|
||||||
|
list = EventBuffer.list('transaction')
|
||||||
|
|
||||||
|
# reset buffer
|
||||||
|
EventBuffer.reset('transaction')
|
||||||
|
|
||||||
|
# get uniq objects
|
||||||
|
list_objects = get_uniq_changes(list)
|
||||||
|
list_objects.each {|_id, item|
|
||||||
|
|
||||||
|
# send background job
|
||||||
|
Delayed::Job.enqueue(Transaction::BackgroundJob.new(item, params))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
result = get_uniq_changes(events)
|
||||||
|
|
||||||
|
result = {
|
||||||
|
1 => {
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'create',
|
||||||
|
ticket_id: 123,
|
||||||
|
article_id: 123,
|
||||||
|
},
|
||||||
|
9 => {
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'update',
|
||||||
|
ticket_id: 123,
|
||||||
|
changes: {
|
||||||
|
attribute1: [before, now],
|
||||||
|
attribute2: [before, now],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result = {
|
||||||
|
9 => {
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'update',
|
||||||
|
ticket_id: 123,
|
||||||
|
article_id: 123,
|
||||||
|
changes: {
|
||||||
|
attribute1: [before, now],
|
||||||
|
attribute2: [before, now],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.get_uniq_changes(events)
|
||||||
|
list_objects = {}
|
||||||
|
events.each { |event|
|
||||||
|
|
||||||
|
# get current state of objects
|
||||||
|
if event[:name] == 'Ticket::Article'
|
||||||
|
article = Ticket::Article.lookup(id: event[:id])
|
||||||
|
|
||||||
|
# next if article is already deleted
|
||||||
|
next if !article
|
||||||
|
|
||||||
|
ticket = article.ticket
|
||||||
|
if !list_objects[ticket.id]
|
||||||
|
list_objects[ticket.id] = {}
|
||||||
|
end
|
||||||
|
list_objects[ticket.id][:object] = 'Ticket'
|
||||||
|
list_objects[ticket.id][:article_id] = article.id
|
||||||
|
list_objects[ticket.id][:ticket_id] = ticket.id
|
||||||
|
|
||||||
|
if !list_objects[ticket.id][:type]
|
||||||
|
list_objects[ticket.id][:type] = 'update'
|
||||||
|
end
|
||||||
|
|
||||||
|
elsif event[:name] == 'Ticket'
|
||||||
|
ticket = Ticket.lookup(id: event[:id])
|
||||||
|
|
||||||
|
# next if ticket is already deleted
|
||||||
|
next if !ticket
|
||||||
|
|
||||||
|
if !list_objects[ticket.id]
|
||||||
|
list_objects[ticket.id] = {}
|
||||||
|
end
|
||||||
|
list_objects[ticket.id][:object] = 'Ticket'
|
||||||
|
list_objects[ticket.id][:ticket_id] = ticket.id
|
||||||
|
|
||||||
|
if !list_objects[ticket.id][:type] || list_objects[ticket.id][:type] == 'update'
|
||||||
|
list_objects[ticket.id][:type] = event[:type]
|
||||||
|
end
|
||||||
|
|
||||||
|
# merge changes
|
||||||
|
if event[:changes]
|
||||||
|
if !list_objects[ticket.id][:changes]
|
||||||
|
list_objects[ticket.id][:changes] = event[:changes]
|
||||||
|
else
|
||||||
|
event[:changes].each {|key, value|
|
||||||
|
if !list_objects[ticket.id][:changes][key]
|
||||||
|
list_objects[ticket.id][:changes][key] = value
|
||||||
|
else
|
||||||
|
list_objects[ticket.id][:changes][key][1] = value[1]
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "unknown object for integration #{event[:name]}"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
list_objects
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_create(record)
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return if Setting.get('import_mode')
|
||||||
|
|
||||||
|
e = {
|
||||||
|
name: record.class.name,
|
||||||
|
type: 'create',
|
||||||
|
data: record,
|
||||||
|
id: record.id,
|
||||||
|
}
|
||||||
|
EventBuffer.add('transaction', e)
|
||||||
|
end
|
||||||
|
|
||||||
|
def before_update(record)
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# ignore updates on articles / we just want send integrations on ticket updates
|
||||||
|
return if record.class.name == 'Ticket::Article'
|
||||||
|
|
||||||
|
# ignore certain attributes
|
||||||
|
real_changes = {}
|
||||||
|
record.changes.each {|key, value|
|
||||||
|
next if key == 'updated_at'
|
||||||
|
next if key == 'first_response'
|
||||||
|
next if key == 'close_time'
|
||||||
|
next if key == 'last_contact_agent'
|
||||||
|
next if key == 'last_contact_customer'
|
||||||
|
next if key == 'last_contact'
|
||||||
|
next if key == 'article_count'
|
||||||
|
next if key == 'create_article_type_id'
|
||||||
|
next if key == 'create_article_sender_id'
|
||||||
|
real_changes[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
# do not send anything if nothing has changed
|
||||||
|
return if real_changes.empty?
|
||||||
|
|
||||||
|
e = {
|
||||||
|
name: record.class.name,
|
||||||
|
type: 'update',
|
||||||
|
data: record,
|
||||||
|
changes: real_changes,
|
||||||
|
id: record.id,
|
||||||
|
}
|
||||||
|
EventBuffer.add('transaction', e)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -180,12 +180,12 @@ returns
|
||||||
tickets.each { |ticket|
|
tickets.each { |ticket|
|
||||||
|
|
||||||
# send notification
|
# send notification
|
||||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
Transaction::BackgroundJob.run(
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'reminder_reached',
|
||||||
ticket_id: ticket.id,
|
ticket_id: ticket.id,
|
||||||
article_id: ticket.articles.last.id,
|
article_id: ticket.articles.last.id,
|
||||||
type: 'reminder_reached',
|
|
||||||
)
|
)
|
||||||
bg.perform
|
|
||||||
|
|
||||||
result.push ticket
|
result.push ticket
|
||||||
}
|
}
|
||||||
|
@ -220,23 +220,23 @@ returns
|
||||||
|
|
||||||
# send escalation
|
# send escalation
|
||||||
if ticket.escalation_time < Time.zone.now
|
if ticket.escalation_time < Time.zone.now
|
||||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
Transaction::BackgroundJob.run(
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'escalation',
|
||||||
ticket_id: ticket.id,
|
ticket_id: ticket.id,
|
||||||
article_id: ticket.articles.last.id,
|
article_id: ticket.articles.last.id,
|
||||||
type: 'escalation',
|
|
||||||
)
|
)
|
||||||
bg.perform
|
|
||||||
result.push ticket
|
result.push ticket
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
# check if warning need to be sent
|
# check if warning need to be sent
|
||||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
Transaction::BackgroundJob.run(
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'escalation_warning',
|
||||||
ticket_id: ticket.id,
|
ticket_id: ticket.id,
|
||||||
article_id: ticket.articles.last.id,
|
article_id: ticket.articles.last.id,
|
||||||
type: 'escalation_warning',
|
|
||||||
)
|
)
|
||||||
bg.perform
|
|
||||||
result.push ticket
|
result.push ticket
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
|
3
app/models/transaction.rb
Normal file
3
app/models/transaction.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class Transaction
|
||||||
|
|
||||||
|
end
|
36
app/models/transaction/background_job.rb
Normal file
36
app/models/transaction/background_job.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Transaction::BackgroundJob
|
||||||
|
def initialize(item, params = {})
|
||||||
|
|
||||||
|
=begin
|
||||||
|
{
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'update',
|
||||||
|
ticket_id: 123,
|
||||||
|
via_web: true,
|
||||||
|
changes: {
|
||||||
|
'attribute1' => [before,now],
|
||||||
|
'attribute2' => [before,now],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
=end
|
||||||
|
|
||||||
|
@item = item
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
Setting.where(area: 'Transaction::Backend').order(:name).each {|setting|
|
||||||
|
backend = Setting.get(setting.name)
|
||||||
|
integration = Kernel.const_get(backend).new(@item, @params)
|
||||||
|
integration.perform
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.run(item, params = {})
|
||||||
|
generic = new(item, params)
|
||||||
|
generic.perform
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,24 +1,29 @@
|
||||||
# encoding: utf-8
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::Ticket::Notification::BackgroundJob
|
class Transaction::Notification
|
||||||
def initialize(params, via_web = false)
|
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
{
|
||||||
|
object: 'Ticket',
|
||||||
type: 'update',
|
type: 'update',
|
||||||
ticket_id: 123,
|
ticket_id: 123,
|
||||||
|
via_web: true,
|
||||||
changes: {
|
changes: {
|
||||||
'attribute1' => [before,now],
|
'attribute1' => [before, now],
|
||||||
'attribute2' => [before,now],
|
'attribute2' => [before, now],
|
||||||
}
|
}
|
||||||
|
},
|
||||||
=end
|
=end
|
||||||
@p = params
|
|
||||||
@via_web = via_web
|
def initialize(item, params = {})
|
||||||
|
@item = item
|
||||||
|
@params = params
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
ticket = Ticket.find(@p[:ticket_id])
|
ticket = Ticket.find(@item[:ticket_id])
|
||||||
if @p[:article_id]
|
if @item[:article_id]
|
||||||
article = Ticket::Article.find(@p[:article_id])
|
article = Ticket::Article.find(@item[:article_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
# find recipients
|
# find recipients
|
||||||
|
@ -59,7 +64,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
||||||
end
|
end
|
||||||
already_checked_recipient_ids = {}
|
already_checked_recipient_ids = {}
|
||||||
possible_recipients.each {|user|
|
possible_recipients.each {|user|
|
||||||
result = NotificationFactory::Mailer.notification_settings(user, ticket, @p[:type])
|
result = NotificationFactory::Mailer.notification_settings(user, ticket, @item[:type])
|
||||||
next if !result
|
next if !result
|
||||||
next if already_checked_recipient_ids[result[:user].id]
|
next if already_checked_recipient_ids[result[:user].id]
|
||||||
already_checked_recipient_ids[result[:user].id] = true
|
already_checked_recipient_ids[result[:user].id] = true
|
||||||
|
@ -73,7 +78,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
||||||
channels = item[:channels]
|
channels = item[:channels]
|
||||||
|
|
||||||
# ignore user who changed it by him self via web
|
# ignore user who changed it by him self via web
|
||||||
if @via_web
|
if @params[:via_web]
|
||||||
next if article && article.updated_by_id == user.id
|
next if article && article.updated_by_id == user.id
|
||||||
next if !article && ticket.updated_by_id == user.id
|
next if !article && ticket.updated_by_id == user.id
|
||||||
end
|
end
|
||||||
|
@ -83,10 +88,10 @@ class Observer::Ticket::Notification::BackgroundJob
|
||||||
|
|
||||||
# ignore if no changes has been done
|
# ignore if no changes has been done
|
||||||
changes = human_changes(user, ticket)
|
changes = human_changes(user, ticket)
|
||||||
next if @p[:type] == 'update' && !article && (!changes || changes.empty?)
|
next if @item[:type] == 'update' && !article && (!changes || changes.empty?)
|
||||||
|
|
||||||
# check if today already notified
|
# check if today already notified
|
||||||
if @p[:type] == 'reminder_reached' || @p[:type] == 'escalation' || @p[:type] == 'escalation_warning'
|
if @item[:type] == 'reminder_reached' || @item[:type] == 'escalation' || @item[:type] == 'escalation_warning'
|
||||||
identifier = user.email
|
identifier = user.email
|
||||||
if !identifier || identifier == ''
|
if !identifier || identifier == ''
|
||||||
identifier = user.login
|
identifier = user.login
|
||||||
|
@ -94,7 +99,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
||||||
already_notified = false
|
already_notified = false
|
||||||
History.list('Ticket', ticket.id).each {|history|
|
History.list('Ticket', ticket.id).each {|history|
|
||||||
next if history['type'] != 'notification'
|
next if history['type'] != 'notification'
|
||||||
next if history['value_to'] !~ /\(#{Regexp.escape(@p[:type])}:/
|
next if history['value_to'] !~ /\(#{Regexp.escape(@item[:type])}:/
|
||||||
next if history['value_to'] !~ /#{Regexp.escape(identifier)}\(/
|
next if history['value_to'] !~ /#{Regexp.escape(identifier)}\(/
|
||||||
next if !history['created_at'].today?
|
next if !history['created_at'].today?
|
||||||
already_notified = true
|
already_notified = true
|
||||||
|
@ -110,59 +115,59 @@ class Observer::Ticket::Notification::BackgroundJob
|
||||||
created_by_id = ticket.updated_by_id || 1
|
created_by_id = ticket.updated_by_id || 1
|
||||||
|
|
||||||
# delete old notifications
|
# delete old notifications
|
||||||
if @p[:type] == 'reminder_reached'
|
if @item[:type] == 'reminder_reached'
|
||||||
seen = false
|
seen = false
|
||||||
created_by_id = 1
|
created_by_id = 1
|
||||||
OnlineNotification.remove_by_type('Ticket', ticket.id, @p[:type], user)
|
OnlineNotification.remove_by_type('Ticket', ticket.id, @item[:type], user)
|
||||||
|
|
||||||
elsif @p[:type] == 'escalation' || @p[:type] == 'escalation_warning'
|
elsif @item[:type] == 'escalation' || @item[:type] == 'escalation_warning'
|
||||||
seen = false
|
seen = false
|
||||||
created_by_id = 1
|
created_by_id = 1
|
||||||
OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation', user)
|
OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation', user)
|
||||||
OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation_warning', user)
|
OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation_warning', user)
|
||||||
|
|
||||||
# on updates without state changes create unseen messages
|
# on updates without state changes create unseen messages
|
||||||
elsif @p[:type] != 'create' && (!@p[:changes] || @p[:changes].empty? || !@p[:changes]['state_id'])
|
elsif @item[:type] != 'create' && (!@item[:changes] || @item[:changes].empty? || !@item[:changes]['state_id'])
|
||||||
seen = false
|
seen = false
|
||||||
else
|
else
|
||||||
seen = ticket.online_notification_seen_state(user.id)
|
seen = ticket.online_notification_seen_state(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
OnlineNotification.add(
|
OnlineNotification.add(
|
||||||
type: @p[:type],
|
type: @item[:type],
|
||||||
object: 'Ticket',
|
object: 'Ticket',
|
||||||
o_id: ticket.id,
|
o_id: ticket.id,
|
||||||
seen: seen,
|
seen: seen,
|
||||||
created_by_id: created_by_id,
|
created_by_id: created_by_id,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
)
|
)
|
||||||
Rails.logger.debug "sent ticket online notifiaction to agent (#{@p[:type]}/#{ticket.id}/#{user.email})"
|
Rails.logger.debug "sent ticket online notifiaction to agent (#{@item[:type]}/#{ticket.id}/#{user.email})"
|
||||||
end
|
end
|
||||||
|
|
||||||
# ignore email channel notificaiton and empty emails
|
# ignore email channel notificaiton and empty emails
|
||||||
if !channels['email'] || !user.email || user.email == ''
|
if !channels['email'] || !user.email || user.email == ''
|
||||||
add_recipient_list(ticket, user, used_channels, @p[:type])
|
add_recipient_list(ticket, user, used_channels, @item[:type])
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
used_channels.push 'email'
|
used_channels.push 'email'
|
||||||
add_recipient_list(ticket, user, used_channels, @p[:type])
|
add_recipient_list(ticket, user, used_channels, @item[:type])
|
||||||
|
|
||||||
# get user based notification template
|
# get user based notification template
|
||||||
# if create, send create message / block update messages
|
# if create, send create message / block update messages
|
||||||
template = nil
|
template = nil
|
||||||
if @p[:type] == 'create'
|
if @item[:type] == 'create'
|
||||||
template = 'ticket_create'
|
template = 'ticket_create'
|
||||||
elsif @p[:type] == 'update'
|
elsif @item[:type] == 'update'
|
||||||
template = 'ticket_update'
|
template = 'ticket_update'
|
||||||
elsif @p[:type] == 'reminder_reached'
|
elsif @item[:type] == 'reminder_reached'
|
||||||
template = 'ticket_reminder_reached'
|
template = 'ticket_reminder_reached'
|
||||||
elsif @p[:type] == 'escalation'
|
elsif @item[:type] == 'escalation'
|
||||||
template = 'ticket_escalation'
|
template = 'ticket_escalation'
|
||||||
elsif @p[:type] == 'escalation_warning'
|
elsif @item[:type] == 'escalation_warning'
|
||||||
template = 'ticket_escalation_warning'
|
template = 'ticket_escalation_warning'
|
||||||
else
|
else
|
||||||
raise "unknown type for notification #{@p[:type]}"
|
raise "unknown type for notification #{@item[:type]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
NotificationFactory::Mailer.notification(
|
NotificationFactory::Mailer.notification(
|
||||||
|
@ -177,7 +182,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
||||||
references: ticket.get_references,
|
references: ticket.get_references,
|
||||||
main_object: ticket,
|
main_object: ticket,
|
||||||
)
|
)
|
||||||
Rails.logger.debug "sent ticket email notifiaction to agent (#{@p[:type]}/#{ticket.id}/#{user.email})"
|
Rails.logger.debug "sent ticket email notifiaction to agent (#{@item[:type]}/#{ticket.id}/#{user.email})"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -200,14 +205,14 @@ class Observer::Ticket::Notification::BackgroundJob
|
||||||
|
|
||||||
def human_changes(user, record)
|
def human_changes(user, record)
|
||||||
|
|
||||||
return {} if !@p[:changes]
|
return {} if !@item[:changes]
|
||||||
locale = user.preferences[:locale] || 'en-us'
|
locale = user.preferences[:locale] || 'en-us'
|
||||||
|
|
||||||
# only show allowed attributes
|
# only show allowed attributes
|
||||||
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
|
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
|
||||||
#puts "AL #{attribute_list.inspect}"
|
#puts "AL #{attribute_list.inspect}"
|
||||||
user_related_changes = {}
|
user_related_changes = {}
|
||||||
@p[:changes].each {|key, value|
|
@item[:changes].each {|key, value|
|
||||||
|
|
||||||
# if no config exists, use all attributes
|
# if no config exists, use all attributes
|
||||||
if !attribute_list || attribute_list.empty?
|
if !attribute_list || attribute_list.empty?
|
227
app/models/transaction/slack.rb
Normal file
227
app/models/transaction/slack.rb
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Transaction::Slack
|
||||||
|
=begin
|
||||||
|
{
|
||||||
|
object: 'Ticket',
|
||||||
|
type: 'update',
|
||||||
|
ticket_id: 123,
|
||||||
|
via_web: true,
|
||||||
|
changes: {
|
||||||
|
'attribute1' => [before, now],
|
||||||
|
'attribute2' => [before, now],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
=end
|
||||||
|
def initialize(item, params = {})
|
||||||
|
@item = item
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
return if @item[:object] != 'Ticket'
|
||||||
|
return if !Setting.get('slack_integration')
|
||||||
|
logo_url = "#{Setting.get('http_type')}://#{Setting.get('fqdn')}/assets/images/#{Setting.get('product_logo')}"
|
||||||
|
|
||||||
|
config = Setting.get('slack_config')
|
||||||
|
return if !config
|
||||||
|
return if !config['items']
|
||||||
|
|
||||||
|
ticket = Ticket.find(@item[:ticket_id])
|
||||||
|
if @item[:article_id]
|
||||||
|
article = Ticket::Article.find(@item[:article_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
# ignore if no changes has been done
|
||||||
|
changes = human_changes(ticket)
|
||||||
|
return if @item[:type] == 'update' && !article && (!changes || changes.empty?)
|
||||||
|
|
||||||
|
# get user based notification template
|
||||||
|
# if create, send create message / block update messages
|
||||||
|
template = nil
|
||||||
|
if @item[:type] == 'create'
|
||||||
|
template = 'ticket_create'
|
||||||
|
elsif @item[:type] == 'update'
|
||||||
|
template = 'ticket_update'
|
||||||
|
elsif @item[:type] == 'reminder_reached'
|
||||||
|
template = 'ticket_reminder_reached'
|
||||||
|
elsif @item[:type] == 'escalation'
|
||||||
|
template = 'ticket_escalation'
|
||||||
|
elsif @item[:type] == 'escalation_warning'
|
||||||
|
template = 'ticket_escalation_warning'
|
||||||
|
else
|
||||||
|
raise "unknown type for notification #{@item[:type]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
user = User.find(1)
|
||||||
|
result = NotificationFactory::Slack.template(
|
||||||
|
template: template,
|
||||||
|
locale: user[:preferences][:locale],
|
||||||
|
objects: {
|
||||||
|
ticket: ticket,
|
||||||
|
article: article,
|
||||||
|
changes: changes,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# good, warning, danger
|
||||||
|
color = '#000000'
|
||||||
|
ticket_state_type = ticket.state.state_type.name
|
||||||
|
if ticket.escalation_time && ticket.escalation_time > Time.zone.now
|
||||||
|
color = '#f35912'
|
||||||
|
elsif ticket_state_type == 'pending reminder'
|
||||||
|
if ticket.pending_time && ticket.pending_time < Time.zone.now
|
||||||
|
color = '#faab00'
|
||||||
|
end
|
||||||
|
elsif ticket_state_type =~ /^(new|open)$/
|
||||||
|
color = '#faab00'
|
||||||
|
elsif ticket_state_type == 'closed'
|
||||||
|
color = '#38ad69'
|
||||||
|
end
|
||||||
|
|
||||||
|
config['items'].each {|item|
|
||||||
|
|
||||||
|
# check action
|
||||||
|
if item['types']
|
||||||
|
hit = false
|
||||||
|
item['types'].each {|type|
|
||||||
|
next if type.to_s != @item[:type].to_s
|
||||||
|
hit = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next if !hit
|
||||||
|
end
|
||||||
|
|
||||||
|
# check group
|
||||||
|
if item['group_ids']
|
||||||
|
hit = false
|
||||||
|
item['group_ids'].each {|group_id|
|
||||||
|
next if group_id.to_s != ticket.group_id.to_s
|
||||||
|
hit = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next if !hit
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails.logger.debug "sent webhook (#{@item[:type]}/#{ticket.id}/#{item['webhook']})"
|
||||||
|
notifier = Slack::Notifier.new(
|
||||||
|
item['webhook'],
|
||||||
|
channel: item['channel'],
|
||||||
|
username: item['username'],
|
||||||
|
icon_url: logo_url,
|
||||||
|
mrkdwn: true,
|
||||||
|
)
|
||||||
|
if item['expand']
|
||||||
|
body = "#{result[:subject]}\n#{result[:body]}"
|
||||||
|
result = notifier.ping body
|
||||||
|
else
|
||||||
|
attachment = {
|
||||||
|
text: result[:body],
|
||||||
|
mrkdwn_in: ['text'],
|
||||||
|
color: color,
|
||||||
|
}
|
||||||
|
result = notifier.ping result[:subject],
|
||||||
|
attachments: [attachment]
|
||||||
|
end
|
||||||
|
if !result
|
||||||
|
Rails.logger.error "Unable to post webhook: #{item['webhook']}"
|
||||||
|
end
|
||||||
|
if result.code.to_s != '200' && result.code.to_s != '201'
|
||||||
|
Rails.logger.error "Unable to post webhook: #{item['webhook']}: #{result.inspect}"
|
||||||
|
end
|
||||||
|
Rails.logger.debug "sent webhook (#{@item[:type]}/#{ticket.id}/#{item['webhook']})"
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def human_changes(record)
|
||||||
|
|
||||||
|
return {} if !@item[:changes]
|
||||||
|
user = User.find(1)
|
||||||
|
locale = user.preferences[:locale] || 'en-us'
|
||||||
|
|
||||||
|
# only show allowed attributes
|
||||||
|
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
|
||||||
|
#puts "AL #{attribute_list.inspect}"
|
||||||
|
user_related_changes = {}
|
||||||
|
@item[:changes].each {|key, value|
|
||||||
|
|
||||||
|
# if no config exists, use all attributes
|
||||||
|
if !attribute_list || attribute_list.empty?
|
||||||
|
user_related_changes[key] = value
|
||||||
|
|
||||||
|
# if config exists, just use existing attributes for user
|
||||||
|
elsif attribute_list[key.to_s]
|
||||||
|
user_related_changes[key] = value
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
changes = {}
|
||||||
|
user_related_changes.each {|key, value|
|
||||||
|
|
||||||
|
# get attribute name
|
||||||
|
attribute_name = key.to_s
|
||||||
|
object_manager_attribute = attribute_list[attribute_name]
|
||||||
|
if attribute_name[-3, 3] == '_id'
|
||||||
|
attribute_name = attribute_name[ 0, attribute_name.length - 3 ].to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# add item to changes hash
|
||||||
|
if key.to_s == attribute_name
|
||||||
|
changes[attribute_name] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
# if changed item is an _id field/reference, do an lookup for the realy values
|
||||||
|
value_id = []
|
||||||
|
value_str = [ value[0], value[1] ]
|
||||||
|
if key.to_s[-3, 3] == '_id'
|
||||||
|
value_id[0] = value[0]
|
||||||
|
value_id[1] = value[1]
|
||||||
|
|
||||||
|
if record.respond_to?(attribute_name) && record.send(attribute_name)
|
||||||
|
relation_class = record.send(attribute_name).class
|
||||||
|
if relation_class && value_id[0]
|
||||||
|
relation_model = relation_class.lookup(id: value_id[0])
|
||||||
|
if relation_model
|
||||||
|
if relation_model['name']
|
||||||
|
value_str[0] = relation_model['name']
|
||||||
|
elsif relation_model.respond_to?('fullname')
|
||||||
|
value_str[0] = relation_model.send('fullname')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if relation_class && value_id[1]
|
||||||
|
relation_model = relation_class.lookup(id: value_id[1])
|
||||||
|
if relation_model
|
||||||
|
if relation_model['name']
|
||||||
|
value_str[1] = relation_model['name']
|
||||||
|
elsif relation_model.respond_to?('fullname')
|
||||||
|
value_str[1] = relation_model.send('fullname')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# check if we have an dedcated display name for it
|
||||||
|
display = attribute_name
|
||||||
|
if object_manager_attribute && object_manager_attribute[:display]
|
||||||
|
|
||||||
|
# delete old key
|
||||||
|
changes.delete(display)
|
||||||
|
|
||||||
|
# set new key
|
||||||
|
display = object_manager_attribute[:display].to_s
|
||||||
|
end
|
||||||
|
changes[display] = if object_manager_attribute && object_manager_attribute[:translate]
|
||||||
|
from = Translation.translate(locale, value_str[0])
|
||||||
|
to = Translation.translate(locale, value_str[1])
|
||||||
|
[from, to]
|
||||||
|
else
|
||||||
|
[value_str[0].to_s, value_str[1].to_s]
|
||||||
|
end
|
||||||
|
}
|
||||||
|
changes
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -14,7 +14,7 @@ Neues Ticket (<%= d 'ticket.title' %>)
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -14,7 +14,7 @@ New Ticket (<%= d 'ticket.title' %>)
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -8,7 +8,7 @@ Ticket ist eskaliert (<%= d 'ticket.title' %>)
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -8,7 +8,7 @@ Ticket is escalated (<%= d 'ticket.title' %>)
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -8,7 +8,7 @@ Ticket wird eskalieren (<%= d 'ticket.title' %>)
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
Ticket will escalated (<%= d 'ticket.title' %>)
|
Ticket will escalate (<%= d 'ticket.title' %>)
|
||||||
|
|
||||||
<p>Hi <%= d 'recipient.firstname' %>,</p>
|
<p>Hi <%= d 'recipient.firstname' %>,</p>
|
||||||
<br>
|
<br>
|
||||||
<p>a ticket (<%= d 'ticket.title' %>) from "<b><%= d 'ticket.customer.longname' %></b>" will escalate at "<%= d 'ticket.escalation_time' %>"!</p>
|
<p>A ticket (<%= d 'ticket.title' %>) from "<b><%= d 'ticket.customer.longname' %></b>" will escalate at "<%= d 'ticket.escalation_time' %>"!</p>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -8,7 +8,7 @@ Warten auf Erinnerung erreicht! (<%= d 'ticket.title' %>)
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -2,13 +2,13 @@ Reminder reached (<%= d 'ticket.title' %>)
|
||||||
|
|
||||||
<p>Hi <%= d 'recipient.firstname' %>,</p>
|
<p>Hi <%= d 'recipient.firstname' %>,</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Ticket needs attention, reminder reached for ticket (<%= d 'ticket.title' %>) with customer "<b><%= d 'ticket.customer.longname' %></b>".</p>
|
<p>A ticket needs attention, reminder reached for (<%= d 'ticket.title' %>) with customer "<b><%= d 'ticket.customer.longname' %></b>".</p>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -19,7 +19,7 @@ Ticket (<%= d 'ticket.title' %>) wurde von "<b><%= d 'ticket.updated_by.longname
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -19,7 +19,7 @@ Ticket (<%= d 'ticket.title' %>) has been updated by "<b><%= d 'ticket.updated_b
|
||||||
<p>
|
<p>
|
||||||
<%= t 'Information' %>:
|
<%= t 'Information' %>:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a 'article' %>
|
<%= a_html 'article' %>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
1
app/views/slack/application.md.erb
Normal file
1
app/views/slack/application.md.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= d 'message', false %>
|
9
app/views/slack/ticket_create/en.md.erb
Normal file
9
app/views/slack/ticket_create/en.md.erb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# <%= d 'ticket.title' %>
|
||||||
|
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Created by <%= d 'ticket.updated_by.longname' %> at <%= d 'ticket.updated_at' %>_
|
||||||
|
* <%= t 'Group' %>: <%= d 'ticket.group.name' %>
|
||||||
|
* <%= t 'Owner' %>: <%= d 'ticket.owner.fullname' %>
|
||||||
|
* <%= t 'State' %>: <%= t d 'ticket.state.name' %>
|
||||||
|
|
||||||
|
<% if @objects[:article] %>
|
||||||
|
<%= a_text 'article' %>
|
||||||
|
<% end %>
|
7
app/views/slack/ticket_escalation/en.md.erb
Normal file
7
app/views/slack/ticket_escalation/en.md.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# <%= d 'ticket.title' %>
|
||||||
|
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Escalated at <%= d 'ticket.escalation_time' %>_
|
||||||
|
A ticket (<%= d 'ticket.title' %>) from "<b><%= d 'ticket.customer.longname' %></b>" is escalated since "<%= d 'ticket.escalation_time' %>"!
|
||||||
|
|
||||||
|
<% if @objects[:article] %>
|
||||||
|
<%= a_text 'article' %>
|
||||||
|
<% end %>
|
7
app/views/slack/ticket_escalation_warning/en.md.erb
Normal file
7
app/views/slack/ticket_escalation_warning/en.md.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# <%= d 'ticket.title' %>
|
||||||
|
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Will escalate at <%= d 'ticket.escalation_time' %>_
|
||||||
|
A ticket (<%= d 'ticket.title' %>) from "<b><%= d 'ticket.customer.longname' %></b>" will escalate at "<%= d 'ticket.escalation_time' %>"!
|
||||||
|
|
||||||
|
<% if @objects[:article] %>
|
||||||
|
<%= a_text 'article' %>
|
||||||
|
<% end %>
|
7
app/views/slack/ticket_reminder_reached/en.md.erb
Normal file
7
app/views/slack/ticket_reminder_reached/en.md.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# <%= d 'ticket.title' %>
|
||||||
|
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Reminder reached!_
|
||||||
|
A ticket needs attention, reminder reached for (<%= d 'ticket.title' %>) with customer "*<%= d 'ticket.customer.longname' %>*".
|
||||||
|
|
||||||
|
<% if @objects[:article] %>
|
||||||
|
<%= a_text 'article' %>
|
||||||
|
<% end %>
|
11
app/views/slack/ticket_update/en.md.erb
Normal file
11
app/views/slack/ticket_update/en.md.erb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# <%= d 'ticket.title' %>
|
||||||
|
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Updated by <%= d 'ticket.updated_by.longname' %> at <%= d 'ticket.updated_at' %>_
|
||||||
|
<% if @objects[:changes] && !@objects[:changes].empty? %>
|
||||||
|
<% @objects[:changes].each do |key, value| %>
|
||||||
|
* <%= t key %>: <%= h value[0] %> -> <%= h value[1] %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if @objects[:article] %>
|
||||||
|
<%= a_text 'article' %>
|
||||||
|
<% end %>
|
|
@ -31,7 +31,6 @@ module Zammad
|
||||||
'observer::_ticket::_article::_communicate_facebook',
|
'observer::_ticket::_article::_communicate_facebook',
|
||||||
'observer::_ticket::_article::_communicate_twitter',
|
'observer::_ticket::_article::_communicate_twitter',
|
||||||
'observer::_ticket::_article::_signature_detection',
|
'observer::_ticket::_article::_signature_detection',
|
||||||
'observer::_ticket::_notification',
|
|
||||||
'observer::_ticket::_reset_new_state',
|
'observer::_ticket::_reset_new_state',
|
||||||
'observer::_ticket::_escalation_calculation',
|
'observer::_ticket::_escalation_calculation',
|
||||||
'observer::_ticket::_ref_object_touch',
|
'observer::_ticket::_ref_object_touch',
|
||||||
|
@ -42,7 +41,8 @@ module Zammad
|
||||||
'observer::_user::_ticket_organization',
|
'observer::_user::_ticket_organization',
|
||||||
'observer::_user::_geo',
|
'observer::_user::_geo',
|
||||||
'observer::_organization::_ref_object_touch',
|
'observer::_organization::_ref_object_touch',
|
||||||
'observer::_sla::_ticket_rebuild_escalation'
|
'observer::_sla::_ticket_rebuild_escalation',
|
||||||
|
'observer::_transaction'
|
||||||
|
|
||||||
# REST api path
|
# REST api path
|
||||||
config.api_path = '/api/v1'
|
config.api_path = '/api/v1'
|
||||||
|
|
144
db/migrate/20160415000001_add_slack_integration.rb
Normal file
144
db/migrate/20160415000001_add_slack_integration.rb
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
class AddSlackIntegration < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
Setting.create_or_update(
|
||||||
|
title: 'Icinga integration',
|
||||||
|
name: 'icinga_integration',
|
||||||
|
area: 'Integration::Switch',
|
||||||
|
description: 'Define if Icinga (http://www.icinga.org) is enabled or not.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'icinga_integration',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: false,
|
||||||
|
preferences: { prio: 1 },
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_or_update(
|
||||||
|
title: 'Sender',
|
||||||
|
name: 'icinga_sender',
|
||||||
|
area: 'Integration::Icinga',
|
||||||
|
description: 'Define the sender email address of Icinga emails.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: false,
|
||||||
|
name: 'icinga_sender',
|
||||||
|
tag: 'input',
|
||||||
|
placeholder: 'icinga@monitoring.example.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: 'icinga@monitoring.example.com',
|
||||||
|
frontend: false,
|
||||||
|
preferences: { prio: 2 },
|
||||||
|
)
|
||||||
|
Setting.create_or_update(
|
||||||
|
title: 'Nagios integration',
|
||||||
|
name: 'nagios_integration',
|
||||||
|
area: 'Integration::Switch',
|
||||||
|
description: 'Define if Nagios (http://www.nagios.org) is enabled or not.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'nagios_integration',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: false,
|
||||||
|
preferences: { prio: 1 },
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_or_update(
|
||||||
|
title: 'Sender',
|
||||||
|
name: 'nagios_sender',
|
||||||
|
area: 'Integration::Nagios',
|
||||||
|
description: 'Define the sender email address of Nagios emails.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: false,
|
||||||
|
name: 'nagios_sender',
|
||||||
|
tag: 'input',
|
||||||
|
placeholder: 'nagios@monitoring.example.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: 'nagios@monitoring.example.com',
|
||||||
|
frontend: false,
|
||||||
|
preferences: { prio: 2 },
|
||||||
|
)
|
||||||
|
|
||||||
|
Setting.create_or_update(
|
||||||
|
title: 'Define transaction backend.',
|
||||||
|
name: '0100_notification',
|
||||||
|
area: 'Transaction::Backend',
|
||||||
|
description: 'Define the transaction backend to send agent notifications.',
|
||||||
|
options: {},
|
||||||
|
state: 'Transaction::Notification',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_or_update(
|
||||||
|
title: 'Define transaction backend.',
|
||||||
|
name: '6000_slack_webhook',
|
||||||
|
area: 'Transaction::Backend',
|
||||||
|
description: 'Define the transaction backend which posts messages to (http://www.slack.com).',
|
||||||
|
options: {},
|
||||||
|
state: 'Transaction::Slack',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Slack integration',
|
||||||
|
name: 'slack_integration',
|
||||||
|
area: 'Integration::Slack',
|
||||||
|
description: 'Define if Slack (http://www.slack.org) is enabled or not.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'slack_integration',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: true,
|
||||||
|
preferences: { prio: 1 },
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_or_update(
|
||||||
|
title: 'Slack config',
|
||||||
|
name: 'slack_config',
|
||||||
|
area: 'Integration::Slack',
|
||||||
|
description: 'Define the slack config.',
|
||||||
|
options: {},
|
||||||
|
state: {
|
||||||
|
items: []
|
||||||
|
},
|
||||||
|
frontend: false,
|
||||||
|
preferences: { prio: 2 },
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
59
db/seeds.rb
59
db/seeds.rb
|
@ -1600,7 +1600,7 @@ Setting.create_if_not_exists(
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Icinga integration',
|
title: 'Icinga integration',
|
||||||
name: 'icinga_integration',
|
name: 'icinga_integration',
|
||||||
area: 'Integration::Icinga',
|
area: 'Integration::Switch',
|
||||||
description: 'Define if Icinga (http://www.icinga.org) is enabled or not.',
|
description: 'Define if Icinga (http://www.icinga.org) is enabled or not.',
|
||||||
options: {
|
options: {
|
||||||
form: [
|
form: [
|
||||||
|
@ -1632,6 +1632,7 @@ Setting.create_if_not_exists(
|
||||||
null: false,
|
null: false,
|
||||||
name: 'icinga_sender',
|
name: 'icinga_sender',
|
||||||
tag: 'input',
|
tag: 'input',
|
||||||
|
placeholder: 'icinga@monitoring.example.com',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -1685,7 +1686,7 @@ Setting.create_if_not_exists(
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Nagios integration',
|
title: 'Nagios integration',
|
||||||
name: 'nagios_integration',
|
name: 'nagios_integration',
|
||||||
area: 'Integration::Nagios',
|
area: 'Integration::Switch',
|
||||||
description: 'Define if Nagios (http://www.nagios.org) is enabled or not.',
|
description: 'Define if Nagios (http://www.nagios.org) is enabled or not.',
|
||||||
options: {
|
options: {
|
||||||
form: [
|
form: [
|
||||||
|
@ -1717,6 +1718,7 @@ Setting.create_if_not_exists(
|
||||||
null: false,
|
null: false,
|
||||||
name: 'nagios_sender',
|
name: 'nagios_sender',
|
||||||
tag: 'input',
|
tag: 'input',
|
||||||
|
placeholder: 'nagios@monitoring.example.com',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -1767,6 +1769,59 @@ Setting.create_if_not_exists(
|
||||||
preferences: { prio: 4 },
|
preferences: { prio: 4 },
|
||||||
frontend: false
|
frontend: false
|
||||||
)
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Define transaction backend.',
|
||||||
|
name: '0100_notification',
|
||||||
|
area: 'Transaction::Backend',
|
||||||
|
description: 'Define the transaction backend to send agent notifications.',
|
||||||
|
options: {},
|
||||||
|
state: 'Transaction::Notification',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Define transaction backend.',
|
||||||
|
name: '6000_slack_webhook',
|
||||||
|
area: 'Transaction::Backend',
|
||||||
|
description: 'Define the transaction backend which posts messages to (http://www.slack.com).',
|
||||||
|
options: {},
|
||||||
|
state: 'Transaction::Slack',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Slack integration',
|
||||||
|
name: 'slack_integration',
|
||||||
|
area: 'Integration::Switch',
|
||||||
|
description: 'Define if Slack (http://www.slack.org) is enabled or not.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'slack_integration',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: true,
|
||||||
|
preferences: { prio: 1 },
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Slack config',
|
||||||
|
name: 'slack_config',
|
||||||
|
area: 'Integration::Slack',
|
||||||
|
description: 'Define the slack config.',
|
||||||
|
options: {},
|
||||||
|
state: {
|
||||||
|
items: []
|
||||||
|
},
|
||||||
|
frontend: false,
|
||||||
|
preferences: { prio: 2 },
|
||||||
|
)
|
||||||
|
|
||||||
signature = Signature.create_if_not_exists(
|
signature = Signature.create_if_not_exists(
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|
|
@ -5,8 +5,17 @@ module NotificationFactory
|
||||||
result = NotificationFactory.template_read(
|
result = NotificationFactory.template_read(
|
||||||
template: 'password_reset',
|
template: 'password_reset',
|
||||||
locale: 'en-us',
|
locale: 'en-us',
|
||||||
format: 'html', # md
|
format: 'html',
|
||||||
type: 'mailer', # slack
|
type: 'mailer',
|
||||||
|
)
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
result = NotificationFactory.template_read(
|
||||||
|
template: 'ticket_update',
|
||||||
|
locale: 'en-us',
|
||||||
|
format: 'md',
|
||||||
|
type: 'slack',
|
||||||
)
|
)
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
@ -56,8 +65,15 @@ returns
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
string = NotificationFactory.application_template_read(
|
string = NotificationFactory.application_template_read(
|
||||||
format: 'html', # md
|
format: 'html',
|
||||||
type: 'mailer', # slack
|
type: 'mailer',
|
||||||
|
)
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
string = NotificationFactory.application_template_read(
|
||||||
|
format: 'md',
|
||||||
|
type: 'slack',
|
||||||
)
|
)
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
|
54
lib/notification_factory/slack.rb
Normal file
54
lib/notification_factory/slack.rb
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
class NotificationFactory::Slack
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
result = NotificationFactory::Slack.template(
|
||||||
|
template: 'ticket_update',
|
||||||
|
locale: 'en-us',
|
||||||
|
objects: {
|
||||||
|
recipient: User.find(2),
|
||||||
|
ticket: Ticket.find(1)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
{
|
||||||
|
subject: 'some subject',
|
||||||
|
body: 'some body',
|
||||||
|
}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.template(data)
|
||||||
|
|
||||||
|
if data[:templateInline]
|
||||||
|
return NotificationFactory::Template.new(data[:objects], data[:locale], data[:templateInline]).render
|
||||||
|
end
|
||||||
|
|
||||||
|
template = NotificationFactory.template_read(
|
||||||
|
locale: data[:locale] || 'en',
|
||||||
|
template: data[:template],
|
||||||
|
format: 'md',
|
||||||
|
type: 'slack',
|
||||||
|
)
|
||||||
|
|
||||||
|
message_subject = NotificationFactory::Template.new(data[:objects], data[:locale], template[:subject]).render
|
||||||
|
message_body = NotificationFactory::Template.new(data[:objects], data[:locale], template[:body]).render
|
||||||
|
|
||||||
|
if !data[:raw]
|
||||||
|
application_template = NotificationFactory.application_template_read(
|
||||||
|
format: 'md',
|
||||||
|
type: 'slack',
|
||||||
|
)
|
||||||
|
data[:objects][:message] = message_body
|
||||||
|
data[:objects][:standalone] = data[:standalone]
|
||||||
|
message_body = NotificationFactory::Template.new(data[:objects], data[:locale], application_template).render
|
||||||
|
end
|
||||||
|
{
|
||||||
|
subject: message_subject.strip!,
|
||||||
|
body: message_body.strip!,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -11,6 +11,8 @@ class NotificationFactory::Template
|
||||||
ERB.new(@template).result(binding)
|
ERB.new(@template).result(binding)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# d - data of object
|
||||||
|
# d('user.firstname', htmlEscape)
|
||||||
def d(key, escape = nil)
|
def d(key, escape = nil)
|
||||||
|
|
||||||
# do validaton, ignore some methodes
|
# do validaton, ignore some methodes
|
||||||
|
@ -45,19 +47,25 @@ class NotificationFactory::Template
|
||||||
h placeholder
|
h placeholder
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# c - config
|
||||||
|
# c('fqdn', htmlEscape)
|
||||||
def c(key, escape = nil)
|
def c(key, escape = nil)
|
||||||
config = Setting.get(key)
|
config = Setting.get(key)
|
||||||
return config if escape == false || (escape.nil? && !@escape)
|
return config if escape == false || (escape.nil? && !@escape)
|
||||||
h config
|
h config
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# t - translation
|
||||||
|
# t('yes', htmlEscape)
|
||||||
def t(key, escape = nil)
|
def t(key, escape = nil)
|
||||||
translation = Translation.translate(@locale, key)
|
translation = Translation.translate(@locale, key)
|
||||||
return translation if escape == false || (escape.nil? && !@escape)
|
return translation if escape == false || (escape.nil? && !@escape)
|
||||||
h translation
|
h translation
|
||||||
end
|
end
|
||||||
|
|
||||||
def a(article)
|
# a_html - article body in html
|
||||||
|
# a_html(article)
|
||||||
|
def a_html(article)
|
||||||
content_type = d "#{article}.content_type", false
|
content_type = d "#{article}.content_type", false
|
||||||
if content_type =~ /html/
|
if content_type =~ /html/
|
||||||
return d "#{article}.body", false
|
return d "#{article}.body", false
|
||||||
|
@ -65,6 +73,19 @@ class NotificationFactory::Template
|
||||||
d("#{article}.body", false).text2html
|
d("#{article}.body", false).text2html
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# a_text - article body in text
|
||||||
|
# a_text(article)
|
||||||
|
def a_text(article)
|
||||||
|
content_type = d "#{article}.content_type", false
|
||||||
|
body = d "#{article}.body", false
|
||||||
|
if content_type =~ /html/
|
||||||
|
body = body.html2text
|
||||||
|
end
|
||||||
|
(body.strip + "\n").gsub(/^(.*?)$/, '> \\1')
|
||||||
|
end
|
||||||
|
|
||||||
|
# h - htmlEscape
|
||||||
|
# h('fqdn', htmlEscape)
|
||||||
def h(key)
|
def h(key)
|
||||||
return key if !key
|
return key if !key
|
||||||
CGI.escapeHTML(key.to_s)
|
CGI.escapeHTML(key.to_s)
|
||||||
|
|
192
test/integration/slack_test.rb
Normal file
192
test/integration/slack_test.rb
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
require 'integration_test_helper'
|
||||||
|
require 'slack'
|
||||||
|
|
||||||
|
class SlackTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
|
# needed to check correct behavior
|
||||||
|
slack_group = Group.create_if_not_exists(
|
||||||
|
name: 'Slack',
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
# check
|
||||||
|
test 'base' do
|
||||||
|
|
||||||
|
if !ENV['SLACK_CI_CHANNEL']
|
||||||
|
raise "ERROR: Need SLACK_CI_CHANNEL - hint SLACK_CI_CHANNEL='ci-zammad'"
|
||||||
|
end
|
||||||
|
if !ENV['SLACK_CI_WEBHOOK']
|
||||||
|
raise "ERROR: Need SLACK_CI_WEBHOOK - hint SLACK_CI_WEBHOOK='https://hooks.slack.com/services/...'"
|
||||||
|
end
|
||||||
|
if !ENV['SLACK_CI_CHECKER_TOKEN']
|
||||||
|
raise "ERROR: Need SLACK_CI_CHECKER_TOKEN - hint SLACK_CI_CHECKER_TOKEN='...'"
|
||||||
|
end
|
||||||
|
|
||||||
|
channel = ENV['SLACK_CI_CHANNEL']
|
||||||
|
webhook = ENV['SLACK_CI_WEBHOOK']
|
||||||
|
|
||||||
|
# set system mode to done / to activate
|
||||||
|
Setting.set('system_init_done', true)
|
||||||
|
Setting.set('slack_integration', true)
|
||||||
|
|
||||||
|
items = [
|
||||||
|
{
|
||||||
|
group_ids: [slack_group.id],
|
||||||
|
types: %w(create update),
|
||||||
|
webhook: webhook,
|
||||||
|
channel: channel,
|
||||||
|
username: 'zammad bot',
|
||||||
|
expand: false,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
Setting.set('slack_config', { items: items })
|
||||||
|
|
||||||
|
# case 1
|
||||||
|
customer = User.find(2)
|
||||||
|
hash = hash_gen
|
||||||
|
text = "#{rand_word}... #{hash}"
|
||||||
|
|
||||||
|
default_group = Group.first
|
||||||
|
ticket1 = Ticket.create(
|
||||||
|
title: text,
|
||||||
|
customer_id: customer.id,
|
||||||
|
group_id: default_group.id,
|
||||||
|
state: Ticket::State.find_by(name: 'new'),
|
||||||
|
priority: Ticket::Priority.find_by(name: '2 normal'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
article1 = Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
body: text,
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'note'),
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||||
|
internal: false,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
Observer::Transaction.commit
|
||||||
|
Delayed::Worker.new.work_off
|
||||||
|
|
||||||
|
# check if message exists
|
||||||
|
assert_not(slack_check(channel, hash))
|
||||||
|
|
||||||
|
ticket1.state = Ticket::State.find_by(name: 'open')
|
||||||
|
ticket1.save
|
||||||
|
|
||||||
|
Observer::Transaction.commit
|
||||||
|
Delayed::Worker.new.work_off
|
||||||
|
|
||||||
|
# check if message exists
|
||||||
|
assert_not(slack_check(channel, hash))
|
||||||
|
|
||||||
|
# case 2
|
||||||
|
hash = hash_gen
|
||||||
|
text = "#{rand_word}... #{hash}"
|
||||||
|
|
||||||
|
ticket2 = Ticket.create(
|
||||||
|
title: text,
|
||||||
|
customer_id: customer.id,
|
||||||
|
group_id: slack_group.id,
|
||||||
|
state: Ticket::State.find_by(name: 'new'),
|
||||||
|
priority: Ticket::Priority.find_by(name: '2 normal'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
article2 = Ticket::Article.create(
|
||||||
|
ticket_id: ticket2.id,
|
||||||
|
body: text,
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'note'),
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||||
|
internal: false,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
Observer::Transaction.commit
|
||||||
|
Delayed::Worker.new.work_off
|
||||||
|
|
||||||
|
# check if message exists
|
||||||
|
assert(slack_check(channel, hash))
|
||||||
|
|
||||||
|
hash = hash_gen
|
||||||
|
text = "#{rand_word}... #{hash}"
|
||||||
|
|
||||||
|
ticket2.title = text
|
||||||
|
ticket2.save
|
||||||
|
|
||||||
|
Observer::Transaction.commit
|
||||||
|
Delayed::Worker.new.work_off
|
||||||
|
|
||||||
|
# check if message exists
|
||||||
|
assert(slack_check(channel, hash))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def hash_gen
|
||||||
|
(0...10).map { ('a'..'z').to_a[rand(26)] }.join
|
||||||
|
end
|
||||||
|
|
||||||
|
def rand_word
|
||||||
|
words = [
|
||||||
|
'dog',
|
||||||
|
'cat',
|
||||||
|
'house',
|
||||||
|
'home',
|
||||||
|
'yesterday',
|
||||||
|
'tomorrow',
|
||||||
|
'new york',
|
||||||
|
'berlin',
|
||||||
|
'coffee script',
|
||||||
|
'java script',
|
||||||
|
'bob smith',
|
||||||
|
'be open',
|
||||||
|
'really nice',
|
||||||
|
'stay tuned',
|
||||||
|
'be a good boy',
|
||||||
|
'invent new things',
|
||||||
|
]
|
||||||
|
words[rand(words.length)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def slack_check(channel_name, search_for)
|
||||||
|
|
||||||
|
Slack.configure do |config|
|
||||||
|
config.token = ENV['SLACK_CI_CHECKER_TOKEN']
|
||||||
|
end
|
||||||
|
|
||||||
|
Slack.auth_test
|
||||||
|
|
||||||
|
client = Slack::Client.new
|
||||||
|
channels = client.channels_list['channels']
|
||||||
|
channel_id = nil
|
||||||
|
channels.each {|channel|
|
||||||
|
next if channel['name'] != channel_name
|
||||||
|
channel_id = channel['id']
|
||||||
|
}
|
||||||
|
if !channel_id
|
||||||
|
raise "ERROR: No such channel '#{channel_name}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
channel_history = client.channels_history(channel: channel_id)
|
||||||
|
if !channel_history
|
||||||
|
raise "ERROR: No history for channel #{channel_name}/#{channel_id}"
|
||||||
|
end
|
||||||
|
if !channel_history['messages']
|
||||||
|
raise "ERROR: No history messages for channel #{channel_name}/#{channel_id}"
|
||||||
|
end
|
||||||
|
channel_history['messages'].each {|message|
|
||||||
|
next if !message['text']
|
||||||
|
if message['text'] =~ /#{search_for}/i
|
||||||
|
p "SUCCESS: message with #{search_for} found!"
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
#raise "ERROR: No such message containing #{search_for} in history of channel #{channel_name}/#{channel_id}"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -864,8 +864,8 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
||||||
ticket1.priority = Ticket::Priority.lookup(name: '3 high')
|
ticket1.priority = Ticket::Priority.lookup(name: '3 high')
|
||||||
ticket1.save
|
ticket1.save
|
||||||
|
|
||||||
list = EventBuffer.list('notification')
|
list = EventBuffer.list('transaction')
|
||||||
list_objects = Observer::Ticket::Notification.get_uniq_changes(list)
|
list_objects = Observer::Transaction.get_uniq_changes(list)
|
||||||
|
|
||||||
assert_equal('some notification event test 1', list_objects[ticket1.id][:changes]['title'][0])
|
assert_equal('some notification event test 1', list_objects[ticket1.id][:changes]['title'][0])
|
||||||
assert_equal('some notification event test 1 - #2', list_objects[ticket1.id][:changes]['title'][1])
|
assert_equal('some notification event test 1 - #2', list_objects[ticket1.id][:changes]['title'][1])
|
||||||
|
@ -878,8 +878,8 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
||||||
ticket1.priority = Ticket::Priority.lookup(name: '1 low')
|
ticket1.priority = Ticket::Priority.lookup(name: '1 low')
|
||||||
ticket1.save
|
ticket1.save
|
||||||
|
|
||||||
list = EventBuffer.list('notification')
|
list = EventBuffer.list('transaction')
|
||||||
list_objects = Observer::Ticket::Notification.get_uniq_changes(list)
|
list_objects = Observer::Transaction.get_uniq_changes(list)
|
||||||
|
|
||||||
assert_equal('some notification event test 1', list_objects[ticket1.id][:changes]['title'][0])
|
assert_equal('some notification event test 1', list_objects[ticket1.id][:changes]['title'][0])
|
||||||
assert_equal('some notification event test 1 - #2 - #3', list_objects[ticket1.id][:changes]['title'][1])
|
assert_equal('some notification event test 1 - #2 - #3', list_objects[ticket1.id][:changes]['title'][1])
|
||||||
|
@ -916,7 +916,7 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
||||||
)
|
)
|
||||||
assert(ticket1, 'ticket created - ticket notification template')
|
assert(ticket1, 'ticket created - ticket notification template')
|
||||||
|
|
||||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
bg = Transaction::Notification.new(
|
||||||
ticket_id: ticket1.id,
|
ticket_id: ticket1.id,
|
||||||
article_id: article.id,
|
article_id: article.id,
|
||||||
type: 'update',
|
type: 'update',
|
||||||
|
@ -992,7 +992,7 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
||||||
assert_no_match(/pending_till/, result[:body])
|
assert_no_match(/pending_till/, result[:body])
|
||||||
assert_no_match(/i18n/, result[:body])
|
assert_no_match(/i18n/, result[:body])
|
||||||
|
|
||||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
bg = Transaction::Notification.new(
|
||||||
ticket_id: ticket1.id,
|
ticket_id: ticket1.id,
|
||||||
article_id: article.id,
|
article_id: article.id,
|
||||||
type: 'update',
|
type: 'update',
|
||||||
|
|
Loading…
Reference in a new issue