Init version of object manager.

This commit is contained in:
Martin Edenhofer 2016-05-19 10:20:38 +02:00
parent b00df72760
commit 1248f4080b
42 changed files with 2435 additions and 759 deletions

View file

@ -204,6 +204,8 @@ class App.ControllerForm extends App.Controller
attribute.autofocus = 'autofocus'
# set required option
if attribute.required is true
attribute.null = false
if !attribute.null
attribute.required = 'required'
else
@ -231,7 +233,7 @@ class App.ControllerForm extends App.Controller
# check if we have a references
parts = attribute.name.split '::'
if parts[0] && parts[1]
if @params[ parts[0] ] && @params[ parts[0] ][ parts[1] ]
if @params[ parts[0] ] && parts[1] of @params[ parts[0] ]
attribute.value = @params[ parts[0] ][ parts[1] ]
# set params value to default
@ -480,15 +482,22 @@ class App.ControllerForm extends App.Controller
for key of param
parts = key.split '::'
if parts[0] && parts[1]
if !(parts[0] of inputSelectObject)
if parts[1] && !inputSelectObject[ parts[0] ]
inputSelectObject[ parts[0] ] = {}
if !parts[2]
inputSelectObject[ parts[0] ][ parts[1] ] = param[ key ]
else
if !(parts[1] of inputSelectObject[ parts[0] ])
inputSelectObject[ parts[0] ][ parts[1] ] = {}
if parts[2] && !inputSelectObject[ parts[0] ][ parts[1] ]
inputSelectObject[ parts[0] ][ parts[1] ] = {}
if parts[3] && !inputSelectObject[ parts[0] ][ parts[1] ][ parts[2] ]
inputSelectObject[ parts[0] ][ parts[1] ][ parts[2] ] = {}
if parts[3]
inputSelectObject[ parts[0] ][ parts[1] ][ parts[2] ][ parts[3] ] = param[ key ]
delete param[ key ]
else if parts[2]
inputSelectObject[ parts[0] ][ parts[1] ][ parts[2] ] = param[ key ]
delete param[ key ]
delete param[ key ]
else if parts[1]
inputSelectObject[ parts[0] ][ parts[1] ] = param[ key ]
delete param[ key ]
# set new object params
for key of inputSelectObject

View file

@ -40,7 +40,6 @@ class App.DashboardFirstSteps extends App.Controller
#container: @el.closest('.content')
head: 'Invite Colleagues'
screen: 'invite_agent'
role: 'Agent'
)
inviteCustomer: (e) ->

View file

@ -4,7 +4,7 @@ class App.UiElement.active extends App.UiElement.ApplicationUiElement
# set attributes
attribute.null = false
attribute.translation = true
attribute.translate = true
# build options list
attribute.options = [

View file

@ -8,6 +8,7 @@ class App.UiElement.boolean extends App.UiElement.ApplicationUiElement
{ name: 'yes', value: true }
{ name: 'no', value: false }
]
attribute.translate = true
# set data type
if attribute.name

View file

@ -0,0 +1,304 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUiElement
@render: (attribute, params = {}) ->
item = $(App.view('object_manager/attribute')(attribute: attribute))
updateDataMap = (localParams, localAttribute, localAttributes, localClassname, localForm, localA) =>
localItem = localForm.closest('.js-data')
values = []
values = {a: 123, b: 'aaa'}
element = $(App.view("object_manager/attribute/#{localParams.data_type}")(
attribute: attribute
values: values
))
@[localParams.data_type](element, localParams, params)
localItem.find('.js-dataMap').html(element)
localItem.find('.js-dataScreens').html(@dataScreens(attribute, localParams, params))
options =
datetime: 'Datetime'
date: 'Date'
input: 'Text'
# select: 'Select'
# boolean: 'Boolean'
# integer: 'Integer'
# autocompletion: 'Autocompletion (AJAX remote URL)'
configureAttributes = [
{ name: attribute.name, display: '', tag: 'select', null: false, options: options, translate: true, default: 'input', disabled: attribute.disabled },
]
dataType = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
handlers: [
updateDataMap,
]
params: params
)
item.find('.js-dataType').html(dataType.form)
item
@dataScreens: (attribute, localParams, params) ->
object = params.object
objects =
Ticket:
Customer:
create:
shown: true
required: false
edit:
shown: true
required: false
Agent:
create_bottom:
show: true
required: false
edit:
shown: true
required: false
User:
Customer:
create:
shown: true
required: false
view:
shown: true
signup:
shown: false
required: false
Agent:
create:
shown: true
required: false
edit:
shown: true
required: false
view:
shown: true
invite_customer:
show: false
required: false
Admin:
create:
shown: true
required: false
edit:
shown: true
required: false
view:
shown: true
invite_agent:
show: false
required: false
invite_customer:
show: false
required: false
Organization:
Customer:
view:
shown: true
Agent:
create:
shown: true
required: false
edit:
shown: true
required: false
view:
shown: true
Admin:
create:
shown: true
required: false
edit:
shown: true
required: false
view:
shown: true
Group:
Admin:
create:
shown: true
required: false
edit:
shown: true
required: false
view:
shown: true
init = false
if params && !params.id
init = true
$(App.view('object_manager/screens')(
attribute: attribute
data: objects[object]
params: params
init: init
))
@input: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'input', type: 'text', null: true, default: '' },
]
inputDefault = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::type', display: 'Type', tag: 'select', null: false, default: 'text', options: {text: 'Text', phone: 'Phone', fax: 'Fax', email: 'Email', url: 'Url'}, translate: true },
]
inputType = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::maxlength', display: 'Maxlength', tag: 'integer', null: false, default: 120 },
]
inputMaxlength = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
item.find('.js-inputDefault').html(inputDefault.form)
item.find('.js-inputType').html(inputType.form)
item.find('.js-inputMaxlength').html(inputMaxlength.form)
@datetime: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::future', display: 'Allow future', tag: 'boolean', null: false, default: true },
]
datetimeFuture = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::past', display: 'Allow past', tag: 'boolean', null: false, default: true },
]
datetimePast = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::diff', display: 'Default time Diff (minutes)', tag: 'integer', null: false, default: 24 },
]
datetimeDiff = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
item.find('.js-datetimeFuture').html(datetimeFuture.form)
item.find('.js-datetimePast').html(datetimePast.form)
item.find('.js-datetimeDiff').html(datetimeDiff.form)
@date: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::future', display: 'Allow future', tag: 'boolean', null: false, default: true },
]
dateFuture = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::past', display: 'Allow past', tag: 'boolean', null: false, default: true },
]
datePast = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::diff', display: 'Default time Diff (hours)', tag: 'integer', null: false, default: 24 },
]
dateDiff = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
item.find('.js-dateFuture').html(dateFuture.form)
item.find('.js-datePast').html(datePast.form)
item.find('.js-dateDiff').html(dateDiff.form)
@integer: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'integer', null: true, default: '', min: 1 },
]
integerDefault = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::min', display: 'Minimal', tag: 'integer', null: false, default: 0, min: 1 },
]
integerMin = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::max', display: 'Maximal', tag: 'integer', null: false, default: 999999999, min: 2 },
]
integerMax = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
item.find('.js-integerDefault').html(integerDefault.form)
item.find('.js-integerMin').html(integerMin.form)
item.find('.js-integerMax').html(integerMax.form)
@select: (item, localParams, params) ->
@boolean: (item, localParams, params) ->
@autocompletion: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'input', type: 'text', null: true, default: '' },
]
autocompletionDefault = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::url', display: 'Url (AJAX Endpoint)', tag: 'input', type: 'url', null: false, default: '', placeholder: 'https://example.com/serials' },
]
autocompletionUrl = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
configureAttributes = [
{ name: 'data_option::method', display: 'Method (AJAX Endpoint)', tag: 'input', type: 'url', null: false, default: '', placeholder: 'GET' },
]
autocompletionMethod = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
item.find('.js-autocompletionDefault').html(autocompletionDefault.form)
item.find('.js-autocompletionUrl').html(autocompletionUrl.form)
item.find('.js-autocompletionMethod').html(autocompletionMethod.form)

View file

@ -0,0 +1,125 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.user_permission
@render: (attribute, params = {}) ->
attribute.options = {}
# get selectable roles and selected roles
roles = []
rolesSelected = {}
rolesRaw = App.Role.search(sortBy: 'name')
for role in rolesRaw
if role.active
roles.push role
if params.role_ids
for role_id in params.role_ids
if role_id.toString() is role.id.toString()
rolesSelected[role.id] = true
# get selectable groups and selected groups
groups = []
groupsSelected = {}
groupsRaw = App.Group.search(sortBy: 'name')
for group in groupsRaw
if group.active
groups.push group
if params.group_ids
for group_id in params.group_ids
if group_id.toString() is group.id.toString()
groupsSelected[group.id] = true
# if only one group is selectable, hide all groups
hideGroups = false
if groups.length <= 1
hideGroups = true
if attribute.hideMode
if attribute.hideMode.rolesSelected
roles = []
rolesSelected = {}
for roleName in attribute.hideMode.rolesSelected
role = App.Role.findByAttribute('name', roleName)
if role
roles.push role
rolesSelected[role.id] = true
if attribute.hideMode.rolesNot
for roleRaw in rolesRaw
hit = false
for roleName in attribute.hideMode.rolesNot
if roleRaw.active && roleRaw.name is roleName
hit = true
if !hit
roles.push roleRaw
# if agent is on new users selected, select all groups
if _.isEmpty(attribute.value)
agentRole = App.Role.findByAttribute('name', 'Agent')
if rolesSelected[agentRole.id]
for group in groups
groupsSelected[group.id] = true
# uniq and sort roles
roles = _.indexBy(roles, 'name')
roles = _.sortBy(roles, (i) -> return i.name)
item = $( App.view('generic/user_permission')(
attribute: attribute
roles: roles
groups: groups
params: params
rolesSelected: rolesSelected
groupsSelected: groupsSelected
hideGroups: hideGroups
) )
getCurrentRoles = ->
currentRoles = []
item.find('[name=role_ids]').each( ->
element = $(@)
checked = element.prop('checked')
return if !checked
role_id = element.prop('value')
role = App.Role.find(role_id)
return if !role
currentRoles.push role
)
currentRoles
# if customer, remove admin and agent
item.find('[name=role_ids]').bind('change', (e) ->
element = $(e.currentTarget)
checked = element.prop('checked')
role_id = element.prop('value')
return if !role_id
role = App.Role.find(role_id)
return if !role
# if agent got deselected
# - hide groups
if !checked
if role.name is 'Agent'
item.find('.js-groupList').addClass('hidden')
return
# if agent is selected
# - show groups
if role.name is 'Agent'
item.find('.js-groupList:not(.js-groupListHide)').removeClass('hidden')
# if role customer is selected
# - deselect agent & admin
# - hide groups
if role.name is 'Customer'
for currentRole in getCurrentRoles()
if currentRole.name is 'Admin' || currentRole.name is 'Agent'
item.find("[name=role_ids][value=#{currentRole.id}]").prop('checked', false)
item.find('.js-groupList').addClass('hidden')
# if role agent or admin is selected
# - deselect customer
else if role.name is 'Agent' || role.name is 'Admin'
for currentRole in getCurrentRoles()
if currentRole.name is 'Customer'
item.find("[name=role_ids][value=#{currentRole.id}]").prop('checked', false)
)
item

View file

@ -14,7 +14,7 @@ class Index extends App.ControllerTabs
@ajax(
id: 'object_manager_attributes_list'
type: 'GET'
url: @apiPath + '/object_manager_attributes_list'
url: "#{@apiPath}/object_manager_attributes_list"
processData: true
success: (data, status, xhr) =>
@stopLoading()
@ -36,22 +36,21 @@ class Index extends App.ControllerTabs
class Items extends App.ControllerContent
events:
'click [data-type="delete"]': 'destroy'
'click .js-up': 'move'
'click .js-down': 'move'
'click .js-new': 'new'
'click .js-edit': 'edit'
'click .js-delete': 'destroy'
'click .js-new': 'new'
'click .js-edit': 'edit'
'click .js-discard': 'discard'
'click .js-execute': 'execute'
constructor: ->
super
# check authentication
return if !@authenticate()
@subscribeId = App.ObjectManagerAttribute.subscribe(@render)
App.ObjectManagerAttribute.fetch()
# ajax call
release: =>
if @subscribeId
App.ObjectManagerAttribute.unsubscribe(@subscribeId)
@ -63,63 +62,22 @@ class Items extends App.ControllerContent
sortBy: 'position'
)
itemsToChange = []
for item in App.ObjectManagerAttribute.search(sortBy: 'object')
if item.to_create is true || item.to_delete is true
itemsToChange.push item
@html App.view('object_manager/index')(
head: @object
items: items
head: @object
items: items
itemsToChange: itemsToChange
)
###
new App.ControllerTable(
el: @el.find('.table-overview')
model: App.ObjectManagerAttribute
objects: objects
bindRow:
events:
'click': @edit
)
###
move: (e) =>
e.preventDefault()
e.stopPropagation()
id = $( e.target ).closest('tr').data('id')
direction = 'up'
if $( e.target ).hasClass('js-down')
direction = 'down'
console.log('M', id, direction)
items = App.ObjectManagerAttribute.search(
filter:
object: @object
sortBy: 'position'
)
count = 0
for item in items
if item.id is id
if direction is 'up'
itemToMove = items[ count - 1 ]
else
itemToMove = items[ count + 1 ]
if itemToMove
movePosition = itemToMove.position
if movePosition is item.position
if direction is 'up'
movePosition -= 1
else
movePosition += 1
itemToMove.position = item.position
itemToMove.save()
console.log(itemToMove, itemToMove.position, count)
item.position = movePosition
item.save()
console.log(item, movePosition, count)
count += 1
new: (e) =>
e.preventDefault()
new App.ControllerGenericNew(
new New(
pageData:
head: @object
title: 'Attribute'
home: 'object_manager'
object: 'ObjectManagerAttribute'
@ -127,6 +85,8 @@ class Items extends App.ControllerContent
navupdate: '#object_manager'
genericObject: 'ObjectManagerAttribute'
container: @el.closest('.content')
item:
object: @object
)
edit: (e) =>
@ -134,136 +94,135 @@ class Items extends App.ControllerContent
id = $( e.target ).closest('tr').data('id')
new Edit(
pageData:
object: 'ObjectManagerAttribute'
head: @object
title: 'Attribute'
home: 'object_manager'
object: 'ObjectManagerAttribute'
objects: 'ObjectManagerAttributes'
navupdate: '#object_manager'
genericObject: 'ObjectManagerAttribute'
container: @el.closest('.content')
callback: @render
id: id
container: @el.closest('.content')
)
destroy: (e) ->
e.stopPropagation()
e.preventDefault()
sessionId = $( e.target ).data('session-id')
id = $(e.target).closest('tr').data('id')
item = App.ObjectManagerAttribute.find(id)
@ajax(
id: 'sessions/' + sessionId
id: "object_manager_attributes/#{id}"
type: 'DELETE'
url: @apiPath + '/sessions/' + sessionId
url: "#{@apiPath}/object_manager_attributes/#{id}"
success: (data) =>
@load()
@render()
)
class Edit extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Edit'
discard: (e) ->
e.preventDefault()
@ajax(
id: 'object_manager_attributes_discard_changes'
type: 'POST'
url: "#{@apiPath}/object_manager_attributes_discard_changes"
success: (data) =>
@render()
)
execute: (e) ->
e.preventDefault()
@ajax(
id: 'object_manager_attributes_execute_migrations'
type: 'POST'
url: "#{@apiPath}/object_manager_attributes_execute_migrations"
success: (data) =>
@render()
)
class New extends App.ControllerGenericNew
onSubmit: (e) =>
params = @formParam(e.target)
params.object = @pageData.head
object = new App[ @genericObject ]
object.load(params)
# validate
errors = object.validate()
if errors
@log 'error', errors
@formValidate( form: e.target, errors: errors )
return false
# disable form
@formDisable(e)
# save object
ui = @
object.save(
done: ->
if ui.callback
item = App[ ui.genericObject ].fullLocal(@id)
ui.callback(item)
ui.close()
fail: (settings, details) ->
ui.log 'errors', details
ui.formEnable(e)
ui.controller.showAlert(details.error_human || details.error || 'Unable to create object!')
)
class Edit extends App.ControllerGenericEdit
content: =>
content = $( App.view('object_manager/edit')(
head: @object
items: []
) )
@item = App[ @genericObject ].find( @id )
@head = @pageData.head || @pageData.object
item = App.ObjectManagerAttribute.find(@id)
# set disabled attributes
configure_attributes = clone(App[ @genericObject ].configure_attributes)
for attribute in configure_attributes
if attribute.name is 'name'
attribute.disabled = true
#if attribute.name is 'data_type'
# attribute.disabled = true
options =
input: 'Text (normal - one line)'
select: 'Selection'
datetime: 'Datetime'
date: 'Date'
textarea: 'Text (normal - multiline)'
richtext: 'Text (richtext)'
checkbox: 'Checkbox'
boolean: 'yes/no'
configureAttributesTop = [
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false },
{ name: 'display', display: 'Anzeige', tag: 'input', type: 'text', limit: 100, 'null': false },
{ name: 'data_type', display: 'Format', tag: 'select', multiple: false, nulloption: true, null: false, options: options, translate: true },
]
controller = new App.ControllerForm(
model: { configure_attributes: configureAttributesTop, className: '' },
params: item
#screen: @screen || 'edit'
el: content.find('.js-top')
autofocus: true
)
# input
configureAttributesInput = [
{ name: 'data_option::type', display: 'Type', tag: 'select', multiple: false, nulloption: true, null: false, options: { text: 'text', email: 'email', url: 'url', email: 'email', password: 'password', phone: 'phone'}, translate: true },
{ name: 'data_option::maxlength', display: 'Max. Length', tag: 'input', type: 'text', limit: 100, 'null': false },
{ name: 'data_option::null', display: 'Required', tag: 'select', multiple: false, nulloption: false, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::autocapitalize', display: 'autocapitalize', tag: 'select', multiple: false, nulloption: true, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::autocomplete', display: 'autocomplete', tag: 'select', multiple: false, nulloption: true, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::default', display: 'Default', tag: 'input', type: 'text', limit: 100, null: true },
{ name: 'data_option::note', display: 'note', tag: 'input', type: 'text', limit: 100, null: true },
]
controller = new App.ControllerForm(
model: { configure_attributes: configureAttributesInput, className: '' },
params: item
el: content.find('.js-input')
autofocus: true
)
# textarea
configureAttributesTextarea = [
{ name: 'data_option::maxlength', display: 'Max. Length', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'data_option::null', display: 'Required', tag: 'select', multiple: false, nulloption: false, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::autocapitalize', display: 'autocapitalize', tag: 'select', multiple: false, nulloption: true, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::note', display: 'autocomplete', tag: 'input', type: 'text', limit: 100, null: true },
]
controller = new App.ControllerForm(
model: { configure_attributes: configureAttributesTextarea, className: '' },
params: item
el: content.find('.js-textarea')
autofocus: true
)
# select
configureAttributesSelect = [
{ name: 'data_option::nulloption', display: 'Empty Selection', tag: 'select', multiple: false, nulloption: false, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::null', display: 'Required', tag: 'boolean', multiple: false, nulloption: false, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::relation', display: 'Relation', tag: 'input', type: 'text', limit: 100, null: true },
{ name: 'data_option::options', display: 'Options', tag: 'hash', multiple: true, null: false },
{ name: 'data_option::translate', display: 'Übersetzen', tag: 'select', multiple: false, nulloption: false, null: false, options: { true: 'no', false: 'yes' }, translate: true },
{ name: 'data_option::note', display: 'Note', tag: 'input', type: 'text', limit: 100, null: true },
]
controller = new App.ControllerForm(
model: { configure_attributes: configureAttributesSelect, className: '' },
params: item
el: content.find('.js-select')
@controller = new App.ControllerForm(
model:
configure_attributes: configure_attributes
params: @item
screen: @screen || 'edit'
autofocus: true
)
@controller.form
###
:options => {
'Incident' => 'Incident',
'Problem' => 'Problem',
'Request for Change' => 'Request for Change',
},
###
onSubmit: (e) =>
params = @formParam(e.target)
params.object = @pageData.head
@item.load(params)
content.find('[name=data_type]').on(
'change',
(e) ->
dataType = $( e.target ).val()
content.find('.js-middle > div').addClass('hide')
content.find(".js-#{dataType}").removeClass('hide')
# validate
errors = @item.validate()
if errors
@log 'error', errors
@formValidate( form: e.target, errors: errors )
return false
# disable form
@formDisable(e)
# save object
ui = @
@item.save(
done: ->
if ui.callback
item = App[ ui.genericObject ].fullLocal(@id)
ui.callback(item)
ui.close()
fail: (settings, details) ->
ui.log 'errors'
ui.formEnable(e)
ui.controller.showAlert(details.error_human || details.error || 'Unable to update object!')
)
content.find('[name=data_type]').trigger('change')
configureAttributesBottom = [
{ name: 'active', display: 'Active', tag: 'active', default: true },
]
controller = new App.ControllerForm(
model: { configure_attributes: configureAttributesBottom, className: '' },
params: item
#screen: @screen || 'edit'
el: content.find('.js-bottom')
)
controller.form
App.Config.set( 'SystemObject', { prio: 1700, parent: '#system', name: 'Objects', target: '#system/object_manager', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )

View file

@ -43,16 +43,16 @@ class App.InviteUser extends App.WizardModal
e.preventDefault()
@showSlide('js-waiting')
@formDisable(e)
@params = @formParam(e.target)
@params.role_ids = [0]
@params = @formParam(e.target)
# set invite flag
@params.invite = true
# find agent role
role = App.Role.findByAttribute('name', @role)
if role
@params.role_ids = role.id
if @role
role = App.Role.findByAttribute('name', @role)
if role
@params.role_ids = role.id
user = new App.User
user.load(@params)
@ -62,7 +62,7 @@ class App.InviteUser extends App.WizardModal
)
if errors
@log 'error new', errors
@formValidate( form: e.target, errors: errors )
@formValidate(form: e.target, errors: errors)
@formEnable(e)
@showSlide('js-user')
return false
@ -77,4 +77,4 @@ class App.InviteUser extends App.WizardModal
@formEnable(e)
@showSlide('js-user')
@showAlert('js-user', details.error_human || details.error)
)
)

View file

@ -597,33 +597,33 @@ class App.Model extends Spine.Model
all_complied = []
if !params
for item in all
item_new = @find( item.id )
item_new = @find(item.id)
all_complied.push @_fillUp(item_new)
return all_complied
for item in all
item_new = @find( item.id )
item_new = @find(item.id)
all_complied.push @_fillUp(item_new)
# filter search
if params.filter
all_complied = @_filter( all_complied, params.filter )
all_complied = @_filter(all_complied, params.filter)
# use extend filter search
if params.filterExtended
all_complied = @_filterExtended( all_complied, params.filterExtended )
all_complied = @_filterExtended(all_complied, params.filterExtended)
# sort by
if params.sortBy != null
all_complied = @_sortBy( all_complied, params.sortBy )
all_complied = @_sortBy(all_complied, params.sortBy)
# order
if params.order
all_complied = @_order( all_complied, params.order )
all_complied = @_order(all_complied, params.order)
all_complied
@_sortBy: ( collection, attribute ) ->
_.sortBy( collection, (item) ->
@_sortBy: (collection, attribute) ->
_.sortBy(collection, (item) ->
# set displayName as default sort attribute
if !attribute
@ -646,20 +646,20 @@ class App.Model extends Spine.Model
item[ attribute ]
)
@_order: ( collection, attribute ) ->
@_order: (collection, attribute) ->
if attribute is 'DESC'
return collection.reverse()
collection
@_filter: ( collection, filter ) ->
@_filter: (collection, filter) ->
for key, value of filter
collection = _.filter( collection, (item) ->
collection = _.filter(collection, (item) ->
if item[key] is value
return item
)
collection
@_filterExtended: ( collection, filters ) ->
@_filterExtended: (collection, filters) ->
collection = _.filter( collection, (item) ->
# check all filters

View file

@ -6,8 +6,8 @@ class App.Group extends App.Model
@configure_attributes = [
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'assignment_timeout', display: 'Assignment Timeout', tag: 'input', note: 'Assignment timeout in minutes if assigned agent is not working on it. Ticket will be shown as unassigend.', type: 'text', limit: 100, null: true },
{ name: 'follow_up_possible', display: 'Follow up possible',tag: 'select', default: 'yes', options: { yes: 'yes', reject: 'reject follow up/do not reopen Ticket', 'new_ticket': 'do not reopen Ticket but create new Ticket' }, null: false, note: 'Follow up for closed ticket possible or not.' },
{ name: 'follow_up_assignment', display: 'Assign Follow Ups', tag: 'select', default: 'yes', options: { true: 'yes', false: 'no' }, 'null': false, note: 'Assign follow up to latest agent again.' },
{ name: 'follow_up_possible', display: 'Follow up possible',tag: 'select', default: 'yes', options: { yes: 'yes', reject: 'reject follow up/do not reopen Ticket', 'new_ticket': 'do not reopen Ticket but create new Ticket' }, null: false, note: 'Follow up for closed ticket possible or not.', translate: true },
{ name: 'follow_up_assignment', display: 'Assign Follow Ups', tag: 'select', default: 'yes', options: { true: 'yes', false: 'no' }, null: false, note: 'Assign follow up to latest agent again.', translate: true },
{ name: 'email_address_id', display: 'Email', tag: 'select', multiple: false, null: true, relation: 'EmailAddress', nulloption: true, do_not_log: true },
{ name: 'signature_id', display: 'Signature', tag: 'select', multiple: false, null: true, relation: 'Signature', nulloption: true, do_not_log: true },
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true },

View file

@ -1,20 +1,13 @@
class App.ObjectManagerAttribute extends App.Model
@configure 'ObjectManagerAttribute', 'name', 'object', 'display', 'active', 'editable', 'data_type', 'data_option', 'screens', 'position', 'updated_at'
@configure 'ObjectManagerAttribute', 'name', 'object', 'display', 'active', 'editable', 'data_type', 'data_option', 'screens', 'position'
@extend Spine.Model.Ajax
@url: @apiPath + '/object_manager_attributes'
@configure_attributes = [
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'display', display: 'Anzeige', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'object', display: 'Object', tag: 'input', readonly: 1 },
{ name: 'position', display: 'Position', tag: 'input', readonly: 1 },
{ name: 'active', display: 'Active', tag: 'active', default: true },
{ name: 'data_type', display: 'Format', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'display', display: 'Anzeige', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'object', display: 'Object', tag: 'input', readonly: 1 },
{ name: 'position', display: 'Position', tag: 'input', readonly: 1 },
{ name: 'active', display: 'Active', tag: 'active', default: true },
{ name: 'data_type', display: 'Format', tag: 'object_manager_attribute', null: false },
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
]
@configure_overview = [
#'name',
'display',
'position',
'data_type',
]
@configure_delete = true

View file

@ -12,8 +12,7 @@ class App.User extends App.Model
{ name: 'organization_id', display: 'Organization', tag: 'select', multiple: false, nulloption: true, null: true, relation: 'Organization', signup: false, info: true, invite_customer: true },
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 50, null: true, autocomplete: 'off', signup: true, },
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true, info: true, invite_customer: true },
{ name: 'role_ids', display: 'Roles', tag: 'checkbox', multiple: true, null: false, relation: 'Role' },
{ name: 'group_ids', display: 'Groups', tag: 'checkbox', multiple: true, null: true, relation: 'Group', invite_agent: true, invite_customer: true },
{ name: 'role_ids', display: 'Permissions', tag: 'user_permission', null: false, invite_agent: true, invite_customer: true, item_class: 'checkbox' },
{ name: 'active', display: 'Active', tag: 'active', default: true },
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
{ name: 'created_at', display: 'Created at', tag: 'datetime', readonly: 1 },

View file

@ -1 +1,4 @@
<input id="<%= @attribute.id %>" type="<%= @attribute.type %>" name="<%= @attribute.name %>" value="<%= @attribute.value %>" class="form-control <%= @attribute.class %>" <% if @attribute.placeholder: %>placeholder="<%- @Ti(@attribute.placeholder) %>"<% end %> <%= @attribute.required %> <%= @attribute.autofocus %> <%- @attribute.autocapitalize %> <%- @attribute.autocomplete %>/>
<input id="<%= @attribute.id %>" type="<%= @attribute.type %>" name="<%= @attribute.name %>" value="<%= @attribute.value %>" class="form-control <%= @attribute.class %>" <% if @attribute.placeholder: %>placeholder="<%- @Ti(@attribute.placeholder) %>"<% end %> <%= @attribute.required %> <%= @attribute.autofocus %> <%- @attribute.autocapitalize %> <%- @attribute.autocomplete %> <% if @attribute.min isnt undefined: %> min="<%= @attribute.min %>"<% end %><% if @attribute.max isnt undefined: %> max="<%= @attribute.max %>"<% end %><% if @attribute.step: %> step="<%= @attribute.step %>"<% end %><% if @attribute.disabled: %> disabled<% end %>/>
<% if @attribute.disabled: %>
<input type="hidden" name="<%= @attribute.name %>" value="<%= @attribute.value %>">
<% end %>

View file

@ -1,5 +1,5 @@
<div class="controls controls--select">
<select id="<%= @attribute.id %>" class="form-control<%= " #{ @attribute.class }" if @attribute.class %>" name="<%= @attribute.name %>" <%= @attribute.multiple %> <%= @attribute.required %> <%= @attribute.autofocus %>>
<select id="<%= @attribute.id %>" class="form-control<%= " #{ @attribute.class }" if @attribute.class %>" name="<%= @attribute.name %>" <%= @attribute.multiple %> <%= @attribute.required %> <%= @attribute.autofocus %> <% if @attribute.disabled: %> disabled<% end %>>
<% if @attribute.options: %>
<% for row in @attribute.options: %>
<option value="<%= row.value %>" <%= row.selected %> <%= row.disabled %>><%= row.name %></option>
@ -9,4 +9,13 @@
<% if not @attribute.multiple: %>
<%- @Icon('arrow-down') %>
<% end %>
<% if @attribute.disabled: %>
<% if @attribute.options: %>
<% for row in @attribute.options: %>
<% if row.selected: %>
<input type="hidden" name="<%= @attribute.name %>" value="<%= row.value %>">
<% end %>
<% end %>
<% end %>
<% end %>
</div>

View file

@ -0,0 +1,22 @@
<div class="checkbox <%= @attribute.class %> checkbox">
<% for role in @roles: %>
<label class="inline-label checkbox-replacement">
<input type="checkbox" value="<%= role.id %>" name="role_ids" <% if @rolesSelected[role.id]: %>checked<% end %>/>
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
<span class="label-text"><%= role.displayName() %> <% if role.note: %>- <span class="help-text"><%= role.note %></span><% end %></span>
</label>
<% if role.name is 'Agent': %>
<div style="padding-left: 20px;" class="js-groupList <% if @hideGroups: %>js-groupListHide hidden<% end %>">
<% for group in @groups: %>
<label class="inline-label checkbox-replacement">
<input type="checkbox" value="<%= group.id %>" name="group_ids" <% if @groupsSelected[group.id]: %>checked<% end %>/>
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
<span class="label-text"><%= group.displayName() %> <% if group.note: %>- <span class="help-text"><%= group.note %></span><% end %></span>
</label>
<% end %>
</div>
<% end %>
<% end %>
</div>

View file

@ -0,0 +1,5 @@
<div class="js-data">
<div class="js-dataType"></div>
<div class="js-dataMap" style="padding: 30px 0 0 30px;"></div>
<div class="js-dataScreens" style="padding: 30px 0 0 30px;"></div>
</div>

View file

@ -0,0 +1,6 @@
<div>
Auto-Vervollständigung
<div class="js-autocompletionDefault"></div>
<div class="js-autocompletionUrl"></div>
<div class="js-autocompletionMethod"></div>
</div>

View file

@ -0,0 +1,29 @@
<div>
<div class="js-selectDefault"></div>
<div class="js-selectOption"></div>
<table class="settings-list" style="width: 100%;">
<thead>
<tr>
<th><%- @T('Key') %>
<th><%- @T('Display') %>
<th style="width: 30px"><%- @T('Default') %>
</thead>
<tbody>
<tr>
<td class="settings-list-control-cell">
true
<td class="settings-list-control-cell">
<input class="form-control form-control--small js-summary" type="text" name="summary" value="<%= @display %>" placeholder="<%- @T('yes') %>" required/>
<td class="settings-list-row-control">
<input class="" type="radio" />
<tr>
<td class="settings-list-control-cell">
false
<td class="settings-list-control-cell">
<input class="form-control form-control--small js-summary" type="text" name="summary" value="<%= @display %>" placeholder="<%- @T('no') %>" required/>
<td class="settings-list-row-control">
<input class="" type="radio" />
</tbody>
</table>
</div>

View file

@ -0,0 +1,5 @@
<div>
<div class="js-dateFuture"></div>
<div class="js-datePast"></div>
<div class="js-dateDiff"></div>
</div>

View file

@ -0,0 +1,5 @@
<div>
<div class="js-datetimeFuture"></div>
<div class="js-datetimePast"></div>
<div class="js-datetimeDiff"></div>
</div>

View file

@ -0,0 +1,5 @@
<div>
<div class="js-inputDefault"></div>
<div class="js-inputType"></div>
<div class="js-inputMaxlength"></div>
</div>

View file

@ -0,0 +1,5 @@
<div>
<div class="js-integerDefault"></div>
<div class="js-integerMin"></div>
<div class="js-integerMax"></div>
</div>

View file

@ -0,0 +1,41 @@
<div>
<div class="js-selectDefault"></div>
<div class="js-selectOption"></div>
<table class="settings-list" style="width: 100%;">
<thead>
<tr>
<th><%- @T('Key') %>
<th><%- @T('Display') %>
<th style="width: 30px"><%- @T('Default') %>
<th style="width: 30px"><%- @T('Action') %>
</thead>
<tbody>
<% for key, display of @values: %>
<tr>
<td class="settings-list-control-cell">
<input class="form-control form-control--small js-summary" type="text" name="summary" value="<%= key %>" required/>
<td class="settings-list-control-cell">
<input class="form-control form-control--small js-summary" type="text" name="summary" value="<%= display %>" required/>
<td class="settings-list-row-control">
<input class="" type="radio" />
<td class="settings-list-row-control">
<div class="btn btn--text js-remove">
<%- @Icon('trash') %> <%- @T('Remove') %>
</div>
<% end %>
<tr>
<td class="settings-list-control-cell">
<input class="form-control form-control--small js-summary" type="text" placeholder="<%- @T('Key') %>"/>
<td class="settings-list-control-cell">
<input class="form-control form-control--small js-summary" type="text" placeholder="<%- @T('Display') %>"/>
<td class="settings-list-row-control">
<input class="" type="radio" />
<td class="settings-list-row-control">
<div class="btn btn--text btn--create js-add">
<%- @Icon('plus-small') %> <%- @T('Add') %>
</div>
</tbody>
</table>
</div>

View file

@ -1,21 +1,42 @@
<div class="page-header">
<div class="page-header-title">
<h1><%- @T( @head ) %> <small><%- @T( 'Object Manager' ) %></small></h1>
<h1><%- @T(@head) %> <small><%- @T('Object Manager') %></small></h1>
</div>
<div class="page-header-meta">
<a class="btn js-restore"><%- @T( 'Restore Defaults' ) %></a>
<a class="btn btn--success js-new"><%- @T( 'New Attribute' ) %></a>
<!--
<a class="btn js-restore"><%- @T('Restore Defaults') %></a>
-->
<a class="btn btn--success js-new"><%- @T('New Attribute') %></a>
</div>
</div>
<div class="page-content">
<% if !_.isEmpty(@itemsToChange): %>
<div class="box box--message">
<h2>Database Update required</h2>
<p><%- @T( 'Changes were made that require a database update. This might take some time.' ) %></p>
<p>
<%- @T('Changes were made that require a database update.') %>
<%- @T('This might take some time where the system can\'t be used.') %>
<%- @T('Please update database changes only in a maintenance timeslot.') %>
</p>
<p>
<%- @T('Changes') %>:
<ul>
<% for item in @itemsToChange: %>
<li>
<% if item.to_create is true: %>
<%- @T('Create') %> <%= item.object %>.<%= item.name %> (<%= item.data_type %>)
<% else if item.to_delete is true: %>
<%- @T('Delete') %> <%= item.object %>.<%= item.name %> (<%= item.data_type %>)
<% end %>
<% end %>
</p>
<div class="box-controls">
<div class="btn btn--text btn--secondary js-discard">Discard Changes</div>
<div class="btn btn--primary js-sync align-right"><%- @T( 'Update Database' ) %></div>
<div class="btn btn--text btn--secondary js-discard"><%- @T('Discard Changes') %></div>
<div class="btn btn--primary js-execute align-right"><%- @T('Update Database') %></div>
</div>
</div>
<% end %>
<!--
<div class="box box--message">
<div class="box-progress">
<div class="box-progress-title"><%- @T('Updating Database') %></div>
@ -24,20 +45,31 @@
</div>
</div>
</div>
-->
<table class="table table-striped table-hover is-disabled">
<thead>
<tr>
<th class=""><%- @T('Display') %></th>
<th class=""><%- @T('Name') %></th>
<th class=""><%- @T('Type') %></th>
<th class="" style="width: 200px;"><%- @T('Type') %></th>
<th class="" style="width: 140px;"><%- @T('Action') %></th>
</tr>
</thead>
<tbody>
<% for item in @items: %>
<tr class="<% if item.active is false: %>is-inactive<% end %> js-edit u-clickable" data-id="<%- item.id %>">
<tr class="<% if item.editable is false: %>is-grayed-out u-notAllowed<% else: %><% if item.active is false: %>is-inactive<% end %> js-edit u-clickable<% end %>" data-id="<%- item.id %>">
<td><%= item.display %></td>
<td><%= item.name %></td>
<td><%= item.data_type %></td>
<td>
<% if item.to_create is true: %>
<%- @T('will be created') %>
<% else if item.to_delete is true: %>
<%- @T('will be deleted') %>
<% else if item.editable isnt false: %>
<a href="#" class="js-delete" title="<%- @Ti('Delete') %>"><%- @Icon('trash') %></a>
<% end %>
</td>
</tr>
<% end %>
</tbody>

View file

@ -0,0 +1,30 @@
<div>
<table class="settings-list" style="width: 100%;">
<thead>
<tr>
<th><%- @T('Role') %>
<th><%- @T('Screen') %>
<th style="width: 50%;"><%- @T('Options') %>
</thead>
<tbody>
<% for role, screenOptions of @data: %>
<tr>
<td class="settings-list-control-cell">
<%= role %>
<td class="settings-list-control-cell">
<td class="settings-list-row-control">
<% for screen, options of screenOptions: %>
<tr>
<td class="settings-list-control-cell">
<td class="settings-list-control-cell">
<%= screen %>
<td class="settings-list-row-control">
<% for key, defaultValue of options: %>
<%= @T(key) %>: <input name="{boolean}screens::<%= screen %>::<%= role %>::<%= key %>" type="checkbox" <% if (@init && defaultValue is true) || (@params && @params.screens && @params.screens[screen] && @params.screens[screen][role] && @params.screens[screen][role][key] is true) : %>checked<% end %> value="true">
<% end %>
<% end %>
<% end %>
</tbody>
</table>
</div>

View file

@ -329,7 +329,7 @@ pre code.hljs {
text-align: center;
white-space: nowrap;
vertical-align: middle;
.icon {
vertical-align: middle;
margin-top: -3px;
@ -339,7 +339,7 @@ pre code.hljs {
&.btn--icon--last .icon {
margin-left: 5px; // so far only used in ticket_zoom secondaryAction dropup
}
&:focus {
box-shadow: 0 0 0 3px hsl(201,62%,90%);
}
@ -348,7 +348,7 @@ pre code.hljs {
padding-top: 5px;
padding-bottom: 4px;
}
&.btn--slim {
padding-left: 12px;
padding-right: 12px;
@ -365,7 +365,7 @@ pre code.hljs {
border: none;
margin: 5px 6px 0;
vertical-align: baseline; /* calendar_subscriptions.jst.eco */
.icon {
vertical-align: middle;
margin-right: 5px;
@ -393,7 +393,7 @@ pre code.hljs {
padding: 2px 11px 0 !important;
display: inline-flex;
align-items: center;
.icon {
margin: -2px 5px 0 -2px;
fill: hsl(0,0%,60%);
@ -402,11 +402,11 @@ pre code.hljs {
.icon:only-child {
margin: 0;
}
&.btn--slim {
padding-left: 7px !important;
padding-right: 7px !important;
&.btn--small {
padding-left: 5px !important;
padding-right: 5px !important;
@ -425,7 +425,7 @@ pre code.hljs {
&.btn--onDark {
background: none;
color: white;
svg {
fill: currentColor;
opacity: 1;
@ -453,7 +453,7 @@ pre code.hljs {
&.btn--success {
color: white;
background: hsl(145,51%,45%);
&:active {
background: hsl(145,51%,35%);
}
@ -461,7 +461,7 @@ pre code.hljs {
&.btn--secondary {
background: white;
color: hsl(145,51%,45%);
&:active {
background: hsl(0,0%,98%);
}
@ -486,7 +486,7 @@ pre code.hljs {
&.btn--secondary {
background: white;
color: hsl(0,65%,55%);
&:active {
background: hsl(0,0%,98%);
}
@ -502,12 +502,12 @@ pre code.hljs {
background: none;
vertical-align: baseline;
text-align: left;
.icon {
fill: currentColor;
opacity: 1;
}
&:active {
color: hsl(203,65%,40%);
background: none;
@ -516,7 +516,7 @@ pre code.hljs {
&.btn--secondary {
color: hsl(0,0%,68%);
text-decoration: underline;
&:active {
color: hsl(0,0%,53%);
}
@ -524,7 +524,7 @@ pre code.hljs {
&.btn--positive {
color: hsl(145,51%,45%);
&:active {
color: hsl(145,51%,30%);
background: none;
@ -533,7 +533,7 @@ pre code.hljs {
&.btn--danger {
color: hsl(0,65%,55%);
&:active {
color: hsl(0,65%,40%);
background: none;
@ -543,7 +543,7 @@ pre code.hljs {
&.btn--subtle {
text-decoration: underline;
color: hsl(0,0%,85%);
&:active {
color: hsl(0,0%,75%);
}
@ -560,7 +560,7 @@ pre code.hljs {
&.btn--quad {
padding: 10px 12px 9px;
.icon {
margin-top: -1px;
}
@ -581,7 +581,7 @@ pre code.hljs {
&.btn--dropdown {
position: relative;
select {
opacity: 0;
width: 100%;
@ -627,13 +627,13 @@ pre code.hljs {
.visibility-change {
/*
Interactive Visibility Change Classes:
<div class="visibility-change">
<svg class="icon-marker" data-visible="active"><use xlink:href="#icon-marker" /></svg>
</div>
Important: HTML Order active > hover > normal
*/
@ -645,7 +645,7 @@ pre code.hljs {
&.is-active [data-visible=active] {
display: block;
& ~ [data-visible=normal] {
display: none
}
@ -653,7 +653,7 @@ pre code.hljs {
&:hover [data-visible=hover] {
display: block;
& ~ [data-visible=normal] {
display: none
}
@ -663,7 +663,7 @@ pre code.hljs {
.btn-group {
display: inline-flex;
flex-wrap: wrap;
&.btn-group--full {
display: flex;
}
@ -671,7 +671,7 @@ pre code.hljs {
& + .btn-group {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid hsl(240,2%,92%);
border-top: 1px solid hsl(240,2%,92%);
}
.btn + .btn {
@ -682,7 +682,7 @@ pre code.hljs {
padding: 6px 10px 5px; /* reporting main.eco */
display: inline-block;
border-radius: 3px;
&.is-selected {
background: hsl(203,65%,55%);
color: white;
@ -695,11 +695,11 @@ pre code.hljs {
align-items: center;
position: relative;
user-select: none;
.dropdown-menu {
margin-bottom: 0;
}
&.is-open .dropdown-menu {
display: block;
}
@ -721,7 +721,7 @@ pre code.hljs {
height: 34px;
align-items: center;
line-height: 35px;
&.is-clickable {
background: hsl(203,65%,55%);
color: white;
@ -734,15 +734,15 @@ pre code.hljs {
&.is-blinking {
animation: pulsate 667ms ease-in-out infinite alternate;
}
&:not(:last-child):not(:only-child) {
border-right: none;
}
&:first-child {
border-radius: 5px 0 0 5px;
}
&:last-child {
border-radius: 0 5px 5px 0;
}
@ -775,9 +775,9 @@ pre code.hljs {
line-height: 12px;
opacity: 0.5;
position: relative;
/*
border in its own layer to make it more
/*
border in its own layer to make it more
translucend but still depend on the currentColor
*/
&:after {
@ -852,7 +852,7 @@ table {
.table {
display: table;
small {
color: inherit;
}
@ -876,7 +876,7 @@ table {
}
td {
height: 40px;
}
}
}
.table th:not(.noTruncate) .table-column-title,
@ -924,7 +924,7 @@ th.align-right {
.table-hover > tbody > tr:hover > td {
background: white;
}
.table-hover > tbody > tr:hover > th {
background: rgba(0,8,14,.015);
}
@ -938,7 +938,7 @@ th.align-right {
padding: 10px;
margin-right: -10px;
z-index: 1;
&:after {
content: "";
display: block;
@ -957,7 +957,7 @@ th.align-right {
.table tr.is-inactive {
opacity: 0.5;
text-decoration: line-through;
a {
color: #bbb;
}
@ -965,7 +965,7 @@ th.align-right {
.table tr.is-grayed-out {
color: hsl(120,1%,77%);
.icon {
opacity: 0.33;
}
@ -1210,7 +1210,7 @@ h3 {
margin: 20px 0 8px;
color: hsl(207,7%,29%);
font-weight: normal;
.subtitle {
display: inline;
font-size: 12px;
@ -1318,7 +1318,7 @@ fieldset > .form-group {
.form-group {
margin-bottom: 16px;
&.form-group--inactive {
opacity: 0.5;
}
@ -1335,7 +1335,7 @@ fieldset > .form-group {
.merge-group {
display: flex;
align-items: stretch;
&.merge-group--inactive {
}
@ -1353,7 +1353,7 @@ fieldset > .form-group {
border-bottom: 1px solid #eee;
}
}
.merge-target,
.merge-source {
flex: 1;
@ -1372,7 +1372,7 @@ fieldset > .form-group {
&:first-of-type {
margin-top: 6px;
.merge-source,
.merge-target {
border-top: 1px solid #eee;
@ -1382,7 +1382,7 @@ fieldset > .form-group {
&:last-of-type {
margin-bottom: 6px;
.merge-source,
.merge-target {
border-bottom: 1px solid #eee;
@ -1393,7 +1393,7 @@ fieldset > .form-group {
.merge-value {
margin-bottom: 3px;
}
.form-group {
padding: 0;
}
@ -1405,7 +1405,7 @@ fieldset > .form-group {
&.merge-group--multi {
.merge-value + .merge-value {
margin-top: 12px;
}
}
}
}
@ -1413,7 +1413,7 @@ fieldset > .form-group {
flex: 1;
align-self: flex-end;
}
.merge-control {
margin-bottom: 5px;
height: 31px;
@ -1434,7 +1434,7 @@ fieldset > .form-group {
display: flex;
align-items: center;
justify-content: center;
.line-arrow {
fill: #e6e6e6;
}
@ -1446,7 +1446,7 @@ fieldset > .form-group {
position: relative;
display: flex;
align-items: center;
label {
margin: 0;
}
@ -1488,7 +1488,7 @@ fieldset > .form-group {
top: -2px;
position: relative;
margin-left: auto;
.icon-help {
display: block;
}
@ -1501,7 +1501,7 @@ fieldset > .form-group {
.form-group.formGroup--halfSize {
width: 50%;
float: left;
.form-control {
min-width: initial;
}
@ -1520,7 +1520,7 @@ fieldset > .form-group {
flex-shrink: 0;
}
input[type="radio"],
input[type="radio"],
input[type="checkbox"] {
margin: 0;
}
@ -1553,7 +1553,7 @@ textarea,
padding: 5px 8px 4px;
height: 31px;
line-height: 20px;
&.form-control--multiline {
min-height: 31px;
}
@ -1598,7 +1598,7 @@ input.time {
padding: 0 6px;
line-height: 42px;
flex-shrink: 0;
&.form-control--small {
line-height: 31px;
}
@ -1657,7 +1657,7 @@ select.form-control:not([multiple]) {
padding: 0;
line-height: inherit;
height: auto;
&:focus {
box-shadow: none;
}
@ -1736,22 +1736,22 @@ input.has-error {
.controls--button {
display: flex;
input,
.form-control {
flex: 1;
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
&:focus + .controls-button {
.controls-button-inner {
border-color: hsl(200,71%,59%);
}
/*
fake the form-control outline
/*
fake the form-control outline
*/
&:before {
@ -1852,7 +1852,7 @@ input.has-error {
align-items: center;
justify-content: center;
@extend .zIndex-8;
.modal-backdrop {
bottom: 0;
width: 200%;
@ -1915,7 +1915,7 @@ input.has-error {
.modal-control {
padding-left: 14px;
padding-right: 14px;
.btn.is-disabled {
opacity: 1;
color: hsl(240,5%,83%);
@ -1958,7 +1958,7 @@ kbd {
display: flex;
}
.pagination > li > a,
.pagination > li > a,
.pagination > li > span {
padding: 0;
width: 31px;
@ -1966,11 +1966,11 @@ kbd {
border-color: #e5e5e5;
}
.pagination > .active > a,
.pagination > .active > span,
.pagination > .active > a:hover,
.pagination > .active > span:hover,
.pagination > .active > a:focus,
.pagination > .active > a,
.pagination > .active > span,
.pagination > .active > a:hover,
.pagination > .active > span:hover,
.pagination > .active > a:focus,
.pagination > .active > span:focus {
background: #0F94D6;
border-color: #0F94D6;
@ -2001,7 +2001,7 @@ kbd {
.page-header-title {
display: flex;
align-items: center;
.zammad-switch {
margin-right: 9px;
}
@ -2017,7 +2017,7 @@ kbd {
justify-self: center;
padding-left: 9px;
margin: 0 auto;
& + .page-header-meta {
margin-left: 0;
flex: none;
@ -2031,7 +2031,7 @@ kbd {
justify-content: flex-end;
flex: 1;
min-width: 0; /* firefox flexbug */
.btn {
overflow: hidden;
text-overflow: ellipsis;
@ -2081,7 +2081,7 @@ kbd {
align-items: center;
justify-content: center;
}
.page-loading-label {
margin-left: 10px;
margin-top: 1px;
@ -2096,7 +2096,7 @@ kbd {
margin: 0;
color: #bcbcbc;
font-size: 12px;
&.help-block--center {
text-align: center;
}
@ -2126,7 +2126,7 @@ kbd {
box-shadow:
0 8px 17px 0 rgba(0, 0, 0, 0.2),
0 6px 20px 0 rgba(0, 0, 0, 0.19);
label {
color: hsl(0,0%,60%);
}
@ -2174,13 +2174,13 @@ kbd {
radial-gradient(circle at 2.58% 98.57%, #392e3e, transparent 51%),
radial-gradient(circle at 82.11% 97.15%, #5c404e, transparent 100%),
radial-gradient(circle at 50% 50%, #8b6b76, #8b6b76 100%);
a {
color: white;
}
.hero-unit {
box-shadow:
box-shadow:
0 8px 17px 0 rgba(0, 0, 0, 0.1),
0 6px 20px 0 rgba(0, 0, 0, 0.09);
}
@ -2227,7 +2227,7 @@ kbd {
bottom: 0;
left: 0;
right: 0;
.icon-logo {
margin-right: 8px;
margin-top: -11px;
@ -2299,12 +2299,12 @@ ol.tabs li {
flex: 1 1 auto;
@extend .u-clickable;
white-space: nowrap;
&.active {
color: white;
background: #444a4f;
box-shadow: none;
.tab-badge {
color: hsl(204,3%,65%);
}
@ -2316,19 +2316,19 @@ ol.tabs li {
display: flex;
align-items: center;
justify-content: center;
.arrow {
margin-left: 10px;
opacity: 0.75;
}
.icon {
fill: hsl(0,0%,70%);
}
&.active {
background: white;
.icon {
fill: #444a4f;
opacity: 1;
@ -2359,7 +2359,7 @@ ol.tabs li {
display: inline-flex;
margin-left: 0;
margin-right: 0;
.tab {
flex: none;
}
@ -2369,11 +2369,11 @@ ol.tabs li {
margin: 28px auto;
font-size: 14px;
border-radius: 8px;
.tab {
height: auto;
padding: 10px 23px 9px;
&:first-child {
border-radius: 8px 0 0 8px;
}
@ -2443,7 +2443,7 @@ ol.tabs li {
align-items: center;
text-decoration: none;
width: calc(33.33% - 6px);
&.auth-provider--wide {
padding-right: 25px;
}
@ -2538,11 +2538,11 @@ ol.tabs li {
}
@keyframes rotateplane {
0% {
0% {
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
} 50% {
} 50% {
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
} 100% {
} 100% {
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
}
}
@ -2551,7 +2551,7 @@ ol.tabs li {
padding: 2px;
margin: -2px 0;
cursor: pointer;
/* :after technique for bigger click area */
&:after {
content: "";
@ -2565,12 +2565,12 @@ ol.tabs li {
.icon-status {
fill: $ok-color;
&.inline {
margin-top: -3px;
vertical-align: middle;
}
&.inactive {
fill: hsl(198,18%,86%);
}
@ -2611,7 +2611,7 @@ ol.tabs li {
*/
form {
margin: 0;
&.form--flexibleWidth .controls {
display: table;
}
@ -2622,7 +2622,7 @@ form {
display: flex;
align-items: center;
margin-top: 10px;
.btn + .btn:not(.align-right) {
margin-left: 20px;
}
@ -2688,13 +2688,13 @@ footer {
height: 41px;
display: none;
align-items: center;
.tabsHolder {
flex: 1;
margin-right: 20px;
min-width: 0; /* Firefox bug fix */
}
.tabs {
margin: 0;
position: relative;
@ -2726,11 +2726,11 @@ footer {
min-width: $minWidth - $sidebarWidth - $navigationWidth;
background: white;
z-index: 1;
box-shadow:
box-shadow:
0 -1px rgba(0,0,0,.05),
0 -2px rgba(0,0,0,.03),
0 -3px rgba(0,0,0,.01);
@media only screen and (max-width: $largeScreenBreakpoint) {
left: $navigationWidth;
min-width: $minWidth - $sidebarWidth;
@ -2855,7 +2855,7 @@ footer {
justify-content: center;
font-size: 16px;
color: hsl(0,0%,45%);
.icon {
margin-right: 10px;
filter: grayscale(90%);
@ -2931,7 +2931,7 @@ footer {
input:not(:checked) + label { // switch background
background: hsl(202,68%,43%);
}
label:after {
background: white;
}
@ -5150,7 +5150,7 @@ footer {
border-radius: 3px;
color: white;
border: none;
&.alert--info {
background: hsl(203,65%,55%);
}

View file

@ -9,14 +9,12 @@ class ObjectManagerAttributesController < ApplicationController
render json: {
objects: ObjectManager.list_frontend_objects,
}
#model_index_render(ObjectManager::Attribute, params)
end
# GET /object_manager_attributes
def index
return if deny_if_not_role(Z_ROLENAME_ADMIN)
render json: ObjectManager::Attribute.list_full
#model_index_render(ObjectManager::Attribute, params)
end
# GET /object_manager_attributes/1
@ -28,18 +26,68 @@ class ObjectManagerAttributesController < ApplicationController
# POST /object_manager_attributes
def create
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_create_render(ObjectManager::Attribute, params)
check_params
object_manager_attribute = ObjectManager::Attribute.add(
object: params[:object],
name: params[:name],
display: params[:display],
data_type: params[:data_type],
data_option: params[:data_option],
active: params[:active],
screens: params[:screens],
position: 1550,
editable: true,
)
render json: object_manager_attribute.attributes_with_associations, status: :created
end
# PUT /object_manager_attributes/1
def update
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_update_render(ObjectManager::Attribute, params)
check_params
object_manager_attribute = ObjectManager::Attribute.add(
object: params[:object],
name: params[:name],
display: params[:display],
data_type: params[:data_type],
data_option: params[:data_option],
active: params[:active],
screens: params[:screens],
position: 1550,
editable: true,
)
render json: object_manager_attribute.attributes_with_associations, status: :ok
end
# DELETE /object_manager_attributes/1
def destroy
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_destory_render(ObjectManager::Attribute, params)
object_manager_attribute = ObjectManager::Attribute.find(params[:id])
ObjectManager::Attribute.remove(
object_lookup_id: object_manager_attribute.object_lookup_id,
name: object_manager_attribute.name,
)
model_destory_render_item
end
# POST /object_manager_attributes_discard_changes
def discard_changes
return if deny_if_not_role(Z_ROLENAME_ADMIN)
ObjectManager::Attribute.discard_changes
render json: {}, status: :ok
end
# POST /object_manager_attributes_execute_migrations
def execute_migrations
return if deny_if_not_role(Z_ROLENAME_ADMIN)
ObjectManager::Attribute.migration_execute
render json: {}, status: :ok
end
private
def check_params
return if !params[:data_option][:null].nil?
params[:data_option][:null] = true
end
end

View file

@ -13,8 +13,8 @@ class Link < ApplicationModel
=begin
links = Link.list(
:link_object => 'Ticket',
:link_object_value => 1
link_object: 'Ticket',
link_object_value: 1
)
=end
@ -55,19 +55,19 @@ class Link < ApplicationModel
=begin
Link.add(
:link_type => 'normal',
:link_object_source => 'Ticket',
:link_object_source_value => 6,
:link_object_target => 'Ticket',
:link_object_target_value => 31
link_type: 'normal',
link_object_source: 'Ticket',
link_object_source_value: 6,
link_object_target: 'Ticket',
link_object_target_value: 31
)
Link.add(
:link_types_id => 12,
:link_object_source_id => 1,
:link_object_source_value => 1,
:link_object_target_id => 1,
:link_object_target_value => 1
link_types_id: 12,
link_object_source_id: 1,
link_object_source_value: 1,
link_object_target_id: 1,
link_object_target_value: 1
)
=end
@ -98,11 +98,11 @@ class Link < ApplicationModel
=begin
Link.remove(
:link_type => 'normal',
:link_object_source => 'Ticket',
:link_object_source_value => 6,
:link_object_target => 'Ticket',
:link_object_target_value => 31
link_type: 'normal',
link_object_source: 'Ticket',
link_object_source_value: 6,
link_object_target: 'Ticket',
link_object_target_value: 31
)
=end

View file

@ -6,7 +6,7 @@ class ObjectManager
list all backend managed object
ObjectManager.list_objects()
ObjectManager.list_objects
=end
@ -18,251 +18,12 @@ list all backend managed object
list all frontend managed object
ObjectManager.list_frontend_objects()
ObjectManager.list_frontend_objects
=end
def self.list_frontend_objects
%w(Ticket User Organization) #, 'Group' ]
end
end
class ObjectManager::Attribute < ApplicationModel
self.table_name = 'object_manager_attributes'
belongs_to :object_lookup, class_name: 'ObjectLookup'
validates :name, presence: true
store :screens
store :data_option
=begin
list of all attributes
result = ObjectManager::Attribute.list_full
result = [
{
name: 'some name',
display: '...',
}.
],
=end
def self.list_full
result = ObjectManager::Attribute.all
attributes = []
assets = {}
result.each {|item|
attribute = item.attributes
attribute[:object] = ObjectLookup.by_id( item.object_lookup_id )
attribute.delete('object_lookup_id')
attributes.push attribute
}
attributes
end
=begin
add a new attribute entry for an object
ObjectManager::Attribute.add(
:object => 'Ticket',
:name => 'group_id',
:frontend => 'Group',
:data_type => 'select',
:data_option => {
:relation => 'Group',
:relation_condition => { :access => 'rw' },
:multiple => false,
:null => true,
:translate => false,
},
:editable => false,
:active => true,
:screens => {
:create => {
'-all-' => {
:required => true,
},
},
:edit => {
:Agent => {
:required => true,
},
},
},
:pending_migration => false,
:position => 20,
:created_by_id => 1,
:updated_by_id => 1,
:created_at => '2014-06-04 10:00:00',
:updated_at => '2014-06-04 10:00:00',
)
=end
def self.add(data)
# lookups
if data[:object]
data[:object_lookup_id] = ObjectLookup.by_name( data[:object] )
end
data.delete(:object)
# check newest entry - is needed
result = ObjectManager::Attribute.find_by(
object_lookup_id: data[:object_lookup_id],
name: data[:name],
)
if result
return result.update_attributes(data)
end
# create history
ObjectManager::Attribute.create(data)
end
=begin
remove attribute entry for an object
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'group_id',
)
use "force: true" to delete also not editable fields
=end
def self.remove(data)
# lookups
if data[:object]
data[:object_lookup_id] = ObjectLookup.by_name(data[:object])
end
# check newest entry - is needed
result = ObjectManager::Attribute.find_by(
object_lookup_id: data[:object_lookup_id],
name: data[:name],
)
if !result
raise "ERROR: No such field #{data[:object]}.#{data[:name]}"
end
if !data[:force] && !result.editable
raise "ERROR: #{data[:object]}.#{data[:name]} can't be removed!"
end
result.destroy
end
=begin
get the attribute model based on object and name
attribute = ObjectManager::Attribute.get(
object: 'Ticket',
name: 'group_id',
)
=end
def self.get(data)
# lookups
if data[:object]
data[:object_lookup_id] = ObjectLookup.by_name( data[:object] )
end
ObjectManager::Attribute.find_by(
object_lookup_id: data[:object_lookup_id],
name: data[:name],
)
end
=begin
get user based list of object attributes
attribute_list = ObjectManager::Attribute.by_object('Ticket', user)
returns:
[
{ name: 'api_key', display: 'API KEY', tag: 'input', null: true, edit: true, maxlength: 32 },
{ name: 'api_ip_regexp', display: 'API IP RegExp', tag: 'input', null: true, edit: true },
{ name: 'api_ip_max', display: 'API IP Max', tag: 'input', null: true, edit: true },
]
=end
def self.by_object(object, user)
# lookups
if object
object_lookup_id = ObjectLookup.by_name(object)
end
# get attributes in right order
result = ObjectManager::Attribute.where(
object_lookup_id: object_lookup_id,
active: true,
).order('position ASC')
attributes = []
result.each {|item|
data = {
name: item.name,
display: item.display,
tag: item.data_type,
#:null => item.null,
}
if item.screens
data[:screen] = {}
item.screens.each {|screen, roles_options|
data[:screen][screen] = {}
roles_options.each {|role, options|
if role == '-all-'
data[:screen][screen] = options
elsif user && user.role?(role)
data[:screen][screen] = options
end
}
}
end
if item.data_option
data = data.merge(item.data_option.symbolize_keys)
end
attributes.push data
}
attributes
end
=begin
get user based list of object attributes as hash
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
returns:
{
'api_key' => { name: 'api_key', display: 'API KEY', tag: 'input', null: true, edit: true, maxlength: 32 },
'api_ip_regexp' => { name: 'api_ip_regexp', display: 'API IP RegExp', tag: 'input', null: true, edit: true },
'api_ip_max' => { name: 'api_ip_max', display: 'API IP Max', tag: 'input', null: true, edit: true },
}
=end
def self.by_object_as_hash(object, user)
list = by_object(object, user)
hash = {}
list.each {|item|
hash[ item[:name] ] = item
}
hash
%w(Ticket User Organization Group)
end
end

View file

@ -180,12 +180,18 @@ returns
tickets.each { |ticket|
article_id = nil
article = Ticket::Article.last_customer_agent_article(ticket.id)
if article
article_id = article.id
end
# send notification
Transaction::BackgroundJob.run(
object: 'Ticket',
type: 'reminder_reached',
object_id: ticket.id,
article_id: ticket.articles.last.id,
article_id: article_id,
user_id: 1,
)
@ -220,13 +226,19 @@ returns
# get sla
sla = ticket.escalation_calculation_get_sla
article_id = nil
article = Ticket::Article.last_customer_agent_article(ticket.id)
if article
article_id = article.id
end
# send escalation
if ticket.escalation_time < Time.zone.now
Transaction::BackgroundJob.run(
object: 'Ticket',
type: 'escalation',
object_id: ticket.id,
article_id: ticket.articles.last.id,
article_id: article_id,
user_id: 1,
)
result.push ticket
@ -238,7 +250,7 @@ returns
object: 'Ticket',
type: 'escalation_warning',
object_id: ticket.id,
article_id: ticket.articles.last.id,
article_id: article_id,
user_id: 1,
)
result.push ticket

View file

@ -61,6 +61,11 @@ class Ticket::Article < ApplicationModel
article
end
def self.last_customer_agent_article(ticket_id)
sender = Ticket::Article::Sender.lookup(name: 'System')
Ticket::Article.where('ticket_id = ? AND sender_id NOT IN (?)', ticket_id, sender.id).order('created_at DESC').first
end
private
# strip not wanted chars

View file

@ -2,11 +2,13 @@ Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path
# object_manager
match api_path + '/object_manager_attributes_list', to: 'object_manager_attributes#list', via: :get
match api_path + '/object_manager_attributes', to: 'object_manager_attributes#index', via: :get
match api_path + '/object_manager_attributes/:id', to: 'object_manager_attributes#show', via: :get
match api_path + '/object_manager_attributes', to: 'object_manager_attributes#create', via: :post
match api_path + '/object_manager_attributes/:id', to: 'object_manager_attributes#update', via: :put
match api_path + '/object_manager_attributes/:id', to: 'object_manager_attributes#destroy', via: :delete
match api_path + '/object_manager_attributes_list', to: 'object_manager_attributes#list', via: :get
match api_path + '/object_manager_attributes', to: 'object_manager_attributes#index', via: :get
match api_path + '/object_manager_attributes/:id', to: 'object_manager_attributes#show', via: :get
match api_path + '/object_manager_attributes', to: 'object_manager_attributes#create', via: :post
match api_path + '/object_manager_attributes/:id', to: 'object_manager_attributes#update', via: :put
match api_path + '/object_manager_attributes/:id', to: 'object_manager_attributes#destroy', via: :delete
match api_path + '/object_manager_attributes_discard_changes', to: 'object_manager_attributes#discard_changes', via: :post
match api_path + '/object_manager_attributes_execute_migrations', to: 'object_manager_attributes#execute_migrations', via: :post
end

View file

@ -447,7 +447,9 @@ class CreateBase < ActiveRecord::Migration
t.column :editable, :boolean, null: false, default: true
t.column :active, :boolean, null: false, default: true
t.column :screens, :string, limit: 2000, null: true
t.column :pending_migration, :boolean, null: false, default: true
t.column :to_create, :boolean, null: false, default: true
t.column :to_migrate, :boolean, null: false, default: true
t.column :to_delete, :boolean, null: false, default: false
t.column :position, :integer, null: false
t.column :created_by_id, :integer, null: false
t.column :updated_by_id, :integer, null: false

View file

@ -2,6 +2,7 @@ class UpdateObjectManager3 < ActiveRecord::Migration
def up
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'organization_id',
display: 'Organization',
@ -29,13 +30,16 @@ class UpdateObjectManager3 < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 900,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'customer_id',
display: 'Customer',
@ -60,7 +64,9 @@ class UpdateObjectManager3 < ActiveRecord::Migration
},
edit: {},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 10,
created_by_id: 1,
updated_by_id: 1,

View file

@ -223,6 +223,7 @@ class UpdateOverview4 < ActiveRecord::Migration
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'title',
display: 'Title',
@ -243,11 +244,14 @@ class UpdateOverview4 < ActiveRecord::Migration
},
edit: {},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 15,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'group_id',
display: 'Group',
@ -275,7 +279,9 @@ class UpdateOverview4 < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 25,
)

View file

@ -2,6 +2,7 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
def up
UserInfo.current_user_id = 1
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'login',
display: 'Login',
@ -26,11 +27,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 100,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'firstname',
display: 'Firstname',
@ -70,11 +74,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 200,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'lastname',
display: 'Lastname',
@ -114,11 +121,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 300,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'email',
display: 'Email',
@ -158,11 +168,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 400,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'web',
display: 'Web',
@ -190,11 +203,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 500,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'phone',
display: 'Phone',
@ -222,11 +238,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 600,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'mobile',
display: 'Mobile',
@ -254,11 +273,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 700,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'fax',
display: 'Fax',
@ -286,11 +308,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 800,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'organization_id',
display: 'Organization',
@ -323,11 +348,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 900,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'department',
display: 'Department',
@ -355,11 +383,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1000,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'street',
display: 'Street',
@ -386,11 +417,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1100,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'zip',
display: 'Zip',
@ -418,11 +452,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1200,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'city',
display: 'City',
@ -450,11 +487,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1300,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'address',
display: 'Address',
@ -482,11 +522,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1350,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'password',
display: 'Password',
@ -515,11 +558,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
view: {}
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1400,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'vip',
display: 'VIP',
@ -551,11 +597,14 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1490,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'note',
display: 'Note',
@ -587,16 +636,20 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1500,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'role_ids',
display: 'Roles',
data_type: 'checkbox',
data_option: {
default: '',
multiple: true,
null: false,
relation: 'Role',
@ -618,51 +671,20 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1600,
)
ObjectManager::Attribute.add(
object: 'User',
name: 'group_ids',
display: 'Groups',
data_type: 'checkbox',
data_option: {
multiple: true,
null: true,
relation: 'Group',
},
editable: false,
active: true,
screens: {
signup: {},
invite_agent: {
'-all-' => {
null: false,
},
},
invite_customer: {},
edit: {
Admin: {
null: true,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
pending_migration: false,
position: 1700,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'active',
display: 'Active',
data_type: 'active',
data_option: {
null: true,
default: true,
},
editable: false,
@ -682,7 +704,9 @@ class ObjectManagerUpdateUser < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1800,
)

View file

@ -2,6 +2,7 @@
class EmailTicketCc < ActiveRecord::Migration
def up
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'cc',
display: 'Cc',
@ -22,7 +23,9 @@ class EmailTicketCc < ActiveRecord::Migration
create_middle: {},
edit: {}
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 11,
created_by_id: 1,
updated_by_id: 1,

View file

@ -2,11 +2,13 @@
class OnlyOneGroup < ActiveRecord::Migration
def up
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'group_id',
display: 'Group',
data_type: 'select',
data_option: {
default: '',
relation: 'Group',
relation_condition: { access: 'rw' },
nulloption: true,
@ -30,17 +32,21 @@ class OnlyOneGroup < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 25,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'group_ids',
display: 'Groups',
data_type: 'checkbox',
data_option: {
default: '',
multiple: true,
null: true,
relation: 'Group',
@ -67,12 +73,15 @@ class OnlyOneGroup < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1700,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'street',
display: 'Street',
@ -99,13 +108,16 @@ class OnlyOneGroup < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1100,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'zip',
display: 'Zip',
@ -133,13 +145,16 @@ class OnlyOneGroup < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1200,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'city',
display: 'City',
@ -167,13 +182,16 @@ class OnlyOneGroup < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1300,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'address',
display: 'Address',
@ -201,7 +219,9 @@ class OnlyOneGroup < ActiveRecord::Migration
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1350,
created_by_id: 1,
updated_by_id: 1,

View file

@ -0,0 +1,60 @@
class RoleGroupRemove < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
object_lookup_id = ObjectLookup.by_name('User')
record = ObjectManager::Attribute.find_by(
object_lookup_id: object_lookup_id,
name: 'role_ids',
)
record.destroy if record
record = ObjectManager::Attribute.find_by(
object_lookup_id: object_lookup_id,
name: 'group_ids',
)
record.destroy if record
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'role_ids',
display: 'Permissions',
data_type: 'user_permission',
data_option: {
null: false,
item_class: 'checkbox',
},
editable: false,
active: true,
screens: {
signup: {},
invite_agent: {
'-all-' => {
null: false,
hideMode: {
rolesSelected: ['Agent'],
rolesNot: ['Customer'],
}
},
},
invite_customer: {},
edit: {
Admin: {
null: true,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 1600,
updated_by_id: 1,
created_by_id: 1,
)
end
end

View file

@ -0,0 +1,302 @@
class UpdateObjectManager < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
add_column :object_manager_attributes, :to_create, :boolean, null: false, default: true
add_column :object_manager_attributes, :to_migrate, :boolean, null: false, default: true
add_column :object_manager_attributes, :to_delete, :boolean, null: false, default: false
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'name',
display: 'Name',
data_type: 'input',
data_option: {
type: 'text',
maxlength: 150,
null: false,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: false,
},
},
edit: {
'-all-' => {
null: false,
},
},
view: {
'-all-' => {
shown: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 200,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'assignment_timeout',
display: 'Assignment Timeout',
data_type: 'integer',
data_option: {
maxlength: 150,
null: true,
note: 'Assignment timeout in minutes if assigned agent is not working on it. Ticket will be shown as unassigend.',
min: 0,
max: 999_999,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 300,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'follow_up_possible',
display: 'Follow up possible',
data_type: 'select',
data_option: {
default: 'yes',
options: {
yes: 'yes',
reject: 'reject follow up/do not reopen Ticket',
new_ticket: 'do not reopen Ticket but create new Ticket'
},
null: false,
note: 'Follow up for closed ticket possible or not.',
translate: true
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 400,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'follow_up_assignment',
display: 'Assign Follow Ups',
data_type: 'select',
data_option: {
default: 'yes',
options: {
true: 'yes',
false: 'no',
},
null: false,
note: 'Assign follow up to latest agent again.',
translate: true
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 500,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'email_address_id',
display: 'Email',
data_type: 'select',
data_option: {
default: '',
multiple: false,
null: true,
relation: 'EmailAddress',
nulloption: true,
do_not_log: true,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 600,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'signature_id',
display: 'Signature',
data_type: 'select',
data_option: {
default: '',
multiple: false,
null: true,
relation: 'Signature',
nulloption: true,
do_not_log: true,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 600,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'note',
display: 'Note',
data_type: 'richtext',
data_option: {
type: 'text',
maxlength: 250,
null: true,
note: 'Notes are visible to agents only, never to customers.',
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
view: {
'-all-' => {
shown: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 1500,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'active',
display: 'Active',
data_type: 'active',
data_option: {
null: true,
default: true,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
Admin: {
null: false,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 1800,
created_by_id: 1,
updated_by_id: 1,
)
end
end

View file

@ -2481,6 +2481,7 @@ Network::Item::Comment.create(
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'title',
display: 'Title',
@ -2501,11 +2502,14 @@ ObjectManager::Attribute.add(
},
edit: {},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 15,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'customer_id',
display: 'Customer',
@ -2530,15 +2534,19 @@ ObjectManager::Attribute.add(
},
edit: {},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 10,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'type',
display: 'Type',
data_type: 'select',
data_option: {
default: '',
options: {
'Incident' => 'Incident',
'Problem' => 'Problem',
@ -2549,7 +2557,7 @@ ObjectManager::Attribute.add(
null: true,
translate: true,
},
editable: false,
editable: true,
active: false,
screens: {
create_middle: {
@ -2564,15 +2572,19 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 20,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'group_id',
display: 'Group',
data_type: 'select',
data_option: {
default: '',
relation: 'Group',
relation_condition: { access: 'rw' },
nulloption: true,
@ -2596,15 +2608,19 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 25,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'owner_id',
display: 'Owner',
data_type: 'select',
data_option: {
default: '',
relation: 'User',
relation_condition: { roles: 'Agent' },
nulloption: true,
@ -2627,10 +2643,13 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 30,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'state_id',
display: 'State',
@ -2674,10 +2693,13 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 40,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'pending_time',
display: 'Pending till',
@ -2710,10 +2732,13 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 41,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'priority_id',
display: 'Priority',
@ -2742,11 +2767,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 80,
)
ObjectManager::Attribute.add(
force: true,
object: 'Ticket',
name: 'tags',
display: 'Tags',
@ -2766,11 +2794,14 @@ ObjectManager::Attribute.add(
},
edit: {},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 900,
)
ObjectManager::Attribute.add(
force: true,
object: 'TicketArticle',
name: 'type_id',
display: 'Type',
@ -2793,11 +2824,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 100,
)
ObjectManager::Attribute.add(
force: true,
object: 'TicketArticle',
name: 'internal',
display: 'Visibility',
@ -2820,11 +2854,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 200,
)
ObjectManager::Attribute.add(
force: true,
object: 'TicketArticle',
name: 'to',
display: 'To',
@ -2844,10 +2881,13 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 300,
)
ObjectManager::Attribute.add(
force: true,
object: 'TicketArticle',
name: 'cc',
display: 'Cc',
@ -2868,11 +2908,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 400,
)
ObjectManager::Attribute.add(
force: true,
object: 'TicketArticle',
name: 'body',
display: 'Text',
@ -2901,11 +2944,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 600,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'login',
display: 'Login',
@ -2930,11 +2976,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 100,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'firstname',
display: 'Firstname',
@ -2974,11 +3023,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 200,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'lastname',
display: 'Lastname',
@ -3018,11 +3070,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 300,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'email',
display: 'Email',
@ -3062,11 +3117,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 400,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'web',
display: 'Web',
@ -3094,11 +3152,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 500,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'phone',
display: 'Phone',
@ -3126,11 +3187,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 600,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'mobile',
display: 'Mobile',
@ -3158,11 +3222,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 700,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'fax',
display: 'Fax',
@ -3190,11 +3257,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 800,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'organization_id',
display: 'Organization',
@ -3227,11 +3297,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 900,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'department',
display: 'Department',
@ -3242,7 +3315,7 @@ ObjectManager::Attribute.add(
null: true,
item_class: 'formGroup--halfSize',
},
editable: false,
editable: true,
active: true,
screens: {
signup: {},
@ -3259,11 +3332,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1000,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'street',
display: 'Street',
@ -3290,11 +3366,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1100,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'zip',
display: 'Zip',
@ -3322,11 +3401,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1200,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'city',
display: 'City',
@ -3354,11 +3436,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1300,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'address',
display: 'Address',
@ -3386,11 +3471,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1350,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'password',
display: 'Password',
@ -3419,11 +3507,14 @@ ObjectManager::Attribute.add(
},
view: {}
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1400,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'vip',
display: 'VIP',
@ -3455,11 +3546,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1490,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'note',
display: 'Note',
@ -3491,50 +3585,21 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1500,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'role_ids',
display: 'Roles',
data_type: 'checkbox',
display: 'Permissions',
data_type: 'user_permission',
data_option: {
multiple: true,
null: false,
relation: 'Role',
},
editable: false,
active: true,
screens: {
signup: {},
invite_agent: {},
invite_customer: {},
edit: {
Admin: {
null: false,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
pending_migration: false,
position: 1600,
)
ObjectManager::Attribute.add(
object: 'User',
name: 'group_ids',
display: 'Groups',
data_type: 'checkbox',
data_option: {
multiple: true,
null: true,
relation: 'Group',
item_class: 'checkbox',
},
editable: false,
active: true,
@ -3543,7 +3608,10 @@ ObjectManager::Attribute.add(
invite_agent: {
'-all-' => {
null: false,
only_shown_if_selectable: true,
hideMode: {
rolesSelected: ['Agent'],
rolesNot: ['Customer'],
}
},
},
invite_customer: {},
@ -3558,16 +3626,20 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
position: 1700,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1600,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'active',
display: 'Active',
data_type: 'active',
data_option: {
null: true,
default: true,
},
editable: false,
@ -3587,11 +3659,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1800,
)
ObjectManager::Attribute.add(
force: true,
object: 'Organization',
name: 'name',
display: 'Name',
@ -3616,17 +3691,19 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 200,
)
ObjectManager::Attribute.add(
force: true,
object: 'Organization',
name: 'shared',
display: 'Shared organization',
data_type: 'boolean',
data_option: {
maxlength: 250,
null: true,
default: true,
note: 'Customers in the organization can view each other items.',
@ -3650,11 +3727,14 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1400,
)
ObjectManager::Attribute.add(
force: true,
object: 'Organization',
name: 'note',
display: 'Note',
@ -3679,16 +3759,20 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1500,
)
ObjectManager::Attribute.add(
force: true,
object: 'Organization',
name: 'active',
display: 'Active',
data_type: 'active',
data_option: {
null: true,
default: true,
},
editable: false,
@ -3705,7 +3789,291 @@ ObjectManager::Attribute.add(
},
},
},
pending_migration: false,
to_create: false,
to_migrate: false,
to_delete: false,
position: 1800,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'name',
display: 'Name',
data_type: 'input',
data_option: {
type: 'text',
maxlength: 150,
null: false,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: false,
},
},
edit: {
'-all-' => {
null: false,
},
},
view: {
'-all-' => {
shown: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 200,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'assignment_timeout',
display: 'Assignment Timeout',
data_type: 'integer',
data_option: {
maxlength: 150,
null: true,
note: 'Assignment timeout in minutes if assigned agent is not working on it. Ticket will be shown as unassigend.',
min: 0,
max: 999_999,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 300,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'follow_up_possible',
display: 'Follow up possible',
data_type: 'select',
data_option: {
default: 'yes',
options: {
yes: 'yes',
reject: 'reject follow up/do not reopen Ticket',
new_ticket: 'do not reopen Ticket but create new Ticket'
},
null: false,
note: 'Follow up for closed ticket possible or not.',
translate: true
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 400,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'follow_up_assignment',
display: 'Assign Follow Ups',
data_type: 'select',
data_option: {
default: 'yes',
options: {
true: 'yes',
false: 'no',
},
null: false,
note: 'Assign follow up to latest agent again.',
translate: true
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 500,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'email_address_id',
display: 'Email',
data_type: 'select',
data_option: {
default: '',
multiple: false,
null: true,
relation: 'EmailAddress',
nulloption: true,
do_not_log: true,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 600,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'signature_id',
display: 'Signature',
data_type: 'select',
data_option: {
default: '',
multiple: false,
null: true,
relation: 'Signature',
nulloption: true,
do_not_log: true,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 600,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'note',
display: 'Note',
data_type: 'richtext',
data_option: {
type: 'text',
maxlength: 250,
null: true,
note: 'Notes are visible to agents only, never to customers.',
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
'-all-' => {
null: true,
},
},
view: {
'-all-' => {
shown: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 1500,
)
ObjectManager::Attribute.add(
force: true,
object: 'Group',
name: 'active',
display: 'Active',
data_type: 'active',
data_option: {
null: true,
default: true,
},
editable: false,
active: true,
screens: {
create: {
'-all-' => {
null: true,
},
},
edit: {
Admin: {
null: false,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 1800,
)
@ -3858,15 +4226,15 @@ Trigger.create_or_update(
},
perform: {
'notification.email' => {
'body' => '<p>Your request (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.<p>
'body' => '<div>Your request (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.</div>
<br/>
<p>To provide additional information, please reply to this email or click on the following link:
<div>To provide additional information, please reply to this email or click on the following link:
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}">#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}</a>
</p>
</div>
<br/>
<p>Your #{config.product_name} Team</p>
<div>Your #{config.product_name} Team</div>
<br/>
<p><i><a href="http://zammad.com">Zammad</a>, your customer support system</i></p>',
<div><i><a href="http://zammad.com">Zammad</a>, your customer support system</i></div>',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})',
},
@ -3897,15 +4265,15 @@ Trigger.create_or_update(
},
perform: {
'notification.email' => {
'body' => '<p>Your follow up for (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.<p>
'body' => '<div>Your follow up for (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.</div>
<br/>
<p>To provide additional information, please reply to this email or click on the following link:
<div>To provide additional information, please reply to this email or click on the following link:
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}">#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}</a>
</p>
</div>
<br/>
<p>Your #{config.product_name} Team</p>
<div>Your #{config.product_name} Team</div>
<br/>
<p><i><a href="http://zammad.com">Zammad</a>, your customer support system</i></p>',
<div><i><a href="http://zammad.com">Zammad</a>, your customer support system</i></div>',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})',
},

View file

@ -0,0 +1,471 @@
# encoding: utf-8
require 'test_helper'
class ObjectManagerTest < ActiveSupport::TestCase
test 'a object manager' do
list_objects = ObjectManager.list_objects
assert_equal(%w(Ticket TicketArticle User Organization Group), list_objects)
list_objects = ObjectManager.list_frontend_objects
assert_equal(%w(Ticket User Organization Group), list_objects)
# create simple attribute
attribute1 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test1',
display: 'Test 1',
data_type: 'input',
data_option: {
maxlength: 200,
type: 'text',
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
editable: false,
to_migrate: false,
)
assert(attribute1)
assert_equal('test1', attribute1.name)
assert_equal(true, attribute1.editable)
assert_equal(true, attribute1.to_create)
assert_equal(true, attribute1.to_migrate)
assert_equal(false, attribute1.to_delete)
assert_equal(true, ObjectManager::Attribute.pending_migration?)
attribute1 = ObjectManager::Attribute.get(
object: 'Ticket',
name: 'test1',
)
assert(attribute1)
assert_equal('test1', attribute1.name)
assert_equal(true, attribute1.editable)
assert_equal(true, attribute1.to_create)
assert_equal(true, attribute1.to_migrate)
assert_equal(false, attribute1.to_delete)
assert_equal(true, ObjectManager::Attribute.pending_migration?)
# delete attribute without execute migrations
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'test1',
)
assert_equal(false, ObjectManager::Attribute.pending_migration?)
assert(ObjectManager::Attribute.migration_execute)
attribute1 = ObjectManager::Attribute.get(
object: 'Ticket',
name: 'test1',
)
assert_not(attribute1)
# create invalid attributes
assert_raises(RuntimeError) {
attribute2 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test2_id',
display: 'Test 2 with id',
data_type: 'input',
data_option: {
maxlength: 200,
type: 'text',
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
assert_raises(RuntimeError) {
attribute3 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test3_ids',
display: 'Test 3 with id',
data_type: 'input',
data_option: {
maxlength: 200,
type: 'text',
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
assert_raises(RuntimeError) {
attribute4 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test4',
display: 'Test 4 with missing data_option[:type]',
data_type: 'input',
data_option: {
maxlength: 200,
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
attribute5 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test5',
display: 'Test 5',
data_type: 'boolean',
data_option: {
default: true,
options: {
true: 'Yes',
false: 'No',
},
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
assert(attribute5)
assert_equal('test5', attribute5.name)
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'test5',
)
assert_raises(RuntimeError) {
attribute6 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test6',
display: 'Test 6',
data_type: 'boolean',
data_option: {
options: {
true: 'Yes',
false: 'No',
},
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
attribute7 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test7',
display: 'Test 7',
data_type: 'select',
data_option: {
default: 1,
options: {
'1' => 'aa',
'2' => 'bb',
},
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
assert(attribute7)
assert_equal('test7', attribute7.name)
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'test7',
)
assert_raises(RuntimeError) {
attribute8 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test8',
display: 'Test 8',
data_type: 'select',
data_option: {
default: 1,
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
attribute9 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test9',
display: 'Test 9',
data_type: 'datetime',
data_option: {
future: true,
past: false,
diff: 24,
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
assert(attribute9)
assert_equal('test9', attribute9.name)
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'test9',
)
assert_raises(RuntimeError) {
attribute10 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test10',
display: 'Test 10',
data_type: 'datetime',
data_option: {
past: false,
diff: 24,
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
attribute11 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test11',
display: 'Test 11',
data_type: 'date',
data_option: {
future: true,
past: false,
diff: 24,
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
assert(attribute11)
assert_equal('test11', attribute11.name)
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'test11',
)
assert_raises(RuntimeError) {
attribute12 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'test12',
display: 'Test 12',
data_type: 'date',
data_option: {
past: false,
diff: 24,
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
assert_equal(false, ObjectManager::Attribute.pending_migration?)
end
test 'b object manager attribute' do
assert_equal(false, ObjectManager::Attribute.pending_migration?)
assert_equal(0, ObjectManager::Attribute.where(to_migrate: true).count)
assert_equal(0, ObjectManager::Attribute.migrations.count)
attribute1 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'attribute1',
display: 'Attribute 1',
data_type: 'input',
data_option: {
maxlength: 200,
type: 'text',
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
assert(attribute1)
assert_equal(true, ObjectManager::Attribute.pending_migration?)
assert_equal(1, ObjectManager::Attribute.where(to_migrate: true).count)
assert_equal(1, ObjectManager::Attribute.migrations.count)
# execute migrations
assert(ObjectManager::Attribute.migration_execute)
assert_equal(false, ObjectManager::Attribute.pending_migration?)
assert_equal(0, ObjectManager::Attribute.where(to_migrate: true).count)
assert_equal(0, ObjectManager::Attribute.migrations.count)
# create example ticket
ticket1 = Ticket.create(
title: 'some attribute test1',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
attribute1: 'some attribute text',
updated_by_id: 1,
created_by_id: 1,
)
assert('ticket1 created', ticket1)
assert_equal('some attribute test1', ticket1.title)
assert_equal('Users', ticket1.group.name)
assert_equal('new', ticket1.state.name)
assert_equal('some attribute text', ticket1.attribute1)
# add additional attributes
attribute2 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'attribute2',
display: 'Attribute 2',
data_type: 'select',
data_option: {
default: '2',
options: {
'1' => 'aa',
'2' => 'bb',
},
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
attribute3 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'attribute3',
display: 'Attribute 3',
data_type: 'datetime',
data_option: {
future: true,
past: false,
diff: 24,
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
attribute4 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'attribute4',
display: 'Attribute 4',
data_type: 'datetime',
data_option: {
future: true,
past: false,
diff: 24,
null: true,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
# execute migrations
assert_equal(true, ObjectManager::Attribute.pending_migration?)
assert(ObjectManager::Attribute.migration_execute)
assert_equal(false, ObjectManager::Attribute.pending_migration?)
# create example ticket
ticket2 = Ticket.create(
title: 'some attribute test2',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
attribute1: 'some attribute text',
attribute2: '1',
attribute3: Time.zone.parse('2016-05-12 00:59:59 UTC'),
attribute4: Date.parse('2016-05-11'),
updated_by_id: 1,
created_by_id: 1,
)
assert('ticket2 created', ticket2)
assert_equal('some attribute test2', ticket2.title)
assert_equal('Users', ticket2.group.name)
assert_equal('new', ticket2.state.name)
assert_equal('some attribute text', ticket2.attribute1)
assert_equal('1', ticket2.attribute2)
assert_equal(Time.zone.parse('2016-05-12 00:59:59 UTC'), ticket2.attribute3)
assert_equal(Date.parse('2016-05-11'), ticket2.attribute4)
# remove attribute
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'attribute1',
)
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'attribute2',
)
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'attribute3',
)
ObjectManager::Attribute.remove(
object: 'Ticket',
name: 'attribute4',
)
assert(ObjectManager::Attribute.migration_execute)
ticket2 = Ticket.find(ticket2.id)
assert('ticket2 created', ticket2)
assert_equal('some attribute test2', ticket2.title)
assert_equal('Users', ticket2.group.name)
assert_equal('new', ticket2.state.name)
assert_equal(nil, ticket2[:attribute1])
assert_equal(nil, ticket2[:attribute2])
assert_equal(nil, ticket2[:attribute3])
assert_equal(nil, ticket2[:attribute4])
end
end