diff --git a/Gemfile b/Gemfile
index c805a5c45..3fd8da512 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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'
diff --git a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
index d597a65ff..e61ec781d 100644
--- a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
+++ b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
@@ -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 = $('
')
- 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 = $('').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: '»'
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()
diff --git a/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee
index 259554e34..53eb612ce 100644
--- a/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee
+++ b/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee
@@ -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()
diff --git a/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee
index 263aeb88e..295ac7565 100644
--- a/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee
+++ b/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee
@@ -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
diff --git a/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee b/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
index 3447539ef..6cf21898c 100644
--- a/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
+++ b/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
@@ -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: '»', 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' )
diff --git a/app/assets/javascripts/app/controllers/customer_ticket_create.js.coffee b/app/assets/javascripts/app/controllers/customer_ticket_create.js.coffee
index e14420ce2..17fb074d6 100644
--- a/app/assets/javascripts/app/controllers/customer_ticket_create.js.coffee
+++ b/app/assets/javascripts/app/controllers/customer_ticket_create.js.coffee
@@ -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' )
\ No newline at end of file
+App.Config.set( 'CustomerTicketNew', { prio: 8003, parent: '#new', name: 'New Ticket', target: '#customer_ticket_new', role: ['Customer'], divider: true }, 'NavBarRight' )
diff --git a/app/assets/javascripts/app/controllers/signup.js.coffee b/app/assets/javascripts/app/controllers/signup.js.coffee
index 15054e6ab..258345ceb 100644
--- a/app/assets/javascripts/app/controllers/signup.js.coffee
+++ b/app/assets/javascripts/app/controllers/signup.js.coffee
@@ -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
)
diff --git a/app/assets/javascripts/app/controllers/ticket_overview.js.coffee b/app/assets/javascripts/app/controllers/ticket_overview.js.coffee
index b154d5f67..f3062317c 100644
--- a/app/assets/javascripts/app/controllers/ticket_overview.js.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_overview.js.coffee
@@ -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) =>
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
index eb8f428a0..183b3f72a 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
@@ -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
diff --git a/app/assets/javascripts/app/controllers/widget/user.js.coffee b/app/assets/javascripts/app/controllers/widget/user.js.coffee
index ac8b48d0d..a6cdf4f7f 100644
--- a/app/assets/javascripts/app/controllers/widget/user.js.coffee
+++ b/app/assets/javascripts/app/controllers/widget/user.js.coffee
@@ -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
)
diff --git a/app/assets/javascripts/app/lib/app_post/auth.js.coffee b/app/assets/javascripts/app/lib/app_post/auth.js.coffee
index 20e1bf495..a9bb71617 100644
--- a/app/assets/javascripts/app/lib/app_post/auth.js.coffee
+++ b/app/assets/javascripts/app/lib/app_post/auth.js.coffee
@@ -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' )
-
diff --git a/app/assets/javascripts/app/lib/core/jquery-1.7.2.min.js b/app/assets/javascripts/app/lib/core/jquery-1.7.2.min.js
deleted file mode 100644
index 16ad06c5a..000000000
--- a/app/assets/javascripts/app/lib/core/jquery-1.7.2.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.7.2 jquery.com | jquery.org/license */
-(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;ca",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q=""+"",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
-a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,""],thead:[1,""],tr:[2,""],td:[3,""],col:[2,""],area:[1,""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
-.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>$2>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/
+
+
+
+
+
+
+
+
diff --git a/config/routes/test.rb b/config/routes/test.rb
index 3eb4206f7..ffe0471b2 100644
--- a/config/routes/test.rb
+++ b/config/routes/test.rb
@@ -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
diff --git a/db/migrate/20140831000001_create_object_manager.rb b/db/migrate/20140831000001_create_object_manager.rb
new file mode 100644
index 000000000..dd3e96271
--- /dev/null
+++ b/db/migrate/20140831000001_create_object_manager.rb
@@ -0,0 +1,1017 @@
+class CreateObjectManager < ActiveRecord::Migration
+ def up
+ add_column :tickets, :pending_time, :timestamp, :null => true
+ add_index :tickets, [:pending_time]
+
+ add_column :tickets, :type, :string, :limit => 100, :null => true
+ add_index :tickets, [:type]
+
+ create_table :object_manager_attributes do |t|
+ t.references :object_lookup, :null => false
+ t.column :name, :string, :limit => 200, :null => false
+ t.column :display, :string, :limit => 200, :null => false
+ t.column :data_type, :string, :limit => 100, :null => false
+ t.column :data_option, :string, :limit => 8000, :null => true
+ t.column :editable, :boolean, :null => false, :default => true
+ t.column :active, :boolean, :null => false, :default => true
+ t.column :screens, :string, :limit => 2000, :null => true
+ t.column :pending_migration, :boolean, :null => false, :default => true
+ t.column :position, :integer, :null => false
+ t.column :created_by_id, :integer, :null => false
+ t.column :updated_by_id, :integer, :null => false
+ t.timestamps
+ end
+ add_index :object_manager_attributes, [:name], :unique => true
+ add_index :object_manager_attributes, [:object_lookup_id]
+
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'customer_id',
+ :display => 'Customer',
+ :data_type => 'autocompletion',
+ :data_option => {
+ :relation => 'User',
+ :autocapitalize => false,
+ :multiple => false,
+ :null => false,
+ :limit => 200,
+ :placeholder => 'Enter Person or Organisation/Company',
+ :help => 'Select the customer of the Ticket or create one.',
+ :helpLink => '»',
+ :minLengt => 2,
+ :translate => false,
+ :source => '#{@apiPath}/users/search',
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_top => {
+ :Agent => {
+ :null => false,
+ },
+ },
+ :edit => {},
+ },
+ :pending_migration => false,
+ :position => 10,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'type',
+ :display => 'Type',
+ :data_type => 'select',
+ :data_option => {
+ :options => {
+ 'Incident' => 'Incident',
+ 'Problem' => 'Problem',
+ 'Request for Change' => 'Request for Change',
+ },
+ :nulloption => true,
+ :multiple => false,
+ :null => true,
+ :translate => true,
+ },
+ :editable => false,
+ :active => false,
+ :screens => {
+ :create_middle => {
+ '-all-' => {
+ :null => false,
+ :item_class => 'column',
+ },
+ },
+ :edit => {
+ :Agent => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 20,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'group_id',
+ :display => 'Group',
+ :data_type => 'select',
+ :data_option => {
+ :relation => 'Group',
+ :relation_condition => { :access => 'rw' },
+ :nulloption => true,
+ :multiple => false,
+ :null => false,
+ :translate => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_middle => {
+ '-all-' => {
+ :null => false,
+ :item_class => 'column',
+ },
+ },
+ :edit => {
+ :Agent => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 20,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'owner_id',
+ :display => 'Owner',
+ :data_type => 'select',
+ :data_option => {
+ :relation => 'User',
+ :relation_condition => { :roles => 'Agent' },
+ :nulloption => true,
+ :multiple => false,
+ :null => true,
+ :translate => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_middle => {
+ :Agent => {
+ :null => true,
+ :item_class => 'column',
+ },
+ },
+ :edit => {
+ :Agent => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 30,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'state_id',
+ :display => 'State',
+ :data_type => 'select',
+ :data_option => {
+ :relation => 'TicketState',
+ :nulloption => true,
+ :multiple => false,
+ :null => false,
+ :default => 2,
+ :translate => true,
+ :filter => [1,2,3,4],
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_middle => {
+ '-all-' => {
+ :null => false,
+ :item_class => 'column',
+ },
+ },
+ :edit => {
+ :Agent => {
+ :nulloption => false,
+ :null => false,
+ :filter => [2,3,4],
+ },
+ :Customer => {
+ :nulloption => false,
+ :null => true,
+ :filter => [2,4],
+ :default => 2,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 40,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'pending_time',
+ :display => 'Pending till',
+ :data_type => 'datetime',
+ :data_option => {
+ :future => true,
+ :past => false,
+ :diff => 24,
+ :null => true,
+ :translate => true,
+ :required_if => {
+ :state_id => [3]
+ },
+ :shown_if => {
+ :state_id => [3]
+ },
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_middle => {
+ '-all-' => {
+ :null => false,
+ :item_class => 'column',
+ },
+ },
+ :edit => {
+ :Agent => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 41,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'priority_id',
+ :display => 'Priority',
+ :data_type => 'select',
+ :data_option => {
+ :relation => 'TicketPriority',
+ :nulloption => true,
+ :multiple => false,
+ :null => false,
+ :default => 2,
+ :translate => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_middle => {
+ :Agent => {
+ :null => false,
+ :item_class => 'column',
+ },
+ },
+ :create_web => {},
+ :edit => {
+ :Agent => {
+ :null => false,
+ :nulloption => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 80,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'tags',
+ :display => 'Tags',
+ :data_type => 'tag',
+ :data_option => {
+ :type => 'text',
+ :null => true,
+ :translate => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_bottom => {
+ :Agent => {
+ :null => true,
+ },
+ },
+ :create_web => {},
+ :edit => {},
+ },
+ :pending_migration => false,
+ :position => 900,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'Ticket',
+ :name => 'title',
+ :display => 'Title',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 200,
+ :null => false,
+ :translate => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_top => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :create_web => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :edit => {},
+ },
+ :pending_migration => false,
+ :position => 15,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'TicketArticle',
+ :name => 'type_id',
+ :display => 'Type',
+ :data_type => 'select',
+ :data_option => {
+ :relation => 'TicketArticleType',
+ :nulloption => false,
+ :multiple => false,
+ :null => false,
+ :default => 9,
+ :translate => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_top => {},
+ :edit => {
+ :Agent => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 100,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'TicketArticle',
+ :name => 'internal',
+ :display => 'Visibility',
+ :data_type => 'select',
+ :data_option => {
+ :options => { :true => 'internal', :false => 'public' },
+ :nulloption => false,
+ :multiple => false,
+ :null => true,
+ :default => false,
+ :translate => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_top => {},
+ :edit => {
+ :Agent => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 200,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'TicketArticle',
+ :name => 'to',
+ :display => 'To',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 1000,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_top => {},
+ :edit => {
+ :Agent => {
+ :null => true,
+ },
+ }, },
+ :pending_migration => false,
+ :position => 300,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+ ObjectManager::Attribute.add(
+ :object => 'TicketArticle',
+ :name => 'cc',
+ :display => 'Cc',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 1000,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_phone_in => {},
+ :create_phone_out => {},
+ :create_email_out => {
+ '-all-' => {
+ :null => true,
+ }
+ },
+ :create_web => {},
+ :edit => {
+ :Agent => {
+ :null => true,
+ },
+ }, },
+ :pending_migration => false,
+ :position => 400,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'TicketArticle',
+ :name => 'body',
+ :display => 'Text',
+ :data_type => 'textarea',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 20000,
+ :upload => true,
+ :rows => 8,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :create_top => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :edit => {
+ :Agent => {
+ :null => true,
+ },
+ :Customer => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 600,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'login',
+ :display => 'Login',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 100,
+ :null => false,
+ :autocapitalize => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ :Agent => {},
+ :Admin => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 100,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'firstname',
+ :display => 'Firstname',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 150,
+ :null => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :invite_agent => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :edit => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 200,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'lastname',
+ :display => 'Lastname',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 150,
+ :null => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :invite_agent => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :edit => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 300,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'email',
+ :display => 'Email',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'email',
+ :maxlength => 150,
+ :null => false,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :invite_agent => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :edit => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 400,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'web',
+ :display => 'Web',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'url',
+ :maxlength => 250,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 500,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'phone',
+ :display => 'Phone',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'phone',
+ :maxlength => 100,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 600,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'mobile',
+ :display => 'Mobile',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'phone',
+ :maxlength => 100,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 700,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'fax',
+ :display => 'Fax',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'phone',
+ :maxlength => 100,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 800,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'organization_id',
+ :display => 'Organization',
+ :data_type => 'select',
+ :data_option => {
+ :multiple => false,
+ :nulloption => true,
+ :null => true,
+ :relation => 'Organization',
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 900,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'department',
+ :display => 'Department',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 200,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1000,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'street',
+ :display => 'Street',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 100,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1100,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'zip',
+ :display => 'Zip',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 100,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1200,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'city',
+ :display => 'City',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 100,
+ :null => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1300,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'password',
+ :display => 'Password',
+ :data_type => 'input',
+ :data_option => {
+ :type => 'password',
+ :maxlength => 100,
+ :null => true,
+ :autocomplete => 'off',
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ :invite_agent => {},
+ :edit => {
+ :Admin => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1400,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'note',
+ :display => 'Note',
+ :data_type => 'textarea',
+ :data_option => {
+ :type => 'text',
+ :maxlength => 250,
+ :null => true,
+ :note => 'Notes are visible to agents only, never to customers.',
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ '-all-' => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1500,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'role_ids',
+ :display => 'Roles',
+ :data_type => 'checkbox',
+ :data_option => {
+ :multiple => true,
+ :null => false,
+ :relation => 'Role',
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ :Admin => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1600,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'group_ids',
+ :display => 'Groups',
+ :data_type => 'checkbox',
+ :data_option => {
+ :multiple => true,
+ :null => true,
+ :relation => 'Group',
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {
+ '-all-' => {
+ :null => false,
+ },
+ },
+ :edit => {
+ :Admin => {
+ :null => true,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1700,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ ObjectManager::Attribute.add(
+ :object => 'User',
+ :name => 'active',
+ :display => 'Active',
+ :data_type => 'boolean',
+ :data_option => {
+ :maxlength => 250,
+ :null => true,
+ :default => true,
+ },
+ :editable => false,
+ :active => true,
+ :screens => {
+ :signup => {},
+ :invite_agent => {},
+ :edit => {
+ :Admin => {
+ :null => false,
+ },
+ },
+ },
+ :pending_migration => false,
+ :position => 1800,
+ :created_by_id => 1,
+ :updated_by_id => 1,
+ )
+
+ end
+
+ def down
+ drop_table :object_manager_attributes
+ end
+end
diff --git a/db/seeds.rb b/db/seeds.rb
index 19d9b3d18..18e72a739 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -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." )
diff --git a/lib/session_helper.rb b/lib/session_helper.rb
index 51deb8120..1e98911f3 100644
--- a/lib/session_helper.rb
+++ b/lib/session_helper.rb
@@ -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
diff --git a/lib/sessions/backend/ticket_create.rb b/lib/sessions/backend/ticket_create.rb
index e950b2e83..f17769a7f 100644
--- a/lib/sessions/backend/ticket_create.rb
+++ b/lib/sessions/backend/ticket_create.rb
@@ -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
diff --git a/public/assets/tests/form.js b/public/assets/tests/form.js
index b3af4bf94..ba70564c2 100644
--- a/public/assets/tests/form.js
+++ b/public/assets/tests/form.js
@@ -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('
form required_if + shown_if
')
+ 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 ')
+
+});
\ No newline at end of file
diff --git a/public/assets/tests/model.js b/public/assets/tests/model.js
new file mode 100644
index 000000000..ae7869f9c
--- /dev/null
+++ b/public/assets/tests/model.js
@@ -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')
+
+});
diff --git a/test/browser/aab_unit_test.rb b/test/browser/aab_unit_test.rb
index fa67511cb..bfb117003 100644
--- a/test/browser/aab_unit_test.rb
+++ b/test/browser/aab_unit_test.rb
@@ -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 = [
{
diff --git a/test/browser/agent_ticket_actions_level3_test.rb b/test/browser/agent_ticket_actions_level3_test.rb
index b7d3a5329..80e535f07 100644
--- a/test/browser/agent_ticket_actions_level3_test.rb
+++ b/test/browser/agent_ticket_actions_level3_test.rb
@@ -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,
},
{
diff --git a/test/browser/agent_ticket_actions_level5_test.rb b/test/browser/agent_ticket_actions_level5_test.rb
index a0a7e9a27..bedf4be63 100644
--- a/test/browser/agent_ticket_actions_level5_test.rb
+++ b/test/browser/agent_ticket_actions_level5_test.rb
@@ -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,
diff --git a/test/browser/agent_user_manage_test.rb b/test/browser/agent_user_manage_test.rb
index 045eac769..9c8e05082 100644
--- a/test/browser/agent_user_manage_test.rb
+++ b/test/browser/agent_user_manage_test.rb
@@ -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,
},
{
diff --git a/test/browser/customer_ticket_create_test.rb b/test/browser/customer_ticket_create_test.rb
index 22c14aa46..1a556846f 100644
--- a/test/browser/customer_ticket_create_test.rb
+++ b/test/browser/customer_ticket_create_test.rb
@@ -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äöü',
},
{
diff --git a/test/browser/taskbar_task_test.rb b/test/browser/taskbar_task_test.rb
index 62039c180..4d0970edb 100644
--- a/test/browser/taskbar_task_test.rb
+++ b/test/browser/taskbar_task_test.rb
@@ -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',
},
{
diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb
index b946a036e..ca6de3afa 100644
--- a/test/browser_test_helper.rb
+++ b/test/browser_test_helper.rb
@@ -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
diff --git a/test/unit/tag_test.rb b/test/unit/tag_test.rb
index 5a8b2100f..5c5945a07 100644
--- a/test/unit/tag_test.rb
+++ b/test/unit/tag_test.rb
@@ -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