Merge branch 'develop' into interface

Conflicts:
	app/assets/javascripts/app/controllers/customer_ticket_create.js.coffee
	app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
	app/assets/javascripts/app/models/user.js.coffee
	app/assets/javascripts/app/views/agent_ticket_create.jst.eco
	app/assets/javascripts/app/views/customer_ticket_create.jst.eco
	app/assets/javascripts/app/views/navigation/personal.jst.eco
	config/routes/test.rb
	test/browser/aab_unit_test.rb
This commit is contained in:
Martin Edenhofer 2014-09-13 13:12:00 +02:00
commit 5a11480447
60 changed files with 12252 additions and 11438 deletions

View file

@ -1,6 +1,6 @@
source 'http://rubygems.org'
gem 'rails', '4.1.4'
gem 'rails', '4.1.5'
gem 'rails-observers'
gem 'activerecord-session_store'

View file

@ -2,7 +2,11 @@ class App.ControllerForm extends App.Controller
constructor: (params) ->
for key, value of params
@[key] = value
@attribute_count = 0
if !@handlers
@handlers = []
@handlers.push @_showHideToggle
@handlers.push @_requiredMandantoryToggle
if !@form
@form = @formGen()
@ -23,33 +27,51 @@ class App.ControllerForm extends App.Controller
fieldset = $('<fieldset></fieldset>')
for attribute_clean in @model.configure_attributes
attribute = _.clone( attribute_clean )
# collect form attributes
@attributes = []
if @model.attributesGet
attributesClean = @model.attributesGet(@screen)
else
attributesClean = App.Model.attributesGet(@screen, @model.configure_attributes )
if !attribute.readonly && ( !@required || @required && attribute[@required] )
for attributeName, attribute of attributesClean
@attribute_count = @attribute_count + 1
# ignore read only attributes
if !attribute.readonly
# add item
item = @formGenItem( attribute, @model.className, fieldset )
item.appendTo(fieldset)
# check generic filter
if @filter && !attribute.filter
if @filter[ attributeName ]
attribute.filter = @filter[ attributeName ]
# if password, add confirm password item
if attribute.type is 'password'
@attributes.push attribute
# set selected value passed on current params
if @params
if attribute.name of @params
attribute.value = @params[attribute.name]
attribute_count = 0
className = @model.className + '_' + Math.floor( Math.random() * 999999 ).toString()
# rename display and name to _confirm
if !attribute.single
attribute.display = attribute.display + ' (confirm)'
attribute.name = attribute.name + '_confirm';
item = @formGenItem( attribute, @model.className, fieldset )
item.appendTo(fieldset)
for attribute in @attributes
attribute_count = attribute_count + 1
if @no_fieldset
# add item
item = @formGenItem( attribute, className, fieldset, attribute_count )
item.appendTo(fieldset)
# if password, add confirm password item
if attribute.type is 'password'
# set selected value passed on current params
if @params
if attribute.name of @params
attribute.value = @params[attribute.name]
# rename display and name to _confirm
if !attribute.single
attribute.display = attribute.display + ' (confirm)'
attribute.name = attribute.name + '_confirm';
item = @formGenItem( attribute, className, fieldset, attribute_count )
item.appendTo(fieldset)
if @noFieldset
fieldset = fieldset.children()
if @fullForm
@ -57,6 +79,13 @@ class App.ControllerForm extends App.Controller
@formClass = ''
fieldset = $('<form class="' + @formClass + '"><button class="btn">' + App.i18n.translateContent('Submit') + '</button></form>').prepend( fieldset )
# bind form events
if @events
for eventSelector, callback of @events
do (eventSelector, callback) =>
evs = eventSelector.split(' ')
fieldset.find( evs[1] ).bind(evs[0], (e) => callback(e) )
# return form
return fieldset
@ -85,7 +114,7 @@ class App.ControllerForm extends App.Controller
null: false
relation: 'User'
autocapitalize: false
help: 'Select the customer of the Ticket or create one.'
help: 'Select the customer of the ticket or create one.'
helpLink: '<a href="" class="customer_new">&raquo;</a>'
callback: @userInfo
class: 'span7'
@ -134,14 +163,14 @@ class App.ControllerForm extends App.Controller
###
formGenItem: (attribute_config, classname, form ) ->
attribute = _.clone( attribute_config )
formGenItem: (attribute_config, classname, form, attribute_count ) ->
attribute = clone( attribute_config )
# create item id
attribute.id = classname + '_' + attribute.name
# set autofocus
if @autofocus && @attribute_count is 1
if @autofocus && attribute_count is 1
attribute.autofocus = 'autofocus'
# set required option
@ -237,6 +266,22 @@ class App.ControllerForm extends App.Controller
else if attribute.tag is 'select'
item = $( App.view('generic/select')( attribute: attribute ) )
# date
else if attribute.tag is 'date'
attribute.type = 'text'
item = $( App.view('generic/date')( attribute: attribute ) )
#item.datetimepicker({
# format: 'Y.m.d'
#});
# date
else if attribute.tag is 'datetime'
attribute.type = 'text'
item = $( App.view('generic/date')( attribute: attribute ) )
#item.datetimepicker({
# format: 'Y.m.d H:i'
#});
# timezone
else if attribute.tag is 'timezone'
attribute.options = []
@ -720,7 +765,7 @@ class App.ControllerForm extends App.Controller
b = (event, item) =>
# set html form attribute
$(local_attribute).val(item.id)
$(local_attribute).val(item.id).trigger('change')
$(local_attribute + '_autocompletion_value_shown').val(item.value)
# call calback
@ -741,8 +786,11 @@ class App.ControllerForm extends App.Controller
}
)
###
source = attribute.source
if typeof(source) is 'string'
source = source.replace('#{@apiPath}', App.Config.get('api_path') );
$(local_attribute_full).autocomplete(
source: attribute.source,
source: source,
minLength: attribute.minLengt || 3,
select: ( event, ui ) =>
b(event, ui.item)
@ -1224,63 +1272,12 @@ class App.ControllerForm extends App.Controller
else
item = $( App.view('generic/input')( attribute: attribute ) )
if attribute.onchange
if typeof attribute.onchange is 'function'
attribute.onchange(attribute)
else
for i of attribute.onchange
a = i.split(/__/)
if a[1]
if a[0] is attribute.name
@attribute = attribute
@classname = classname
@attributes_clean = attributes_clean
@change = a
b = =>
# console.log 'aaa', @attribute
attribute = @attribute
change = @change
classname = @classname
attributes_clean = @attributes_clean
ui = @
$( '#' + @attribute.id ).bind('change', ->
ui.log 'change', @, attribute, change
ui.log change[0] + ' has changed - changing ' + change[1]
item = $( ui.formGenItem(attribute, classname, attributes_clean) )
ui.log item, classname
)
App.Delay.set( b, 100, undefined, 'form_change' )
# if attribute.onchange[]
ui = @
item.bind('change', ->
if ui.form_data
params = App.ControllerForm.params(@)
for i of ui.form_data
a = i.split(/__/)
if a[1] && a[0] is attribute.name
newListAttribute = i
changedAttribute = a[0]
toChangeAttribute = a[1]
# get new option list
newListAttributes = ui['form_data'][newListAttribute][ params['group_id'] ]
# find element to replace
for item in ui.model.configure_attributes
if item.name is toChangeAttribute
item.display = false
item['filter'][toChangeAttribute] = newListAttributes
if params[changedAttribute]
item.default = params[toChangeAttribute]
if !item.default
delete item['default']
newElement = ui.formGenItem( item, classname, form )
# replace new option list
form.find('[name="' + toChangeAttribute + '"]').replaceWith( newElement )
)
if @handlers
item.bind('change', (e) =>
params = App.ControllerForm.params( $(e.target) )
for handler in @handlers
handler(params, attribute, @attributes, classname, form, @)
)
# bind dependency
if @dependency
@ -1342,6 +1339,44 @@ class App.ControllerForm extends App.Controller
el.find('[name="' + key + '"]').parents('.form-group').addClass('hide')
el.find('[name="' + key + '"]').addClass('is-hidden')
_mandantory: (name, el = @el) ->
if !_.isArray(name)
name = [name]
for key in name
el.find('[name="' + key + '"]').attr('required', true)
el.find('[name="' + key + '"]').parents('.form-group').find('label span').html('*')
_optional: (name, el = @el) ->
if !_.isArray(name)
name = [name]
for key in name
el.find('[name="' + key + '"]').attr('required', false)
el.find('[name="' + key + '"]').parents('.form-group').find('label span').html('')
_showHideToggle: (params, changedAttribute, attributes, classname, form, ui) =>
for attribute in attributes
if attribute.shown_if
hit = false
for refAttribute, refValue of attribute.shown_if
if params[refAttribute] && params[refAttribute].toString() is refValue.toString()
hit = true
if hit
ui._show(attribute.name)
else
ui._hide(attribute.name)
_requiredMandantoryToggle: (params, changedAttribute, attributes, classname, form, ui) =>
for attribute in attributes
if attribute.required_if
hit = false
for refAttribute, refValue of attribute.required_if
if params[refAttribute] && params[refAttribute].toString() is refValue.toString()
hit = true
if hit
ui._mandantory(attribute.name)
else
ui._optional(attribute.name)
# sort attribute.options
_sortOptions: (attribute) ->
@ -1402,6 +1437,7 @@ class App.ControllerForm extends App.Controller
list = []
if attribute.filter
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter', attribute.filter
# function based filter
@ -1429,6 +1465,22 @@ class App.ControllerForm extends App.Controller
if record['id'] is key
list.push record
# data based filter
else if attribute.filter && _.isArray attribute.filter
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter-array', attribute.filter
# check all records
for record in App[ attribute.relation ].search( sortBy: attribute.sortBy )
# check all filter attributes
for key in attribute.filter
# check all filter values as array
# if it's matching, use it for selection
if record['id'] is key
list.push record
# no data filter matched
else
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter-data no filter matched'
@ -1515,9 +1567,10 @@ class App.ControllerForm extends App.Controller
validate: (params) ->
App.Model.validate(
model: @model,
params: params,
)
model: @model
params: params
screen: @screen
)
# get all params of the form
@params: (form) ->
@ -1640,13 +1693,8 @@ class App.ControllerForm extends App.Controller
# show new errors
for key, msg of data.errors
$(data.form).parents().find('[name*="' + key + '"]').parents('div .form-group').addClass('has-error')
$(data.form).parents().find('[name*="' + key + '"]').parent().find('.help-inline').html(msg)
$(data.form).parents('form').find('[name="' + key + '"]').parents('div .form-group').addClass('has-error')
$(data.form).parents('form').find('[name="' + key + '"]').parent().find('.help-inline').html(msg)
# set autofocus
$(data.form).parents().find('.has-error').find('input, textarea').first().focus()
# # enable form again
# if $(data.form).parents().find('.has-error').html()
# @formEnable(data.form)
$(data.form).parents('form').find('.has-error').find('input, textarea').first().focus()

View file

@ -7,11 +7,11 @@ class App.ControllerGenericNew extends App.ControllerModal
@html App.view('generic/admin/new')( head: @pageData.object )
new App.ControllerForm(
el: @el.find('#object_new'),
model: App[ @genericObject ],
params: @item,
required: @required,
autofocus: true,
el: @el.find('#object_new')
model: App[ @genericObject ]
params: @item
screen: @screen || 'edit'
autofocus: true
)
@modalShow()
@ -59,7 +59,7 @@ class App.ControllerGenericEdit extends App.ControllerModal
el: @el.find('#object_edit')
model: App[ @genericObject ]
params: @item
required: @required
screen: @screen || 'edit'
autofocus: true
)
@modalShow()

View file

@ -99,6 +99,7 @@ class App.ControllerTable extends App.Controller
data.model = {}
overview = data.overview || data.model.configure_overview || []
attributes = data.attributes || data.model.configure_attributes || {}
attributes = App.Model.attributesGet(false, attributes)
destroy = data.model.configure_delete
# check if table is empty
@ -146,13 +147,13 @@ class App.ControllerTable extends App.Controller
header = []
for item in overview
headerFound = false
for attribute in attributes
if attribute.name is item
for attributeName, attribute of attributes
if attributeName is item
headerFound = true
header.push attribute
else
rowWithoutId = item + '_id'
if attribute.name is rowWithoutId
if attributeName is rowWithoutId
headerFound = true
header.push attribute

View file

@ -1,9 +1,10 @@
class App.TicketCreate extends App.Controller
events:
'click .customer_new': 'userNew'
'submit form': 'submit'
'click .submit': 'submit'
'click .cancel': 'cancel'
'click .type-tabs .tab': 'changeFormType'
'click .customer_new': 'userNew'
'submit form': 'submit'
'click .submit': 'submit'
'click .cancel': 'cancel'
constructor: (params) ->
super
@ -13,40 +14,18 @@ class App.TicketCreate extends App.Controller
# set title
@form_id = App.ControllerForm.formId()
@form_meta = undefined
@edit_form = undefined
# set article attributes
default_type = 'call_inbound'
if !@type
@type = default_type
article_sender_type_map =
call_inbound:
sender: 'Customer'
article: 'phone'
title: 'Call Inbound'
call_outbound:
sender: 'Agent'
article: 'phone'
title: 'Call Outbound'
email:
sender: 'Agent'
article: 'email'
title: 'Email'
@article_attributes = article_sender_type_map[@type]
# define default type
@default_type = 'phone-in'
# remember split info if exists
split = ''
if @ticket_id && @article_id
split = "/#{@ticket_id}/#{@article_id}"
# if no map entry exists, route to default
if !@article_attributes
@navigate '#ticket/create/' + default_type + split
return
# update navbar highlighting
@navupdate '#ticket/create/' + @type + '/id/' + @id + split
@navupdate '#ticket/create/id/' + @id + split
@fetch(params)
@ -55,20 +34,72 @@ class App.TicketCreate extends App.Controller
@log 'notice', 'error', defaults
@render(defaults)
changeFormType: (e) =>
type = $(e.target).data('type')
if !type
type = $(e.target).parent().data('type')
@setFormTypeInUi(type)
setFormTypeInUi: (type) =>
# detect current form type
if !type
type = @el.find('.type-tabs .tab.active').data('type')
if !type
type = @default_type
# reset all tabs
tabs = @el.find('.type-tabs .tab')
tabs.removeClass('active')
tabIcons = @el.find('.type-tabs .tab .icon')
tabIcons.addClass('gray')
tabIcons.removeClass('white')
# set active tab
selectedTab = @el.find(".type-tabs .tab[data-type='#{type}']")
selectedTab.addClass('active')
selectedTabIcon = @el.find(".type-tabs .tab[data-type='#{type}'] .icon")
selectedTabIcon.removeClass('gray')
selectedTabIcon.addClass('white')
# set form type attributes
articleSenderTypeMap =
'phone-in':
sender: 'Customer'
article: 'phone'
title: 'Call Inbound'
screen: 'create_phone_in'
'phone-out':
sender: 'Agent'
article: 'phone'
title: 'Call Outbound'
screen: 'create_phone_out'
'email-out':
sender: 'Agent'
article: 'email'
title: 'Email'
screen: 'create_email_out'
@articleAttributes = articleSenderTypeMap[type]
# update form
@el.find('[name="formSenderType"]').val(type)
meta: =>
text = App.i18n.translateInline( @article_attributes['title'] )
subject = @el.find('[name=subject]').val()
if subject
text = "#{text}: #{subject}"
text = ''
if @articleAttributes
text = App.i18n.translateInline( @articleAttributes['title'] )
title = @el.find('[name=title]').val()
if title
text = "#{text}: #{title}"
meta =
url: @url()
head: text
title: text
id: @type
id: @id
iconClass: 'pen'
url: =>
'#ticket/create/' + @type + '/id/' + @id
'#ticket/create/id/' + @id
activate: =>
@navupdate '#'
@ -101,7 +132,7 @@ class App.TicketCreate extends App.Controller
if cache && !params.ticket_id && !params.article_id
# get edit form attributes
@edit_form = cache.edit_form
@form_meta = cache.form_meta
# load assets
App.Collection.loadAssets( cache.assets )
@ -109,7 +140,7 @@ class App.TicketCreate extends App.Controller
@render()
else
@ajax(
id: 'ticket_create'
id: 'ticket_create' + @task_key
type: 'GET'
url: @apiPath + '/ticket_create'
data:
@ -122,7 +153,7 @@ class App.TicketCreate extends App.Controller
App.Store.write( 'ticket_create_attributes', data )
# get edit form attributes
@edit_form = data.edit_form
@form_meta = data.form_meta
# load assets
App.Collection.loadAssets( data.assets )
@ -144,43 +175,90 @@ class App.TicketCreate extends App.Controller
render: (template = {}) ->
# set defaults
defaults =
state_id: App.TicketState.findByAttribute( 'name', 'open' ).id
priority_id: App.TicketPriority.findByAttribute( 'name', '2 normal' ).id
# generate form
configure_attributes = [
{ name: 'customer_id', display: 'Customer', tag: 'autocompletion', type: 'text', limit: 200, null: false, relation: 'User', class: 'span7', autocapitalize: false, help: 'Select the customer of the Ticket or create one.', helpLink: '<a href="" class="customer_new">&raquo;</a>', callback: @localUserInfo, source: @apiPath + '/users/search', minLengt: 2 },
{ name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: false, filter: @edit_form, nulloption: true, relation: 'Group', default: defaults['group_id'], class: 'span7', },
{ name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, null: true, filter: @edit_form, nulloption: true, relation: 'User', default: defaults['owner_id'], class: 'span7', },
{ name: 'tags', display: 'Tags', tag: 'tag', type: 'text', null: true, default: defaults['tags'], class: 'span7', },
{ name: 'subject', display: 'Subject', tag: 'input', type: 'text', limit: 200, null: false, default: defaults['subject'], class: 'span7', },
{ name: 'body', display: 'Text', tag: 'textarea', rows: 8, null: false, default: defaults['body'], class: 'span7', upload: true },
{ name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, filter: @edit_form, relation: 'TicketState', default: defaults['state_id'], translate: true, class: 'medium' },
{ name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, filter: @edit_form, relation: 'TicketPriority', default: defaults['priority_id'], translate: true, class: 'medium' },
]
@html App.view('agent_ticket_create')(
head: 'New Ticket'
title: @article_attributes['title']
agent: @isRole('Agent')
admin: @isRole('Admin')
)
params = undefined
# get params
params = {}
if template && !_.isEmpty( template.options )
params = template.options
else if App.TaskManager.get(@task_key) && !_.isEmpty( App.TaskManager.get(@task_key).state )
params = App.TaskManager.get(@task_key).state
formChanges = (params, attribute, attributes, classname, form, ui) =>
if @form_meta.dependencies && @form_meta.dependencies[attribute.name]
dependency = @form_meta.dependencies[attribute.name][ parseInt(params[attribute.name]) ]
if dependency
for fieldNameToChange of dependency
filter = []
if dependency[fieldNameToChange]
filter = dependency[fieldNameToChange]
# find element to replace
for item in attributes
if item.name is fieldNameToChange
item.display = false
item['filter'] = {}
item['filter'][ fieldNameToChange ] = filter
item.default = params[item.name]
#if !item.default
# delete item['default']
newElement = ui.formGenItem( item, classname, form )
# replace new option list
form.find('[name="' + fieldNameToChange + '"]').replaceWith( newElement )
new App.ControllerForm(
el: @el.find('.ticket_create')
form_id: @form_id
model:
configure_attributes: configure_attributes
className: 'create_' + @type + '_' + @id
el: @el.find('.ticket-form-top')
form_id: @form_id
model: App.Ticket
screen: 'create_top'
events:
'change [name=customer_id]': @localUserInfo
handlers: [
formChanges
]
filter: @form_meta.filter
autofocus: true
form_data: @edit_form
params: params
)
new App.ControllerForm(
el: @el.find('.article-form-top')
form_id: @form_id
model: App.TicketArticle
screen: 'create_top'
params: params
)
new App.ControllerForm(
el: @el.find('.ticket-form-middle')
form_id: @form_id
model: App.Ticket
screen: 'create_middle'
events:
'change [name=customer_id]': @localUserInfo
handlers: [
formChanges
]
filter: @form_meta.filter
params: params
noFieldset: true
)
new App.ControllerForm(
el: @el.find('.ticket-form-bottom')
form_id: @form_id
model: App.Ticket
screen: 'create_bottom'
events:
'change [name=customer_id]': @localUserInfo
handlers: [
formChanges
]
filter: @form_meta.filter
params: params
)
@ -190,11 +268,15 @@ class App.TicketCreate extends App.Controller
# template_id: template['id']
# )
# set type selector
@setFormTypeInUi( params['formSenderType'] )
# remember form params of init load
@formDefault = @formParam( @el.find('.ticket-create') )
# show text module UI
@textModule = new App.WidgetTextModule(
el: @el.find('.ticket-create').find('textarea')
el: @el.find('form').find('textarea')
)
$('#tags').tokenfield()
@ -202,14 +284,17 @@ class App.TicketCreate extends App.Controller
# start auto save
@autosave()
localUserInfo: (params) =>
localUserInfo: (e) =>
params = App.ControllerForm.params( $(e.target).closest('form') )
# update text module UI
callback = (user) =>
@textModule.reload(
ticket:
customer: user
)
if @textModule
@textModule.reload(
ticket:
customer: user
)
@userInfo(
user_id: params.customer_id
@ -223,7 +308,8 @@ class App.TicketCreate extends App.Controller
create_screen: @
)
cancel: ->
cancel: (e) ->
e.preventDefault()
@navigate '#'
submit: (e) ->
@ -237,11 +323,11 @@ class App.TicketCreate extends App.Controller
params.title = params.subject
# create ticket
object = new App.Ticket
ticket = new App.Ticket
# find sender_id
sender = App.TicketArticleSender.findByAttribute( 'name', @article_attributes['sender'] )
type = App.TicketArticleType.findByAttribute( 'name', @article_attributes['article'] )
sender = App.TicketArticleSender.findByAttribute( 'name', @articleAttributes['sender'] )
type = App.TicketArticleType.findByAttribute( 'name', @articleAttributes['article'] )
if params.group_id
group = App.Group.find( params.group_id )
@ -251,6 +337,7 @@ class App.TicketCreate extends App.Controller
params['article'] = {
to: (group && group.name) || ''
from: params.customer_id_autocompletion
cc: params.cc
subject: params.subject
body: params.body
type_id: type.id
@ -261,6 +348,7 @@ class App.TicketCreate extends App.Controller
params['article'] = {
from: (group && group.name) || ''
to: params.customer_id_autocompletion
cc: params.cc
subject: params.subject
body: params.body
type_id: type.id
@ -268,15 +356,38 @@ class App.TicketCreate extends App.Controller
form_id: @form_id
}
object.load(params)
ticket.load(params)
# validate form
errors = object.validate()
ticketErrorsTop = ticket.validate(
screen: 'create_top'
)
ticketErrorsMiddle = ticket.validate(
screen: 'create_middle'
)
ticketErrorsBottom = ticket.validate(
screen: 'create_bottom'
)
article = new App.TicketArticle
article.load( params['article'] )
articleErrors = article.validate(
screen: 'create_top'
)
# collect whole validation result
errors = {}
errors = _.extend( errors, ticketErrorsTop )
errors = _.extend( errors, ticketErrorsMiddle )
errors = _.extend( errors, ticketErrorsBottom )
errors = _.extend( errors, articleErrors )
# show errors in form
if errors
if !_.isEmpty( errors )
@log 'error', errors
@formValidate( form: e.target, errors: errors )
@formValidate(
form: e.target
errors: errors
)
# save ticket, create article
else
@ -284,7 +395,7 @@ class App.TicketCreate extends App.Controller
# disable form
@formDisable(e)
ui = @
object.save(
ticket.save(
done: ->
# notify UI
@ -325,10 +436,10 @@ class UserNew extends App.ControllerModal
@html App.view('agent_user_create')( head: 'New User' )
new App.ControllerForm(
el: @el.find('#form-user'),
model: App.User,
required: 'quick',
autofocus: true,
el: @el.find('#form-user')
model: App.User
screen: 'edit'
autofocus: true
)
@modalShow()
@ -390,7 +501,7 @@ class Router extends App.ControllerPermanent
split = "/#{params['ticket_id']}/#{params['article_id']}"
id = Math.floor( Math.random() * 99999 )
@navigate "#ticket/create/#{params['type']}/id/#{id}#{split}"
@navigate "#ticket/create/id/#{id}#{split}"
return
# cleanup params
@ -400,20 +511,16 @@ class Router extends App.ControllerPermanent
type: params.type
id: params.id
App.TaskManager.add( 'TicketCreateScreen-' + params['type'] + '-' + params['id'], 'TicketCreate', clean_params )
App.TaskManager.add( 'TicketCreateScreen-' + params['id'], 'TicketCreate', clean_params )
# create new ticket routes/controller
App.Config.set( 'ticket/create', Router, 'Routes' )
App.Config.set( 'ticket/create/:type', Router, 'Routes' )
App.Config.set( 'ticket/create/:type/id/:id', Router, 'Routes' )
App.Config.set( 'ticket/create/', Router, 'Routes' )
App.Config.set( 'ticket/create/id/:id', Router, 'Routes' )
# split ticket
App.Config.set( 'ticket/create/:type/:ticket_id/:article_id', Router, 'Routes' )
App.Config.set( 'ticket/create/:type/id/:id/:ticket_id/:article_id', Router, 'Routes' )
# set new task actions
App.Config.set( 'TicketNewCallOutbound', { prio: 8001, parent: '#new', name: 'Call Outbound', target: '#ticket/create/call_outbound', role: ['Agent'], divider: true }, 'NavBarRight' )
App.Config.set( 'TicketNewCallInbound', { prio: 8002, parent: '#new', name: 'Call Inbound', target: '#ticket/create/call_inbound', role: ['Agent'], divider: true }, 'NavBarRight' )
App.Config.set( 'TicketNewEmail', { prio: 8003, parent: '#new', name: 'Email', target: '#ticket/create/email', role: ['Agent'], divider: true }, 'NavBarRight' )
App.Config.set( 'ticket/create/:ticket_id/:article_id', Router, 'Routes' )
App.Config.set( 'ticket/create/id/:id/:ticket_id/:article_id', Router, 'Routes' )
# set new actions
App.Config.set( 'TicketCreate', { prio: 8003, parent: '#new', name: 'New Ticket', target: '#ticket/create', role: ['Agent'], divider: true }, 'NavBarRight' )

View file

@ -13,11 +13,11 @@ class Index extends App.ControllerContent
# set title
@title 'New Ticket'
@form_id = App.ControllerForm.formId()
@form_meta = undefined
@fetch(params)
@navupdate '#customer_ticket_new'
@edit_form = undefined
# get data / in case also ticket data for split
fetch: (params) ->
@ -27,7 +27,7 @@ class Index extends App.ControllerContent
if cache
# get edit form attributes
@edit_form = cache.edit_form
@form_meta = cache.form_meta
# load assets
App.Collection.loadAssets( cache.assets )
@ -45,7 +45,7 @@ class Index extends App.ControllerContent
App.Store.write( 'ticket_create_attributes', data )
# get edit form attributes
@edit_form = data.edit_form
@form_meta = data.form_meta
# load assets
App.Collection.loadAssets( data.assets )
@ -87,27 +87,77 @@ class Index extends App.ControllerContent
return item if item && _.contains( group_ids, item.id.toString() )
)
# generate form
configure_attributes = [
{ name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: false, filter: groupFilter, nulloption: true, relation: 'Group', default: defaults['group_id'], class: 'span7', },
# { name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, null: true, filter: @edit_form, nulloption: true, relation: 'User', default: defaults['owner_id'], class: 'span7', },
{ name: 'subject', display: 'Subject', tag: 'input', type: 'text', limit: 100, null: false, default: defaults['subject'], class: 'span7', },
{ name: 'body', display: 'Text', tag: 'textarea', rows: 10, null: false, default: defaults['body'], class: 'span7', upload: true },
# { name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, filter: @edit_form, relation: 'TicketState', default: defaults['state_id'], translate: true, class: 'medium' },
# { name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, filter: @edit_form, relation: 'TicketPriority', default: defaults['priority_id'], translate: true, class: 'medium' },
]
formChanges = (params, attribute, attributes, classname, form, ui) =>
if @form_meta.dependencies && @form_meta.dependencies[attribute.name]
dependency = @form_meta.dependencies[attribute.name][ parseInt(params[attribute.name]) ]
if dependency
for fieldNameToChange of dependency
filter = []
if dependency[fieldNameToChange]
filter = dependency[fieldNameToChange]
# find element to replace
for item in attributes
if item.name is fieldNameToChange
item.display = false
item['filter'] = {}
item['filter'][ fieldNameToChange ] = filter
item.default = params[item.name]
#if !item.default
# delete item['default']
newElement = ui.formGenItem( item, classname, form )
# replace new option list
form.find('[name="' + fieldNameToChange + '"]').replaceWith( newElement )
@html App.view('customer_ticket_create')( head: 'New Ticket' )
new App.ControllerForm(
el: @el.find('#form_create')
form_id: @form_id
model:
configure_attributes: configure_attributes
className: 'create'
el: @el.find('.ticket-form-top')
form_id: @form_id
model: App.Ticket
screen: 'create_top'#@article_attributes['screen']
handlers: [
formChanges
]
filter: @form_meta.filter
autofocus: true
form_data: @edit_form
params: defaults
)
new App.ControllerForm(
el: @el.find('.article-form-top')
form_id: @form_id
model: App.TicketArticle
screen: 'create_top'#@article_attributes['screen']
params: defaults
)
new App.ControllerForm(
el: @el.find('.ticket-form-middle')
form_id: @form_id
model: App.Ticket
screen: 'create_middle'#@article_attributes['screen']
handlers: [
formChanges
]
filter: @form_meta.filter
params: defaults
noFieldset: true
)
#new App.ControllerForm(
# el: @el.find('.ticket-form-bottom')
# form_id: @form_id
# model: App.Ticket
# screen: 'create_bottom'#@article_attributes['screen']
# handlers: [
# formChanges
# ]
# filter: @form_meta.filter
# params: defaults
#)
new App.ControllerDrox(
el: @el.find('.sidebar')
data:
@ -128,19 +178,21 @@ class Index extends App.ControllerContent
params.customer_id = @Session.get('id')
# set prio
priority = App.TicketPriority.findByAttribute( 'name', '2 normal' )
params.priority_id = priority.id
if !params.priority_id
priority = App.TicketPriority.findByAttribute( 'name', '2 normal' )
params.priority_id = priority.id
# set state
state = App.TicketState.findByAttribute( 'name', 'new' )
params.state_id = state.id
if !params.state_id
state = App.TicketState.findByAttribute( 'name', 'new' )
params.state_id = state.id
# fillup params
if !params.title
params.title = params.subject
# create ticket
object = new App.Ticket
ticket = new App.Ticket
@log 'CustomerTicketCreate', 'notice', 'updateAttributes', params
# find sender_id
@ -160,16 +212,35 @@ class Index extends App.ControllerContent
form_id: @form_id
}
object.load(params)
ticket.load(params)
# validate form
errors = object.validate()
ticketErrorsTop = ticket.validate(
screen: 'create_top'
)
ticketErrorsMiddle = ticket.validate(
screen: 'create_middle'
)
article = new App.TicketArticle
article.load(params['article'])
articleErrors = article.validate(
screen: 'create_top'
)
# collect whole validation
errors = {}
errors = _.extend( errors, ticketErrorsTop )
errors = _.extend( errors, ticketErrorsMiddle )
errors = _.extend( errors, articleErrors )
# show errors in form
if errors
if !_.isEmpty(errors)
@log 'CustomerTicketCreate', 'error', 'can not create', errors
@formValidate( form: e.target, errors: errors )
@formValidate(
form: e.target
errors: errors
)
# save ticket, create article
else
@ -177,7 +248,7 @@ class Index extends App.ControllerContent
# disable form
@formDisable(e)
ui = @
object.save(
ticket.save(
done: ->
# redirect to zoom
@ -189,4 +260,4 @@ class Index extends App.ControllerContent
)
App.Config.set( 'customer_ticket_new', Index, 'Routes' )
App.Config.set( 'CustomerTicketNew', { prio: 8003, parent: '#new', name: 'New Ticket', target: '#customer_ticket_new', role: ['Customer'], divider: true }, 'NavBarRight' )
App.Config.set( 'CustomerTicketNew', { prio: 8003, parent: '#new', name: 'New Ticket', target: '#customer_ticket_new', role: ['Customer'], divider: true }, 'NavBarRight' )

View file

@ -32,7 +32,7 @@ class Index extends App.ControllerContent
new App.ControllerForm(
el: @el.find('form')
model: App.User
required: 'signup'
screen: 'signup'
autofocus: true
)

View file

@ -322,7 +322,7 @@ class Table extends App.ControllerContent
configure_attributes: @configure_attributes_ticket
className: 'create'
form_data: @bulk
no_fieldset: true
noFieldset: true
)
#html.delegate('.bulk-action-form', 'submit', (e) =>
html.bind('submit', (e) =>

View file

@ -5,7 +5,9 @@ class App.TicketZoom extends App.Controller
# check authentication
return if !@authenticate()
@edit_form = undefined
@navupdate '#'
@form_meta = undefined
@ticket_id = params.ticket_id
@article_id = params.article_id
@signature = undefined
@ -112,7 +114,7 @@ class App.TicketZoom extends App.Controller
@ticket_article_ids = data.ticket_article_ids
# get edit form attributes
@edit_form = data.edit_form
@form_meta = data.form_meta
# get signature
@signature = data.signature
@ -145,7 +147,7 @@ class App.TicketZoom extends App.Controller
new App.ControllerForm(
el: @el.find('.edit')
model: App.Ticket
required: required
screen: 'edit'
params: App.Ticket.find(@ticket.id)
)
# start link info controller
@ -163,13 +165,14 @@ class App.TicketZoom extends App.Controller
new App.ControllerForm(
el: @el.find('.customer-edit')
model: App.User
required: 'quick'
screen: 'edit'
params: App.User.find(@ticket.customer_id)
)
new App.ControllerForm(
el: @el.find('.organization-edit')
model: App.Organization
params: App.Organization.find(@ticket.organitaion_id)
screen: 'edit'
)
@TicketAction()
@ -214,7 +217,8 @@ class App.TicketZoom extends App.Controller
new Edit(
ticket: @ticket
el: @el.find('.ticket-edit')
edit_form: @edit_form
#el: @el.find('.edit')
form_meta: @form_meta
task_key: @task_key
ui: @
)
@ -244,9 +248,13 @@ class TicketTitle extends App.Controller
render: (ticket) =>
@html App.view('ticket_zoom/title')(
ticket: ticket
ticket: ticket
isCustomer: @isRole('Customer')
)
# show frontend times
@frontendTimeUpdate()
update: (e) =>
$this = $(e.target)
title = $this.html()
@ -298,7 +306,7 @@ class Sidebar extends App.Controller
if name
if name is @currentTab
@toggleSidebar()
else
else
@el.find('.ticket-zoom .sidebar-tab').removeClass('active')
$(e.target).closest('.sidebar-tab').addClass('active')
@ -372,57 +380,55 @@ class Edit extends App.Controller
formChanged: !_.isEmpty( App.TaskManager.get(@task_key).state )
)
@configure_attributes_ticket = [
{ name: 'state_id', display: 'State', tag: 'select', multiple: false, null: true, relation: 'TicketState', filter: @edit_form, translate: true, class: 'span2', item_class: 'pull-left' },
{ name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: true, relation: 'TicketPriority', filter: @edit_form, translate: true, class: 'span2', item_class: 'pull-left' },
{ name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: true, relation: 'Group', filter: @edit_form, class: 'span2', item_class: 'pull-left' },
{ name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, null: true, relation: 'User', filter: @edit_form, nulloption: true, class: 'span2', item_class: 'pull-left' },
]
if @isRole('Customer')
@configure_attributes_ticket = [
{ name: 'state_id', display: 'State', tag: 'select', multiple: false, null: true, relation: 'TicketState', filter: @edit_form, translate: true, class: 'span2', item_class: 'pull-left' },
{ name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: true, relation: 'TicketPriority', filter: @edit_form, translate: true, class: 'span2', item_class: 'pull-left' },
]
@configure_attributes_article = [
{ name: 'type_id', display: 'Type', tag: 'select', multiple: false, null: true, relation: 'TicketArticleType', filter: @edit_form, default: '9', translate: true, class: 'medium' },
{ name: 'internal', display: 'Visibility', tag: 'select', null: true, options: { true: 'internal', false: 'public' }, class: 'medium', item_class: '', default: false },
{ name: 'to', display: 'To', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', hide: true },
{ name: 'cc', display: 'Cc', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', hide: true },
# { name: 'subject', display: 'Subject', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', hide: true },
{ name: 'in_reply_to', display: 'In Reply to', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', item_class: 'hide' },
{ name: 'body', display: 'Text', tag: 'textarea', rows: 6, limit: 100, null: true, class: 'span7', item_class: '', upload: true },
]
if @isRole('Customer')
@configure_attributes_article = [
{ name: 'to', display: 'To', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', hide: true },
{ name: 'cc', display: 'Cc', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', hide: true },
# { name: 'subject', display: 'Subject', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', hide: true },
{ name: 'in_reply_to', display: 'In Reply to', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', item_class: 'hide' },
{ name: 'body', display: 'Text', tag: 'textarea', rows: 6, limit: 100, null: true, class: 'span7', item_class: '', upload: true },
]
@form_id = App.ControllerForm.formId()
defaults = ticket
defaults = ticket.attributes()
if @isRole('Customer')
delete defaults['state_id']
delete defaults['state']
if !_.isEmpty( App.TaskManager.get(@task_key).state )
defaults = App.TaskManager.get(@task_key).state
new App.ControllerForm(
el: @el.find('.form-ticket-update')
form_id: @form_id
model:
configure_attributes: @configure_attributes_ticket
className: 'update_ticket_' + ticket.id
params: defaults
form_data: @edit_form
)
formChanges = (params, attribute, attributes, classname, form, ui) =>
if @form_meta.dependencies && @form_meta.dependencies[attribute.name]
dependency = @form_meta.dependencies[attribute.name][ parseInt(params[attribute.name]) ]
if dependency
for fieldNameToChange of dependency
filter = []
if dependency[fieldNameToChange]
filter = dependency[fieldNameToChange]
# find element to replace
for item in attributes
if item.name is fieldNameToChange
item.display = false
item['filter'] = {}
item['filter'][ fieldNameToChange ] = filter
item.default = params[item.name]
#if !item.default
# delete item['default']
newElement = ui.formGenItem( item, classname, form )
# replace new option list
form.find('[name="' + fieldNameToChange + '"]').replaceWith( newElement )
new App.ControllerForm(
el: @el.find('.form-ticket-update')
form_id: @form_id
model: App.Ticket
screen: 'edit'
handlers: [
formChanges
]
filter: @form_meta.filter
params: defaults
)
new App.ControllerForm(
el: @el.find('.form-article-update')
form_id: @form_id
model:
configure_attributes: @configure_attributes_article
className: 'update_ticket_' + ticket.id
form_data: @edit_form
model: App.TicketArticle
screen: 'edit'
filter:
type_id: [1,9,5]
params: defaults
dependency: [
{
@ -440,13 +446,24 @@ class Edit extends App.Controller
bind: {
name: 'type_id'
relation: 'TicketArticleType'
value: ['note', 'twitter status', 'twitter direct-message']
value: ['note', 'phone', 'twitter status']
},
change: {
action: 'hide'
name: ['to', 'cc'],
},
},
{
bind: {
name: 'type_id'
relation: 'TicketArticleType'
value: ['twitter direct-message']
},
change: {
action: 'show'
name: ['to'],
},
},
]
)
@ -680,10 +697,16 @@ class Edit extends App.Controller
@autosaveStop()
params = @formParam(e.target)
# get ticket
ticket = App.Ticket.fullLocal( @ticket.id )
@log 'notice', 'update', params, ticket
# update local ticket
# create local article
# find sender_id
if @isRole('Customer')
sender = App.TicketArticleSender.findByAttribute( 'name', 'Customer' )
@ -696,17 +719,16 @@ class Edit extends App.Controller
params.sender_id = sender.id
# update ticket
ticket_update = {}
for item in @configure_attributes_ticket
ticket_update[item.name] = params[item.name]
for key, value of params
ticket[key] = value
# check owner assignment
if !@isRole('Customer')
if !ticket_update['owner_id']
ticket_update['owner_id'] = 1
if !ticket['owner_id']
ticket['owner_id'] = 1
# check if title exists
if !ticket_update['title'] && !ticket.title
if !ticket['title']
alert( App.i18n.translateContent('Title needed') )
return
@ -732,22 +754,32 @@ class Edit extends App.Controller
@autosaveStart()
return
ticket.load( ticket_update )
@log 'notice', 'update ticket', ticket_update, ticket
# submit ticket & article
@log 'notice', 'update ticket', ticket
# disable form
@formDisable(e)
# validate ticket
errors = ticket.validate()
errors = ticket.validate(
screen: 'edit'
)
if errors
@log 'error', 'update', errors
@log 'error', errors
@formValidate(
form: e.target
errors: errors
screen: 'edit'
)
@formEnable(e)
@autosaveStart()
return
# validate article
if params['body']
articleAttributes = App.TicketArticle.attributesGet( 'edit' )
if params['body'] || ( articleAttributes['body'] && articleAttributes['body']['null'] is false )
article = new App.TicketArticle
params.from = @Session.get( 'firstname' ) + ' ' + @Session.get( 'lastname' )
params.ticket_id = ticket.id
@ -761,6 +793,11 @@ class Edit extends App.Controller
errors = article.validate()
if errors
@log 'error', 'update article', errors
@formValidate(
form: e.target
errors: errors
screen: 'edit'
)
@formEnable(e)
@autosaveStart()
return

View file

@ -109,13 +109,12 @@ class App.WidgetUser extends App.ControllerDrox
edit: (e) =>
e.preventDefault()
new App.ControllerGenericEdit(
id: @user_id,
genericObject: 'User',
required: 'quick',
pageData: {
title: 'Users',
object: 'User',
objects: 'Users',
},
id: @user_id
genericObject: 'User'
screen: 'edit'
pageData:
title: 'Users'
object: 'User'
objects: 'Users'
callback: @render
)

View file

@ -69,12 +69,26 @@ class App.Auth
App.Event.trigger( 'auth:logout' )
App.Event.trigger( 'ui:rerender' )
# update model definition
if data.models
for model, attributes of data.models
for attribute in attributes
App[model].attributes.push attribute.name
App[model].configure_attributes.push attribute
return false;
# clear local store
if type isnt 'check'
App.Event.trigger( 'clearStore' )
# update model definition
if data.models
for model, attributes of data.models
for attribute in attributes
App[model].attributes.push attribute.name
App[model].configure_attributes.push attribute
# update config
for key, value of data.config
App.Config.set( key, value )
@ -129,4 +143,3 @@ class App.Auth
App.Event.trigger( 'auth:logout' )
App.Event.trigger( 'ui:rerender' )
App.Event.trigger( 'clearStore' )

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

0
app/assets/javascripts/app/lib/spine/ajax.coffee Executable file → Normal file
View file

0
app/assets/javascripts/app/lib/spine/bindings.coffee Executable file → Normal file
View file

0
app/assets/javascripts/app/lib/spine/list.coffee Executable file → Normal file
View file

0
app/assets/javascripts/app/lib/spine/local.coffee Executable file → Normal file
View file

0
app/assets/javascripts/app/lib/spine/manager.coffee Executable file → Normal file
View file

9
app/assets/javascripts/app/lib/spine/relation.coffee Executable file → Normal file
View file

@ -1,6 +1,5 @@
Spine = @Spine or require('spine')
isArray = Spine.isArray
require = @require or ((value) -> eval(value))
class Collection extends Spine.Module
constructor: (options = {}) ->
@ -111,8 +110,14 @@ underscore = (str) ->
.replace(/-/g, '_')
.toLowerCase()
requireModel = (model) ->
if typeof model is 'string'
require?(model) or eval(model)
else
model
association = (name, model, record, fkey, Ctor) ->
model = require(model) if typeof model is 'string'
model = requireModel(model) if typeof model is 'string'
new Ctor(name: name, model: model, record: record, fkey: fkey)
Spine.Model.extend

30
app/assets/javascripts/app/lib/spine/route.coffee Executable file → Normal file
View file

@ -71,6 +71,9 @@ class Route extends Spine.Module
@change()
@unbind: ->
unbindResult = Spine.Events.unbind.apply this, arguments
return unbindResult if arguments.length > 0
return if @options.shim
if @history
@ -80,33 +83,30 @@ class Route extends Spine.Module
@navigate: (args...) ->
options = {}
lastArg = args[args.length - 1]
if typeof lastArg is 'object'
options = args.pop()
else if typeof lastArg is 'boolean'
options.trigger = args.pop()
options = $.extend({}, @options, options)
path = args.join('/')
return if @path is path
@path = path
@trigger('navigate', @path)
if options.trigger
@trigger('navigate', @path)
routes = @matchRoutes(@path, options)
unless routes.length
if typeof options.redirect is 'function'
return options.redirect.apply this, [@path, options]
else
if options.redirect is true
@redirect(@path)
routes = @matchRoutes(@path, options) if options.trigger
return if options.shim
unless routes.length
if typeof options.redirect is 'function'
return options.redirect.apply this, [@path, options]
else
if options.redirect is true
@redirect(@path)
if @history and options.replace
if options.shim
true
else if @history and options.replace
history.replaceState({}, document.title, @path)
else if @history
history.pushState({}, document.title, @path)

17
app/assets/javascripts/app/lib/spine/spine.coffee Executable file → Normal file
View file

@ -149,13 +149,14 @@ class Model extends Module
@toString: -> "#{@className}(#{@attributes.join(", ")})"
@find: (id, notFound = @notFound) ->
record = @irecords[id]?.clone()
return record or notFound?(id)
@irecords[id]?.clone() or notFound?(id)
@notFound: (id) -> return null
@findAll: (ids, notFound) ->
(@find(id) for id in ids when @find(id, notFound))
@exists: (id) ->
return if @irecords[id] then true else false
@notFound: (id) -> null
@exists: (id) -> Boolean @irecords[id]
@addRecord: (record, options = {}) ->
if record.id and @irecords[record.id]
@ -321,8 +322,8 @@ class Model extends Module
result
eql: (rec) ->
!!(rec and rec.constructor is @constructor and
((rec.cid is @cid) or (rec.id and rec.id is @id)))
rec and rec.constructor is @constructor and
((rec.cid is @cid) or (rec.id and rec.id is @id))
save: (options = {}) ->
unless options.validate is false
@ -634,7 +635,7 @@ makeArray = (args) ->
Spine = @Spine = {}
module?.exports = Spine
Spine.version = '1.3.1'
Spine.version = '1.3.2'
Spine.isArray = isArray
Spine.isBlank = isBlank
Spine.$ = $

View file

@ -73,55 +73,116 @@ class App.Model extends Spine.Model
''
@validate: ( data = {} ) ->
return if !data['model'].configure_attributes
# based on model attrbutes
if App[ data['model'] ] && App[ data['model'] ].attributesGet
attributes = App[ data['model'] ].attributesGet( data['screen'] )
# based on custom attributes
else if data['model'].configure_attributes
attributes = App.Model.attributesGet( data['screen'], data['model'].configure_attributes )
# check required_if attributes
for attributeName, attribute of attributes
if attribute['required_if']
for key, values of attribute['required_if']
localValues = data['params'][key]
if !_.isArray( localValues )
localValues = [ localValues ]
match = false
for value in values
if localValues
for localValue in localValues
if value && localValue && value.toString() is localValue.toString()
match = true
if match is true
attribute['null'] = false
else
attribute['null'] = true
# check attributes/each attribute of object
errors = {}
for attribute in data['model'].configure_attributes
for attributeName, attribute of attributes
# only if attribute is not read only
if !attribute.readonly
# check required // if null is defined && null is false
if 'null' of attribute && !attribute[null]
if 'null' of attribute && !attribute['null']
# check :: fields
parts = attribute.name.split '::'
if parts[0] && !parts[1]
# key exists not in hash || value is '' || value is undefined
if !( attribute.name of data['params'] ) || data['params'][attribute.name] is '' || data['params'][attribute.name] is undefined
errors[attribute.name] = 'is required'
if !( attributeName of data['params'] ) || data['params'][attributeName] is '' || data['params'][attributeName] is undefined
errors[attributeName] = 'is required'
else if parts[0] && parts[1] && !parts[2]
# key exists not in hash || value is '' || value is undefined
if !data.params[parts[0]] || !( parts[1] of data.params[parts[0]] ) || data.params[parts[0]][parts[1]] is '' || data.params[parts[0]][parts[1]] is undefined
errors[attribute.name] = 'is required'
errors[attributeName] = 'is required'
else
throw "can't parse '#{attribute.name}'"
# check confirm password
if attribute.type is 'password' && data['params'][attribute.name] && "#{attribute.name}_confirm" of data['params']
if attribute.type is 'password' && data['params'][attributeName] && "#{attributeName}_confirm" of data['params']
# get confirm password
if data['params'][attribute.name] isnt data['params']["#{attribute.name}_confirm"]
errors[attribute.name] = 'didn\'t match'
errors["#{attribute.name}_confirm"] = ''
if data['params'][attributeName] isnt data['params']["#{attributeName}_confirm"]
errors[attributeName] = 'didn\'t match'
errors["#{attributeName}_confirm"] = ''
# return error object
if !_.isEmpty(errors)
console.log 'error', 'validation vailed', errors
console.log 'error', 'validation failed', errors
return errors
# return no errors
return
validate: ->
###
attributes = App.Model.attributesGet(optionalScreen, optionalAttributesList)
###
@attributesGet: (screen = undefined, attributes = false) ->
if !attributes
attributes = clone( App[ @.className ].configure_attributes )
else
attributes = clone( attributes )
# in case if no configure_attributes exist
return if !attributes
attributesNew = {}
# check params of screen if screen is requested
if screen
for attribute in attributes
if attribute.screen
if attribute && attribute.screen && attribute.screen[ screen ] && !_.isEmpty( attribute.screen[ screen ] )
for item, value of attribute.screen[ screen ]
attribute[item] = value
attributesNew[ attribute.name ] = attribute
if !screen || _.isEmpty( attributesNew )
for attribute in attributes
attributesNew[ attribute.name ] = attribute
console.log(attributesNew)
attributesNew
validate: (params = {}) ->
App.Model.validate(
model: @constructor,
params: @,
model: @constructor.className
params: @
screen: params.screen
)
isOnline: ->

View file

@ -5,7 +5,7 @@ class App.Group extends App.Model
@configure_attributes = [
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false, 'class': 'span4' },
{ name: 'assignment_timeout', display: 'Assignment Timout', tag: 'input', note: 'Assignment timout in minutes if assigned agent is not working on it. Ticket will be shown as unassigend.', type: 'text', limit: 100, 'null': true, 'class': 'span4' },
{ 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, 'class': 'span4' },
{ 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.', 'class': 'span4' },
{ 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.', 'class': 'span4' },
{ name: 'email_address_id', display: 'Email', tag: 'select', multiple: false, null: true, relation: 'EmailAddress', nulloption: true, class: 'span4' },
@ -19,4 +19,4 @@ class App.Group extends App.Model
]
uiUrl: ->
'#group/zoom/' + @id
'#group/zoom/' + @id

View file

@ -6,20 +6,20 @@ class App.User extends App.Model
# @hasMany 'roles', 'App.Role'
@configure_attributes = [
{ name: 'login', display: 'Login', tag: 'input', type: 'text', limit: 100, null: false, class: 'span4', autocapitalize: false, signup: false, quick: false },
{ name: 'firstname', display: 'Firstname', tag: 'input', type: 'text', limit: 100, null: false, class: 'span4', signup: true, quick: true, info: true, invite_agent: true },
{ name: 'lastname', display: 'Lastname', tag: 'input', type: 'text', limit: 100, null: false, class: 'span4', signup: true, quick: true, info: true, invite_agent: true },
{ name: 'email', display: 'Email', tag: 'input', type: 'email', limit: 100, null: false, class: 'span4', signup: true, quick: true, info: true, invite_agent: true },
{ name: 'web', display: 'Web', tag: 'input', type: 'url', limit: 100, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'phone', display: 'Phone', tag: 'input', type: 'phone', limit: 100, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'mobile', display: 'Mobile', tag: 'input', type: 'phone', limit: 100, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'fax', display: 'Fax', tag: 'input', type: 'phone', limit: 100, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'organization_id', display: 'Organization', tag: 'select', multiple: false, nulloption: true, null: true, relation: 'Organization', class: 'span4', signup: false, quick: true, info: true },
{ name: 'department', display: 'Department', tag: 'input', type: 'text', limit: 200, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'street', display: 'Street', tag: 'input', type: 'text', limit: 100, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'zip', display: 'Zip', tag: 'input', type: 'text', limit: 100, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'city', display: 'City', tag: 'input', type: 'text', limit: 100, null: true, class: 'span4', signup: false, quick: true, info: true },
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 50, null: true, autocomplete: 'off', class: 'span4', signup: true, quick: false },
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true, class: 'span4', quick: true, info: true },
{ name: 'firstname', display: 'Firstname', tag: 'input', type: 'text', limit: 100, null: false, class: 'span4', signup: true, info: true, invite_agent: true },
{ name: 'lastname', display: 'Lastname', tag: 'input', type: 'text', limit: 100, null: false, class: 'span4', signup: true, info: true, invite_agent: true },
{ name: 'email', display: 'Email', tag: 'input', type: 'email', limit: 100, null: false, class: 'span4', signup: true, info: true, invite_agent: true },
{ name: 'web', display: 'Web', tag: 'input', type: 'url', limit: 100, null: true, class: 'span4', signup: false, info: true },
{ name: 'phone', display: 'Phone', tag: 'input', type: 'phone', limit: 100, null: true, class: 'span4', signup: false, info: true },
{ name: 'mobile', display: 'Mobile', tag: 'input', type: 'phone', limit: 100, null: true, class: 'span4', signup: false, info: true },
{ name: 'fax', display: 'Fax', tag: 'input', type: 'phone', limit: 100, null: true, class: 'span4', signup: false, info: true },
{ name: 'organization_id', display: 'Organization', tag: 'select', multiple: false, nulloption: true, null: true, relation: 'Organization', class: 'span4', signup: false, info: true },
{ name: 'department', display: 'Department', tag: 'input', type: 'text', limit: 200, null: true, class: 'span4', signup: false, info: true },
{ name: 'street', display: 'Street', tag: 'input', type: 'text', limit: 100, null: true, class: 'span4', signup: false, info: true },
{ name: 'zip', display: 'Zip', tag: 'input', type: 'text', limit: 100, null: true, class: 'span4', signup: false, info: true },
{ name: 'city', display: 'City', tag: 'input', type: 'text', limit: 100, null: true, class: 'span4', signup: false, info: true },
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 50, null: true, autocomplete: 'off', class: 'span4', signup: true, },
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true, class: 'span4', info: true },
{ name: 'role_ids', display: 'Roles', tag: 'checkbox', multiple: true, null: false, relation: 'Role', class: 'span4' },
{ name: 'group_ids', display: 'Groups', tag: 'checkbox', multiple: true, null: true, relation: 'Group', class: 'span4', invite_agent: true },
{ name: 'active', display: 'Active', tag: 'boolean', default: true, null: true, class: 'span4' },

View file

@ -2,87 +2,41 @@
<div class="create-new main flex">
<div class="box">
<div class="page-header">
<h1><%- @T( 'Create New Ticket' ) %></h1>
<h1><%- @T( @head ) %></h1>
</div>
<ul class="horizontal tabs type-tabs">
<li class="tab u-textTruncate active">
<div class="white email channel icon"></div>
<%- @T( 'E-Mail' ) %>
<li class="tab u-textTruncate">
<li class="tab u-textTruncate" data-type="phone-in">
<div class="gray received-calls channel icon"></div>
<%- @T( 'Received Call' ) %>
</li>
<li class="tab u-textTruncate">
<li class="tab u-textTruncate" data-type="phone-out">
<div class="gray outbound-calls channel icon"></div>
<%- @T( 'Outbound Call' ) %>
</li>
<li class="tab u-textTruncate" data-type="email-out">
<div class="gray email channel icon"></div>
<%- @T( 'Send E-Mail' ) %>
</li>
</ul>
<form role="form">
<div class="form-group">
<label for="customer"><%- @T('Customer') %></label>
<input class="form-control" id="customer" placeholder="<%- @T('Enter Person or Organisation/Company') %>">
</div>
<div class="form-group">
<label for="subject"><%- @T('Subject') %></label>
<input class="form-control" id="subject">
</div>
<div class="form-group">
<label for="text"><%- @T('Text') %></label>
<textarea class="form-control" id="text" rows="4"></textarea>
</div>
<form role="form" class="form-large ticket-create">
<input type="hidden" name="formSenderType"/>
<div class="ticket-form-top"></div>
<div class="article-form-top"></div>
<div class="formset-inset">
<div class="horizontal two-columns">
<div class="form-group column">
<label for="owner"><%- @T('Owner') %></label>
<select class="form-control" id="owner">
<option>Support -> Oliver Ruhm</option>
<option>Engineering -> Web-Dev</option>
</select>
</div>
<div class="form-group column">
<label for="type"><%- @T('Type') %></label>
<select class="form-control" id="type">
<option>Inquiry</option>
<option>Support</option>
<option selected>Question</option>
</select>
</div>
<div class="form-group column">
<label for="status"><%- @T('Status') %></label>
<select class="form-control" id="status">
<option selected>open</option>
<option>closed</option>
</select>
</div>
<div class="form-group column">
<label for="proprity"><%- @T('Priority') %></label>
<select class="form-control" id="proprity">
<option>low</option>
<option selected>normal</option>
<option>high</option>
</select>
</div>
</div>
<div class="form-group">
<label for="tags"><%- @T('Tags') %></label>
<input class="form-control" id="tags" placeholder="<%- @T('Tag hinzufügen') %>" value="New Website, Feedback">
</div>
<div class="ticket-form-middle horizontal two-columns"></div>
<div class="ticket-form-bottom"></div>
</div>
<input type="hidden" value="" name="article_type"/>
<div class="form-actions horizontal">
<a class="subtle-link standalone cancel" href="#/"><%- @T( 'Cancel & Go Back' ) %></a>
<button type="submit" class="btn btn-create submit align-right"><%- @T( 'Create Ticket' ) %></button>
<button type="submit" class="btn btn-create submit align-right"><%- @T( 'Create' ) %></button>
</div>
</form>
</div>
@ -208,4 +162,4 @@
<div class="text_module"></div>
</div>
</div>
</div>
</div>

View file

@ -1,18 +1,27 @@
<div class="main flex">
<div class="create-new horizontal flex">
<div class="create-new main flex">
<div class="box">
<div class="page-header">
<h1><%- @T( 'New Ticket' ) %></h1>
</div>
<div class="ticket-create vertical">
<div class="page-header">
<h1 class="pull-left"><%- @T( 'New Ticket' ) %></h1>
</div>
<div class="page-content">
<form class="form-horizontal form-large">
<input type="hidden" value="" name="article_type"/>
<div id="form_create"></div>
<div class="form-actions">
<button type="reset" class="btn btn-default cancel"><%- @T( 'Cancel' ) %></button>
<button type="submit" class="btn btn-primary submit"><%- @T( 'Create' ) %></button>
<form role="form" class="form-large ticket-create">
<div class="ticket-form-top"></div>
<div class="article-form-top"></div>
<div class="formset-inset">
<div class="ticket-form-middle horizontal two-columns"></div>
<div class="ticket-form-bottom"></div>
</div>
<div class="form-actions horizontal">
<a class="subtle-link standalone cancel" href="#/"><%- @T( 'Cancel & Go Back' ) %></a>
<button type="submit" class="btn btn-create submit align-right"><%- @T( 'Create' ) %></button>
</div>
</form>
</div>
</div>

View file

@ -1,5 +1,5 @@
<div class="form-group <%= @attribute.item_class %>">
<label for="<%= @attribute.id %>"><%- @T( @attribute.display ) %><% if !@attribute.null: %> *<% end %></label>
<label for="<%= @attribute.id %>"><%- @T( @attribute.display ) %> <span><% if !@attribute.null: %>*<% end %></span></label>
<div class="controls">
<%- @item %><% if @attribute.note: %><span class="glyphicon glyphicon-question-sign help-message" title="<%- @Ti( @attribute.note ) + ' ' %>"></span><% end %>
<% if @attribute.remove: %><span><a href="#" class="glyphicon glyphicon-minus"></a></span><% end %>

View file

@ -1,3 +1,3 @@
<input id="<%= @attribute.id %>" type="hidden" name="<%= @attribute.name %>" value="<%= @attribute.value %>" <%= @attribute.required %> />
<input id="<%= @attribute.id %>_autocompletion" type="text" name="<%= @attribute.name %>_autocompletion" value="<%= @attribute.valueShown %>" class="form-control <%= @attribute.class %>" <%= @attribute.required %> <%= @attribute.autofocus %> <%- @attribute.autocapitalize %>/>
<input id="<%= @attribute.id %>_autocompletion" type="text" name="<%= @attribute.name %>_autocompletion" value="<%= @attribute.valueShown %>" class="form-control <%= @attribute.class %>" <%= @attribute.required %> <%= @attribute.autofocus %> <%- @attribute.autocapitalize %> <% if @attribute.placeholder: %>placeholder="<%- @T(@attribute.placeholder) %>"<% end %>/>
<input id="<%= @attribute.id %>_autocompletion_value_shown" type="hidden" name="<%= @attribute.name %>_autocompletion_value_shown" value="<%= @attribute.valueShown %>"/>

View file

@ -0,0 +1 @@
<input id="<%= @attribute.id %>" type="<%= @attribute.type %>" class="form-control <%= @attribute.class %>" placeholder="<%= @attribute.placeholder %>" name="<%= @attribute.name %>" value="<%= @attribute.value %>" <%= @attribute.required %>>

View file

@ -1 +1 @@
<input id="<%= @attribute.id %>" type="<%= @attribute.type %>" name="<%= @attribute.name %>" value="<%= @attribute.value %>" class="form-control <%= @attribute.class %>" <%= @attribute.required %> <%= @attribute.autofocus %> <%- @attribute.autocapitalize %> <%- @attribute.autocomplete %>/>
<input id="<%= @attribute.id %>" type="<%= @attribute.type %>" name="<%= @attribute.name %>" value="<%= @attribute.value %>" class="form-control <%= @attribute.class %>" <% if @attribute.placeholder: %>placeholder="<%- @T(@attribute.placeholder) %>"<% end %> <%= @attribute.required %> <%= @attribute.autofocus %> <%- @attribute.autocapitalize %> <%- @attribute.autocomplete %>/>

View file

@ -1,5 +1,7 @@
<select id="<%= @attribute.id %>" class="form-control <%= @attribute.class %>" name="<%= @attribute.name %>" <%= @attribute.multiple %> <%= @attribute.required %> <%= @attribute.autofocus %>>
<% if @attribute.options: %>
<% for row in @attribute.options: %>
<option value="<%= row.value %>" <%= row.selected %> <%= row.disabled %>><%= row.name %></option>
<% end %>
<% end %>
</select>

View file

@ -1,8 +1,14 @@
<% for item in @items: %>
<% if item.child: %>
<li class="dropdown <%= item.class %> <% if @open_tab[item.target] : %>open<% end %>">
<a href="<%= item.target %>" class="dropdown-toggle" data-toggle="dropdown">
<%- @T( item.name ) %> <b class="caret"></b>
<a href="<%= item.target %>" class="dropdown-toggle horizontal center" data-toggle="dropdown">
<span class="nav-item-icon">
<span class="<%= item.class %> icon"></span>
</span>
<span class="nav-item-name flex">
<%- @T( item.name ) %>
</span>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<% for item in item.child: %>

View file

@ -5,9 +5,9 @@
// the compiled file.
//
//= require ./app/lib/core/jquery-1.11.0.js
//= require ./app/lib/core/jquery-2.1.1.js
//= require ./app/lib/core/jquery-ui-1.8.23.custom.min.js
//= require ./app/lib/core/underscore-1.6.0.js
//= require ./app/lib/core/underscore-1.7.0.js
//= require ./app/lib/animations/velocity.min.js
//= require ./app/lib/animations/velocity.ui.js
@ -69,6 +69,13 @@ function difference(object1, object2) {
return changes;
}
function clone(object) {
if (!object) {
return object
}
return JSON.parse(JSON.stringify(object));
}
jQuery.event.special.remove = {
remove: function(e) {
if (e.handler) e.handler();

View file

@ -658,13 +658,13 @@ label {
.form-control:focus,
.tokenfield.focus {
box-shadow: none;
border-color: #419ed7;
border-color: #419ed7 !important;
}
.has-error .form-control,
.has-error .form-control:focus {
box-shadow: none;
border-color: red;
border-color: red !important;
}
.help-inline {
@ -752,6 +752,9 @@ label {
margin-left: 10px;
}
.help-block {
color: #bcbcbc;
}
.form-horizontal .help-inline, .form-horizontal .help-block {
font-size: 12px;
margin-top: 4px;
@ -3240,7 +3243,7 @@ footer {
.tokenfield .token {
padding: 0 0 0 10px;
margin: -1px 5px 0 0;
margin: -1px 5px 8px 0;
height: 26px;
line-height: 27px;
color: white;
@ -3255,7 +3258,7 @@ footer {
}
.tokenfield .token .token-label {
padding: 0;
padding: 0;
}
.tokenfield.form-control {

View file

@ -33,8 +33,12 @@ class SessionsController < ApplicationController
# auto population of default collections
collections, assets = SessionHelper::default_collections(user)
# add session user assets
assets = user.assets(assets)
# get models
models = SessionHelper::models(user)
# check logon session
logon_session_key = nil
if params['logon_session']
@ -50,6 +54,7 @@ class SessionsController < ApplicationController
# return new session data
render :json => {
:session => user,
:models => models,
:collections => collections,
:assets => assets,
:logon_session => logon_session_key,
@ -75,9 +80,13 @@ class SessionsController < ApplicationController
end
if !user_id
# get models
models = SessionHelper::models()
render :json => {
:error => 'no valid session',
:config => config_frontend,
:models => models,
}
return
end
@ -89,13 +98,18 @@ class SessionsController < ApplicationController
# auto population of default collections
collections, assets = SessionHelper::default_collections(user)
# add session user assets
assets = user.assets(assets)
# get models
models = SessionHelper::models(user)
# return current session
render :json => {
:session => user,
:models => models,
:collections => collections,
:assets => assets,
:assets => assets,
:config => config_frontend,
}
end

View file

@ -255,24 +255,12 @@ class TicketsController < ApplicationController
)
end
# get related users
assets = {}
assets = ticket.assets(assets)
# get attributes to update
attributes_to_change = Ticket::ScreenOptions.attributes_to_change( :user => current_user, :ticket => ticket )
attributes_to_change[:owner_id].each { |user_id|
user = User.find(user_id)
assets = user.assets(assets)
}
attributes_to_change[:group_id__owner_id].each {|group_id, user_ids|
user_ids.each {|user_id|
user = User.find(user_id)
assets = user.assets(assets)
}
}
# get related users
assets = attributes_to_change[:assets]
assets = ticket.assets(assets)
# get related articles
articles = Ticket::Article.where( :ticket_id => params[:id] )
@ -297,6 +285,7 @@ class TicketsController < ApplicationController
:ticket_article_ids => article_ids,
:signature => signature,
:assets => assets,
:form_meta => attributes_to_change,
:edit_form => attributes_to_change,
}
end
@ -307,24 +296,11 @@ class TicketsController < ApplicationController
# get attributes to update
attributes_to_change = Ticket::ScreenOptions.attributes_to_change(
:user => current_user,
# :ticket_id => params[:ticket_id],
# :article_id => params[:article_id]
:ticket_id => params[:ticket_id],
:article_id => params[:article_id]
)
assets = {}
assets[ User.to_app_model ] = {}
attributes_to_change[:owner_id].each { |user_id|
user = User.find(user_id)
assets = user.assets(assets)
}
attributes_to_change[:group_id__owner_id].each {|group_id, user_ids|
user_ids.each {|user_id|
user = User.find(user_id)
assets = user.assets(assets)
}
}
assets = attributes_to_change[:assets]
# split data
split = {}
if params[:ticket_id] && params[:article_id]
@ -332,12 +308,6 @@ class TicketsController < ApplicationController
split[:ticket_id] = ticket.id
assets = ticket.assets(assets)
owner_ids = []
ticket.agent_of_group.each { |user|
owner_ids.push user.id
assets = user.assets(assets)
}
# get related articles
article = Ticket::Article.find( params[:article_id] )
split[:article_id] = article.id
@ -348,13 +318,19 @@ class TicketsController < ApplicationController
render :json => {
:split => split,
:assets => assets,
:edit_form => attributes_to_change,
:form_meta => {
:filter => attributes_to_change[:filter],
:dependencies => attributes_to_change[:dependencies],
}
}
end
# GET /api/v1/tickets/search
def search
# permit nested conditions
params.require(:condition).permit!
# build result list
tickets = Ticket.search(
:limit => params[:limit],

View file

@ -0,0 +1,147 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class ObjectManager
=begin
add a new activity entry for an object
ObjectManager.listObjects()
=end
def self.listObjects
['Ticket', 'TicketArticle', 'Group', 'Organization', 'User']
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
add a new activity 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,
},
: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.where(
:object_lookup_id => data[:object_lookup_id],
:name => data[:name],
).first
if result
# raise "ERROR: attribute #{data[:name]} for #{data[:object]} already exists"
return result.update_attributes(data)
end
# create history
ObjectManager::Attribute.create(data)
end
=begin
get 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.is_role(role)
data[:screen][screen] = options
end
}
}
end
if item.data_option
data = data.merge( item.data_option )
end
attributes.push data
}
attributes
end
end

View file

@ -56,6 +56,8 @@ class Ticket < ApplicationModel
belongs_to :create_article_type, :class_name => 'Ticket::Article::Type'
belongs_to :create_article_sender, :class_name => 'Ticket::Article::Sender'
self.inheritance_column = nil
attr_accessor :callback_loop
=begin

View file

@ -50,6 +50,9 @@ returns
params[:article] = self.find( params[:article_id] )
end
filter = {}
assets = {}
# get ticket states
state_ids = []
if params[:ticket]
@ -63,44 +66,21 @@ returns
state_type = Ticket::StateType.where( :name => type ).first
if state_type
state_type.states.each {|state|
assets = state.assets(assets)
state_ids.push state.id
}
end
}
# get owner
owner_ids = []
if params[:ticket]
params[:ticket].agent_of_group.each { |user|
owner_ids.push user.id
}
end
# get group
group_ids = []
Group.where( :active => true ).each { |group|
group_ids.push group.id
}
# get group / user relations
agents = {}
Ticket::ScreenOptions.agents.each { |user|
agents[ user.id ] = 1
}
groups_users = {}
group_ids.each {|group_id|
groups_users[ group_id ] = []
Group.find( group_id ).users.each {|user|
next if !agents[ user.id ]
groups_users[ group_id ].push user.id
}
}
filter[:state_id] = state_ids
# get priorities
priority_ids = []
Ticket::Priority.where( :active => true ).each { |priority|
assets = priority.assets(assets)
priority_ids.push priority.id
}
filter[:priority_id] = priority_ids
type_ids = []
if params[:ticket]
@ -115,14 +95,29 @@ returns
end
}
end
filter[:type_id] = type_ids
# get group / user relations
agents = {}
Ticket::ScreenOptions.agents.each { |user|
agents[ user.id ] = 1
}
dependencies = { :group_id => { '' => [] } }
Group.where( :active => true ).each { |group|
assets = group.assets(assets)
dependencies[:group_id][group.id] = { :owner_id => [] }
group.users.each {|user|
next if !agents[ user.id ]
assets = user.assets(assets)
dependencies[:group_id][ group.id ][ :owner_id ].push user.id
}
}
return {
:type_id => type_ids,
:state_id => state_ids,
:priority_id => priority_ids,
:owner_id => owner_ids,
:group_id => group_ids,
:group_id__owner_id => groups_users,
:assets => assets,
:filter => filter,
:dependencies => dependencies,
}
end
@ -131,8 +126,8 @@ returns
list tickets by customer groupd in state categroie open and closed
result = Ticket::ScreenOptions.list_by_customer(
:customer_id => 123,
:limit => 15, # optional, default 15
:customer_id => 123,
:limit => 15, # optional, default 15
)
returns
@ -152,13 +147,13 @@ returns
# get tickets
tickets_open = Ticket.where(
:customer_id => data[:customer_id],
:state_id => state_list_open
:customer_id => data[:customer_id],
:state_id => state_list_open
).limit( data[:limit] || 15 ).order('created_at DESC')
tickets_closed = Ticket.where(
:customer_id => data[:customer_id],
:state_id => state_list_closed
:customer_id => data[:customer_id],
:state_id => state_list_closed
).limit( data[:limit] || 15 ).order('created_at DESC')
return {

View file

@ -0,0 +1,16 @@
<link rel="stylesheet" href="/assets/tests/qunit-1.10.0.css">
<script src="/assets/tests/qunit-1.10.0.js"></script>
<script src="/assets/tests/model.js"></script>
<style type="text/css">
body {
padding-top: 0px;
}
</style>
<script type="text/javascript">
</script>
<div id="qunit"></div>

View file

@ -2,6 +2,7 @@ Zammad::Application.routes.draw do
match '/tests-core', :to => 'tests#core', :via => :get
match '/tests-ui', :to => 'tests#ui', :via => :get
match '/tests-model', :to => 'tests#model', :via => :get
match '/tests-form', :to => 'tests#form', :via => :get
match '/tests-table', :to => 'tests#table', :via => :get
match '/tests/wait/:sec', :to => 'tests#wait', :via => :get

File diff suppressed because it is too large Load diff

View file

@ -1311,20 +1311,20 @@ Link::Object.create_if_not_exists( :name => 'Question/Answer' )
Link::Object.create_if_not_exists( :name => 'Idea' )
Link::Object.create_if_not_exists( :name => 'Bug' )
Ticket::StateType.create_if_not_exists( :name => 'new', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :name => 'open', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :name => 'pending reminder', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :name => 'pending action', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :name => 'closed', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :name => 'merged', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :name => 'removed', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :id => 1, :name => 'new', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :id => 2, :name => 'open', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :id => 3, :name => 'pending reminder', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :id => 4, :name => 'pending action', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :id => 5, :name => 'closed', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :id => 6, :name => 'merged', :updated_by_id => 1 )
Ticket::StateType.create_if_not_exists( :id => 7, :name => 'removed', :updated_by_id => 1 )
Ticket::State.create_if_not_exists( :name => 'new', :state_type_id => Ticket::StateType.where(:name => 'new').first.id )
Ticket::State.create_if_not_exists( :name => 'open', :state_type_id => Ticket::StateType.where(:name => 'open').first.id )
Ticket::State.create_if_not_exists( :name => 'pending', :state_type_id => Ticket::StateType.where(:name => 'pending reminder').first.id )
Ticket::State.create_if_not_exists( :name => 'closed', :state_type_id => Ticket::StateType.where(:name => 'closed').first.id )
Ticket::State.create_if_not_exists( :name => 'merged', :state_type_id => Ticket::StateType.where(:name => 'merged').first.id )
Ticket::State.create_if_not_exists( :name => 'removed', :state_type_id => Ticket::StateType.where(:name => 'removed').first.id )
Ticket::State.create_if_not_exists( :id => 1, :name => 'new', :state_type_id => Ticket::StateType.where(:name => 'new').first.id )
Ticket::State.create_if_not_exists( :id => 2, :name => 'open', :state_type_id => Ticket::StateType.where(:name => 'open').first.id )
Ticket::State.create_if_not_exists( :id => 3, :name => 'pending', :state_type_id => Ticket::StateType.where(:name => 'pending reminder').first.id )
Ticket::State.create_if_not_exists( :id => 4, :name => 'closed', :state_type_id => Ticket::StateType.where(:name => 'closed').first.id )
Ticket::State.create_if_not_exists( :id => 5, :name => 'merged', :state_type_id => Ticket::StateType.where(:name => 'merged').first.id )
Ticket::State.create_if_not_exists( :id => 6, :name => 'removed', :state_type_id => Ticket::StateType.where(:name => 'removed').first.id )
Ticket::Priority.create_if_not_exists( :name => '1 low' )
Ticket::Priority.create_if_not_exists( :name => '2 normal' )
@ -1683,7 +1683,7 @@ Translation.create_if_not_exists( :locale => 'de', :source => "Direction", :targ
Translation.create_if_not_exists( :locale => 'de', :source => "Owner", :target => "Besitzer" )
Translation.create_if_not_exists( :locale => 'de', :source => "Subject", :target => "Betreff" )
Translation.create_if_not_exists( :locale => 'de', :source => "Priority", :target => "Priorität" )
Translation.create_if_not_exists( :locale => 'de', :source => "Select the customer of the Ticket or create one.", :target => "Wähle den Kundn f<>r das Ticket oder erstell einen neuen." )
Translation.create_if_not_exists( :locale => 'de', :source => "Select the customer of the ticket or create one.", :target => "Wähle den Kunden f<>r das Ticket oder erstelle einen Neuen." )
Translation.create_if_not_exists( :locale => 'de', :source => "New Ticket", :target => "Neues Ticket" )
Translation.create_if_not_exists( :locale => 'de', :source => "Firstname", :target => "Vorname" )
Translation.create_if_not_exists( :locale => 'de', :source => "Lastname", :target => "Nachname" )
@ -1756,7 +1756,7 @@ Translation.create_if_not_exists( :locale => 'de', :source => "Forgot your passw
Translation.create_if_not_exists( :locale => 'de', :source => "Templates", :target => "Vorlagen" )
Translation.create_if_not_exists( :locale => 'de', :source => "Delete", :target => "Löschen" )
Translation.create_if_not_exists( :locale => 'de', :source => "Apply", :target => "Übernehmen" )
Translation.create_if_not_exists( :locale => 'de', :source => "Save as Template", :target => "Als Template speichern" )
Translation.create_if_not_exists( :locale => 'de', :source => "Save as Template", :target => "Als Vorlage speichern" )
Translation.create_if_not_exists( :locale => 'de', :source => "Save", :target => "Speichern" )
Translation.create_if_not_exists( :locale => 'de', :source => "Open Tickets", :target => "Offene Ticket" )
Translation.create_if_not_exists( :locale => 'de', :source => "Closed Tickets", :target => "Geschlossene Ticket" )
@ -1771,7 +1771,7 @@ Translation.create_if_not_exists( :locale => 'de', :source => "3 high", :target
Translation.create_if_not_exists( :locale => 'de', :source => "public", :target => "öffentlich" )
Translation.create_if_not_exists( :locale => 'de', :source => "internal", :target => "intern" )
Translation.create_if_not_exists( :locale => 'de', :source => "Attach files", :target => "Dateien anhängen" )
Translation.create_if_not_exists( :locale => 'de', :source => "Visability", :target => "Sichtbarkeit" )
Translation.create_if_not_exists( :locale => 'de', :source => "Visibility", :target => "Sichtbarkeit" )
Translation.create_if_not_exists( :locale => 'de', :source => "Actions", :target => "Aktionen" )
Translation.create_if_not_exists( :locale => 'de', :source => "Email", :target => "E-Mail" )
Translation.create_if_not_exists( :locale => 'de', :source => "email", :target => "E-Mail" )
@ -1791,7 +1791,7 @@ Translation.create_if_not_exists( :locale => 'de', :source => "Change Customer",
Translation.create_if_not_exists( :locale => 'de', :source => "My Tickets", :target => "Meine Tickets" )
Translation.create_if_not_exists( :locale => 'de', :source => "My Organization Tickets", :target => "Meine Organisations Tickets" )
Translation.create_if_not_exists( :locale => 'de', :source => "My Organization", :target => "Meine Organisation" )
Translation.create_if_not_exists( :locale => 'de', :source => "Assignment Timout", :target => "Zeitliche Zuweisungsüberschritung" )
Translation.create_if_not_exists( :locale => 'de', :source => "Assignment Timeout", :target => "Zeitliche Zuweisungsüberschritung" )
Translation.create_if_not_exists( :locale => 'de', :source => "We've sent password reset instructions to your email address.", :target => "Wir haben Ihnen die Anleitung zum zurücksetzen Ihres Passworts an Ihre E-Mail-Adresse gesendet." )
Translation.create_if_not_exists( :locale => 'de', :source => "Enter your username or email address", :target => "Bitte geben Sie Ihren Benutzernamen oder E-Mail-Adresse ein" )
Translation.create_if_not_exists( :locale => 'de', :source => "Choose your new password.", :target => "Wählen Sie Ihr neues Passwort." )

View file

@ -16,6 +16,16 @@ module SessionHelper
return default_collection, assets
end
def self.models(user = nil)
models = {}
objects = ObjectManager.listObjects
objects.each {|object|
attributes = ObjectManager::Attribute.by_object(object, user)
models[object] = attributes
}
models
end
def self.cleanup_expired
# web sessions

View file

@ -8,9 +8,9 @@ class Sessions::Backend::TicketCreate
def load
# get whole collection
# get attributes to update
ticket_create_attributes = Ticket::ScreenOptions.attributes_to_change(
:current_user_id => @user.id,
:user => @user.id,
)
# no data exists
@ -38,32 +38,31 @@ class Sessions::Backend::TicketCreate
# set new timeout
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 25.seconds } )
create_attributes = self.load
ticket_create_attributes = self.load
return if !ticket_create_attributes
return if !create_attributes
users = {}
create_attributes[:owner_id].each {|user_id|
if !users[user_id]
users[user_id] = User.find(user_id).attributes
end
}
data = {
:users => users,
:edit_form => create_attributes,
:assets => ticket_create_attributes[:assets],
:form_meta => {
:filter => ticket_create_attributes[:filter],
:dependencies => ticket_create_attributes[:dependencies],
}
}
if !@client
return {
:collection => 'ticket_create_attributes',
:data => create_attributes,
:data => data,
}
end
@client.log 'notify', "push ticket_create for user #{ @user.id }"
@client.send({
:collection => 'ticket_create_attributes',
:data => create_attributes,
:data => data,
})
end

View file

@ -495,3 +495,54 @@ test( "form selector", function() {
deepEqual( params, test_params, 'form param check via $("#form").parent()' );
});
test( "form required_if + shown_if", function() {
$('#forms').append('<hr><h1>form required_if + shown_if</h1><div><form id="form7"></form></div>')
var el = $('#form7')
var defaults = {
input2: 'some name66',
}
new App.ControllerForm({
el: el,
model: {
configure_attributes: [
{ name: 'input1', display: 'Input1', tag: 'input', type: 'text', limit: 100, null: true, default: 'some not used default33' },
{ name: 'input2', display: 'Input2', tag: 'input', type: 'text', limit: 100, null: true, default: 'some used default', required_if: { active: true }, shown_if: { active: true } },
{ name: 'active', display: 'Active', tag: 'boolean', type: 'boolean', 'default': true, null: false },
],
},
params: defaults,
});
test_params = {
input1: "some not used default33",
input2: "some name66",
active: true
};
params = App.ControllerForm.params( el )
deepEqual( params, test_params, 'form param check via $("#form")' );
equal( el.find('[name="input2"]').attr('required'), 'required', 'check required attribute of input2 ')
equal( el.find('[name="input2"]').is(":visible"), true, 'check visible attribute of input2 ')
el.find('[name="active"]').val('{boolean}::false').trigger('change')
test_params = {
input1: "some not used default33",
active: false
};
params = App.ControllerForm.params( el )
deepEqual( params, test_params, 'form param check via $("#form")' );
equal( el.find('[name="input2"]').attr('required'), undefined, 'check required attribute of input2 ')
equal( el.find('[name="input2"]').is(":visible"), false, 'check visible attribute of input2 ')
el.find('[name="active"]').val('{boolean}::true').trigger('change')
test_params = {
input1: "some not used default33",
input2: "some name66",
active: true
};
params = App.ControllerForm.params( el )
deepEqual( params, test_params, 'form param check via $("#form")' );
equal( el.find('[name="input2"]').attr('required'), 'required', 'check required attribute of input2 ')
equal( el.find('[name="input2"]').is(":visible"), true, 'check visible attribute of input2 ')
});

View file

@ -0,0 +1,200 @@
// model
test( "model basic tests", function() {
// define model
var configure_attributes_org = _.clone( App.Ticket.configure_attributes )
var attribute1 = {
name: 'test1', display: 'Test 1', tag: 'input', type: 'text', limit: 200, 'null': false
};
App.Ticket.configure_attributes.push( attribute1 )
var attribute2 = {
name: 'test2', display: 'Test 2', tag: 'input', type: 'text', limit: 200, 'null': true
};
App.Ticket.configure_attributes.push( attribute2 )
var attribute3 = {
name: 'pending_time1', display: 'Pending till1', tag: 'input', type: 'text', limit: 200, 'null': false, required_if: { state_id: [3] },
};
App.Ticket.configure_attributes.push( attribute3 )
var attribute4 = {
name: 'pending_time2', display: 'Pending till2', tag: 'input', type: 'text', limit: 200, 'null': true, required_if: { state_id: [3] },
};
App.Ticket.configure_attributes.push( attribute4 )
// check validation
console.log('TEST 1')
var ticket = new App.Ticket()
ticket.load({title: 'some title'})
var error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
ok( !error['pending_time1'], 'pending_time1 is not required')
ok( !error['pending_time2'], 'pending_time2 is not required')
console.log('TEST 2')
ticket.title = 'some new title'
ticket.state_id = [2,3]
ticket.test2 = 123
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title exists')
ok( !error['state_id'], 'state_id is')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
ok( error['pending_time1'], 'pending_time1 is required')
ok( error['pending_time2'], 'pending_time2 is required')
console.log('TEST 3')
ticket.title = 'some new title'
ticket.state_id = [2,1]
ticket.test2 = 123
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title exists')
ok( !error['state_id'], 'state_id is')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
ok( !error['pending_time1'], 'pending_time1 is required')
ok( !error['pending_time2'], 'pending_time2 is required')
console.log('TEST 4')
ticket.title = 'some new title'
ticket.state_id = [2,3]
ticket.test2 = 123
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title exists')
ok( !error['state_id'], 'state_id is')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
ok( error['pending_time1'], 'pending_time1 is required')
ok( error['pending_time2'], 'pending_time2 is required')
console.log('TEST 5')
ticket.title = 'some new title'
ticket.state_id = [2,3]
ticket.test2 = 123
ticket.pending_time1 = '2014-10-10 09:00'
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title exists')
ok( !error['state_id'], 'state_id is')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
ok( !error['pending_time1'], 'pending_time1 is required')
ok( error['pending_time2'], 'pending_time2 is required')
// define model with screen
App.Ticket.configure_attributes = configure_attributes_org
var attribute1 = {
name: 'test1', display: 'Test 1', tag: 'input', type: 'text', limit: 200, 'null': false, screen: { some_screen: { required_if: { state_id: [3] } } },
};
App.Ticket.configure_attributes.push( attribute1 )
var attribute2 = {
name: 'test2', display: 'Test 2', tag: 'input', type: 'text', limit: 200, 'null': true, screen: { some_screen: { required_if: { state_id: [3] } } },
};
App.Ticket.configure_attributes.push( attribute2 )
var attribute3 = {
name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: false, relation: 'Group', screen: { some_screen: { null: false } },
};
App.Ticket.configure_attributes.push( attribute3 )
var attribute4 = {
name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, null: false, relation: 'User', screen: { some_screen: { null: false } },
};
App.Ticket.configure_attributes.push( attribute4 )
var attribute5 = {
name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', screen: { some_screen: { null: false } },
};
App.Ticket.configure_attributes.push( attribute5 )
// check validation with screen
console.log('TEST 6')
ticket = new App.Ticket()
ticket.load({title: 'some title'})
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
console.log('TEST 7')
ticket.state_id = 3
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( !error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
console.log('TEST 8')
ticket.state_id = 2
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( !error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
console.log('TEST 9')
ticket.state_id = undefined
error = ticket.validate({screen: 'some_screen'})
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( error['state_id'], 'state_id is required')
ok( !error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is required')
console.log('TEST 10')
ticket.state_id = 2
error = ticket.validate({screen: 'some_screen'})
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( !error['state_id'], 'state_id is required')
ok( !error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
console.log('TEST 11')
ticket.state_id = 3
error = ticket.validate({screen: 'some_screen'})
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( !error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( error['test2'], 'test2 is required')
console.log('TEST 12')
ticket.state_id = 2
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( !error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
console.log('TEST 13')
ticket.state_id = 3
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( !error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is required')
console.log('TEST 14')
ticket.state_id = 2
error = ticket.validate()
ok( error['group_id'], 'group_id is required')
ok( !error['title'], 'title is required')
ok( !error['state_id'], 'state_id is required')
ok( error['test1'], 'test1 is required')
ok( !error['test2'], 'test2 is not required')
});

View file

@ -46,6 +46,28 @@ class AAbUnitTest < TestCase
]
browser_single_test(tests)
end
def test_model
tests = [
{
:name => 'start',
:instance => browser_instance,
:url => browser_url + '/tests-model',
:action => [
{
:execute => 'wait',
:value => 8,
},
{
:execute => 'match',
:css => '.result .failed',
:value => '0',
:match_result => true,
},
],
},
]
browser_single_test(tests)
end
def test_form
tests = [
{

View file

@ -111,8 +111,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance2,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => true,
},
{
@ -127,8 +128,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance2,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => false,
},
@ -150,8 +152,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance1,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => true,
},
@ -204,8 +207,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance1,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => false,
},
{
@ -219,8 +223,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance2,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => false,
},
@ -246,8 +251,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance1,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => false,
},
{
@ -261,8 +267,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance2,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => false,
},
@ -280,8 +287,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance2,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => true,
},
{
@ -298,8 +306,9 @@ class AgentTicketActionsLevel3Test < TestCase
{
:where => :instance2,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => true,
},
{
@ -315,7 +324,9 @@ class AgentTicketActionsLevel3Test < TestCase
:where => :instance2,
:execute => 'match',
:css => '.content_permanent.active',
:value => 'Discard your unsaved changes.',
:css => '.content_permanent.active .reset-message',
:value => '(Discard your unsaved changes.|Verwerfen der)',
:no_quote => true,
:match_result => false,
},
{

View file

@ -3,8 +3,8 @@ require 'browser_test_helper'
class AgentTicketActionLevel5Test < TestCase
def test_I
random = 'text_module_test_' + rand(999999).to_s
random2 = 'text_module_test_' + rand(999999).to_s
random = 'text_module_test_' + rand(99999999).to_s
random2 = 'text_module_test_' + rand(99999999).to_s
# user
tests = [
@ -153,9 +153,9 @@ class AgentTicketActionLevel5Test < TestCase
browser_signle_test_with_login(tests, { :username => 'master@example.com' })
end
def test_II
random = 'text_II_module_test_' + rand(999999).to_s
random = 'text_II_module_test_' + rand(99999999).to_s
user_rand = rand(999999).to_s
user_rand = rand(99999999).to_s
login = 'agent-text-module-' + user_rand
firstname = 'Text' + user_rand
lastname = 'Module' + user_rand
@ -191,7 +191,7 @@ class AgentTicketActionLevel5Test < TestCase
{
:where => :instance2,
:execute => 'set',
:css => '.active input[name=subject]',
:css => '.active input[name=title]',
:value => 'A',
},
{
@ -207,7 +207,7 @@ class AgentTicketActionLevel5Test < TestCase
{
:where => :instance2,
:execute => 'set',
:css => '.active input[name=subject]',
:css => '.active input[name=title]',
:value => 'B',
},
@ -330,7 +330,7 @@ class AgentTicketActionLevel5Test < TestCase
{
:where => :instance2,
:execute => 'set',
:css => '.active .ticket_create input[name="customer_id_autocompletion"]',
:css => '.active .ticket-create input[name="customer_id_autocompletion"]',
:value => 'nicole',
},
{
@ -340,13 +340,13 @@ class AgentTicketActionLevel5Test < TestCase
{
:where => :instance2,
:execute => 'sendkey',
:css => '.active .ticket_create input[name="customer_id_autocompletion"]',
:css => '.active .ticket-create input[name="customer_id_autocompletion"]',
:value => :arrow_down,
},
{
:where => :instance2,
:execute => 'sendkey',
:css => '.active .ticket_create input[name="customer_id_autocompletion"]',
:css => '.active .ticket-create input[name="customer_id_autocompletion"]',
:value => :tab,
},
{
@ -425,7 +425,7 @@ class AgentTicketActionLevel5Test < TestCase
},
{
:execute => 'wait',
:value => 4,
:value => 2,
},
{
:where => :instance2,
@ -536,7 +536,7 @@ class AgentTicketActionLevel5Test < TestCase
},
{
:execute => 'wait',
:value => 0.2,
:value => 0.5,
},
# {
# :where => :instance2,

View file

@ -142,7 +142,7 @@ class AgentUserManageTest < TestCase
},
{
:execute => 'set',
:css => '.active .ticket_create input[name="customer_id_autocompletion"]',
:css => '.active .ticket-create input[name="customer_id_autocompletion"]',
:value => customer_user_email,
},
{
@ -151,12 +151,12 @@ class AgentUserManageTest < TestCase
},
{
:execute => 'sendkey',
:css => '.active .ticket_create input[name="customer_id_autocompletion"]',
:css => '.active .ticket-create input[name="customer_id_autocompletion"]',
:value => :arrow_down,
},
{
:execute => 'sendkey',
:css => '.active .ticket_create input[name="customer_id_autocompletion"]',
:css => '.active .ticket-create input[name="customer_id_autocompletion"]',
:value => :tab,
},
{

View file

@ -27,7 +27,7 @@ class CustomerTicketCreateTest < TestCase
},
{
:execute => 'set',
:css => '.ticket-create input[name="subject"]',
:css => '.ticket-create input[name="title"]',
:value => 'some subject 123äöü',
},
{

View file

@ -32,12 +32,12 @@ class TaskbarTaskTest < TestCase
},
{
:execute => 'check',
:css => '.active .ticket_create',
:css => '.active .ticket-create',
:result => true,
},
{
:execute => 'set',
:css => '.active .ticket_create input[name="subject"]',
:css => '.active .ticket-create input[name="title"]',
:value => 'some test AAA',
},
{
@ -113,7 +113,7 @@ class TaskbarTaskTest < TestCase
},
{
:execute => 'set',
:css => '.active .ticket_create input[name="subject"]',
:css => '.active .ticket-create input[name="title"]',
:value => 'INBOUND TEST#1',
},
{
@ -122,7 +122,7 @@ class TaskbarTaskTest < TestCase
},
{
:execute => 'set',
:css => '.active .ticket_create textarea[name="body"]',
:css => '.active .ticket-create textarea[name="body"]',
:value => 'INBOUND BODY TEST#1',
},
{
@ -139,7 +139,7 @@ class TaskbarTaskTest < TestCase
},
{
:execute => 'set',
:css => '.active .ticket_create input[name="subject"]',
:css => '.active .ticket-create input[name="title"]',
:value => 'OUTBOUND TEST#1',
},
{
@ -148,7 +148,7 @@ class TaskbarTaskTest < TestCase
},
{
:execute => 'set',
:css => '.active .ticket_create textarea[name="body"]',
:css => '.active .ticket-create textarea[name="body"]',
:value => 'OUTBOUND BODY TEST#1',
},
{

View file

@ -297,40 +297,40 @@ class TestCase < Test::Unit::TestCase
elsif action[:execute] == 'create_ticket'
instance.find_element( { :css => 'a[href="#new"]' } ).click
instance.find_element( { :css => 'a[href="#ticket/create/call_inbound"]' } ).click
element = instance.find_element( { :css => '.active .ticket_create' } )
element = instance.find_element( { :css => '.active .ticket-create' } )
if !element
assert( false, "(#{test[:name]}) no ticket create screen found!" )
return
end
sleep 2
element = instance.find_element( { :css => '.active .ticket_create input[name="customer_id_autocompletion"]' } )
element = instance.find_element( { :css => '.active .ticket-create input[name="customer_id_autocompletion"]' } )
element.clear
element.send_keys( 'nico*' )
sleep 4
element = instance.find_element( { :css => '.active .ticket_create input[name="customer_id_autocompletion"]' } )
element = instance.find_element( { :css => '.active .ticket-create input[name="customer_id_autocompletion"]' } )
element.send_keys( :arrow_down )
sleep 0.2
element = instance.find_element( { :css => '.active .ticket_create input[name="customer_id_autocompletion"]' } )
element = instance.find_element( { :css => '.active .ticket-create input[name="customer_id_autocompletion"]' } )
element.send_keys( :tab )
sleep 0.1
element = instance.find_element( { :css => '.active .ticket_create select[name="group_id"]' } )
element = instance.find_element( { :css => '.active .ticket-create select[name="group_id"]' } )
dropdown = Selenium::WebDriver::Support::Select.new(element)
dropdown.select_by( :text, action[:group])
sleep 0.1
element = instance.find_element( { :css => '.active .ticket_create input[name="subject"]' } )
element = instance.find_element( { :css => '.active .ticket-create input[name="title"]' } )
element.clear
element.send_keys( action[:subject] )
sleep 0.1
element = instance.find_element( { :css => '.active .ticket_create textarea[name="body"]' } )
element = instance.find_element( { :css => '.active .ticket-create textarea[name="body"]' } )
element.clear
element.send_keys( action[:body] )
if action[:do_not_submit]
assert( true, "(#{test[:name]}) ticket created without submit" )
return
end
sleep 0.5
sleep 0.8
instance.find_element( { :css => '.active .form-actions button[type="submit"]' } ).click
sleep 2
sleep 1
(1..14).each {|loop|
if instance.current_url =~ /#{Regexp.quote('#ticket/zoom/')}/
assert( true, "(#{test[:name]}) ticket created" )
@ -338,7 +338,7 @@ class TestCase < Test::Unit::TestCase
end
sleep 0.5
}
assert( true, "(#{test[:name]}) ticket creation failed, can't get zoom url" )
assert( false, "(#{test[:name]}) ticket creation failed, can't get zoom url" )
return
elsif action[:execute] == 'close_all_tasks'
for i in 1..100

View file

@ -74,26 +74,57 @@ class TagTest < ActiveSupport::TestCase
},
},
# test 5
{
:tag_remove => {
:item => 'tag1',
:object => 'Object1',
:o_id => 123,
:created_by_id => 1
},
:verify => {
:object => 'Object1',
:items => {
'tag1' => false,
'tag2' => true,
},
},
},
]
tests.each { |test|
success = Tag.tag_add( test[:tag_add] )
assert( success, "Tag.tag_add successful")
list = Tag.tag_list( test[:tag_add] )
tags = nil
if test[:tag_add]
tags = test[:tag_add]
success = Tag.tag_add( tags )
assert( success, "Tag.tag_add successful")
else
tags = test[:tag_remove]
success = Tag.tag_remove( tags )
assert( success, "Tag.tag_remove successful")
end
list = Tag.tag_list( tags )
test[:verify][:items].each {|key, value|
if value == true
assert( list.include?( key ), "Tag verify #{ test[:tag_add][:item] }")
assert( list.include?( key ), "Tag verify - should exists but exists #{ key }")
else
assert( !list.include?( key ), "Tag verify #{ test[:tag_add][:item] }")
assert( !list.include?( key ), "Tag verify - exists but should not #{ key }")
end
}
}
# delete tags
tests.each { |test|
success = Tag.tag_remove( test[:tag_add] )
tags = nil
if test[:tag_add]
tags = test[:tag_add]
else
tags = test[:tag_remove]
end
success = Tag.tag_remove( tags )
assert( success, "Tag.tag_remove successful")
list = Tag.tag_list( test[:tag_add] )
assert( !list.include?( test[:tag_add][:item] ), "Tag entry destroyed")
list = Tag.tag_list( tags )
assert( !list.include?( tags[:item] ), "Tag entry destroyed")
}
end
end