Initial idoit integration.

This commit is contained in:
Martin Edenhofer 2017-09-06 08:39:02 +02:00
parent 83d11b122e
commit 6da0181e2a
23 changed files with 934 additions and 129 deletions

View file

@ -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) ->
#

View file

@ -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

View file

@ -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'
)

View file

@ -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
@ -99,10 +102,10 @@ class App.TicketCreate extends App.Controller
if title
text = "#{text}: #{title}"
meta =
url: @url()
head: text
title: text
id: @id
url: @url()
head: text
title: text
id: @id
iconClass: 'pen'
url: =>
@ -335,25 +338,42 @@ class App.TicketCreate extends App.Controller
user: App.Session.get()
)
new Sidebar(
el: @sidebar
params: @formDefault
textModule: @textModule
$('#tags').tokenfield()
@sidebarWidget = new App.TicketCreateSidebar(
el: @sidebar
params: @formDefault
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')

View file

@ -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
)

View file

@ -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')

View file

@ -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')

View file

@ -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')

View file

@ -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, @)

View file

@ -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

View file

@ -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
@sidebarBackends[key] = new sidebarBackends[key](
ticket: ticket
taskGet: @taskGet
formMeta: @formMeta
markForm: @markForm
)
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

View file

@ -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')

View 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>

View file

@ -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 %>

View file

@ -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>

View file

@ -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 %>

View 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

View 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

View file

@ -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

View 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

View file

@ -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
View 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

View file

@ -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',