Merge branch 'develop' of github.com:martini/zammad into develop
This commit is contained in:
commit
9c27582068
22 changed files with 438 additions and 15 deletions
|
@ -528,6 +528,9 @@ class App.Controller extends Spine.Controller
|
||||||
# replace new option list
|
# replace new option list
|
||||||
form.find('[name="' + fieldNameToChange + '"]').closest('.form-group').replaceWith( newElement )
|
form.find('[name="' + fieldNameToChange + '"]').closest('.form-group').replaceWith( newElement )
|
||||||
|
|
||||||
|
stopPropagation: (e) ->
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
class App.ControllerPermanent extends App.Controller
|
class App.ControllerPermanent extends App.Controller
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
|
@ -8,6 +8,9 @@ class App.UiElement.ticket_perform_action
|
||||||
name: 'Ticket'
|
name: 'Ticket'
|
||||||
model: 'Ticket'
|
model: 'Ticket'
|
||||||
|
|
||||||
|
operators =
|
||||||
|
'ticket.tags': ['add', 'remove']
|
||||||
|
|
||||||
# megre config
|
# megre config
|
||||||
elements = {}
|
elements = {}
|
||||||
for groupKey, groupMeta of groups
|
for groupKey, groupMeta of groups
|
||||||
|
@ -21,11 +24,11 @@ class App.UiElement.ticket_perform_action
|
||||||
config = _.clone(row)
|
config = _.clone(row)
|
||||||
elements["#{groupKey}.#{config.name}"] = config
|
elements["#{groupKey}.#{config.name}"] = config
|
||||||
|
|
||||||
[defaults, groups, elements]
|
[defaults, groups, operators, elements]
|
||||||
|
|
||||||
@render: (attribute, params = {}) ->
|
@render: (attribute, params = {}) ->
|
||||||
|
|
||||||
[defaults, groups, elements] = @defaults()
|
[defaults, groups, operators, elements] = @defaults()
|
||||||
|
|
||||||
selector = @buildAttributeSelector(groups, elements)
|
selector = @buildAttributeSelector(groups, elements)
|
||||||
|
|
||||||
|
@ -53,6 +56,7 @@ class App.UiElement.ticket_perform_action
|
||||||
elementRow = $(e.target).closest('.js-filterElement')
|
elementRow = $(e.target).closest('.js-filterElement')
|
||||||
|
|
||||||
@rebuildAttributeSelectors(item, elementRow, groupAndAttribute)
|
@rebuildAttributeSelectors(item, elementRow, groupAndAttribute)
|
||||||
|
@buildOperator(item, elementRow, groupAndAttribute, elements, undefined, attribute, operators)
|
||||||
@buildValue(item, elementRow, groupAndAttribute, elements, undefined, attribute)
|
@buildValue(item, elementRow, groupAndAttribute, elements, undefined, attribute)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,6 +67,7 @@ class App.UiElement.ticket_perform_action
|
||||||
for groupAndAttribute, meta of params[attribute.name]
|
for groupAndAttribute, meta of params[attribute.name]
|
||||||
selectorExists = true
|
selectorExists = true
|
||||||
value = meta.value
|
value = meta.value
|
||||||
|
operator = meta.operator
|
||||||
|
|
||||||
# get selector rows
|
# get selector rows
|
||||||
elementFirst = item.find('.js-filterElement').first()
|
elementFirst = item.find('.js-filterElement').first()
|
||||||
|
@ -71,6 +76,7 @@ class App.UiElement.ticket_perform_action
|
||||||
# clone, rebuild and append
|
# clone, rebuild and append
|
||||||
elementClone = elementFirst.clone(true)
|
elementClone = elementFirst.clone(true)
|
||||||
@rebuildAttributeSelectors(item, elementClone, groupAndAttribute)
|
@rebuildAttributeSelectors(item, elementClone, groupAndAttribute)
|
||||||
|
@buildOperator(item, elementClone, groupAndAttribute, elements, value, attribute, operators, operator)
|
||||||
@buildValue(item, elementClone, groupAndAttribute, elements, value, attribute)
|
@buildValue(item, elementClone, groupAndAttribute, elements, value, attribute)
|
||||||
elementLast.after(elementClone)
|
elementLast.after(elementClone)
|
||||||
|
|
||||||
|
@ -93,6 +99,26 @@ class App.UiElement.ticket_perform_action
|
||||||
|
|
||||||
item
|
item
|
||||||
|
|
||||||
|
@buildOperator: (elementFull, elementRow, groupAndAttribute, elements, value, attribute, operators, operator) ->
|
||||||
|
name = "#{attribute.name}::#{groupAndAttribute}::operator"
|
||||||
|
if !operators[groupAndAttribute]
|
||||||
|
elementRow.find('.js-operator').html('')
|
||||||
|
return
|
||||||
|
|
||||||
|
# get current operator
|
||||||
|
if !operator
|
||||||
|
operator = elementRow.find('.js-operator select').val()
|
||||||
|
|
||||||
|
# build new operator
|
||||||
|
selection = $("<select class=\"form-control\" name=\"#{name}\"></select>")
|
||||||
|
for operatorKey in operators[groupAndAttribute]
|
||||||
|
operatorKeyName = App.i18n.translateInline(operatorKey)
|
||||||
|
selected = ''
|
||||||
|
if operatorKey is operator
|
||||||
|
selected = 'selected'
|
||||||
|
selection.append("<option value=\"#{operatorKey}\" #{selected}>#{operatorKeyName}</option>")
|
||||||
|
elementRow.find('.js-operator').html(selection)
|
||||||
|
|
||||||
@buildValue: (elementFull, elementRow, groupAndAttribute, elements, value, attribute) ->
|
@buildValue: (elementFull, elementRow, groupAndAttribute, elements, value, attribute) ->
|
||||||
|
|
||||||
# do nothing if item already exists
|
# do nothing if item already exists
|
||||||
|
@ -163,7 +189,7 @@ class App.UiElement.ticket_perform_action
|
||||||
@humanText: (condition) ->
|
@humanText: (condition) ->
|
||||||
none = App.i18n.translateContent('No filter.')
|
none = App.i18n.translateContent('No filter.')
|
||||||
return [none] if _.isEmpty(condition)
|
return [none] if _.isEmpty(condition)
|
||||||
[defaults, groups, elements] = @defaults()
|
[defaults, groups, operators, elements] = @defaults()
|
||||||
rules = []
|
rules = []
|
||||||
for attribute, value of condition
|
for attribute, value of condition
|
||||||
|
|
||||||
|
|
27
app/assets/javascripts/app/controllers/macro.coffee
Normal file
27
app/assets/javascripts/app/controllers/macro.coffee
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
class Index extends App.ControllerContent
|
||||||
|
constructor: ->
|
||||||
|
super
|
||||||
|
|
||||||
|
# check authentication
|
||||||
|
return if !@authenticate()
|
||||||
|
|
||||||
|
new App.ControllerGenericIndex(
|
||||||
|
el: @el
|
||||||
|
id: @id
|
||||||
|
genericObject: 'Macro'
|
||||||
|
pageData:
|
||||||
|
title: 'Macros'
|
||||||
|
home: 'macros'
|
||||||
|
object: 'Macro'
|
||||||
|
objects: 'Macros'
|
||||||
|
navupdate: '#macros'
|
||||||
|
notes: [
|
||||||
|
'TextModules are ...'
|
||||||
|
]
|
||||||
|
buttons: [
|
||||||
|
{ name: 'New Macro', 'data-type': 'new', class: 'btn--success' }
|
||||||
|
]
|
||||||
|
container: @el.closest('.content')
|
||||||
|
)
|
||||||
|
|
||||||
|
App.Config.set( 'Macros', { prio: 2310, name: 'Macros', parent: '#manage', target: '#manage/macros', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )
|
|
@ -308,6 +308,12 @@ class App.TicketZoom extends App.Controller
|
||||||
el: @el.find('.ticket-meta')
|
el: @el.find('.ticket-meta')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
new App.TicketZoomAttributeBar(
|
||||||
|
ticket: @ticket
|
||||||
|
el: @el.find('.js-attributeBar')
|
||||||
|
callback: @submit
|
||||||
|
)
|
||||||
|
|
||||||
@form_id = App.ControllerForm.formId()
|
@form_id = App.ControllerForm.formId()
|
||||||
|
|
||||||
new App.TicketZoomArticleNew(
|
new App.TicketZoomArticleNew(
|
||||||
|
@ -338,7 +344,7 @@ class App.TicketZoom extends App.Controller
|
||||||
user_id: @ticket.customer_id
|
user_id: @ticket.customer_id
|
||||||
size: 50
|
size: 50
|
||||||
)
|
)
|
||||||
new App.TicketZoomSidebar(
|
@sidebar = new App.TicketZoomSidebar(
|
||||||
el: @el.find('.tabsSidebar')
|
el: @el.find('.tabsSidebar')
|
||||||
sidebarState: @sidebarState
|
sidebarState: @sidebarState
|
||||||
ticket: @ticket
|
ticket: @ticket
|
||||||
|
@ -462,7 +468,7 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
resetButton.removeClass('hide')
|
resetButton.removeClass('hide')
|
||||||
|
|
||||||
submit: (e) =>
|
submit: (e, macro = {}) =>
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
ticketParams = @formParam( @$('.edit') )
|
ticketParams = @formParam( @$('.edit') )
|
||||||
|
@ -477,6 +483,25 @@ class App.TicketZoom extends App.Controller
|
||||||
for key, value of ticketParams
|
for key, value of ticketParams
|
||||||
ticket[key] = value
|
ticket[key] = value
|
||||||
|
|
||||||
|
# apply macro
|
||||||
|
for key, content of macro
|
||||||
|
attributes = key.split('.')
|
||||||
|
if attributes[0] is 'ticket'
|
||||||
|
|
||||||
|
# apply tag changes
|
||||||
|
if attributes[1] is 'tags'
|
||||||
|
if @sidebar && @sidebar.tagWidget
|
||||||
|
tags = content.value.split(',')
|
||||||
|
for tag in tags
|
||||||
|
if content.operator is 'remove'
|
||||||
|
@sidebar.tagWidget.remove(tag)
|
||||||
|
else
|
||||||
|
@sidebar.tagWidget.add(tag)
|
||||||
|
|
||||||
|
# apply direct value changes
|
||||||
|
else
|
||||||
|
ticket[attributes[1]] = content.value
|
||||||
|
|
||||||
# set defaults
|
# set defaults
|
||||||
if !@isRole('Customer')
|
if !@isRole('Customer')
|
||||||
if !ticket['owner_id']
|
if !ticket['owner_id']
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
class App.TicketZoomAttributeBar extends App.Controller
|
||||||
|
elements:
|
||||||
|
'.buttonDropdown': 'buttonDropdown'
|
||||||
|
|
||||||
|
events:
|
||||||
|
'mousedown .js-openDropdownMacro': 'toggleDropdownMacro'
|
||||||
|
'click .js-openDropdownMacro': 'stopPropagation'
|
||||||
|
'mouseup .js-dropdownActionMacro': 'performTicketMacro'
|
||||||
|
'mouseenter .js-dropdownActionMacro': 'onActionMacroMouseEnter'
|
||||||
|
'mouseleave .js-dropdownActionMacro': 'onActionMacroMouseLeave'
|
||||||
|
|
||||||
|
constructor: ->
|
||||||
|
super
|
||||||
|
|
||||||
|
@subscribeId = App.Macro.subscribe(@render)
|
||||||
|
@render()
|
||||||
|
|
||||||
|
release: =>
|
||||||
|
App.Macro.unsubscribe(@subscribeId)
|
||||||
|
|
||||||
|
render: =>
|
||||||
|
macros = App.Macro.all()
|
||||||
|
if _.isEmpty(macros) || !@isRole('Agent')
|
||||||
|
macroDisabled = true
|
||||||
|
@html App.view('ticket_zoom/attribute_bar')(
|
||||||
|
macros: macros
|
||||||
|
macroDisabled: macroDisabled
|
||||||
|
)
|
||||||
|
|
||||||
|
toggleDropdownMacro: =>
|
||||||
|
if @buttonDropdown.hasClass 'is-open'
|
||||||
|
@closeMacroDropdown()
|
||||||
|
else
|
||||||
|
@buttonDropdown.addClass 'is-open'
|
||||||
|
$(document).bind 'click.buttonDropdown', @closeMacroDropdown
|
||||||
|
|
||||||
|
closeMacroDropdown: =>
|
||||||
|
@buttonDropdown.removeClass 'is-open'
|
||||||
|
$(document).unbind 'click.buttonDropdown'
|
||||||
|
|
||||||
|
performTicketMacro: (e) =>
|
||||||
|
macroId = $(e.target).data('id')
|
||||||
|
console.log "perform action", @$(e.currentTarget).text(), macroId
|
||||||
|
macro = App.Macro.find(macroId)
|
||||||
|
|
||||||
|
@callback(e, macro.perform)
|
||||||
|
@closeMacroDropdown()
|
||||||
|
|
||||||
|
onActionMacroMouseEnter: (e) =>
|
||||||
|
@$(e.currentTarget).addClass('is-active')
|
||||||
|
|
||||||
|
onActionMacroMouseLeave: (e) =>
|
||||||
|
@$(e.currentTarget).removeClass('is-active')
|
|
@ -50,14 +50,14 @@ class App.TicketZoomSidebar extends App.Controller
|
||||||
|
|
||||||
if !@isRole('Customer')
|
if !@isRole('Customer')
|
||||||
el.append('<div class="tags"></div>')
|
el.append('<div class="tags"></div>')
|
||||||
new App.WidgetTag(
|
@tagWidget = new App.WidgetTag(
|
||||||
el: el.find('.tags')
|
el: el.find('.tags')
|
||||||
object_type: 'Ticket'
|
object_type: 'Ticket'
|
||||||
object: ticket
|
object: ticket
|
||||||
tags: @tags
|
tags: @tags
|
||||||
)
|
)
|
||||||
el.append('<div class="links"></div>')
|
el.append('<div class="links"></div>')
|
||||||
new App.WidgetLink(
|
@linkWidget = new App.WidgetLink(
|
||||||
el: el.find('.links')
|
el: el.find('.links')
|
||||||
object_type: 'Ticket'
|
object_type: 'Ticket'
|
||||||
object: ticket
|
object: ticket
|
||||||
|
|
|
@ -65,8 +65,10 @@ class App.WidgetTag extends App.Controller
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
item = @$('[name="new_tag"]').val()
|
item = @$('[name="new_tag"]').val()
|
||||||
return if !item
|
return if !item
|
||||||
|
@add(item)
|
||||||
|
|
||||||
if _.contains(@tagList, item)
|
add: (item) =>
|
||||||
|
if _.contains(@tags, item)
|
||||||
@render()
|
@render()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -90,6 +92,10 @@ class App.WidgetTag extends App.Controller
|
||||||
item = $(e.target).parents('li').find('.js-tag').text()
|
item = $(e.target).parents('li').find('.js-tag').text()
|
||||||
return if !item
|
return if !item
|
||||||
|
|
||||||
|
@remove(item)
|
||||||
|
|
||||||
|
remove: (item) =>
|
||||||
|
|
||||||
@tags = _.filter(@tags, (tagItem) -> return tagItem if tagItem isnt item )
|
@tags = _.filter(@tags, (tagItem) -> return tagItem if tagItem isnt item )
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@ class App.PrettyDate
|
||||||
diff = diff.toString().replace('-', '')
|
diff = diff.toString().replace('-', '')
|
||||||
diff = parseFloat(diff)
|
diff = parseFloat(diff)
|
||||||
|
|
||||||
|
if diff < 60
|
||||||
|
return App.i18n.translateInline('just now')
|
||||||
|
|
||||||
if direction is 'past' && !escalation && diff > ( 60 * 60 * 24 * 14 )
|
if direction is 'past' && !escalation && diff > ( 60 * 60 * 24 * 14 )
|
||||||
return App.i18n.translateDate(time)
|
return App.i18n.translateDate(time)
|
||||||
|
|
||||||
|
|
20
app/assets/javascripts/app/models/macro.coffee
Normal file
20
app/assets/javascripts/app/models/macro.coffee
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class App.Macro extends App.Model
|
||||||
|
@configure 'Macro', 'name', 'perform', 'active'
|
||||||
|
@extend Spine.Model.Ajax
|
||||||
|
@url: @apiPath + '/macros'
|
||||||
|
@configure_attributes = [
|
||||||
|
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
|
||||||
|
{ name: 'perform', display: 'Execute changes on objects.', tag: 'ticket_perform_action', null: true },
|
||||||
|
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
||||||
|
{ name: 'note', display: 'Note', tag: 'textarea', limit: 250, null: true },
|
||||||
|
{ name: 'active', display: 'Active', tag: 'active', default: true },
|
||||||
|
]
|
||||||
|
@configure_delete = true
|
||||||
|
@configure_overview = [
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
|
||||||
|
@description = '''
|
||||||
|
Macros are....
|
||||||
|
|
||||||
|
'''
|
|
@ -6,6 +6,7 @@
|
||||||
<%- @Icon('arrow-down', 'dropdown-arrow') %>
|
<%- @Icon('arrow-down', 'dropdown-arrow') %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="controls js-operator"></div>
|
||||||
<div class="controls js-value"></div>
|
<div class="controls js-value"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-controls">
|
<div class="filter-controls">
|
||||||
|
|
|
@ -634,7 +634,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="attributeBar">
|
<div class="attributeBar">
|
||||||
<div class="btn js-reset hide">Discard your unsaved changes.</div>
|
<div class="btn js-reset">Discard your unsaved changes.</div>
|
||||||
<div class="buttonDropdown dropdown dropup">
|
<div class="buttonDropdown dropdown dropup">
|
||||||
<div class="btn btn--primary btn--split--first js-submit">Update</div>
|
<div class="btn btn--primary btn--split--first js-submit">Update</div>
|
||||||
<div class="btn btn--primary btn--slim btn--split--last js-openDropdown"><%- @Icon('arrow-up') %></div>
|
<div class="btn btn--primary btn--slim btn--split--last js-openDropdown"><%- @Icon('arrow-up') %></div>
|
||||||
|
|
|
@ -25,8 +25,5 @@
|
||||||
|
|
||||||
<div class="tabsSidebar tabsSidebar--attributeBarSpacer vertical"></div>
|
<div class="tabsSidebar tabsSidebar--attributeBarSpacer vertical"></div>
|
||||||
|
|
||||||
<div class="attributeBar">
|
<div class="attributeBar js-attributeBar"></div>
|
||||||
<div class="btn js-reset hide"><%- @T('Discard your unsaved changes.') %></div>
|
|
||||||
<div class="btn btn--primary js-submit"><%- @T('Update') %></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="btn js-reset hide"><%- @T('Discard your unsaved changes.') %></div>
|
||||||
|
<% if @macroDisabled: %>
|
||||||
|
<div class="btn btn--primary js-submit"><%- @T('Update') %></div>
|
||||||
|
<% else: %>
|
||||||
|
<div class="buttonDropdown dropdown dropup">
|
||||||
|
<div class="btn btn--primary btn--split--first js-submit"><%- @T('Update') %></div>
|
||||||
|
<div class="btn btn--primary btn--slim btn--split--last js-openDropdownMacro"><%- @Icon('arrow-up') %></div>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="userAction">
|
||||||
|
<% for macro in @macros: %>
|
||||||
|
<li class="js-dropdownActionMacro" role="menuitem" data-id="<%= macro.id %>"><%- @T(macro.displayName()) %>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
155
app/controllers/macros_controller.rb
Normal file
155
app/controllers/macros_controller.rb
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class MacrosController < ApplicationController
|
||||||
|
before_action :authentication_check
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Format:
|
||||||
|
JSON
|
||||||
|
|
||||||
|
Example:
|
||||||
|
{
|
||||||
|
"id":1,
|
||||||
|
"name":"some text_module",
|
||||||
|
"perform":{
|
||||||
|
"ticket.priority_id": 5,
|
||||||
|
"ticket.state_id": 2,
|
||||||
|
},
|
||||||
|
"active":true,
|
||||||
|
"updated_at":"2012-09-14T17:51:53Z",
|
||||||
|
"created_at":"2012-09-14T17:51:53Z",
|
||||||
|
"updated_by_id":2,
|
||||||
|
"created_by_id":2,
|
||||||
|
}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Resource:
|
||||||
|
GET /api/v1/macros.json
|
||||||
|
|
||||||
|
Response:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "some_name1",
|
||||||
|
...
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "some_name2",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Test:
|
||||||
|
curl http://localhost/api/v1/macros.json -v -u #{login}:#{password}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def index
|
||||||
|
model_index_render(Macro, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Resource:
|
||||||
|
GET /api/v1/macros/#{id}.json
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "name_1",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Test:
|
||||||
|
curl http://localhost/api/v1/macros/#{id}.json -v -u #{login}:#{password}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def show
|
||||||
|
model_show_render(Macro, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Resource:
|
||||||
|
POST /api/v1/macros.json
|
||||||
|
|
||||||
|
Payload:
|
||||||
|
{
|
||||||
|
"name": "some name",
|
||||||
|
"perform":{
|
||||||
|
"ticket.priority_id": 5,
|
||||||
|
"ticket.state_id": 2,
|
||||||
|
},
|
||||||
|
"active":true,
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "some_name",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Test:
|
||||||
|
curl http://localhost/api/v1/macros.json -v -u #{login}:#{password} -H "Content-Type: application/json" -X POST -d '{"name": "some_name","active": true, "note": "some note"}'
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def create
|
||||||
|
model_create_render(Macro, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Resource:
|
||||||
|
PUT /api/v1/macros/{id}.json
|
||||||
|
|
||||||
|
Payload:
|
||||||
|
{
|
||||||
|
"name": "some name",
|
||||||
|
"perform":{
|
||||||
|
"ticket.priority_id": 5,
|
||||||
|
"ticket.state_id": 2,
|
||||||
|
},
|
||||||
|
"active":true,
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "some_name",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Test:
|
||||||
|
curl http://localhost/api/v1/macros.json -v -u #{login}:#{password} -H "Content-Type: application/json" -X PUT -d '{"name": "some_name","active": true, "note": "some note"}'
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def update
|
||||||
|
model_update_render(Macro, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Resource:
|
||||||
|
DELETE /api/v1/macros/{id}.json
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{}
|
||||||
|
|
||||||
|
Test:
|
||||||
|
curl http://localhost/api/v1/macros.json -v -u #{login}:#{password} -H "Content-Type: application/json" -X DELETE
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
model_destory_render(Macro, params)
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,10 @@ module ExtraCollection
|
||||||
def session( collections, assets, user )
|
def session( collections, assets, user )
|
||||||
|
|
||||||
# all ticket stuff
|
# all ticket stuff
|
||||||
|
collections[ Macro.to_app_model ] = []
|
||||||
|
Macro.all.each {|item|
|
||||||
|
assets = item.assets(assets)
|
||||||
|
}
|
||||||
collections[ Ticket::StateType.to_app_model ] = []
|
collections[ Ticket::StateType.to_app_model ] = []
|
||||||
Ticket::StateType.all.each {|item|
|
Ticket::StateType.all.each {|item|
|
||||||
assets = item.assets(assets)
|
assets = item.assets(assets)
|
||||||
|
|
7
app/models/macro.rb
Normal file
7
app/models/macro.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Macro < ApplicationModel
|
||||||
|
store :perform
|
||||||
|
validates :name, presence: true
|
||||||
|
|
||||||
|
end
|
11
config/routes/macro.rb
Normal file
11
config/routes/macro.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Zammad::Application.routes.draw do
|
||||||
|
api_path = Rails.configuration.api_path
|
||||||
|
|
||||||
|
# macros
|
||||||
|
match api_path + '/macros', to: 'macros#index', via: :get
|
||||||
|
match api_path + '/macros/:id', to: 'macros#show', via: :get
|
||||||
|
match api_path + '/macros', to: 'macros#create', via: :post
|
||||||
|
match api_path + '/macros/:id', to: 'macros#update', via: :put
|
||||||
|
match api_path + '/macros/:id', to: 'macros#destroy', via: :delete
|
||||||
|
|
||||||
|
end
|
34
db/migrate/20151015000001_create_macro.rb
Normal file
34
db/migrate/20151015000001_create_macro.rb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
class CreateMacro < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
create_table :macros do |t|
|
||||||
|
t.string :name, limit: 250, null: true
|
||||||
|
t.string :perform, limit: 5000, null: false
|
||||||
|
t.boolean :active, null: false, default: true
|
||||||
|
t.string :note, limit: 250, null: true
|
||||||
|
t.integer :updated_by_id, null: false
|
||||||
|
t.integer :created_by_id, null: false
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
add_index :macros, [:name], unique: true
|
||||||
|
|
||||||
|
UserInfo.current_user_id = 1
|
||||||
|
Macro.create_or_update(
|
||||||
|
name: 'Close & Tag as Spam',
|
||||||
|
perform: {
|
||||||
|
'ticket.state_id': {
|
||||||
|
value: Ticket::State.find_by(name: 'closed').id,
|
||||||
|
},
|
||||||
|
'ticket.tags': {
|
||||||
|
operator: 'add',
|
||||||
|
value: 'spam',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
note: 'example macro',
|
||||||
|
active: true,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :macros
|
||||||
|
end
|
||||||
|
end
|
15
db/seeds.rb
15
db/seeds.rb
|
@ -1572,6 +1572,21 @@ Ticket::Article::Sender.create_if_not_exists( id: 1, name: 'Agent' )
|
||||||
Ticket::Article::Sender.create_if_not_exists( id: 2, name: 'Customer' )
|
Ticket::Article::Sender.create_if_not_exists( id: 2, name: 'Customer' )
|
||||||
Ticket::Article::Sender.create_if_not_exists( id: 3, name: 'System' )
|
Ticket::Article::Sender.create_if_not_exists( id: 3, name: 'System' )
|
||||||
|
|
||||||
|
Macro.create_if_not_exists(
|
||||||
|
name: 'Close & Tag as Spam',
|
||||||
|
perform: {
|
||||||
|
'ticket.state_id': {
|
||||||
|
value: Ticket::State.find_by(name: 'closed').id,
|
||||||
|
},
|
||||||
|
'ticket.tags': {
|
||||||
|
operator: 'add',
|
||||||
|
value: 'spam',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
note: 'example macro',
|
||||||
|
active: true,
|
||||||
|
)
|
||||||
|
|
||||||
UserInfo.current_user_id = user_community.id
|
UserInfo.current_user_id = user_community.id
|
||||||
ticket = Ticket.create(
|
ticket = Ticket.create(
|
||||||
group_id: Group.where( name: 'Users' ).first.id,
|
group_id: Group.where( name: 'Users' ).first.id,
|
||||||
|
|
4
lib/sessions/backend/collections/macro.rb
Normal file
4
lib/sessions/backend/collections/macro.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
class Sessions::Backend::Collections::Macors < Sessions::Backend::Collections::Base
|
||||||
|
model_set 'Macro'
|
||||||
|
add_if_not_role 'Customer'
|
||||||
|
end
|
|
@ -137,6 +137,10 @@ test( 'form checks', function() {
|
||||||
'ticket.priority_id': {
|
'ticket.priority_id': {
|
||||||
value: 3,
|
value: 3,
|
||||||
},
|
},
|
||||||
|
'ticket.tags': {
|
||||||
|
operator: 'remove',
|
||||||
|
value: 'tag1, tag2',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
new App.ControllerForm({
|
new App.ControllerForm({
|
||||||
|
@ -193,6 +197,10 @@ test( 'form checks', function() {
|
||||||
'ticket.priority_id': {
|
'ticket.priority_id': {
|
||||||
value: '3',
|
value: '3',
|
||||||
},
|
},
|
||||||
|
'ticket.tags': {
|
||||||
|
operator: 'remove',
|
||||||
|
value: 'tag1, tag2',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
working_hours: {
|
working_hours: {
|
||||||
mon: {
|
mon: {
|
||||||
|
@ -328,6 +336,10 @@ test( 'form checks', function() {
|
||||||
'ticket.priority_id': {
|
'ticket.priority_id': {
|
||||||
value: '3',
|
value: '3',
|
||||||
},
|
},
|
||||||
|
'ticket.tags': {
|
||||||
|
operator: 'remove',
|
||||||
|
value: 'tag1, tag2',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
deepEqual( params, test_params, 'form param check' );
|
deepEqual( params, test_params, 'form param check' );
|
||||||
|
|
|
@ -5,7 +5,10 @@ test( "check pretty date", function() {
|
||||||
|
|
||||||
// past
|
// past
|
||||||
var result = App.PrettyDate.humanTime( current );
|
var result = App.PrettyDate.humanTime( current );
|
||||||
equal( result, '0 minutes ago', 'right now')
|
equal( result, 'just now', 'just now')
|
||||||
|
|
||||||
|
result = App.PrettyDate.humanTime( current - 15000 );
|
||||||
|
equal( result, 'just now', 'just now')
|
||||||
|
|
||||||
result = App.PrettyDate.humanTime( current - 60000 );
|
result = App.PrettyDate.humanTime( current - 60000 );
|
||||||
equal( result, '1 minute ago', '1 min ago')
|
equal( result, '1 minute ago', '1 min ago')
|
||||||
|
@ -60,7 +63,10 @@ test( "check pretty date", function() {
|
||||||
// future
|
// future
|
||||||
current = new Date()
|
current = new Date()
|
||||||
result = App.PrettyDate.humanTime( current );
|
result = App.PrettyDate.humanTime( current );
|
||||||
equal( result, '0 minutes ago', 'right now')
|
equal( result, 'just now', 'just now')
|
||||||
|
|
||||||
|
result = App.PrettyDate.humanTime( current.getTime() + 55000 );
|
||||||
|
equal( result, 'just now', 'just now')
|
||||||
|
|
||||||
result = App.PrettyDate.humanTime( current.getTime() + 65000 );
|
result = App.PrettyDate.humanTime( current.getTime() + 65000 );
|
||||||
equal( result, 'in 1 minute', 'in 1 min')
|
equal( result, 'in 1 minute', 'in 1 min')
|
||||||
|
|
Loading…
Reference in a new issue