Initial idoit integration.
This commit is contained in:
parent
83d11b122e
commit
6da0181e2a
23 changed files with 934 additions and 129 deletions
|
@ -185,6 +185,17 @@ class App.Controller extends Spine.Controller
|
|||
formValidate: (data) ->
|
||||
App.ControllerForm.validate(data)
|
||||
|
||||
# get all query params of the url
|
||||
queryParam: ->
|
||||
return if !@query
|
||||
pairs = @query.split(';')
|
||||
params = {}
|
||||
for pair in pairs
|
||||
result = pair.match('(.+?)=(.*)')
|
||||
if result && result[1]
|
||||
params[result[1]] = result[2]
|
||||
params
|
||||
|
||||
# redirectToLogin: (data) ->
|
||||
#
|
||||
|
||||
|
|
|
@ -309,6 +309,23 @@ class App.ControllerConfirm extends App.ControllerModal
|
|||
if @callback
|
||||
@callback()
|
||||
|
||||
class App.ControllerErrorModal extends App.ControllerModal
|
||||
buttonClose: true
|
||||
buttonCancel: false
|
||||
buttonSubmit: 'Close'
|
||||
#buttonClass: 'btn--danger'
|
||||
head: 'Error'
|
||||
#small: true
|
||||
#shown: true
|
||||
|
||||
content: ->
|
||||
@message
|
||||
|
||||
onSubmit: =>
|
||||
@close()
|
||||
if @callback
|
||||
@callback()
|
||||
|
||||
class App.ControllerDrox extends App.Controller
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
class Index extends App.ControllerIntegrationBase
|
||||
featureIntegration: 'idoit_integration'
|
||||
featureName: 'i-doit'
|
||||
featureConfig: 'idoit_config'
|
||||
description: [
|
||||
['This service allows you to connect i-doit objects with Zammad.']
|
||||
]
|
||||
events:
|
||||
'change .js-switch input': 'switch'
|
||||
|
||||
render: =>
|
||||
super
|
||||
new Form(
|
||||
el: @$('.js-form')
|
||||
)
|
||||
|
||||
new App.HttpLog(
|
||||
el: @$('.js-log')
|
||||
facility: 'idoit'
|
||||
)
|
||||
|
||||
class Form extends App.Controller
|
||||
events:
|
||||
'submit form': 'update'
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
@render()
|
||||
|
||||
currentConfig: ->
|
||||
App.Setting.get('idoit_config')
|
||||
|
||||
setConfig: (value) ->
|
||||
App.Setting.set('idoit_config', value, {notify: true})
|
||||
|
||||
render: =>
|
||||
@config = @currentConfig()
|
||||
|
||||
@html App.view('integration/idoit')(
|
||||
config: @config
|
||||
)
|
||||
|
||||
update: (e) =>
|
||||
e.preventDefault()
|
||||
@config = @formParam(e.target)
|
||||
@validateAndSave()
|
||||
|
||||
validateAndSave: =>
|
||||
@ajax(
|
||||
id: 'idoit'
|
||||
type: 'POST'
|
||||
url: "#{@apiPath}/integration/idoit/verify"
|
||||
data: JSON.stringify(
|
||||
method: 'cmdb.object_types'
|
||||
api_token: @config.api_token
|
||||
endpoint: @config.endpoint
|
||||
client_id: @config.client_id
|
||||
)
|
||||
success: (data, status, xhr) =>
|
||||
if data.result is 'failed'
|
||||
new App.ControllerErrorModal(
|
||||
message: data.message
|
||||
container: @el.closest('.content')
|
||||
)
|
||||
return
|
||||
@setConfig(@config)
|
||||
|
||||
error: (data, status) =>
|
||||
|
||||
# do not close window if request is aborted
|
||||
return if status is 'abort'
|
||||
|
||||
details = data.responseJSON || {}
|
||||
@notify(
|
||||
type: 'error'
|
||||
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!')
|
||||
)
|
||||
)
|
||||
|
||||
class State
|
||||
@current: ->
|
||||
App.Setting.get('idoit_integration')
|
||||
|
||||
App.Config.set(
|
||||
'IntegrationIdoit'
|
||||
{
|
||||
name: 'i-doit'
|
||||
target: '#system/integration/idoit'
|
||||
description: 'CMDB to document complex relations of your network components.'
|
||||
controller: Index
|
||||
state: State
|
||||
}
|
||||
'NavBarIntegrations'
|
||||
)
|
|
@ -9,6 +9,7 @@ class App.TicketCreate extends App.Controller
|
|||
|
||||
constructor: (params) ->
|
||||
super
|
||||
@sidebarState = {}
|
||||
|
||||
# define default type
|
||||
@default_type = 'phone-in'
|
||||
|
@ -91,6 +92,8 @@ class App.TicketCreate extends App.Controller
|
|||
else
|
||||
@$('[name="cc"]').closest('.form-group').addClass('hide')
|
||||
|
||||
App.TaskManager.touch(@task_key)
|
||||
|
||||
meta: =>
|
||||
text = ''
|
||||
if @articleAttributes
|
||||
|
@ -335,25 +338,42 @@ class App.TicketCreate extends App.Controller
|
|||
user: App.Session.get()
|
||||
)
|
||||
|
||||
new Sidebar(
|
||||
$('#tags').tokenfield()
|
||||
|
||||
@sidebarWidget = new App.TicketCreateSidebar(
|
||||
el: @sidebar
|
||||
params: @formDefault
|
||||
textModule: @textModule
|
||||
sidebarState: @sidebarState
|
||||
task_key: @task_key
|
||||
query: @query
|
||||
)
|
||||
|
||||
$('#tags').tokenfield()
|
||||
if @formDefault.customer_id
|
||||
callback = (customer) =>
|
||||
@localUserInfoCallback(@formDefault, customer)
|
||||
App.User.full(@formDefault.customer_id, callback)
|
||||
|
||||
# update taskbar with new meta data
|
||||
App.TaskManager.touch(@task_key)
|
||||
|
||||
localUserInfo: (e) =>
|
||||
|
||||
return if !@sidebarWidget
|
||||
params = App.ControllerForm.params($(e.target).closest('form'))
|
||||
|
||||
new Sidebar(
|
||||
el: @sidebar
|
||||
params: params
|
||||
textModule: @textModule
|
||||
if params.customer_id
|
||||
callback = (customer) =>
|
||||
@localUserInfoCallback(params, customer)
|
||||
App.User.full(params.customer_id, callback)
|
||||
return
|
||||
@localUserInfoCallback(params)
|
||||
|
||||
localUserInfoCallback: (params, customer = {}) =>
|
||||
@sidebarWidget.render(params)
|
||||
@textModule.reload(
|
||||
config: App.Config.all()
|
||||
user: App.Session.get()
|
||||
ticket:
|
||||
customer: customer
|
||||
)
|
||||
|
||||
cancel: (e) ->
|
||||
|
@ -478,6 +498,10 @@ class App.TicketCreate extends App.Controller
|
|||
# scroll to top
|
||||
ui.scrollTo()
|
||||
|
||||
# add sidebar params
|
||||
if ui.sidebarWidget
|
||||
ui.sidebarWidget.commit(ticket_id: @id)
|
||||
|
||||
# access to group
|
||||
for group_id, access of App.Session.get('group_ids')
|
||||
if @group_id.toString() is group_id.toString()
|
||||
|
@ -498,114 +522,6 @@ class App.TicketCreate extends App.Controller
|
|||
)
|
||||
)
|
||||
|
||||
class Sidebar extends App.Controller
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
# load user
|
||||
if @params['customer_id']
|
||||
App.User.full(@params['customer_id'], @render)
|
||||
return
|
||||
|
||||
# render ui
|
||||
@render()
|
||||
|
||||
render: (user) =>
|
||||
|
||||
items = []
|
||||
if user
|
||||
|
||||
showCustomer = (el) =>
|
||||
# update text module UI
|
||||
if @textModule
|
||||
@textModule.reload(
|
||||
ticket:
|
||||
customer: user
|
||||
user: App.Session.get()
|
||||
)
|
||||
|
||||
new App.WidgetUser(
|
||||
el: el
|
||||
user_id: user.id
|
||||
)
|
||||
|
||||
editCustomer = (e, el) =>
|
||||
new App.ControllerGenericEdit(
|
||||
id: @params.customer_id
|
||||
genericObject: 'User'
|
||||
screen: 'edit'
|
||||
pageData:
|
||||
title: 'Users'
|
||||
object: 'User'
|
||||
objects: 'Users'
|
||||
container: @el.closest('.content')
|
||||
)
|
||||
items.push {
|
||||
head: 'Customer'
|
||||
name: 'customer'
|
||||
icon: 'person'
|
||||
actions: [
|
||||
{
|
||||
title: 'Edit Customer'
|
||||
name: 'Edit Customer'
|
||||
class: 'glyphicon glyphicon-edit'
|
||||
callback: editCustomer
|
||||
},
|
||||
]
|
||||
callback: showCustomer
|
||||
}
|
||||
|
||||
if user.organization_id
|
||||
editOrganization = (e, el) =>
|
||||
new App.ControllerGenericEdit(
|
||||
id: user.organization_id
|
||||
genericObject: 'Organization'
|
||||
pageData:
|
||||
title: 'Organizations'
|
||||
object: 'Organization'
|
||||
objects: 'Organizations'
|
||||
container: @el.closest('.content')
|
||||
)
|
||||
showOrganization = (el) ->
|
||||
new App.WidgetOrganization(
|
||||
el: el
|
||||
organization_id: user.organization_id
|
||||
)
|
||||
items.push {
|
||||
head: 'Organization'
|
||||
name: 'organization'
|
||||
icon: 'group'
|
||||
actions: [
|
||||
{
|
||||
title: 'Edit Organization'
|
||||
name: 'Edit Organization'
|
||||
class: 'glyphicon glyphicon-edit'
|
||||
callback: editOrganization
|
||||
},
|
||||
]
|
||||
callback: showOrganization
|
||||
}
|
||||
|
||||
showTemplates = (el) ->
|
||||
|
||||
# show template UI
|
||||
new App.WidgetTemplate(
|
||||
el: el
|
||||
#template_id: template['id']
|
||||
)
|
||||
|
||||
items.push {
|
||||
head: 'Templates'
|
||||
name: 'template'
|
||||
icon: 'templates'
|
||||
callback: showTemplates
|
||||
}
|
||||
|
||||
new App.Sidebar(
|
||||
el: @el
|
||||
items: items
|
||||
)
|
||||
|
||||
class Router extends App.ControllerPermanent
|
||||
requiredPermission: 'ticket.agent'
|
||||
constructor: (params) ->
|
||||
|
@ -621,6 +537,9 @@ class Router extends App.ControllerPermanent
|
|||
if params.customer_id
|
||||
split = "/customer/#{params.customer_id}"
|
||||
|
||||
if params.query
|
||||
split = "/query/#{params.query}"
|
||||
|
||||
id = Math.floor( Math.random() * 99999 )
|
||||
@navigate "#ticket/create/id/#{id}#{split}"
|
||||
return
|
||||
|
@ -631,6 +550,7 @@ class Router extends App.ControllerPermanent
|
|||
article_id: params.article_id
|
||||
type: params.type
|
||||
customer_id: params.customer_id
|
||||
query: params.query
|
||||
id: params.id
|
||||
|
||||
App.TaskManager.execute(
|
||||
|
@ -646,6 +566,7 @@ App.Config.set('ticket/create/', Router, 'Routes')
|
|||
App.Config.set('ticket/create/id/:id', Router, 'Routes')
|
||||
App.Config.set('ticket/create/customer/:customer_id', Router, 'Routes')
|
||||
App.Config.set('ticket/create/id/:id/customer/:customer_id', Router, 'Routes')
|
||||
App.Config.set('ticket/create/id/:id/query/:query', Router, 'Routes')
|
||||
|
||||
# split ticket
|
||||
App.Config.set('ticket/create/:ticket_id/:article_id', Router, 'Routes')
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
class App.TicketCreateSidebar extends App.Controller
|
||||
constructor: ->
|
||||
super
|
||||
@render()
|
||||
|
||||
reload: (args) =>
|
||||
for key, backend of @sidebarBackends
|
||||
if backend && backend.reload
|
||||
backend.reload(args)
|
||||
|
||||
commit: (args) =>
|
||||
for key, backend of @sidebarBackends
|
||||
if backend && backend.commit
|
||||
backend.commit(args)
|
||||
|
||||
render: (params) =>
|
||||
if params
|
||||
@params = params
|
||||
@sidebarBackends ||= {}
|
||||
@sidebarItems = []
|
||||
sidebarBackends = App.Config.get('TicketCreateSidebar')
|
||||
keys = _.keys(sidebarBackends).sort()
|
||||
for key in keys
|
||||
if !@sidebarBackends[key] || !@sidebarBackends[key].reload
|
||||
@sidebarBackends[key] = new sidebarBackends[key](
|
||||
params: @params
|
||||
query: @query
|
||||
taskGet: @taskGet
|
||||
)
|
||||
else
|
||||
@sidebarBackends[key].reload(
|
||||
params: @params
|
||||
query: @query
|
||||
)
|
||||
item = @sidebarBackends[key].sidebarItem()
|
||||
if item
|
||||
@sidebarItems.push item
|
||||
|
||||
new App.Sidebar(
|
||||
el: @el
|
||||
sidebarState: @sidebarState
|
||||
items: @sidebarItems
|
||||
)
|
|
@ -0,0 +1,38 @@
|
|||
class SidebarCustomer extends App.Controller
|
||||
sidebarItem: =>
|
||||
return if !@permissionCheck('ticket.agent')
|
||||
return if !@params.customer_id
|
||||
{
|
||||
head: 'Customer'
|
||||
name: 'customer'
|
||||
icon: 'person'
|
||||
actions: [
|
||||
{
|
||||
title: 'Edit Customer'
|
||||
name: 'customer-edit'
|
||||
callback: @editCustomer
|
||||
},
|
||||
]
|
||||
callback: @showCustomer
|
||||
}
|
||||
|
||||
showCustomer: (el) =>
|
||||
@el = el
|
||||
new App.WidgetUser(
|
||||
el: @el
|
||||
user_id: @params.customer_id
|
||||
)
|
||||
|
||||
editCustomer: =>
|
||||
new App.ControllerGenericEdit(
|
||||
id: @params.customer_id
|
||||
genericObject: 'User'
|
||||
screen: 'edit'
|
||||
pageData:
|
||||
title: 'Users'
|
||||
object: 'User'
|
||||
objects: 'Users'
|
||||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
App.Config.set('200-Customer', SidebarCustomer, 'TicketCreateSidebar')
|
|
@ -0,0 +1,41 @@
|
|||
class SidebarOrganization extends App.Controller
|
||||
sidebarItem: =>
|
||||
return if !@permissionCheck('ticket.agent')
|
||||
return if !@params.customer_id
|
||||
return if !App.User.exists(@params.customer_id)
|
||||
customer = App.User.find(@params.customer_id)
|
||||
@organization_id = customer.organization_id
|
||||
return if !@organization_id
|
||||
{
|
||||
head: 'Organization'
|
||||
name: 'organization'
|
||||
icon: 'group'
|
||||
actions: [
|
||||
{
|
||||
title: 'Edit Organization'
|
||||
name: 'organization-edit'
|
||||
callback: @editOrganization
|
||||
},
|
||||
]
|
||||
callback: @showOrganization
|
||||
}
|
||||
|
||||
showOrganization: (el) =>
|
||||
@el = el
|
||||
new App.WidgetOrganization(
|
||||
el: @el
|
||||
organization_id: @organization_id
|
||||
)
|
||||
|
||||
editOrganization: =>
|
||||
new App.ControllerGenericEdit(
|
||||
id: @organization_id,
|
||||
genericObject: 'Organization'
|
||||
pageData:
|
||||
title: 'Organizations'
|
||||
object: 'Organization'
|
||||
objects: 'Organizations'
|
||||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
App.Config.set('300-Organization', SidebarOrganization, 'TicketCreateSidebar')
|
|
@ -0,0 +1,21 @@
|
|||
class SidebarTemplate extends App.Controller
|
||||
sidebarItem: =>
|
||||
return if !@permissionCheck('ticket.agent')
|
||||
{
|
||||
head: 'Templates'
|
||||
name: 'template'
|
||||
icon: 'templates'
|
||||
actions: []
|
||||
callback: @showTemplates
|
||||
}
|
||||
|
||||
showTemplates: (el) =>
|
||||
@el = el
|
||||
|
||||
# show template UI
|
||||
new App.WidgetTemplate(
|
||||
el: el
|
||||
#template_id: template['id']
|
||||
)
|
||||
|
||||
App.Config.set('100-Template', SidebarTemplate, 'TicketCreateSidebar')
|
|
@ -0,0 +1,100 @@
|
|||
class App.IdoitObjectSelector extends App.ControllerModal
|
||||
buttonClose: true
|
||||
buttonCancel: true
|
||||
buttonSubmit: true
|
||||
head: 'i-doit'
|
||||
|
||||
content: ->
|
||||
@ajax(
|
||||
id: 'idoit-object-selector'
|
||||
type: 'POST'
|
||||
url: "#{@apiPath}/integration/idoit"
|
||||
data: JSON.stringify(method: 'cmdb.object_types')
|
||||
success: (data, status, xhr) =>
|
||||
if data.result is 'failed'
|
||||
@contentInline = data.message
|
||||
@render()
|
||||
return
|
||||
|
||||
result = _.sortBy(data.response.result, 'title')
|
||||
@contentInline = $(App.view('integration/idoit_object_selector')())
|
||||
|
||||
@contentInline.find('.js-typeSelect').html(@renderTypeSelector(result))
|
||||
|
||||
@contentInline.filter('.js-search').on('change', 'select, input', (e) =>
|
||||
params = @formParam(e.target)
|
||||
@search(params)
|
||||
)
|
||||
@contentInline.filter('.js-search').on('keyup', 'input', (e) =>
|
||||
params = @formParam(e.target)
|
||||
@search(params)
|
||||
)
|
||||
@render()
|
||||
@$('.js-input').focus()
|
||||
|
||||
error: (xhr, status, error) =>
|
||||
|
||||
# do not close window if request is aborted
|
||||
return if status is 'abort'
|
||||
|
||||
# show error message
|
||||
@contentInline = 'Unable to load content'
|
||||
@render()
|
||||
)
|
||||
''
|
||||
|
||||
search: (filter) =>
|
||||
if _.isEmpty(filter.title)
|
||||
delete filter.title
|
||||
else
|
||||
filter.title = "%#{filter.title}%"
|
||||
@ajax(
|
||||
id: 'idoit-object-selector'
|
||||
type: 'POST'
|
||||
url: "#{@apiPath}/integration/idoit"
|
||||
data: JSON.stringify(method: 'cmdb.objects', filter: filter)
|
||||
success: (data, status, xhr) =>
|
||||
@renderResult(data.response.result)
|
||||
|
||||
error: (xhr, status, error) =>
|
||||
|
||||
# do not close window if request is aborted
|
||||
return if status is 'abort'
|
||||
|
||||
# show error message
|
||||
@contentInline = 'Unable to load content'
|
||||
@render()
|
||||
)
|
||||
|
||||
renderResult: (items) =>
|
||||
table = App.view('integration/idoit_object_result')(
|
||||
items: items
|
||||
)
|
||||
@el.find('.js-result').html(table)
|
||||
|
||||
renderTypeSelector: (result) ->
|
||||
options = {}
|
||||
for item in result
|
||||
options[item.id] = item.title
|
||||
return App.UiElement.searchable_select.render(
|
||||
name: 'type'
|
||||
multiple: false
|
||||
limit: 100
|
||||
null: false
|
||||
nulloption: false
|
||||
options: options
|
||||
)
|
||||
|
||||
onSubmit: (e) =>
|
||||
form = @el.find('.js-result')
|
||||
params = @formParam(form)
|
||||
return if _.isEmpty(params.object_id)
|
||||
|
||||
if _.isArray(params.object_id)
|
||||
object_ids = params.object_id
|
||||
else
|
||||
object_ids = [params.object_id]
|
||||
|
||||
@formDisable(form)
|
||||
@callback(object_ids, @)
|
||||
|
|
@ -473,6 +473,7 @@ class App.TicketZoom extends App.Controller
|
|||
sidebarState: @sidebarState
|
||||
object_id: @ticket_id
|
||||
model: 'Ticket'
|
||||
query: @query
|
||||
taskGet: @taskGet
|
||||
task_key: @task_key
|
||||
formMeta: @formMeta
|
||||
|
@ -797,6 +798,9 @@ class App.TicketZoom extends App.Controller
|
|||
# reset form after save
|
||||
@reset()
|
||||
|
||||
if @sidebar
|
||||
@sidebar.commit()
|
||||
|
||||
if taskAction is 'closeNextInOverview'
|
||||
if @overview_id
|
||||
current_position = 0
|
||||
|
|
|
@ -9,18 +9,32 @@ class App.TicketZoomSidebar extends App.ObserverController
|
|||
if backend && backend.reload
|
||||
backend.reload(args)
|
||||
|
||||
commit: (args) =>
|
||||
for key, backend of @sidebarBackends
|
||||
if backend && backend.commit
|
||||
backend.commit(args)
|
||||
|
||||
render: (ticket) =>
|
||||
@sidebarBackends = {}
|
||||
@sidebarBackends ||= {}
|
||||
@sidebarItems = []
|
||||
sidebarBackends = App.Config.get('TicketZoomSidebar')
|
||||
keys = _.keys(sidebarBackends).sort()
|
||||
for key in keys
|
||||
if !@sidebarBackends[key] || !@sidebarBackends[key].reload
|
||||
@sidebarBackends[key] = new sidebarBackends[key](
|
||||
ticket: ticket
|
||||
query: @query
|
||||
taskGet: @taskGet
|
||||
formMeta: @formMeta
|
||||
markForm: @markForm
|
||||
)
|
||||
else
|
||||
@sidebarBackends[key].reload(
|
||||
params: @params
|
||||
query: @query
|
||||
formMeta: @formMeta
|
||||
markForm: @markForm
|
||||
)
|
||||
item = @sidebarBackends[key].sidebarItem()
|
||||
if item
|
||||
@sidebarItems.push item
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
class SidebarIdoit extends App.Controller
|
||||
sidebarItem: =>
|
||||
return if !@Config.get('idoit_integration')
|
||||
{
|
||||
head: 'i-doit'
|
||||
name: 'idoit'
|
||||
icon: 'printer'
|
||||
actions: [
|
||||
{
|
||||
title: 'Change Objects'
|
||||
name: 'objects-change'
|
||||
callback: @changeObjects
|
||||
},
|
||||
]
|
||||
callback: @showObjects
|
||||
}
|
||||
|
||||
changeObjects: =>
|
||||
new App.IdoitObjectSelector(
|
||||
task_key: @task_key
|
||||
container: @el.closest('.content')
|
||||
callback: (objectIds, objectSelectorUi) =>
|
||||
if @ticket && @ticket.id
|
||||
@updateTicket(@ticket.id, objectIds, =>
|
||||
objectSelectorUi.close()
|
||||
@showObjectsContent(objectIds)
|
||||
)
|
||||
return
|
||||
objectSelectorUi.close()
|
||||
@showObjectsContent(objectIds)
|
||||
)
|
||||
|
||||
showObjects: (el) =>
|
||||
@el = el
|
||||
|
||||
# show placeholder
|
||||
@objectIds ||= []
|
||||
if @ticket && @ticket.preferences && @ticket.preferences.idoit && @ticket.preferences.idoit.object_ids
|
||||
@objectIds = @ticket.preferences.idoit.object_ids
|
||||
queryParams = @queryParam()
|
||||
if queryParams && queryParams.idoit_object_ids
|
||||
@objectIds.push queryParams.idoit_object_ids
|
||||
@showObjectsContent()
|
||||
|
||||
showObjectsContent: (objectIds) =>
|
||||
if objectIds
|
||||
@objectIds = @objectIds.concat(objectIds)
|
||||
|
||||
# show placeholder
|
||||
if _.isEmpty(@objectIds)
|
||||
@html("<div>#{App.i18n.translateInline('none')}</div>")
|
||||
return
|
||||
|
||||
# ajax call to show items
|
||||
@ajax(
|
||||
id: "idoit-#{@task_key}"
|
||||
type: 'POST'
|
||||
url: "#{@apiPath}/integration/idoit"
|
||||
data: JSON.stringify(method: 'cmdb.objects', filter: ids: @objectIds)
|
||||
success: (data, status, xhr) =>
|
||||
if data.response
|
||||
@showList(data.response.result)
|
||||
return
|
||||
@showError('Unable to load data...')
|
||||
|
||||
error: (xhr, status, error) =>
|
||||
|
||||
# do not close window if request is aborted
|
||||
return if status is 'abort'
|
||||
|
||||
# show error message
|
||||
@showError('Unable to load data...')
|
||||
)
|
||||
|
||||
showList: (objects) =>
|
||||
list = $(App.view('ticket_zoom/sidebar_idoit')(
|
||||
objects: objects
|
||||
))
|
||||
list.delegate('.js-delete', 'click', (e) =>
|
||||
e.preventDefault()
|
||||
objectId = $(e.currentTarget).attr 'data-object-id'
|
||||
@delete(objectId)
|
||||
)
|
||||
@html(list)
|
||||
|
||||
showError: (message) =>
|
||||
@html App.i18n.translateInline(message)
|
||||
|
||||
delete: (objectId) =>
|
||||
localObjects = []
|
||||
for localObjectId in @objectIds
|
||||
if objectId.toString() isnt localObjectId.toString()
|
||||
localObjects.push localObjectId
|
||||
@objectIds = localObjects
|
||||
if @ticket && @ticket.id
|
||||
@updateTicket(@ticket.id, @objectIds)
|
||||
@showObjectsContent()
|
||||
|
||||
commit: (args) =>
|
||||
return if @ticket && @ticket.id
|
||||
return if !@objectIds
|
||||
return if _.isEmpty(@objectIds)
|
||||
return if !args
|
||||
return if !args.ticket_id
|
||||
@updateTicket(args.ticket_id, @objectIds)
|
||||
|
||||
updateTicket: (ticket_id, objectIds, callback) =>
|
||||
App.Ajax.request(
|
||||
id: "idoit-update-#{ticket_id}"
|
||||
type: 'POST'
|
||||
url: "#{@apiPath}/integration/idoit_ticket_update"
|
||||
data: JSON.stringify(ticket_id: ticket_id, object_ids: objectIds)
|
||||
success: (data, status, xhr) ->
|
||||
if callback
|
||||
callback(objectIds)
|
||||
|
||||
error: (xhr, status, details) =>
|
||||
|
||||
# do not close window if request is aborted
|
||||
return if status is 'abort'
|
||||
|
||||
# show error message
|
||||
@log 'errors', details
|
||||
@notify(
|
||||
type: 'error'
|
||||
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
|
||||
timeout: 6000
|
||||
)
|
||||
)
|
||||
|
||||
App.Config.set('500-Idoit', SidebarIdoit, 'TicketCreateSidebar')
|
||||
App.Config.set('500-Idoit', SidebarIdoit, 'TicketZoomSidebar')
|
25
app/assets/javascripts/app/views/integration/idoit.jst.eco
Normal file
25
app/assets/javascripts/app/views/integration/idoit.jst.eco
Normal file
|
@ -0,0 +1,25 @@
|
|||
<form>
|
||||
<h2><%- @T('Settings') %></h2>
|
||||
<div class="settings-entry">
|
||||
<table class="settings-list" style="width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="20%"><%- @T('Name') %>
|
||||
<th width="80%"><%- @T('Value') %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="settings-list-row-control"><%- @T('API token') %> *
|
||||
<td class="settings-list-control-cell"><input type="text" class="form-control form-control--small" value="<%= @config.api_token %>" name="api_token">
|
||||
<tr>
|
||||
<td class="settings-list-row-control"><%- @T('Endpoint') %> *
|
||||
<td class="settings-list-control-cell"><input type="text" class="form-control form-control--small" value="<%= @config.endpoint %>" placeholder="https://idoit.example.com/i-doit/" name="endpoint">
|
||||
<tr>
|
||||
<td class="settings-list-row-control"><%- @T('Client ID') %>
|
||||
<td class="settings-list-control-cell"><input type="text" class="form-control form-control--small" value="<%= @config.client_id %>" name="client_id">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
|
||||
</form>
|
|
@ -0,0 +1,25 @@
|
|||
<hr>
|
||||
<% if _.isEmpty(@items): %>
|
||||
<%- @T('none') %>
|
||||
<% else: %>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th style="width: 30px"></th>
|
||||
<th style="width: 50px"><%- @T('ID') %></th>
|
||||
<th><%- @T('Name') %></th>
|
||||
<th><%- @T('Status') %></th>
|
||||
<th><%- @T('Link') %></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% for item in @items: %>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="object_id" value="<%= item.id %>"/></td>
|
||||
<td><%= item.id %></td>
|
||||
<td><%= item.title %></td>
|
||||
<td><%= item.cmdb_status_title %></td>
|
||||
<td><a href="<%- item.link %>" target="_blank">i-doit</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
|
@ -0,0 +1,5 @@
|
|||
<form class="js-search flex horizontal">
|
||||
<div class="controls controls--select js-typeSelect"></div>
|
||||
<input type="text" name="title" value="" autocomplete="off" placeholder="<%- @Ti('Search') %>" class="form-control"/>
|
||||
</form>
|
||||
<form class="js-result"></form>
|
|
@ -0,0 +1,13 @@
|
|||
<% for object in @objects: %>
|
||||
<div class="sidebar-block">
|
||||
<label class="horizontal">
|
||||
<%- @T(object.title) %>
|
||||
<div class="list-item-delete js-delete" data-object-id="<%= object.id %>" data-type="remove">
|
||||
<%- @Icon('diagonal-cross') %>
|
||||
</div>
|
||||
</label>
|
||||
<%- @T('ID') %>: <a href="<%- object.link %>" target="_blank"><%= object.id %><br></a>
|
||||
<%- @T('Status') %>: <%= object.cmdb_status_title %><br>
|
||||
<%- @T('Type') %>: <%= object.type_title %><br>
|
||||
</div>
|
||||
<% end %>
|
51
app/controllers/integration/idoit_controller.rb
Normal file
51
app/controllers/integration/idoit_controller.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
class Integration::IdoitController < ApplicationController
|
||||
prepend_before_action -> { authentication_check(permission: ['agent.integration.idoit', 'admin.integration.idoit']) }, except: [:verify]
|
||||
prepend_before_action -> { authentication_check(permission: ['admin.integration.idoit']) }, only: [:verify]
|
||||
|
||||
def verify
|
||||
response = ::Idoit.verify(params[:api_token], params[:endpoint], params[:client_id])
|
||||
render json: {
|
||||
result: 'ok',
|
||||
response: response,
|
||||
}
|
||||
rescue => e
|
||||
logger.error e
|
||||
|
||||
render json: {
|
||||
result: 'failed',
|
||||
message: e.message,
|
||||
}
|
||||
end
|
||||
|
||||
def query
|
||||
response = ::Idoit.query(params[:method], params[:filter])
|
||||
render json: {
|
||||
result: 'ok',
|
||||
response: response,
|
||||
}
|
||||
rescue => e
|
||||
logger.error e
|
||||
|
||||
render json: {
|
||||
result: 'failed',
|
||||
message: e.message,
|
||||
}
|
||||
end
|
||||
|
||||
def update
|
||||
params[:object_ids] ||= []
|
||||
ticket = Ticket.find(params[:ticket_id])
|
||||
access!(ticket, 'read')
|
||||
ticket.preferences[:idoit] ||= {}
|
||||
ticket.preferences[:idoit][:object_ids] ||= []
|
||||
ticket.preferences[:idoit][:object_ids].concat(params[:object_ids])
|
||||
ticket.save!
|
||||
|
||||
render json: {
|
||||
result: 'ok',
|
||||
}
|
||||
end
|
||||
|
||||
end
|
9
config/routes/integration_idoit.rb
Normal file
9
config/routes/integration_idoit.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
Zammad::Application.routes.draw do
|
||||
api_path = Rails.configuration.api_path
|
||||
|
||||
match api_path + '/integration/idoit', to: 'integration/idoit#query', via: :post
|
||||
match api_path + '/integration/idoit', to: 'integration/idoit#query', via: :get
|
||||
match api_path + '/integration/idoit/verify', to: 'integration/idoit#verify', via: :post
|
||||
match api_path + '/integration/idoit_ticket_update', to: 'integration/idoit#update', via: :post
|
||||
|
||||
end
|
|
@ -8,3 +8,4 @@ rm app/assets/javascripts/app/controllers/karma.coffee
|
|||
rm app/assets/javascripts/app/controllers/report.coffee
|
||||
rm app/assets/javascripts/app/controllers/report_profile.coffee
|
||||
rm app/assets/javascripts/app/controllers/_integration/check_mk.coffee
|
||||
rm app/assets/javascripts/app/controllers/_integration/idoit.coffee
|
||||
|
|
49
db/migrate/20170816000001_idoit_support.rb
Normal file
49
db/migrate/20170816000001_idoit_support.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
class IdoitSupport < ActiveRecord::Migration
|
||||
def up
|
||||
|
||||
# return if it's a new setup
|
||||
return if !Setting.find_by(name: 'system_init_done')
|
||||
|
||||
Setting.create_if_not_exists(
|
||||
title: 'i-doit integration',
|
||||
name: 'idoit_integration',
|
||||
area: 'Integration::Switch',
|
||||
description: 'Defines if i-doit (http://www.i-doit) is enabled or not.',
|
||||
options: {
|
||||
form: [
|
||||
{
|
||||
display: '',
|
||||
null: true,
|
||||
name: 'idoit_integration',
|
||||
tag: 'boolean',
|
||||
options: {
|
||||
true => 'yes',
|
||||
false => 'no',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
state: false,
|
||||
preferences: {
|
||||
prio: 1,
|
||||
authentication: true,
|
||||
permission: ['admin.integration'],
|
||||
},
|
||||
frontend: true
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'i-doit config',
|
||||
name: 'idoit_config',
|
||||
area: 'Integration::Idoit',
|
||||
description: 'Defines the i-doit config.',
|
||||
options: {},
|
||||
state: {},
|
||||
preferences: {
|
||||
prio: 2,
|
||||
permission: ['admin.integration'],
|
||||
},
|
||||
frontend: false,
|
||||
)
|
||||
end
|
||||
|
||||
end
|
|
@ -3038,6 +3038,46 @@ Setting.create_if_not_exists(
|
|||
},
|
||||
frontend: false,
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'i-doit integration',
|
||||
name: 'idoit_integration',
|
||||
area: 'Integration::Switch',
|
||||
description: 'Defines if i-doit (http://www.i-doit) is enabled or not.',
|
||||
options: {
|
||||
form: [
|
||||
{
|
||||
display: '',
|
||||
null: true,
|
||||
name: 'idoit_integration',
|
||||
tag: 'boolean',
|
||||
options: {
|
||||
true => 'yes',
|
||||
false => 'no',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
state: false,
|
||||
preferences: {
|
||||
prio: 1,
|
||||
authentication: true,
|
||||
permission: ['admin.integration'],
|
||||
},
|
||||
frontend: true
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'i-doit config',
|
||||
name: 'idoit_config',
|
||||
area: 'Integration::Idoit',
|
||||
description: 'Defines the i-doit config.',
|
||||
options: {},
|
||||
state: {},
|
||||
preferences: {
|
||||
prio: 2,
|
||||
permission: ['admin.integration'],
|
||||
},
|
||||
frontend: false,
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Defines sync transaction backend.',
|
||||
name: '0100_trigger',
|
||||
|
|
149
lib/idoit.rb
Normal file
149
lib/idoit.rb
Normal file
|
@ -0,0 +1,149 @@
|
|||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
class Idoit
|
||||
|
||||
=begin
|
||||
|
||||
get list ob types
|
||||
|
||||
result = Idoit.verify(api_token, endpoint, client_id)
|
||||
|
||||
returns
|
||||
|
||||
array with cmdb.object_types or an exeption if no data was able to retrive
|
||||
|
||||
=end
|
||||
|
||||
def self.verify(api_token, endpoint, _client_id = nil)
|
||||
raise 'api_token required' if api_token.blank?
|
||||
raise 'endpoint required' if endpoint.blank?
|
||||
|
||||
params = {
|
||||
apikey: api_token,
|
||||
}
|
||||
|
||||
_query('cmdb.object_types', params, _url_cleanup(endpoint))
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
get list ob types
|
||||
|
||||
result = Idoit.query(method, filter)
|
||||
|
||||
result = Idoit.query(method, { type: '59' })
|
||||
|
||||
returns
|
||||
|
||||
result = [
|
||||
{
|
||||
"id": "1",
|
||||
"title": "System service",
|
||||
"container": "0",
|
||||
"const": "C__OBJTYPE__SERVICE",
|
||||
"color": "987384",
|
||||
"image": "https://demo.panic.at/i-doit/images/objecttypes/service.jpg",
|
||||
"icon": "images/icons/silk/application_osx_terminal.png",
|
||||
"cats": "4",
|
||||
"tree_group": "1",
|
||||
"status": "2",
|
||||
"type_group": "1",
|
||||
"type_group_title": "Software"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"title": "Application",
|
||||
"container": "0",
|
||||
"const": "C__OBJTYPE__APPLICATION",
|
||||
"color": "E4B9D7",
|
||||
"image": "https://demo.panic.at/i-doit/images/objecttypes/application.jpg",
|
||||
"icon": "images/icons/silk/application_xp.png",
|
||||
"cats": "20",
|
||||
"tree_group": "1",
|
||||
"status": "2",
|
||||
"type_group": "1",
|
||||
"type_group_title": "Software"
|
||||
},
|
||||
]
|
||||
|
||||
or with filter:
|
||||
|
||||
"result": [
|
||||
{
|
||||
"id": "26",
|
||||
"title": "demo.panic.at",
|
||||
"sysid": "SYSID_1485512390",
|
||||
"type": "59",
|
||||
"created": "2017-01-27 11:19:24",
|
||||
"updated": "2017-01-27 11:19:49",
|
||||
"type_title": "Virtual server",
|
||||
"type_group_title": "Infrastructure",
|
||||
"status": "2",
|
||||
"cmdb_status": "6",
|
||||
"cmdb_status_title": "in operation",
|
||||
"image": "https://demo.panic.at/i-doit/images/objecttypes/empty.png"
|
||||
},
|
||||
],
|
||||
|
||||
=end
|
||||
|
||||
def self.query(method, filter = {})
|
||||
setting = Setting.get('idoit_config')
|
||||
raise 'Unable for find api_token in config' if setting[:api_token].blank?
|
||||
raise 'Unable for find endpoint in config' if setting[:endpoint].blank?
|
||||
|
||||
#translator_key = Setting.get('translator_key')
|
||||
params = {
|
||||
apikey: setting[:api_token],
|
||||
}
|
||||
if filter.present?
|
||||
params[:filter] = filter
|
||||
end
|
||||
_query(method, params, _url_cleanup(setting[:endpoint]))
|
||||
end
|
||||
|
||||
def self._query(method, params, url)
|
||||
result = UserAgent.post(
|
||||
url,
|
||||
{
|
||||
method: method,
|
||||
params: params,
|
||||
version: '2.0',
|
||||
},
|
||||
{
|
||||
json: true,
|
||||
open_timeout: 6,
|
||||
read_timeout: 16,
|
||||
log: {
|
||||
facility: 'idoit',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
raise "Can't fetch objects from #{url}: Unable to parse response from server. Invalid JSON response." if !result.success? && result.error =~ /JSON::ParserError:.+?\s+unexpected\s+token\s+at\s+'<\!DOCTYPE\s+html/i
|
||||
raise "Can't fetch objects from #{url}: #{result.error}" if !result.success?
|
||||
|
||||
# add link to idoit
|
||||
if result.data['result'].class == Array
|
||||
result.data['result'].each { |item|
|
||||
next if !item['id']
|
||||
item['link'] = "#{_url_cleanup_baseurl(url)}/?objID=#{item['id']}"
|
||||
item['link'].gsub!(%r{([^:])//+}, '\\1/')
|
||||
}
|
||||
end
|
||||
result.data
|
||||
end
|
||||
|
||||
def self._url_cleanup(url)
|
||||
raise "Invalid endpoint '#{url}', need to start with http:// or https://" if url !~ %r{^http(s|)://}i
|
||||
url = _url_cleanup_baseurl(url)
|
||||
url = "#{url}/src/jsonrpc.php"
|
||||
url.gsub(%r{([^:])//+}, '\\1/')
|
||||
end
|
||||
|
||||
def self._url_cleanup_baseurl(url)
|
||||
raise "Invalid endpoint '#{url}', need to start with http:// or https://" if url !~ %r{^http(s|)://}i
|
||||
url.gsub!(%r{src/jsonrpc.php}, '')
|
||||
url.gsub(%r{([^:])//+}, '\\1/')
|
||||
end
|
||||
end
|
|
@ -25,6 +25,7 @@ class AgentTicketUpdateAndReloadTest < TestCase
|
|||
sleep 6
|
||||
|
||||
# check if customer is shown in sidebar
|
||||
click(css: '.active .tabsSidebar-tab[data-tab="customer"]')
|
||||
match(
|
||||
css: '.active .sidebar[data-tab="customer"]',
|
||||
value: 'nicole',
|
||||
|
@ -46,6 +47,7 @@ class AgentTicketUpdateAndReloadTest < TestCase
|
|||
reload()
|
||||
|
||||
# check if customer is still shown in sidebar
|
||||
click(css: '.active .tabsSidebar-tab[data-tab="customer"]')
|
||||
watch_for(
|
||||
css: '.active .sidebar[data-tab="customer"]',
|
||||
value: 'nicole',
|
||||
|
|
Loading…
Reference in a new issue