diff --git a/app/assets/javascripts/app/controllers/_application_controller_form.coffee b/app/assets/javascripts/app/controllers/_application_controller_form.coffee
index b083280c8..46ec95120 100644
--- a/app/assets/javascripts/app/controllers/_application_controller_form.coffee
+++ b/app/assets/javascripts/app/controllers/_application_controller_form.coffee
@@ -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
diff --git a/app/assets/javascripts/app/controllers/_dashboard/first_steps.coffee b/app/assets/javascripts/app/controllers/_dashboard/first_steps.coffee
index 2267d36e4..21de356f3 100644
--- a/app/assets/javascripts/app/controllers/_dashboard/first_steps.coffee
+++ b/app/assets/javascripts/app/controllers/_dashboard/first_steps.coffee
@@ -40,7 +40,6 @@ class App.DashboardFirstSteps extends App.Controller
#container: @el.closest('.content')
head: 'Invite Colleagues'
screen: 'invite_agent'
- role: 'Agent'
)
inviteCustomer: (e) ->
diff --git a/app/assets/javascripts/app/controllers/_ui_element/active.coffee b/app/assets/javascripts/app/controllers/_ui_element/active.coffee
index 56d0b363e..1cb8b20cb 100644
--- a/app/assets/javascripts/app/controllers/_ui_element/active.coffee
+++ b/app/assets/javascripts/app/controllers/_ui_element/active.coffee
@@ -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 = [
diff --git a/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee b/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee
index 04951ae39..5855903c7 100644
--- a/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee
+++ b/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee
@@ -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
diff --git a/app/assets/javascripts/app/controllers/_ui_element/object_manager_attribute.coffee b/app/assets/javascripts/app/controllers/_ui_element/object_manager_attribute.coffee
new file mode 100644
index 000000000..02d9758b3
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/_ui_element/object_manager_attribute.coffee
@@ -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)
diff --git a/app/assets/javascripts/app/controllers/_ui_element/user_permission.coffee b/app/assets/javascripts/app/controllers/_ui_element/user_permission.coffee
new file mode 100644
index 000000000..e8fb263a2
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/_ui_element/user_permission.coffee
@@ -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
diff --git a/app/assets/javascripts/app/controllers/object_manager.coffee b/app/assets/javascripts/app/controllers/object_manager.coffee
index 96541febf..a400bbf1c 100644
--- a/app/assets/javascripts/app/controllers/object_manager.coffee
+++ b/app/assets/javascripts/app/controllers/object_manager.coffee
@@ -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' )
diff --git a/app/assets/javascripts/app/controllers/widget/invite_user.coffee b/app/assets/javascripts/app/controllers/widget/invite_user.coffee
index e1fafc792..3d6ebb686 100644
--- a/app/assets/javascripts/app/controllers/widget/invite_user.coffee
+++ b/app/assets/javascripts/app/controllers/widget/invite_user.coffee
@@ -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)
- )
\ No newline at end of file
+ )
diff --git a/app/assets/javascripts/app/models/_application_model.coffee b/app/assets/javascripts/app/models/_application_model.coffee
index 777a1b456..9c8cc2fc5 100644
--- a/app/assets/javascripts/app/models/_application_model.coffee
+++ b/app/assets/javascripts/app/models/_application_model.coffee
@@ -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
diff --git a/app/assets/javascripts/app/models/group.coffee b/app/assets/javascripts/app/models/group.coffee
index b2bcad9a0..da4114b61 100644
--- a/app/assets/javascripts/app/models/group.coffee
+++ b/app/assets/javascripts/app/models/group.coffee
@@ -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 },
diff --git a/app/assets/javascripts/app/models/object_manager_attribute.coffee b/app/assets/javascripts/app/models/object_manager_attribute.coffee
index 048808183..431fea039 100644
--- a/app/assets/javascripts/app/models/object_manager_attribute.coffee
+++ b/app/assets/javascripts/app/models/object_manager_attribute.coffee
@@ -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
\ No newline at end of file
diff --git a/app/assets/javascripts/app/models/user.coffee b/app/assets/javascripts/app/models/user.coffee
index b26cc7e8f..499baf2e6 100644
--- a/app/assets/javascripts/app/models/user.coffee
+++ b/app/assets/javascripts/app/models/user.coffee
@@ -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 },
diff --git a/app/assets/javascripts/app/views/generic/input.jst.eco b/app/assets/javascripts/app/views/generic/input.jst.eco
index af5458069..ee309c9a8 100644
--- a/app/assets/javascripts/app/views/generic/input.jst.eco
+++ b/app/assets/javascripts/app/views/generic/input.jst.eco
@@ -1 +1,4 @@
- placeholder="<%- @Ti(@attribute.placeholder) %>"<% end %> <%= @attribute.required %> <%= @attribute.autofocus %> <%- @attribute.autocapitalize %> <%- @attribute.autocomplete %>/>
+ 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: %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/generic/select.jst.eco b/app/assets/javascripts/app/views/generic/select.jst.eco
index 173712069..710756869 100644
--- a/app/assets/javascripts/app/views/generic/select.jst.eco
+++ b/app/assets/javascripts/app/views/generic/select.jst.eco
@@ -1,5 +1,5 @@
- " name="<%= @attribute.name %>" <%= @attribute.multiple %> <%= @attribute.required %> <%= @attribute.autofocus %>>
+ " name="<%= @attribute.name %>" <%= @attribute.multiple %> <%= @attribute.required %> <%= @attribute.autofocus %> <% if @attribute.disabled: %> disabled<% end %>>
<% if @attribute.options: %>
<% for row in @attribute.options: %>
<%= row.disabled %>><%= row.name %>
@@ -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: %>
+
+ <% end %>
+<% end %>
+<% end %>
+<% end %>
diff --git a/app/assets/javascripts/app/views/generic/user_permission.jst.eco b/app/assets/javascripts/app/views/generic/user_permission.jst.eco
new file mode 100644
index 000000000..102de2943
--- /dev/null
+++ b/app/assets/javascripts/app/views/generic/user_permission.jst.eco
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/object_manager/attribute.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute.jst.eco
new file mode 100644
index 000000000..0d8c8a92b
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute.jst.eco
@@ -0,0 +1,5 @@
+
diff --git a/app/assets/javascripts/app/views/object_manager/attribute/autocompletion.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute/autocompletion.jst.eco
new file mode 100644
index 000000000..11b95c52f
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute/autocompletion.jst.eco
@@ -0,0 +1,6 @@
+
+Auto-Vervollständigung
+
+
+
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/object_manager/attribute/boolean.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute/boolean.jst.eco
new file mode 100644
index 000000000..d24c01284
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute/boolean.jst.eco
@@ -0,0 +1,29 @@
+
diff --git a/app/assets/javascripts/app/views/object_manager/attribute/date.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute/date.jst.eco
new file mode 100644
index 000000000..023d9338d
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute/date.jst.eco
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/object_manager/attribute/datetime.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute/datetime.jst.eco
new file mode 100644
index 000000000..290a6a32b
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute/datetime.jst.eco
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/object_manager/attribute/input.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute/input.jst.eco
new file mode 100644
index 000000000..fcf25f609
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute/input.jst.eco
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/object_manager/attribute/integer.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute/integer.jst.eco
new file mode 100644
index 000000000..fceb40612
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute/integer.jst.eco
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/object_manager/attribute/select.jst.eco b/app/assets/javascripts/app/views/object_manager/attribute/select.jst.eco
new file mode 100644
index 000000000..600b2908f
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/attribute/select.jst.eco
@@ -0,0 +1,41 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/object_manager/index.jst.eco b/app/assets/javascripts/app/views/object_manager/index.jst.eco
index 6d0e45a53..c740f905c 100644
--- a/app/assets/javascripts/app/views/object_manager/index.jst.eco
+++ b/app/assets/javascripts/app/views/object_manager/index.jst.eco
@@ -1,21 +1,42 @@
+ <% if !_.isEmpty(@itemsToChange): %>
Database Update required
-
<%- @T( 'Changes were made that require a database update. This might take some time.' ) %>
+
+ <%- @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.') %>
+
+
+ <%- @T('Changes') %>:
+
+ <% for item in @itemsToChange: %>
+
+ <% 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 %>
+
-
Discard Changes
-
<%- @T( 'Update Database' ) %>
+
<%- @T('Discard Changes') %>
+
<%- @T('Update Database') %>
+ <% end %>
+
<%- @T('Display') %>
<%- @T('Name') %>
- <%- @T('Type') %>
+ <%- @T('Type') %>
+ <%- @T('Action') %>
<% for item in @items: %>
-
+
<%= item.display %>
<%= item.name %>
<%= item.data_type %>
+
+ <% 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: %>
+ <%- @Icon('trash') %>
+ <% end %>
+
<% end %>
diff --git a/app/assets/javascripts/app/views/object_manager/screens.jst.eco b/app/assets/javascripts/app/views/object_manager/screens.jst.eco
new file mode 100644
index 000000000..6f4e58d7d
--- /dev/null
+++ b/app/assets/javascripts/app/views/object_manager/screens.jst.eco
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss
index 857adbb2b..fe0d67b7e 100644
--- a/app/assets/stylesheets/zammad.scss
+++ b/app/assets/stylesheets/zammad.scss
@@ -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:
-
+
-
+
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%);
}
diff --git a/app/controllers/object_manager_attributes_controller.rb b/app/controllers/object_manager_attributes_controller.rb
index 6a42b73fd..8412fcd98 100644
--- a/app/controllers/object_manager_attributes_controller.rb
+++ b/app/controllers/object_manager_attributes_controller.rb
@@ -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
diff --git a/app/models/link.rb b/app/models/link.rb
index e45b1253f..78beb34ba 100644
--- a/app/models/link.rb
+++ b/app/models/link.rb
@@ -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
diff --git a/app/models/object_manager.rb b/app/models/object_manager.rb
index 187f3f3e6..9470acec5 100644
--- a/app/models/object_manager.rb
+++ b/app/models/object_manager.rb
@@ -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
diff --git a/app/models/ticket.rb b/app/models/ticket.rb
index 190e0a087..9d449ab8a 100644
--- a/app/models/ticket.rb
+++ b/app/models/ticket.rb
@@ -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
diff --git a/app/models/ticket/article.rb b/app/models/ticket/article.rb
index 11ee02369..e28ac941c 100644
--- a/app/models/ticket/article.rb
+++ b/app/models/ticket/article.rb
@@ -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
diff --git a/config/routes/object_manager_attribute.rb b/config/routes/object_manager_attribute.rb
index 54c3bcd57..391140b39 100644
--- a/config/routes/object_manager_attribute.rb
+++ b/config/routes/object_manager_attribute.rb
@@ -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
diff --git a/db/migrate/20120101000001_create_base.rb b/db/migrate/20120101000001_create_base.rb
index a4af1b1ff..dde5f8445 100644
--- a/db/migrate/20120101000001_create_base.rb
+++ b/db/migrate/20120101000001_create_base.rb
@@ -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
diff --git a/db/migrate/20150977000001_update_object_manager3.rb b/db/migrate/20150977000001_update_object_manager3.rb
index eb8757de4..1a5c030bf 100644
--- a/db/migrate/20150977000001_update_object_manager3.rb
+++ b/db/migrate/20150977000001_update_object_manager3.rb
@@ -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,
diff --git a/db/migrate/20151012000001_update_overview4.rb b/db/migrate/20151012000001_update_overview4.rb
index 200174d27..5ebfa515b 100644
--- a/db/migrate/20151012000001_update_overview4.rb
+++ b/db/migrate/20151012000001_update_overview4.rb
@@ -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,
)
diff --git a/db/migrate/20160217000001_object_manager_update_user.rb b/db/migrate/20160217000001_object_manager_update_user.rb
index e739db608..e15dac3d4 100644
--- a/db/migrate/20160217000001_object_manager_update_user.rb
+++ b/db/migrate/20160217000001_object_manager_update_user.rb
@@ -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,
)
diff --git a/db/migrate/20160303000001_email_ticket_cc.rb b/db/migrate/20160303000001_email_ticket_cc.rb
index d1ff3bd8b..bfb6e11d9 100644
--- a/db/migrate/20160303000001_email_ticket_cc.rb
+++ b/db/migrate/20160303000001_email_ticket_cc.rb
@@ -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,
diff --git a/db/migrate/20160307000001_only_one_group.rb b/db/migrate/20160307000001_only_one_group.rb
index 569f4ebe3..f55a5f344 100644
--- a/db/migrate/20160307000001_only_one_group.rb
+++ b/db/migrate/20160307000001_only_one_group.rb
@@ -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,
diff --git a/db/migrate/20160506000003_role_group_remove.rb b/db/migrate/20160506000003_role_group_remove.rb
new file mode 100644
index 000000000..2a69acea6
--- /dev/null
+++ b/db/migrate/20160506000003_role_group_remove.rb
@@ -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
diff --git a/db/migrate/20160512000001_update_object_manager.rb b/db/migrate/20160512000001_update_object_manager.rb
new file mode 100644
index 000000000..476d9d660
--- /dev/null
+++ b/db/migrate/20160512000001_update_object_manager.rb
@@ -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
diff --git a/db/seeds.rb b/db/seeds.rb
index 806c18abb..f74c00f23 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -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' => 'Your request (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.
+ 'body' => '
Your request (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.
-To provide additional information, please reply to this email or click on the following link:
+
-Your #{config.product_name} Team
+Your #{config.product_name} Team
-Zammad , your customer support system
',
+Zammad , your customer support system ',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})',
},
@@ -3897,15 +4265,15 @@ Trigger.create_or_update(
},
perform: {
'notification.email' => {
- 'body' => 'Your follow up for (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.
+ 'body' => '
Your follow up for (#{config.ticket_hook}#{ticket.number}) has been received and will be reviewed by our support staff.
-To provide additional information, please reply to this email or click on the following link:
+
-Your #{config.product_name} Team
+Your #{config.product_name} Team
-Zammad , your customer support system
',
+Zammad , your customer support system ',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})',
},
diff --git a/test/unit/object_manager_test.rb b/test/unit/object_manager_test.rb
new file mode 100644
index 000000000..a71f852d0
--- /dev/null
+++ b/test/unit/object_manager_test.rb
@@ -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