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 'browser'
|
||||
|
||||
# integrations
|
||||
gem 'slack-notifier'
|
||||
|
||||
# event machine
|
||||
gem 'eventmachine'
|
||||
gem 'em-websocket'
|
||||
|
|
|
@ -267,6 +267,7 @@ GEM
|
|||
simplecov-html (0.10.0)
|
||||
simplecov-rcov (0.2.3)
|
||||
simplecov (>= 0.4.1)
|
||||
slack-notifier (1.5.1)
|
||||
slop (3.6.0)
|
||||
spring (1.6.4)
|
||||
sprockets (3.5.2)
|
||||
|
@ -361,6 +362,7 @@ DEPENDENCIES
|
|||
simple-rss
|
||||
simplecov
|
||||
simplecov-rcov
|
||||
slack-notifier
|
||||
spring
|
||||
sprockets
|
||||
sqlite3
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
class Icinga extends App.ControllerTabs
|
||||
header: 'Icinga'
|
||||
constructor: ->
|
||||
super
|
||||
return if !@authenticate(false, 'Admin')
|
||||
@title 'Icinga', true
|
||||
@tabs = [
|
||||
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Icinga' } }
|
||||
class Index extends App.ControllerIntegrationBase
|
||||
featureIntegration: 'icinga_integration'
|
||||
featureName: 'Icinga'
|
||||
featureConfig: 'icinga_config'
|
||||
description: [
|
||||
['This service receives emails from %s and creates tickets with host and service.', 'Icinga']
|
||||
['If the host and service is recovered again, the ticket will be closed automatically.']
|
||||
]
|
||||
@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
|
||||
header: 'Nagios'
|
||||
constructor: ->
|
||||
super
|
||||
return if !@authenticate(false, 'Admin')
|
||||
@title 'Nagios', true
|
||||
@tabs = [
|
||||
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Nagios' } }
|
||||
class Index extends App.ControllerIntegrationBase
|
||||
featureIntegration: 'nagios_integration'
|
||||
featureName: 'Nagios'
|
||||
featureConfig: 'nagios_config'
|
||||
description: [
|
||||
['This service receives emails from %s and creates tickets with host and service.', 'Nagios']
|
||||
['If the host and service is recovered again, the ticket will be closed automatically.']
|
||||
]
|
||||
@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 = []
|
||||
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
|
||||
|
||||
@html App.view('profile/linked_accounts')(
|
||||
|
@ -55,7 +55,7 @@ class Index extends App.Controller
|
|||
id: 'account'
|
||||
type: 'DELETE'
|
||||
url: @apiPath + '/users/account'
|
||||
data: JSON.stringify({ provider: provider, uid: uid })
|
||||
data: JSON.stringify(provider: provider, uid: uid)
|
||||
processData: true
|
||||
success: @success
|
||||
error: @error
|
||||
|
@ -64,7 +64,7 @@ class Index extends App.Controller
|
|||
success: (data, status, xhr) =>
|
||||
@notify(
|
||||
type: 'success'
|
||||
msg: App.i18n.translateContent( 'Successfully!' )
|
||||
msg: App.i18n.translateContent('Successfully!')
|
||||
)
|
||||
update = =>
|
||||
@render()
|
||||
|
@ -72,10 +72,10 @@ class Index extends App.Controller
|
|||
|
||||
error: (xhr, status, error) =>
|
||||
@render()
|
||||
data = JSON.parse( xhr.responseText )
|
||||
data = JSON.parse(xhr.responseText)
|
||||
@notify(
|
||||
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
|
||||
constructor: ->
|
||||
super
|
||||
|
@ -115,11 +205,11 @@ class App.SettingsAreaItem extends App.Controller
|
|||
|
||||
if @setting.preferences.session_check
|
||||
App.Auth.loginCheck()
|
||||
fail: ->
|
||||
fail: (settings, details) ->
|
||||
ui.formEnable(e)
|
||||
App.Event.trigger 'notify', {
|
||||
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
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,8 +1,41 @@
|
|||
class IndexRouter extends App.ControllerNavSidbar
|
||||
authenticateRequired: true
|
||||
configKey: 'NavBarIntegration'
|
||||
class Index extends App.ControllerContent
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
App.Config.set('integration', IndexRouter, 'Routes')
|
||||
App.Config.set('integration/:target', IndexRouter, 'Routes')
|
||||
# check authentication
|
||||
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/:channel_id', 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('Channels', { prio: 2500, name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -58,4 +58,4 @@ class Index extends App.ControllerContent
|
|||
@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'
|
||||
@extend Spine.Model.Ajax
|
||||
@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 %>">
|
||||
<h2><%- @T( @setting.title ) %></h2>
|
||||
<p class="help-text"><%- @RichText( @setting.description ) %></p>
|
||||
<h2><%- @T(@setting.title) %></h2>
|
||||
<p class="help-text"><%- @RichText(@setting.description) %></p>
|
||||
<div class="horizontal end">
|
||||
<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>
|
||||
</form>
|
|
@ -6962,6 +6962,7 @@ output {
|
|||
}
|
||||
|
||||
th, td {
|
||||
vertical-align: top;
|
||||
padding: 10px;
|
||||
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 = {})
|
||||
|
||||
# execute ticket transactions
|
||||
Observer::Ticket::Notification.transaction(params)
|
||||
# add attribute if execution is via web
|
||||
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
|
||||
|
|
|
@ -180,12 +180,12 @@ returns
|
|||
tickets.each { |ticket|
|
||||
|
||||
# send notification
|
||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
||||
Transaction::BackgroundJob.run(
|
||||
object: 'Ticket',
|
||||
type: 'reminder_reached',
|
||||
ticket_id: ticket.id,
|
||||
article_id: ticket.articles.last.id,
|
||||
type: 'reminder_reached',
|
||||
)
|
||||
bg.perform
|
||||
|
||||
result.push ticket
|
||||
}
|
||||
|
@ -220,23 +220,23 @@ returns
|
|||
|
||||
# send escalation
|
||||
if ticket.escalation_time < Time.zone.now
|
||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
||||
Transaction::BackgroundJob.run(
|
||||
object: 'Ticket',
|
||||
type: 'escalation',
|
||||
ticket_id: ticket.id,
|
||||
article_id: ticket.articles.last.id,
|
||||
type: 'escalation',
|
||||
)
|
||||
bg.perform
|
||||
result.push ticket
|
||||
next
|
||||
end
|
||||
|
||||
# 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,
|
||||
article_id: ticket.articles.last.id,
|
||||
type: 'escalation_warning',
|
||||
)
|
||||
bg.perform
|
||||
result.push ticket
|
||||
}
|
||||
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
|
||||
def initialize(params, via_web = false)
|
||||
class Transaction::Notification
|
||||
|
||||
=begin
|
||||
{
|
||||
object: 'Ticket',
|
||||
type: 'update',
|
||||
ticket_id: 123,
|
||||
via_web: true,
|
||||
changes: {
|
||||
'attribute1' => [before,now],
|
||||
'attribute2' => [before,now],
|
||||
'attribute1' => [before, now],
|
||||
'attribute2' => [before, now],
|
||||
}
|
||||
},
|
||||
=end
|
||||
@p = params
|
||||
@via_web = via_web
|
||||
|
||||
def initialize(item, params = {})
|
||||
@item = item
|
||||
@params = params
|
||||
end
|
||||
|
||||
def perform
|
||||
ticket = Ticket.find(@p[:ticket_id])
|
||||
if @p[:article_id]
|
||||
article = Ticket::Article.find(@p[:article_id])
|
||||
ticket = Ticket.find(@item[:ticket_id])
|
||||
if @item[:article_id]
|
||||
article = Ticket::Article.find(@item[:article_id])
|
||||
end
|
||||
|
||||
# find recipients
|
||||
|
@ -59,7 +64,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
|||
end
|
||||
already_checked_recipient_ids = {}
|
||||
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 already_checked_recipient_ids[result[:user].id]
|
||||
already_checked_recipient_ids[result[:user].id] = true
|
||||
|
@ -73,7 +78,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
|||
channels = item[:channels]
|
||||
|
||||
# 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 && ticket.updated_by_id == user.id
|
||||
end
|
||||
|
@ -83,10 +88,10 @@ class Observer::Ticket::Notification::BackgroundJob
|
|||
|
||||
# ignore if no changes has been done
|
||||
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
|
||||
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
|
||||
if !identifier || identifier == ''
|
||||
identifier = user.login
|
||||
|
@ -94,7 +99,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
|||
already_notified = false
|
||||
History.list('Ticket', ticket.id).each {|history|
|
||||
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['created_at'].today?
|
||||
already_notified = true
|
||||
|
@ -110,59 +115,59 @@ class Observer::Ticket::Notification::BackgroundJob
|
|||
created_by_id = ticket.updated_by_id || 1
|
||||
|
||||
# delete old notifications
|
||||
if @p[:type] == 'reminder_reached'
|
||||
if @item[:type] == 'reminder_reached'
|
||||
seen = false
|
||||
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
|
||||
created_by_id = 1
|
||||
OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation', user)
|
||||
OnlineNotification.remove_by_type('Ticket', ticket.id, 'escalation_warning', user)
|
||||
|
||||
# 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
|
||||
else
|
||||
seen = ticket.online_notification_seen_state(user.id)
|
||||
end
|
||||
|
||||
OnlineNotification.add(
|
||||
type: @p[:type],
|
||||
type: @item[:type],
|
||||
object: 'Ticket',
|
||||
o_id: ticket.id,
|
||||
seen: seen,
|
||||
created_by_id: created_by_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
|
||||
|
||||
# ignore email channel notificaiton and empty emails
|
||||
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
|
||||
end
|
||||
|
||||
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
|
||||
# if create, send create message / block update messages
|
||||
template = nil
|
||||
if @p[:type] == 'create'
|
||||
if @item[:type] == 'create'
|
||||
template = 'ticket_create'
|
||||
elsif @p[:type] == 'update'
|
||||
elsif @item[:type] == 'update'
|
||||
template = 'ticket_update'
|
||||
elsif @p[:type] == 'reminder_reached'
|
||||
elsif @item[:type] == 'reminder_reached'
|
||||
template = 'ticket_reminder_reached'
|
||||
elsif @p[:type] == 'escalation'
|
||||
elsif @item[:type] == 'escalation'
|
||||
template = 'ticket_escalation'
|
||||
elsif @p[:type] == 'escalation_warning'
|
||||
elsif @item[:type] == 'escalation_warning'
|
||||
template = 'ticket_escalation_warning'
|
||||
else
|
||||
raise "unknown type for notification #{@p[:type]}"
|
||||
raise "unknown type for notification #{@item[:type]}"
|
||||
end
|
||||
|
||||
NotificationFactory::Mailer.notification(
|
||||
|
@ -177,7 +182,7 @@ class Observer::Ticket::Notification::BackgroundJob
|
|||
references: ticket.get_references,
|
||||
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
|
||||
|
@ -200,14 +205,14 @@ class Observer::Ticket::Notification::BackgroundJob
|
|||
|
||||
def human_changes(user, record)
|
||||
|
||||
return {} if !@p[:changes]
|
||||
return {} if !@item[:changes]
|
||||
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 = {}
|
||||
@p[:changes].each {|key, value|
|
||||
@item[:changes].each {|key, value|
|
||||
|
||||
# if no config exists, use all attributes
|
||||
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>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -14,7 +14,7 @@ New Ticket (<%= d 'ticket.title' %>)
|
|||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -8,7 +8,7 @@ Ticket ist eskaliert (<%= d 'ticket.title' %>)
|
|||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -8,7 +8,7 @@ Ticket is escalated (<%= d 'ticket.title' %>)
|
|||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -8,7 +8,7 @@ Ticket wird eskalieren (<%= d 'ticket.title' %>)
|
|||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
Ticket will escalated (<%= d 'ticket.title' %>)
|
||||
Ticket will escalate (<%= d 'ticket.title' %>)
|
||||
|
||||
<p>Hi <%= d 'recipient.firstname' %>,</p>
|
||||
<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>
|
||||
<% if @objects[:article] %>
|
||||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -8,7 +8,7 @@ Warten auf Erinnerung erreicht! (<%= d 'ticket.title' %>)
|
|||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -2,13 +2,13 @@ Reminder reached (<%= d 'ticket.title' %>)
|
|||
|
||||
<p>Hi <%= d 'recipient.firstname' %>,</p>
|
||||
<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>
|
||||
<% if @objects[:article] %>
|
||||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -19,7 +19,7 @@ Ticket (<%= d 'ticket.title' %>) wurde von "<b><%= d 'ticket.updated_by.longname
|
|||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
@ -19,7 +19,7 @@ Ticket (<%= d 'ticket.title' %>) has been updated by "<b><%= d 'ticket.updated_b
|
|||
<p>
|
||||
<%= t 'Information' %>:
|
||||
<blockquote type="cite">
|
||||
<%= a 'article' %>
|
||||
<%= a_html 'article' %>
|
||||
</blockquote>
|
||||
</p>
|
||||
<% 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_twitter',
|
||||
'observer::_ticket::_article::_signature_detection',
|
||||
'observer::_ticket::_notification',
|
||||
'observer::_ticket::_reset_new_state',
|
||||
'observer::_ticket::_escalation_calculation',
|
||||
'observer::_ticket::_ref_object_touch',
|
||||
|
@ -42,7 +41,8 @@ module Zammad
|
|||
'observer::_user::_ticket_organization',
|
||||
'observer::_user::_geo',
|
||||
'observer::_organization::_ref_object_touch',
|
||||
'observer::_sla::_ticket_rebuild_escalation'
|
||||
'observer::_sla::_ticket_rebuild_escalation',
|
||||
'observer::_transaction'
|
||||
|
||||
# REST api path
|
||||
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(
|
||||
title: 'Icinga integration',
|
||||
name: 'icinga_integration',
|
||||
area: 'Integration::Icinga',
|
||||
area: 'Integration::Switch',
|
||||
description: 'Define if Icinga (http://www.icinga.org) is enabled or not.',
|
||||
options: {
|
||||
form: [
|
||||
|
@ -1632,6 +1632,7 @@ Setting.create_if_not_exists(
|
|||
null: false,
|
||||
name: 'icinga_sender',
|
||||
tag: 'input',
|
||||
placeholder: 'icinga@monitoring.example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1685,7 +1686,7 @@ Setting.create_if_not_exists(
|
|||
Setting.create_if_not_exists(
|
||||
title: 'Nagios integration',
|
||||
name: 'nagios_integration',
|
||||
area: 'Integration::Nagios',
|
||||
area: 'Integration::Switch',
|
||||
description: 'Define if Nagios (http://www.nagios.org) is enabled or not.',
|
||||
options: {
|
||||
form: [
|
||||
|
@ -1717,6 +1718,7 @@ Setting.create_if_not_exists(
|
|||
null: false,
|
||||
name: 'nagios_sender',
|
||||
tag: 'input',
|
||||
placeholder: 'nagios@monitoring.example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1767,6 +1769,59 @@ Setting.create_if_not_exists(
|
|||
preferences: { prio: 4 },
|
||||
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(
|
||||
id: 1,
|
||||
|
|
|
@ -5,8 +5,17 @@ module NotificationFactory
|
|||
result = NotificationFactory.template_read(
|
||||
template: 'password_reset',
|
||||
locale: 'en-us',
|
||||
format: 'html', # md
|
||||
type: 'mailer', # slack
|
||||
format: 'html',
|
||||
type: 'mailer',
|
||||
)
|
||||
|
||||
or
|
||||
|
||||
result = NotificationFactory.template_read(
|
||||
template: 'ticket_update',
|
||||
locale: 'en-us',
|
||||
format: 'md',
|
||||
type: 'slack',
|
||||
)
|
||||
|
||||
returns
|
||||
|
@ -56,8 +65,15 @@ returns
|
|||
=begin
|
||||
|
||||
string = NotificationFactory.application_template_read(
|
||||
format: 'html', # md
|
||||
type: 'mailer', # slack
|
||||
format: 'html',
|
||||
type: 'mailer',
|
||||
)
|
||||
|
||||
or
|
||||
|
||||
string = NotificationFactory.application_template_read(
|
||||
format: 'md',
|
||||
type: 'slack',
|
||||
)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
# d - data of object
|
||||
# d('user.firstname', htmlEscape)
|
||||
def d(key, escape = nil)
|
||||
|
||||
# do validaton, ignore some methodes
|
||||
|
@ -45,19 +47,25 @@ class NotificationFactory::Template
|
|||
h placeholder
|
||||
end
|
||||
|
||||
# c - config
|
||||
# c('fqdn', htmlEscape)
|
||||
def c(key, escape = nil)
|
||||
config = Setting.get(key)
|
||||
return config if escape == false || (escape.nil? && !@escape)
|
||||
h config
|
||||
end
|
||||
|
||||
# t - translation
|
||||
# t('yes', htmlEscape)
|
||||
def t(key, escape = nil)
|
||||
translation = Translation.translate(@locale, key)
|
||||
return translation if escape == false || (escape.nil? && !@escape)
|
||||
h translation
|
||||
end
|
||||
|
||||
def a(article)
|
||||
# a_html - article body in html
|
||||
# a_html(article)
|
||||
def a_html(article)
|
||||
content_type = d "#{article}.content_type", false
|
||||
if content_type =~ /html/
|
||||
return d "#{article}.body", false
|
||||
|
@ -65,6 +73,19 @@ class NotificationFactory::Template
|
|||
d("#{article}.body", false).text2html
|
||||
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)
|
||||
return key if !key
|
||||
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.save
|
||||
|
||||
list = EventBuffer.list('notification')
|
||||
list_objects = Observer::Ticket::Notification.get_uniq_changes(list)
|
||||
list = EventBuffer.list('transaction')
|
||||
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 - #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.save
|
||||
|
||||
list = EventBuffer.list('notification')
|
||||
list_objects = Observer::Ticket::Notification.get_uniq_changes(list)
|
||||
list = EventBuffer.list('transaction')
|
||||
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 - #2 - #3', list_objects[ticket1.id][:changes]['title'][1])
|
||||
|
@ -916,7 +916,7 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
|||
)
|
||||
assert(ticket1, 'ticket created - ticket notification template')
|
||||
|
||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
||||
bg = Transaction::Notification.new(
|
||||
ticket_id: ticket1.id,
|
||||
article_id: article.id,
|
||||
type: 'update',
|
||||
|
@ -992,7 +992,7 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
|||
assert_no_match(/pending_till/, result[:body])
|
||||
assert_no_match(/i18n/, result[:body])
|
||||
|
||||
bg = Observer::Ticket::Notification::BackgroundJob.new(
|
||||
bg = Transaction::Notification.new(
|
||||
ticket_id: ticket1.id,
|
||||
article_id: article.id,
|
||||
type: 'update',
|
||||
|
|
Loading…
Reference in a new issue