Implemented issue #195 - Take over attachment on ticket split.

This commit is contained in:
Martin Edenhofer 2017-12-05 15:49:59 +01:00
parent de55dbeb33
commit 75ac4252f3
7 changed files with 128 additions and 104 deletions

View file

@ -1,8 +1,7 @@
# coffeelint: disable=camel_case_classes # coffeelint: disable=camel_case_classes
class App.UiElement.richtext class App.UiElement.richtext
@render: (attribute) -> @render: (attribute, params) ->
item = $( App.view('generic/richtext')(attribute: attribute) )
item = $( App.view('generic/richtext')( attribute: attribute ) )
item.find('[contenteditable]').ce( item.find('[contenteditable]').ce(
mode: attribute.type mode: attribute.type
maxlength: attribute.maxlength maxlength: attribute.maxlength
@ -15,42 +14,42 @@ class App.UiElement.richtext
new App[plugin.controller](params) new App[plugin.controller](params)
if attribute.upload if attribute.upload
item.append( $( App.view('generic/attachment')( attribute: attribute ) ) ) @attachments = []
item.append( $( App.view('generic/attachment')(attribute: attribute) ) )
renderAttachment = (file) => renderFile = (file) =>
item.find('.attachments').append( App.view('generic/attachment_item')( item.find('.attachments').append(App.view('generic/attachment_item')(file))
fileName: file.filename @attachments.push file
fileSize: App.Utils.humanFileSize(file.size)
store_id: file.store_id
))
item.on(
'click'
"[data-id=#{file.store_id}]", (e) =>
@attachments = _.filter(
@attachments,
(item) ->
return if item.id isnt file.store_id
item
)
store_id = $(e.currentTarget).data('id')
# delete attachment from storage if params && params.attachments
App.Ajax.request( for file in params.attachments
type: 'DELETE' renderFile(file)
url: "#{App.Config.get('api_path')}/ticket_attachment_upload"
data: JSON.stringify(store_id: store_id),
processData: false
)
# remove attachment from dom # remove items
element = $(e.currentTarget).closest('.attachments') item.find('.attachments').on('click', '.js-delete', (e) =>
$(e.currentTarget).closest('.attachment').remove() id = $(e.currentTarget).data('id')
# empty .attachment (remove spaces) to keep css working, thanks @mrflix :-o @attachments = _.filter(
if element.find('.attachment').length == 0 @attachments,
element.empty() (item) ->
return if item.id.toString() is id.toString()
item
) )
@attachments = [] # delete attachment from storage
App.Ajax.request(
type: 'DELETE'
url: "#{App.Config.get('api_path')}/ticket_attachment_upload"
data: JSON.stringify(id: id),
processData: false
)
# remove attachment from dom
element = $(e.currentTarget).closest('.attachments')
$(e.currentTarget).closest('.attachment').remove()
if element.find('.attachment').length == 0
element.empty()
)
@progressBar = item.find('.attachmentUpload-progressBar') @progressBar = item.find('.attachmentUpload-progressBar')
@progressText = item.find('.js-percentage') @progressText = item.find('.js-percentage')
@attachmentPlaceholder = item.find('.attachmentPlaceholder') @attachmentPlaceholder = item.find('.attachmentPlaceholder')
@ -84,7 +83,6 @@ class App.UiElement.richtext
# Called after received response from the server # Called after received response from the server
onCompleted: (response) => onCompleted: (response) =>
response = JSON.parse(response) response = JSON.parse(response)
@attachments.push response.data
@attachmentPlaceholder.removeClass('hide') @attachmentPlaceholder.removeClass('hide')
@attachmentUpload.addClass('hide') @attachmentUpload.addClass('hide')
@ -93,7 +91,7 @@ class App.UiElement.richtext
@progressBar.width(parseInt(0) + '%') @progressBar.width(parseInt(0) + '%')
@progressText.text('') @progressText.text('')
renderAttachment(response.data) renderFile(response.data)
item.find('input').val('') item.find('input').val('')
App.Log.debug 'UiElement.richtext', 'upload complete', response.data App.Log.debug 'UiElement.richtext', 'upload complete', response.data
@ -111,4 +109,5 @@ class App.UiElement.richtext
) )
) )
App.Delay.set(u, 100, undefined, 'form_upload') App.Delay.set(u, 100, undefined, 'form_upload')
item item

View file

@ -14,6 +14,8 @@ class App.TicketCreate extends App.Controller
# define default type # define default type
@default_type = 'phone-in' @default_type = 'phone-in'
@formId = App.ControllerForm.formId()
# remember split info if exists # remember split info if exists
@split = '' @split = ''
if @ticket_id && @article_id if @ticket_id && @article_id
@ -158,7 +160,7 @@ class App.TicketCreate extends App.Controller
# get data / in case also ticket data for split # get data / in case also ticket data for split
buildScreen: (params) => buildScreen: (params) =>
if !params.ticket_id && !params.article_id if _.isEmpty(params.ticket_id) && _.isEmpty(params.article_id)
if !_.isEmpty(params.customer_id) if !_.isEmpty(params.customer_id)
@render(options: { customer_id: params.customer_id }) @render(options: { customer_id: params.customer_id })
return return
@ -173,6 +175,7 @@ class App.TicketCreate extends App.Controller
data: data:
ticket_id: params.ticket_id ticket_id: params.ticket_id
article_id: params.article_id article_id: params.article_id
form_id: @formId
processData: true processData: true
success: (data, status, xhr) => success: (data, status, xhr) =>
@ -194,6 +197,9 @@ class App.TicketCreate extends App.Controller
else else
t.body = App.Utils.text2html(a.body) t.body = App.Utils.text2html(a.body)
# add attachments
t.attachments = data.attachments
# render page # render page
@render(options: t) @render(options: t)
) )
@ -206,18 +212,15 @@ class App.TicketCreate extends App.Controller
params = template.options params = template.options
else if App.TaskManager.get(@task_key) && !_.isEmpty(App.TaskManager.get(@task_key).state) else if App.TaskManager.get(@task_key) && !_.isEmpty(App.TaskManager.get(@task_key).state)
params = App.TaskManager.get(@task_key).state params = App.TaskManager.get(@task_key).state
if !_.isEmpty(params['form_id'])
@formId = params['form_id']
if params['form_id'] @html(App.view('agent_ticket_create')(
@form_id = params['form_id']
else
@form_id = App.ControllerForm.formId()
@html App.view('agent_ticket_create')(
head: 'New Ticket' head: 'New Ticket'
agent: @permissionCheck('ticket.agent') agent: @permissionCheck('ticket.agent')
admin: @permissionCheck('admin') admin: @permissionCheck('admin')
form_id: @form_id form_id: @formId
) ))
signatureChanges = (params, attribute, attributes, classname, form, ui) => signatureChanges = (params, attribute, attributes, classname, form, ui) =>
if attribute && attribute.name is 'group_id' if attribute && attribute.name is 'group_id'
@ -272,7 +275,7 @@ class App.TicketCreate extends App.Controller
} }
new App.ControllerForm( new App.ControllerForm(
el: @$('.ticket-form-top') el: @$('.ticket-form-top')
form_id: @form_id form_id: @formId
model: App.Ticket model: App.Ticket
screen: 'create_top' screen: 'create_top'
events: events:
@ -288,14 +291,14 @@ class App.TicketCreate extends App.Controller
new App.ControllerForm( new App.ControllerForm(
el: @$('.article-form-top') el: @$('.article-form-top')
form_id: @form_id form_id: @formId
model: App.TicketArticle model: App.TicketArticle
screen: 'create_top' screen: 'create_top'
params: params params: params
) )
new App.ControllerForm( new App.ControllerForm(
el: @$('.ticket-form-middle') el: @$('.ticket-form-middle')
form_id: @form_id form_id: @formId
model: App.Ticket model: App.Ticket
screen: 'create_middle' screen: 'create_middle'
events: events:
@ -310,7 +313,7 @@ class App.TicketCreate extends App.Controller
) )
new App.ControllerForm( new App.ControllerForm(
el: @$('.ticket-form-bottom') el: @$('.ticket-form-bottom')
form_id: @form_id form_id: @formId
model: App.Ticket model: App.Ticket
screen: 'create_bottom' screen: 'create_bottom'
events: events:
@ -420,7 +423,7 @@ class App.TicketCreate extends App.Controller
body: params.body body: params.body
type_id: type.id type_id: type.id
sender_id: sender.id sender_id: sender.id
form_id: @form_id form_id: @formId
content_type: 'text/html' content_type: 'text/html'
} }
else else
@ -432,7 +435,7 @@ class App.TicketCreate extends App.Controller
body: params.body body: params.body
type_id: type.id type_id: type.id
sender_id: sender.id sender_id: sender.id
form_id: @form_id form_id: @formId
content_type: 'text/html' content_type: 'text/html'
} }

View file

@ -349,7 +349,7 @@ class LayoutRefCommunicationReply extends App.ControllerContent
file = @uploadQueue.shift() file = @uploadQueue.shift()
# console.log "working of", file, "from", @uploadQueue # console.log "working of", file, "from", @uploadQueue
@fakeUpload file.name, file.size, @workOfUploadQueue @fakeUpload(file.name, file.size, @workOfUploadQueue)
humanFileSize: (size) -> humanFileSize: (size) ->
i = Math.floor( Math.log(size) / Math.log(1024) ) i = Math.floor( Math.log(size) / Math.log(1024) )
@ -363,27 +363,27 @@ class LayoutRefCommunicationReply extends App.ControllerContent
@attachmentPlaceholder.removeClass('hide') @attachmentPlaceholder.removeClass('hide')
@attachmentUpload.addClass('hide') @attachmentUpload.addClass('hide')
fakeUpload: (fileName, fileSize, callback) -> fakeUpload: (filename, size, callback) ->
@attachmentPlaceholder.addClass('hide') @attachmentPlaceholder.addClass('hide')
@attachmentUpload.removeClass('hide') @attachmentUpload.removeClass('hide')
progress = 0 progress = 0
duration = fileSize / 1024 duration = size / 1024
for i in [0..100] for i in [0..100]
setTimeout @updateUploadProgress, i*duration/100 , i setTimeout @updateUploadProgress, i*duration/100 , i
setTimeout (=> setTimeout (=>
callback() callback()
@renderAttachment(fileName, fileSize) @renderAttachment(filename, size)
), duration ), duration
renderAttachment: (fileName, fileSize) => renderAttachment: (filename, size) =>
@attachments.push([fileName, fileSize]) @attachments.push([filename, size])
@attachmentsHolder.append App.view('generic/attachment_item') @attachmentsHolder.append(App.view('generic/attachment_item')
fileName: fileName filename: filename
fileSize: @humanFileSize(fileSize) size: @humanFileSize(size)
)
App.Config.set( 'layout_ref/communication_reply/:content', LayoutRefCommunicationReply, 'Routes' ) App.Config.set( 'layout_ref/communication_reply/:content', LayoutRefCommunicationReply, 'Routes' )

View file

@ -245,7 +245,7 @@ class App.TicketZoomArticleNew extends App.Controller
controller = new App.ControllerForm( controller = new App.ControllerForm(
el: @$('.recipients') el: @$('.recipients')
model: model:
configure_attributes: configure_attributes, configure_attributes: configure_attributes
) )
@$('[data-name="body"]').ce({ @$('[data-name="body"]').ce({
@ -255,12 +255,13 @@ class App.TicketZoomArticleNew extends App.Controller
}) })
html5Upload.initialize( html5Upload.initialize(
uploadUrl: App.Config.get('api_path') + '/ticket_attachment_upload', uploadUrl: App.Config.get('api_path') + '/ticket_attachment_upload'
dropContainer: @$('.article-add').get(0), dropContainer: @$('.article-add').get(0)
cancelContainer: @cancelContainer, cancelContainer: @cancelContainer
inputField: @$('.article-attachment input').get(0), inputField: @$('.article-attachment input').get(0)
key: 'File', key: 'File'
data: { form_id: @form_id }, data:
form_id: @form_id
maxSimultaneousUploads: 1, maxSimultaneousUploads: 1,
onFileAdded: (file) => onFileAdded: (file) =>
@ -303,6 +304,8 @@ class App.TicketZoomArticleNew extends App.Controller
) )
) )
@bindAttachmentDelete()
# show text module UI # show text module UI
if !@permissionCheck('ticket.customer') if !@permissionCheck('ticket.customer')
textModule = new App.WidgetTextModule( textModule = new App.WidgetTextModule(
@ -737,33 +740,29 @@ class App.TicketZoomArticleNew extends App.Controller
@articleNewEdit.parent().removeClass('is-dropTarget') if @dragEventCounter is 0 @articleNewEdit.parent().removeClass('is-dropTarget') if @dragEventCounter is 0
renderAttachment: (file) => renderAttachment: (file) =>
@attachmentsHolder.append App.view('generic/attachment_item') @attachmentsHolder.append(App.view('generic/attachment_item')(file))
fileName: file.filename
fileSize: @humanFileSize( file.size )
store_id: file.store_id
@attachmentsHolder.on(
'click'
"[data-id=#{file.store_id}]", (e) =>
@attachments = _.filter(
@attachments,
(item) ->
return if item.id isnt file.store_id
item
)
store_id = $(e.currentTarget).data('id')
# delete attachment from storage bindAttachmentDelete: =>
App.Ajax.request( @attachmentsHolder.on('click', '.js-delete', (e) =>
type: 'DELETE' id = $(e.currentTarget).data('id')
url: App.Config.get('api_path') + '/ticket_attachment_upload' @attachments = _.filter(
data: JSON.stringify(store_id: store_id) @attachments,
processData: false (item) ->
) return if item.id.toString() is id.toString()
item
)
# remove attachment from dom # delete attachment from storage
element = $(e.currentTarget).closest('.attachments') App.Ajax.request(
$(e.currentTarget).closest('.attachment').remove() type: 'DELETE'
# empty .attachment (remove spaces) to keep css working, thanks @mrflix :-o url: App.Config.get('api_path') + '/ticket_attachment_upload'
if element.find('.attachment').length == 0 data: JSON.stringify(id: id)
element.empty() processData: false
)
# remove attachment from dom
element = $(e.currentTarget).closest('.attachments')
$(e.currentTarget).closest('.attachment').remove()
if element.find('.attachment').length == 0
element.empty()
) )

View file

@ -1,7 +1,7 @@
<div class="attachment"> <div class="attachment">
<div class="attachment-name"><%= @fileName %></div> <div class="attachment-name"><%= @filename %></div>
<div class="attachment-size"><%= @fileSize %></div> <div class="attachment-size"><%= @humanFileSize(@size) %></div>
<div class="attachment-delete js-delete" data-id="<%= @store_id %>"> <div class="attachment-delete js-delete" data-id="<%= @id %>">
<%- @Icon('diagonal-cross') %><%- @T('Delete File') %> <%- @Icon('diagonal-cross') %><%- @T('Delete File') %>
</div> </div>
</div> </div>

View file

@ -150,13 +150,16 @@ class TicketArticlesController < ApplicationController
# DELETE /ticket_attachment_upload # DELETE /ticket_attachment_upload
def ticket_attachment_upload_delete def ticket_attachment_upload_delete
if params[:store_id]
Store.remove_item(params[:store_id]) if params[:id].present?
Store.remove_item(params[:id])
render json: { render json: {
success: true, success: true,
} }
return return
elsif params[:form_id] end
if params[:form_id].present?
Store.remove( Store.remove(
object: 'UploadCache', object: 'UploadCache',
o_id: params[:form_id], o_id: params[:form_id],
@ -167,7 +170,7 @@ class TicketArticlesController < ApplicationController
return return
end end
render json: { message: 'No such store_id or form_id!' }, status: :unprocessable_entity render json: { message: 'No such id or form_id!' }, status: :unprocessable_entity
end end
# POST /ticket_attachment_upload # POST /ticket_attachment_upload
@ -198,7 +201,7 @@ class TicketArticlesController < ApplicationController
render json: { render json: {
success: true, success: true,
data: { data: {
store_id: store.id, id: store.id,
filename: file.original_filename, filename: file.original_filename,
size: store.size, size: store.size,
} }

View file

@ -357,8 +357,28 @@ class TicketsController < ApplicationController
article = Ticket::Article.find(params[:article_id]) article = Ticket::Article.find(params[:article_id])
assets = article.assets(assets) assets = article.assets(assets)
attachments = []
if params[:form_id].present?
attachments = Store.list(
object: 'UploadCache',
o_id: params[:form_id],
).to_a
article.attachments.each do |attachment|
next if attachment.preferences['Content-ID'].present?
file = Store.add(
object: 'UploadCache',
o_id: params[:form_id],
data: attachment.content,
filename: attachment.filename,
preferences: attachment.preferences,
)
attachments.push file
end
end
render json: { render json: {
assets: assets assets: assets,
attachments: attachments,
} }
end end