diff --git a/app/assets/javascripts/app/controllers/agent_ticket_create.coffee b/app/assets/javascripts/app/controllers/agent_ticket_create.coffee
index ecc02de68..938409c13 100644
--- a/app/assets/javascripts/app/controllers/agent_ticket_create.coffee
+++ b/app/assets/javascripts/app/controllers/agent_ticket_create.coffee
@@ -132,6 +132,14 @@ class App.TicketCreate extends App.Controller
@$('[name="cc"], [name="group_id"], [name="customer_id"]').on('change', =>
@updateSecurityOptions()
)
+
+ @listenTo(App.Group, 'refresh', =>
+ @sidebarWidget.render(@params())
+ )
+
+ @$('[name="group_id"]').bind('change', =>
+ @sidebarWidget.render(@params())
+ )
@updateSecurityOptions()
# show cc
@@ -174,6 +182,7 @@ class App.TicketCreate extends App.Controller
@navupdate("#ticket/create/id/#{@id}#{@split}", type: 'menu')
@autosaveStart()
@controllerBind('ticket_create_rerender', (template) => @renderQueue(template))
+ @controllerBind('ticket_create_import_draft_attachments', @importDraftAttachments)
# initially hide sidebar on mobile
if window.matchMedia('(max-width: 767px)').matches
@@ -183,6 +192,7 @@ class App.TicketCreate extends App.Controller
hide: =>
@autosaveStop()
@controllerUnbind('ticket_create_rerender', (template) => @renderQueue(template))
+ @controllerUnbind('ticket_create_import_draft_attachments')
changed: =>
return true if @hasAttachments()
@@ -283,6 +293,18 @@ class App.TicketCreate extends App.Controller
return if !@formMeta
App.QueueManager.run(@queueKey)
+ importDraftAttachments: (options = {}) =>
+ @ajax
+ id: 'import_attachments'
+ type: 'POST'
+ url: "#{@apiPath}/tickets/shared_drafts/#{options.shared_draft_id}/import_attachments"
+ data: JSON.stringify({ form_id: @formId })
+ processData: true
+ success: (data, status, xhr) ->
+ App.Event.trigger(options.callbackName, { success: true, attachments: data.attachments })
+ error: ->
+ App.Event.trigger(options.callbackName, { success: false })
+
updateTaskManagerAttachments: (attribute, attachments) =>
taskData = App.TaskManager.get(@taskKey)
return if _.isEmpty(taskData)
@@ -306,12 +328,13 @@ class App.TicketCreate extends App.Controller
params.priority_id ||= App.TicketPriority.findByAttribute( 'default_create', true )?.id
@html(App.view('agent_ticket_create')(
- head: __('New Ticket')
- agent: @permissionCheck('ticket.agent')
- admin: @permissionCheck('admin')
- types: @types,
- availableTypes: @availableTypes
- form_id: @formId
+ head: __('New Ticket')
+ agent: @permissionCheck('ticket.agent')
+ admin: @permissionCheck('admin')
+ types: @types,
+ availableTypes: @availableTypes
+ form_id: @formId
+ shared_draft_id: template.shared_draft_id || params.shared_draft_id
))
App.Ticket.configure_attributes.push {
diff --git a/app/assets/javascripts/app/controllers/agent_ticket_create/sidebar_shared_draft.coffee b/app/assets/javascripts/app/controllers/agent_ticket_create/sidebar_shared_draft.coffee
new file mode 100644
index 000000000..cd0266361
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/agent_ticket_create/sidebar_shared_draft.coffee
@@ -0,0 +1,29 @@
+class SidebarSharedDraft extends App.Controller
+ sidebarItem: =>
+ return if !@permissionCheck('ticket.agent')
+
+ group = App.Group.find @params.group_id
+
+ return if !group?.shared_drafts
+
+ @item = {
+ name: 'shared_draft'
+ badgeIcon: 'note'
+ sidebarHead: __('Shared Drafts')
+ sidebarActions: []
+ sidebarCallback: @showDrafts
+ }
+ @item
+
+ showDrafts: (el) =>
+ @el = el
+
+ # show template UI
+ new App.WidgetSharedDraft(
+ el: el
+ taskKey: @taskKey
+ group_id: @params.group_id
+ active_draft_id: @params.shared_draft_id
+ )
+
+App.Config.set('400-SharedDraft', SidebarSharedDraft, 'TicketCreateSidebar')
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.coffee
index 4e5312175..db85fa10f 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom.coffee
@@ -2,14 +2,15 @@ class App.TicketZoom extends App.Controller
@include App.TicketNavigable
elements:
- '.main': 'main'
- '.ticketZoom': 'ticketZoom'
- '.scrollPageHeader': 'scrollPageHeader'
+ '.main': 'main'
+ '.ticketZoom': 'ticketZoom'
+ '.scrollPageHeader': 'scrollPageHeader'
events:
'click .js-submit': 'submit'
'click .js-bookmark': 'bookmark'
'click .js-reset': 'reset'
+ 'click .js-draft': 'draft'
'click .main': 'muteTask'
constructor: (params) ->
@@ -187,6 +188,9 @@ class App.TicketZoom extends App.Controller
# remember mentions
@mentions = data.mentions
+ if draft = App.TicketSharedDraftZoom.findByAttribute 'ticket_id', @ticket_id
+ draft.remove(clear: true)
+
App.Collection.loadAssets(data.assets, targetModel: 'Ticket')
# get ticket
@@ -481,11 +485,13 @@ class App.TicketZoom extends App.Controller
)
@attributeBar = new App.TicketZoomAttributeBar(
- ticket: @ticket
- el: elLocal.find('.js-attributeBar')
- overview_id: @overview_id
- callback: @submit
- taskKey: @taskKey
+ ticket: @ticket
+ el: elLocal.find('.js-attributeBar')
+ overview_id: @overview_id
+ macroCallback: @submit
+ draftCallback: @saveDraft
+ draftState: @draftState()
+ taskKey: @taskKey
)
#if @shown
# @attributeBar.start()
@@ -965,6 +971,55 @@ class App.TicketZoom extends App.Controller
@submitPost(e, ticket, macro)
)
+ saveDraft: (e) =>
+ e.stopPropagation()
+ e.preventDefault()
+
+ params =
+ new_article: @articleNew.params()
+ ticket_attributes: @ticketParams()
+
+ loaded_draft_id = params.new_article.shared_draft_id
+
+ params.form_id = params.new_article['form_id']
+ delete params.new_article['form_id']
+ delete params.new_article['shared_draft_id']
+
+ sharedDraft = @sharedDraft()
+
+ draftExists = sharedDraft?
+ isLoaded = loaded_draft_id == String(sharedDraft?.id)
+
+ matches = draftExists &&
+ _.isEqual(sharedDraft.new_article, params.new_article) &&
+ _.isEqual(sharedDraft.ticket_attributes, params.ticket_attributes)
+
+ if draftExists && !(isLoaded && matches)
+ new App.TicketSharedDraftOverwriteModal(
+ onShowDraft: @draft
+ onSaveDraft: =>
+ @draftSaveToServer(params)
+ )
+
+ return
+
+ @draftSaveToServer(params)
+
+ draftSaveToServer: (params) =>
+ @draftSaving()
+
+ @ajax
+ id: 'ticket_shared_draft_update'
+ type: 'PUT'
+ url: @apiPath + '/tickets/' + @ticket_id + '/shared_draft'
+ processData: true
+ data: JSON.stringify(params)
+ success: (data, status, xhr) =>
+ App.Collection.loadAssets(data.assets)
+ @draftFetched()
+ error: =>
+ @draftFetched()
+
submitPost: (e, ticket, macro) =>
taskAction = @$('.js-secondaryActionButtonLabel').data('type')
@@ -1034,6 +1089,49 @@ class App.TicketZoom extends App.Controller
bookmark: (e) ->
$(e.currentTarget).find('.bookmark.icon').toggleClass('filled')
+ draft: (e) =>
+ e.preventDefault()
+
+ new App.TicketSharedDraftModal(
+ container: @el.closest('.content')
+ hasChanges: App.TaskManager.worker(@taskKey).changed()
+ parent: @
+ shared_draft: @sharedDraft()
+ )
+
+ fetchDraft: ->
+ @ajax(
+ id: "ticket_#{@ticket_id}_shared_draft"
+ type: 'GET'
+ url: "#{@apiPath}/tickets/#{@ticket_id}/shared_draft"
+ processData: true
+ success: (data, status, xhr) =>
+ App.Collection.loadAssets(data.assets)
+ @draftFetched()
+ )
+
+ draftSaving: ->
+ @updateDraftButton(true, 'saving')
+
+ updateDraftButton: (visible, state) ->
+ button = @el.find('.js-draft')
+
+ button.toggleClass('hide', !visible)
+ button.find('.attributeBar-draft--available').toggleClass('hide', state != 'available')
+ button.find('.attributeBar-draft--saving').toggleClass('hide', state != 'saving')
+ button.attr('disabled', state == 'saving')
+
+ @el.find('.js-dropdownActionSaveDraft').attr('disabled', state == 'saving')
+
+ draftFetched: ->
+ @updateDraftButton(@sharedDraft()?, 'available')
+
+ draftState: ->
+ @sharedDraft()?
+
+ sharedDraft: ->
+ App.TicketSharedDraftZoom.findByAttribute 'ticket_id', @ticket_id
+
reset: (e) =>
if e
e.preventDefault()
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee
index d70d4b2a8..032831eec 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee
@@ -53,13 +53,16 @@ class App.TicketZoomArticleNew extends App.Controller
@setArticleTypePre(data.type.name, data.signaturePosition)
- @openTextarea(null, true, true)
+ @openTextarea(null, true, !data.nofocus)
for key, value of data.article
if key is 'body'
@$("[data-name=\"#{key}\"]").html(value)
else
@$("[name=\"#{key}\"]").val(value).trigger('change')
+
+ @$('[name=shared_draft_id]').val(data.shared_draft_id)
+
@setArticleTypePost(data.type.name, data.signaturePosition)
# set focus into field
@@ -76,6 +79,8 @@ class App.TicketZoomArticleNew extends App.Controller
@tokanice(data.type.name)
)
+ @controllerBind('ui::ticket::import_draft_attachments', @importDraftAttachments)
+
# add article attachment
@controllerBind('ui::ticket::addArticleAttachent', (data) =>
return if data.ticket?.id?.toString() isnt @ticket_id.toString() && data.form_id isnt @form_id
@@ -634,6 +639,26 @@ class App.TicketZoomArticleNew extends App.Controller
@richTextUploadDeleteCallback?(@attachments)
)
+ importDraftAttachments: (options) =>
+ return if @ticket.id != options.ticket_id
+
+ @ajax
+ id: 'import_attachments'
+ type: 'POST'
+ url: "#{@apiPath}/tickets/#{@ticket.id}/shared_draft/import_attachments"
+ data: JSON.stringify({ form_id: @form_id })
+ processData: true
+ success: (data, status, xhr) =>
+ App.Event.trigger('ui::ticket::addArticleAttachent', {
+ ticket: @ticket
+ attachments: data.attachments
+ form_id: @form_id
+ })
+
+ App.Event.trigger(options.callbackName, { success: true })
+ error: ->
+ App.Event.trigger(options.callbackName, { success: false })
+
actions: ->
actionConfig = App.Config.get('TicketZoomArticleAction')
keys = _.keys(actionConfig).sort()
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee
index f2bc4ab0c..a481cfe60 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee
@@ -9,6 +9,9 @@ class App.TicketZoomAttributeBar extends App.Controller
'mouseup .js-dropdownActionMacro': 'performTicketMacro'
'mouseenter .js-dropdownActionMacro': 'onActionMacroMouseEnter'
'mouseleave .js-dropdownActionMacro': 'onActionMacroMouseLeave'
+ 'mouseup .js-dropdownActionSaveDraft': 'saveDraft'
+ 'mouseenter .js-dropdownActionSaveDraft': 'onActionMacroMouseEnter'
+ 'mouseleave .js-dropdownActionSaveDraft': 'onActionMacroMouseLeave'
'click .js-secondaryAction': 'chooseSecondaryAction'
searchCondition: {}
@@ -31,19 +34,45 @@ class App.TicketZoomAttributeBar extends App.Controller
@render()
)
+ @controllerBind('ui::ticket::updateSharedDraft', (data) =>
+ return if data.taskKey isnt @taskKey
+ @render(data)
+ )
+
+ @listenTo(App.Group, 'refresh', (refreshed_group) =>
+ selected_group_id = @el.closest('.content').find('[name=group_id]').val()
+ selected_group = App.Group.find selected_group_id
+
+ return if !selected_group
+ return if !refreshed_group
+ return if refreshed_group.id != selected_group.id
+
+ return if @sharedDraftsEnabled == selected_group.shared_drafts
+
+ @render({ newGroupId: selected_group.id })
+ )
+
getAction: ->
return App.Session.get().preferences.secondaryAction || App.Config.get('ticket_secondary_action') || 'stayOnTab'
release: =>
App.Macro.unsubscribe(@subscribeId)
- render: =>
-
+ render: (options = {}) =>
# remember current reset state
resetButtonShown = false
if @resetButton.get(0) && !@resetButton.hasClass('hide')
resetButtonShown = true
+ group = App.Group.find options?.newGroupId || @ticket.group_id
+ draft = App.TicketSharedDraftZoom.findByAttribute 'ticket_id', @ticket.id
+ accessibleGroups = App.User.current().allGroupIds('change')
+ sharedDraftButtonShown = group?.shared_drafts && _.contains(accessibleGroups, String(group.id))
+ sharedDraftsEnabled = group?.shared_drafts && _.contains(accessibleGroups, String(group.id))
+ sharedButtonVisible = sharedDraftsEnabled && draft?
+
+ @sharedDraftsEnabled = sharedDraftsEnabled
+
macros = App.Macro.getList()
@macroLastUpdated = App.Macro.lastUpdatedAt()
@@ -59,11 +88,15 @@ class App.TicketZoomAttributeBar extends App.Controller
@possibleMacros.push macro
localeEl = $(App.view('ticket_zoom/attribute_bar')(
- macros: @possibleMacros
- macroDisabled: macroDisabled
- overview_id: @overview_id
- resetButtonShown: resetButtonShown
+ macros: @possibleMacros
+ macroDisabled: macroDisabled
+ sharedButtonVisible: sharedButtonVisible
+ sharedDraftsDisabled: !sharedDraftsEnabled
+ overview_id: @overview_id
+ resetButtonShown: resetButtonShown
+ sharedDraftButtonShown: sharedDraftButtonShown
))
+
@setSecondaryAction(@secondaryAction, localeEl)
if @ticket.currentView() is 'agent'
@@ -74,6 +107,26 @@ class App.TicketZoomAttributeBar extends App.Controller
@html localeEl
+ @el.find('.js-draft').popover(
+ trigger: 'hover'
+ container: 'body'
+ html: true
+ animation: false
+ delay: 100
+ placement: 'auto'
+ sanitize: false
+ content: =>
+ draft = App.TicketSharedDraftZoom.findByAttribute 'ticket_id', @ticket?.id
+ timestamp = App.ViewHelpers.humanTime(draft?.updated_at)
+ user = App.User.find draft?.updated_by_id
+ name = user?.displayName()
+
+ content = App.i18n.translatePlain('Last change %s
by %s', timestamp, name)
+
+ # needs linebreak to align vertically without title
+ '
' + content
+ )
+
start: =>
return if !@taskbarWatcher
@taskbarWatcher.start()
@@ -106,9 +159,12 @@ class App.TicketZoomAttributeBar extends App.Controller
macroId = $(e.currentTarget).data('id')
macro = App.Macro.find(macroId)
- @callback(e, macro)
+ @macroCallback(e, macro)
@closeMacroMenu()
+ saveDraft: (e) =>
+ @draftCallback(e)
+
onActionMacroMouseEnter: (e) =>
@$(e.currentTarget).addClass('is-active')
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/form_handler_shared_draft.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/form_handler_shared_draft.coffee
new file mode 100644
index 000000000..45192bd0e
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/ticket_zoom/form_handler_shared_draft.coffee
@@ -0,0 +1,9 @@
+class TicketZoomFormHandlerSharedDraft
+
+ # central method, is getting called on every ticket form change
+ # but only trigger event for group_id changes
+ @run: (params, attribute, attributes, classname, form, ui) ->
+ return if attribute.name isnt 'group_id'
+ App.Event.trigger('ui::ticket::updateSharedDraft', { taskKey: ui.taskKey, newGroupId: params.group_id })
+
+App.Config.set('150-ticketFormSharedDraft', TicketZoomFormHandlerSharedDraft, 'TicketZoomFormHandler')
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee
index 22102a951..b61523e68 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee
@@ -9,12 +9,13 @@ class Edit extends App.Controller
return if data.ticket_id.toString() isnt @ticket.id.toString()
@ticket = App.Ticket.find(@ticket.id)
- @formMeta = data.form_meta
- @render()
+ if data.form_meta
+ @formMeta = data.form_meta
+ @render(data.draft)
)
@render()
- render: =>
+ render: (draft = {}) =>
defaults = @ticket.attributes()
delete defaults.article # ignore article infos
followUpPossible = App.Group.find(defaults.group_id).follow_up_possible
@@ -45,7 +46,7 @@ class Edit extends App.Controller
handlersConfig: handlers
filter: @formMeta.filter
formMeta: @formMeta
- params: defaults
+ params: _.extend(defaults, draft)
isDisabled: editable
taskKey: @taskKey
core_workflow: {
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/time_accounting.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/time_accounting.coffee
index cd510af21..7810f63fc 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom/time_accounting.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom/time_accounting.coffee
@@ -4,7 +4,7 @@ class App.TicketZoomTimeAccounting extends App.ControllerModal
buttonSubmit: __('Account Time')
buttonClass: 'btn--success'
leftButtons: [{
- className: 'btn--text btn--subtle js-skip',
+ className: 'js-skip',
text: __('skip')
}]
head: __('Time Accounting')
diff --git a/app/assets/javascripts/app/controllers/widget/shared_draft.coffee b/app/assets/javascripts/app/controllers/widget/shared_draft.coffee
new file mode 100644
index 000000000..34ffdd254
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/widget/shared_draft.coffee
@@ -0,0 +1,108 @@
+class App.WidgetSharedDraft extends App.Controller
+ constructor: ->
+ super
+ @load()
+ @subscribeId = App.TicketSharedDraftStart.subscribe(@render)
+ @render()
+
+ events:
+ 'click .shared-draft-item': 'clicked'
+ 'click .js-create': 'create'
+ 'click .js-update': 'update'
+ 'input #shared_draft_name': 'sharedDraftNameChanged'
+
+ elements:
+ '#shared_draft_name': 'sharedDraftNameInput'
+
+ render: =>
+ active_draft = App.TicketSharedDraftStart.find(@active_draft_id)
+
+ @html App.view('widget/shared_draft')(
+ shared_drafts: @visibleDrafts()
+ active_draft: active_draft
+ )
+
+ load: =>
+ @ajax
+ id: 'shared_drafts_index'
+ type: 'GET'
+ url: @apiPath + '/tickets/shared_drafts'
+ processData: true
+ success: (data, status, xhr) =>
+ App.TicketSharedDraftStart.deleteAll()
+ App.Collection.loadAssets(data.assets)
+ @render()
+
+ visibleDrafts: ->
+ App.TicketSharedDraftStart.findAllByAttribute 'group_id', parseInt(@group_id)
+
+ clicked: (e) ->
+ shared_draft_id = e.currentTarget.getAttribute('shared-draft-id')
+ draft = App.TicketSharedDraftStart.find shared_draft_id
+ hasChanges = App.TaskManager.worker(@taskKey).changed()
+
+ new App.TicketSharedDraftModal(
+ container: @el.closest('.content')
+ shared_draft: draft
+ hasChanges: hasChanges
+ parent: @
+ )
+
+ getParams: ->
+ form = @formParam(@el.closest('.content').find('.ticket-create'))
+ meta = @formParam(@el)
+ form_id = form.form_id
+
+ delete form.form_id
+
+ return false if meta.name.trim() == ''
+
+ JSON.stringify({
+ name: meta.name
+ group_id: form.group_id
+ form_id: form_id
+ content: form
+ })
+
+ success: (data, status, xhr) =>
+ App.Collection.loadAssets(data.assets)
+ @render()
+
+ highlightError: ->
+ @sharedDraftNameInput
+ .addClass('has-error')
+ .focus()
+
+ false
+
+ sharedDraftNameChanged: (e) ->
+ @sharedDraftNameInput.removeClass('has-error')
+
+ create: (e) ->
+ @onAction(e,
+ id: 'shared_drafts_create'
+ type: 'POST'
+ url: @apiPath + '/tickets/shared_drafts'
+ )
+
+ update: (e) ->
+ @onAction(e,
+ id: 'shared_drafts_update'
+ type: 'PATCH'
+ url: @apiPath + '/tickets/shared_drafts/' + @active_draft_id
+ )
+
+ onAction: (e, options) ->
+ e.preventDefault()
+
+ params = @getParams()
+
+ return @highlightError() if !params
+
+ @ajax _.extend(options, { data: params, success: @success })
+
+ release: =>
+ if @subscribeId
+ App.TicketSharedDraftStart.unsubscribe(@subscribeId)
+
+ super
diff --git a/app/assets/javascripts/app/controllers/widget/sidebar.coffee b/app/assets/javascripts/app/controllers/widget/sidebar.coffee
index 88ad4071e..ead89e342 100644
--- a/app/assets/javascripts/app/controllers/widget/sidebar.coffee
+++ b/app/assets/javascripts/app/controllers/widget/sidebar.coffee
@@ -102,13 +102,22 @@ class App.Sidebar extends App.Controller
toggleTabAction: (name) =>
return if !name
+ # remove active state
+ @tabs.removeClass('active')
+
+ if name == 'shared_draft'
+ draft_enabled = _.find @items, (elem) -> elem?.item?.name == 'shared_draft' and elem.sidebarItem()?
+
+ if !draft_enabled?
+ name = 'template'
+
+ available_sidebar = _.first @items, (elem) -> elem.sidebarItem()?
+ available_sidebar?.shown = true
+
# remember sidebarState for outsite
if @sidebarState
@sidebarState.active = name
- # remove active state
- @tabs.removeClass('active')
-
# add active state
@$('.tabsSidebar-tab[data-tab=' + name + ']').addClass('active')
diff --git a/app/assets/javascripts/app/models/group.coffee b/app/assets/javascripts/app/models/group.coffee
index 43d50af87..9929c8396 100644
--- a/app/assets/javascripts/app/models/group.coffee
+++ b/app/assets/javascripts/app/models/group.coffee
@@ -1,5 +1,5 @@
class App.Group extends App.Model
- @configure 'Group', 'name', 'assignment_timeout', 'follow_up_possible', 'follow_up_assignment', 'email_address_id', 'signature_id', 'note', 'active', 'updated_at'
+ @configure 'Group', 'name', 'assignment_timeout', 'follow_up_possible', 'follow_up_assignment', 'email_address_id', 'signature_id', 'note', 'active', 'shared_drafts', 'updated_at'
@extend Spine.Model.Ajax
@url: @apiPath + '/groups'
@@ -13,6 +13,7 @@ class App.Group extends App.Model
{ name: 'note', display: __('Note'), tag: 'textarea', note: __('Notes are visible to agents only, never to customers.'), limit: 250, null: true },
{ name: 'updated_at', display: __('Updated'), tag: 'datetime', readonly: 1 },
{ name: 'active', display: __('Active'), tag: 'active', default: true },
+ { name: 'shared_drafts', display: __('Shared Drafts'), tag: 'active' },
]
@configure_clone = true
@configure_overview = [
diff --git a/app/assets/javascripts/app/models/ticket.coffee b/app/assets/javascripts/app/models/ticket.coffee
index fb5a43cb7..c97515eec 100644
--- a/app/assets/javascripts/app/models/ticket.coffee
+++ b/app/assets/javascripts/app/models/ticket.coffee
@@ -374,3 +374,11 @@ class App.Ticket extends App.Model
return false if !user.permission('ticket.agent')
return true if @isAccessibleByOwner(user)
return @isAccessibleByGroup(user, permission)
+
+ attributes: ->
+ attrs = super
+
+ if @shared_draft_id
+ attrs.shared_draft_id = @shared_draft_id
+
+ attrs
diff --git a/app/assets/javascripts/app/models/ticket_article.coffee b/app/assets/javascripts/app/models/ticket_article.coffee
index 991649264..b16ee5022 100644
--- a/app/assets/javascripts/app/models/ticket_article.coffee
+++ b/app/assets/javascripts/app/models/ticket_article.coffee
@@ -59,3 +59,11 @@ class App.TicketArticle extends App.Model
if attachment && (!attachment.preferences || attachment.preferences && attachment.preferences['original-format'] isnt true)
attachments.push attachment
attachments
+
+ attributes: ->
+ attrs = super
+
+ if @shared_draft_id
+ attrs.shared_draft_id = @shared_draft_id
+
+ attrs
diff --git a/app/assets/javascripts/app/models/ticket_shared_draft_start.coffee b/app/assets/javascripts/app/models/ticket_shared_draft_start.coffee
new file mode 100644
index 000000000..d41b9d7eb
--- /dev/null
+++ b/app/assets/javascripts/app/models/ticket_shared_draft_start.coffee
@@ -0,0 +1,6 @@
+class App.TicketSharedDraftStart extends App.Model
+ @configure 'TicketSharedDraftStart', 'name', 'group_id'
+ @extend Spine.Model.Ajax
+ @url: @apiPath + '/tickets/shared_drafts'
+
+ @needsLoading: true
diff --git a/app/assets/javascripts/app/models/ticket_shared_draft_zoom.coffee b/app/assets/javascripts/app/models/ticket_shared_draft_zoom.coffee
new file mode 100644
index 000000000..94a1b24d9
--- /dev/null
+++ b/app/assets/javascripts/app/models/ticket_shared_draft_zoom.coffee
@@ -0,0 +1,4 @@
+class App.TicketSharedDraftZoom extends App.Model
+ @configure 'TicketSharedDraftZoom', 'ticket_id', 'new_article', 'ticket_attributes'
+
+ @needsLoading: false
diff --git a/app/assets/javascripts/app/views/agent_ticket_create.jst.eco b/app/assets/javascripts/app/views/agent_ticket_create.jst.eco
index 8b5849f54..dacf7d567 100644
--- a/app/assets/javascripts/app/views/agent_ticket_create.jst.eco
+++ b/app/assets/javascripts/app/views/agent_ticket_create.jst.eco
@@ -26,6 +26,7 @@