Fixes #3773 - Inconstant alignment in the listing of attachments/submit button in new article area
Fixes #3774 - Broken dialog whiling uploading oversized attachment
This commit is contained in:
parent
09fc94cf72
commit
94011846d6
8 changed files with 195 additions and 139 deletions
|
@ -6,7 +6,7 @@ class App.UiElement.richtext
|
|||
attribute.value = attribute.value.text
|
||||
|
||||
item = $( App.view('generic/richtext')(attribute: attribute, toolButtons: @toolButtons) )
|
||||
@contenteditable = item.find('[contenteditable]').ce(
|
||||
item.find('[contenteditable]').ce(
|
||||
mode: attribute.type
|
||||
maxlength: attribute.maxlength
|
||||
buttons: attribute.buttons
|
||||
|
@ -21,12 +21,12 @@ class App.UiElement.richtext
|
|||
new App[plugin.controller](params)
|
||||
|
||||
if attribute.upload
|
||||
@attachments = []
|
||||
attachments = []
|
||||
item.append( $( App.view('generic/attachment')(attribute: attribute) ) )
|
||||
|
||||
renderFile = (file) =>
|
||||
renderFile = (file) ->
|
||||
item.find('.attachments').append(App.view('generic/attachment_item')(file))
|
||||
@attachments.push file
|
||||
attachments.push file
|
||||
|
||||
if params && params.attachments
|
||||
for file in params.attachments
|
||||
|
@ -46,10 +46,10 @@ class App.UiElement.richtext
|
|||
, form.form_id)
|
||||
|
||||
# remove items
|
||||
item.find('.attachments').on('click', '.js-delete', (e) =>
|
||||
item.find('.attachments').on('click', '.js-delete', (e) ->
|
||||
id = $(e.currentTarget).data('id')
|
||||
@attachments = _.filter(
|
||||
@attachments,
|
||||
attachments = _.filter(
|
||||
attachments,
|
||||
(item) ->
|
||||
return if item.id.toString() is id.toString()
|
||||
item
|
||||
|
@ -71,67 +71,35 @@ class App.UiElement.richtext
|
|||
element.empty()
|
||||
)
|
||||
|
||||
@progressBar = item.find('.attachmentUpload-progressBar')
|
||||
@progressText = item.find('.js-percentage')
|
||||
@attachmentPlaceholder = item.find('.attachmentPlaceholder')
|
||||
@attachmentUpload = item.find('.attachmentUpload')
|
||||
@attachmentsHolder = item.find('.attachments')
|
||||
@cancelContainer = item.find('.js-cancel')
|
||||
App.Delay.set( ->
|
||||
uploader = new App.Html5Upload(
|
||||
uploadUrl: "#{App.Config.get('api_path')}/attachments"
|
||||
dropContainer: item.closest('form')
|
||||
cancelContainer: item.find('.js-cancel')
|
||||
inputField: item.find('input')
|
||||
data:
|
||||
form_id: item.closest('form').find('[name=form_id]').val()
|
||||
|
||||
u = => html5Upload.initialize(
|
||||
uploadUrl: "#{App.Config.get('api_path')}/attachments"
|
||||
dropContainer: item.closest('form').get(0)
|
||||
cancelContainer: @cancelContainer
|
||||
inputField: item.find('input').get(0)
|
||||
maxSimultaneousUploads: 1,
|
||||
key: 'File'
|
||||
data:
|
||||
form_id: item.closest('form').find('[name=form_id]').val()
|
||||
onFileAdded: (file) =>
|
||||
onFileStartCallback: ->
|
||||
item.find('[contenteditable]').trigger('fileUploadStart')
|
||||
|
||||
file.on(
|
||||
onStart: =>
|
||||
@attachmentPlaceholder.addClass('hide')
|
||||
@attachmentUpload.removeClass('hide')
|
||||
@cancelContainer.removeClass('hide')
|
||||
item.find('[contenteditable]').trigger('fileUploadStart')
|
||||
App.Log.debug 'UiElement.richtext', 'upload start'
|
||||
onFileCompletedCallback: (response) ->
|
||||
renderFile(response.data)
|
||||
item.find('input').val('')
|
||||
item.find('[contenteditable]').trigger('fileUploadStop', ['completed'])
|
||||
|
||||
onAborted: =>
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
item.find('input').val('')
|
||||
item.find('[contenteditable]').trigger('fileUploadStop', ['aborted'])
|
||||
onFileAbortedCallback: ->
|
||||
item.find('input').val('')
|
||||
item.find('[contenteditable]').trigger('fileUploadStop', ['aborted'])
|
||||
|
||||
# Called after received response from the server
|
||||
onCompleted: (response) =>
|
||||
response = JSON.parse(response)
|
||||
attachmentPlaceholder: item.find('.attachmentPlaceholder')
|
||||
attachmentUpload: item.find('.attachmentUpload')
|
||||
progressBar: item.find('.attachmentUpload-progressBar')
|
||||
progressText: item.find('.js-percentage')
|
||||
)
|
||||
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
|
||||
# reset progress bar
|
||||
@progressBar.width(parseInt(0) + '%')
|
||||
@progressText.text('')
|
||||
|
||||
renderFile(response.data)
|
||||
item.find('input').val('')
|
||||
item.find('[contenteditable]').trigger('fileUploadStop', ['completed'])
|
||||
App.Log.debug 'UiElement.richtext', 'upload complete', response.data
|
||||
|
||||
# Called during upload progress, first parameter
|
||||
# is decimal value from 0 to 100.
|
||||
onProgress: (progress, fileSize, uploadedBytes) =>
|
||||
@progressBar.width(parseInt(progress) + '%')
|
||||
@progressText.text(parseInt(progress))
|
||||
# hide cancel on 90%
|
||||
if parseInt(progress) >= 90
|
||||
@cancelContainer.addClass('hide')
|
||||
App.Log.debug 'UiElement.richtext', 'uploadProgress ', parseInt(progress)
|
||||
|
||||
)
|
||||
)
|
||||
App.Delay.set(u, 100, undefined, 'form_upload')
|
||||
uploader.render()
|
||||
, 100, undefined, 'form_upload')
|
||||
|
||||
item
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ class App.TicketCreate extends App.Controller
|
|||
events:
|
||||
'click .type-tabs .tab': 'changeFormType'
|
||||
'submit form': 'submit'
|
||||
'click .js-cancel': 'cancel'
|
||||
'click .form-controls .js-cancel': 'cancel'
|
||||
'click .js-active-toggle': 'toggleButton'
|
||||
|
||||
types: {
|
||||
|
@ -184,8 +184,11 @@ class App.TicketCreate extends App.Controller
|
|||
@controllerUnbind('ticket_create_rerender', (template) => @renderQueue(template))
|
||||
|
||||
changed: =>
|
||||
return true if @hasAttachments()
|
||||
|
||||
formCurrent = @formParam( @$('.ticket-create') )
|
||||
diff = difference(@formDefault, formCurrent)
|
||||
|
||||
return false if !diff || _.isEmpty(diff)
|
||||
return true
|
||||
|
||||
|
@ -461,6 +464,9 @@ class App.TicketCreate extends App.Controller
|
|||
params: =>
|
||||
params = @formParam(@$('.main form'))
|
||||
|
||||
hasAttachments: =>
|
||||
@$('.richtext .attachments .attachment').length > 0
|
||||
|
||||
submit: (e) =>
|
||||
e.preventDefault()
|
||||
|
||||
|
@ -563,7 +569,7 @@ class App.TicketCreate extends App.Controller
|
|||
# save ticket, create article
|
||||
# check attachment
|
||||
if article['body']
|
||||
if @$('.richtext .attachments .attachment').length < 1
|
||||
if !@hasAttachments()
|
||||
matchingWord = App.Utils.checkAttachmentReference(article['body'])
|
||||
if matchingWord
|
||||
if !confirm(App.i18n.translateContent('You use %s in text but no attachment is attached. Do you want to continue?', matchingWord))
|
||||
|
|
|
@ -117,7 +117,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
|||
|
||||
@tokanice(@type)
|
||||
|
||||
if @defaults.body or @isIE10()
|
||||
if @defaults.body or @attachments or @isIE10()
|
||||
@openTextarea(null, true)
|
||||
|
||||
tokanice: (type = 'email') ->
|
||||
|
@ -191,82 +191,30 @@ class App.TicketZoomArticleNew extends App.Controller
|
|||
maxlength: 150000
|
||||
})
|
||||
|
||||
html5Upload.initialize(
|
||||
uploadUrl: "#{App.Config.get('api_path')}/upload_caches/#{@form_id}"
|
||||
dropContainer: @$('.article-add').get(0)
|
||||
cancelContainer: @cancelContainer
|
||||
inputField: @$('.article-attachment input').get(0)
|
||||
key: 'File'
|
||||
maxSimultaneousUploads: 1
|
||||
onFileAdded: (file) =>
|
||||
new App.Html5Upload(
|
||||
uploadUrl: "#{App.Config.get('api_path')}/upload_caches/#{@form_id}"
|
||||
dropContainer: @$('.article-add')
|
||||
cancelContainer: @cancelContainer
|
||||
inputField: @$('.article-attachment input')
|
||||
|
||||
file.on(
|
||||
onFileStartCallback: =>
|
||||
@callbackFileUploadStart?()
|
||||
|
||||
onStart: =>
|
||||
@attachmentPlaceholder.addClass('hide')
|
||||
@attachmentUpload.removeClass('hide')
|
||||
@cancelContainer.removeClass('hide')
|
||||
onFileCompletedCallback: (response) =>
|
||||
@attachments.push response.data
|
||||
@renderAttachment(response.data)
|
||||
@$('.article-attachment input').val('')
|
||||
|
||||
if @callbackFileUploadStart
|
||||
@callbackFileUploadStart()
|
||||
@callbackFileUploadStop?()
|
||||
|
||||
onAborted: =>
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
@$('.article-attachment input').val('')
|
||||
onFileAbortedCallback: =>
|
||||
@callbackFileUploadStop?()
|
||||
|
||||
if @callbackFileUploadStop
|
||||
@callbackFileUploadStop()
|
||||
|
||||
# Called after received response from the server
|
||||
onCompleted: (response) =>
|
||||
|
||||
response = JSON.parse(response)
|
||||
@attachments.push response.data
|
||||
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
|
||||
# reset progress bar
|
||||
@progressBar.width(parseInt(0) + '%')
|
||||
@progressText.text('')
|
||||
|
||||
@renderAttachment(response.data)
|
||||
@$('.article-attachment input').val('')
|
||||
|
||||
if @callbackFileUploadStop
|
||||
@callbackFileUploadStop()
|
||||
|
||||
# Called during upload progress, first parameter
|
||||
# is decimal value from 0 to 100.
|
||||
onProgress: (progress, fileSize, uploadedBytes) =>
|
||||
@progressBar.width(parseInt(progress) + '%')
|
||||
@progressText.text(parseInt(progress))
|
||||
# hide cancel on 90%
|
||||
if parseInt(progress) >= 90
|
||||
@cancelContainer.addClass('hide')
|
||||
|
||||
# Called when upload failed
|
||||
onError: (message) =>
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
@$('.article-attachment input').val('')
|
||||
|
||||
if @callbackFileUploadStop
|
||||
@callbackFileUploadStop()
|
||||
|
||||
new App.ControllerModal(
|
||||
head: 'Upload Failed'
|
||||
buttonCancel: 'Cancel'
|
||||
buttonCancelClass: 'btn--danger'
|
||||
buttonSubmit: false
|
||||
message: message
|
||||
shown: true
|
||||
small: true
|
||||
container: @el.closest('.content')
|
||||
)
|
||||
)
|
||||
)
|
||||
attachmentPlaceholder: @attachmentPlaceholder
|
||||
attachmentUpload: @attachmentUpload
|
||||
progressBar: @progressBar
|
||||
progressText: @progressText
|
||||
).render()
|
||||
|
||||
@bindAttachmentDelete()
|
||||
|
||||
|
|
98
app/assets/javascripts/app/lib/app_post/html5_upload.coffee
Normal file
98
app/assets/javascripts/app/lib/app_post/html5_upload.coffee
Normal file
|
@ -0,0 +1,98 @@
|
|||
class App.Html5Upload extends App.Controller
|
||||
uploadUrl: null
|
||||
maxSimultaneousUploads: 1
|
||||
key: 'File'
|
||||
data: null
|
||||
|
||||
onFileStartCallback: null
|
||||
onFileCompletedCallback: null
|
||||
onFileAbortedCallback: null
|
||||
|
||||
dropContainer: null
|
||||
cancelContainer: null
|
||||
inputField: null
|
||||
attachmentPlaceholder: null
|
||||
attachmentUpload: null
|
||||
progressBar: null
|
||||
progressText: null
|
||||
|
||||
render: =>
|
||||
html5Upload.initialize(
|
||||
uploadUrl: @uploadUrl
|
||||
dropContainer: @dropContainer.get(0)
|
||||
cancelContainer: @cancelContainer
|
||||
inputField: @inputField.get(0)
|
||||
maxSimultaneousUploads: @maxSimultaneousUploads
|
||||
key: @key
|
||||
data: @data
|
||||
onFileAdded: @onFileAdded
|
||||
)
|
||||
|
||||
onFileAdded: (file) =>
|
||||
file.on(
|
||||
onStart: @onFileStart
|
||||
onAborted: @onFileAborted
|
||||
onCompleted: @onFileCompleted
|
||||
onProgress: @onFileProgress
|
||||
onError: @onFileError
|
||||
)
|
||||
|
||||
onFileStart: =>
|
||||
@attachmentPlaceholder.addClass('hide')
|
||||
@attachmentUpload.removeClass('hide')
|
||||
@cancelContainer.removeClass('hide')
|
||||
|
||||
App.Log.debug 'Html5Upload', 'upload start'
|
||||
@onFileStartCallback?()
|
||||
|
||||
onFileProgress: (progress, fileSize, uploadedBytes) =>
|
||||
progress = parseInt(progress)
|
||||
|
||||
@progressBar.width(progress + '%')
|
||||
@progressText.text(progress)
|
||||
# hide cancel on 90%
|
||||
if progress >= 90
|
||||
@cancelContainer.addClass('hide')
|
||||
|
||||
App.Log.debug 'Html5Upload', 'uploadProgress ', progress
|
||||
|
||||
|
||||
onFileCompleted: (response) =>
|
||||
response = JSON.parse(response)
|
||||
|
||||
@hideFileUploading()
|
||||
@onFileCompletedCallback?(response)
|
||||
|
||||
App.Log.debug 'Html5Upload', 'upload complete', response.data
|
||||
|
||||
onFileAborted: =>
|
||||
@hideFileUploading()
|
||||
@onFileAbortedCallback?()
|
||||
|
||||
App.Log.debug 'Html5Upload', 'upload aborted'
|
||||
|
||||
onFileError: (message) =>
|
||||
@hideFileUploading()
|
||||
@inputField.val('')
|
||||
|
||||
@callbackFileUploadStop?()
|
||||
|
||||
new App.ControllerModal(
|
||||
head: 'Upload Failed'
|
||||
buttonCancel: 'Cancel'
|
||||
buttonCancelClass: 'btn--danger'
|
||||
buttonSubmit: false
|
||||
message: message || 'Cannot upload file'
|
||||
shown: true
|
||||
small: true
|
||||
container: @inputField.closest('.content')
|
||||
)
|
||||
|
||||
App.Log.debug 'Html5Upload', 'upload error'
|
||||
|
||||
hideFileUploading: =>
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
|
||||
@progressBar.width('0%')
|
||||
@progressText.text('0')
|
|
@ -255,7 +255,7 @@
|
|||
manager.ajaxUpload(manager.uploadsQueue.shift());
|
||||
}
|
||||
};
|
||||
xhr.abort = function (event) {
|
||||
xhr.onabort = function (event) {
|
||||
console.log('Upload abort');
|
||||
|
||||
// Reduce number of active uploads:
|
||||
|
@ -269,6 +269,7 @@
|
|||
// Triggered when upload fails:
|
||||
xhr.onerror = function () {
|
||||
console.log('Upload failed: ', upload.fileName);
|
||||
upload.events.onError('Upload failed: ' + upload.fileName);
|
||||
};
|
||||
|
||||
// Append additional data if provided:
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<%- @T('Uploading') %> (<span class="js-percentage">0</span>%) ...
|
||||
</div>
|
||||
<div class="attachmentUpload-cancel js-cancel">
|
||||
<%- @Icon('diagonal-cross') %></div><%- @T('Cancel Upload') %>
|
||||
<%- @Icon('diagonal-cross') %><%- @T('Cancel Upload') %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attachmentUpload-progressBar" style="width: 0%"></div>
|
||||
|
|
|
@ -7063,6 +7063,11 @@ footer {
|
|||
.ticket-create .attachments:not(:empty) {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 56px;
|
||||
}
|
||||
|
||||
.ticket-create .attachment--row {
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.attachment.attachment--row {
|
||||
|
|
|
@ -469,6 +469,36 @@ RSpec.describe 'Ticket Create', type: :system do
|
|||
|
||||
expect(page).to have_no_selector(:task_with, task_key)
|
||||
end
|
||||
|
||||
it 'asks for confirmation if attachment was added' do
|
||||
visit 'ticket/create'
|
||||
|
||||
within :active_content do
|
||||
page.find('input#fileUpload_1', visible: :all).set(Rails.root.join('test/data/mail/mail001.box'))
|
||||
await_empty_ajax_queue
|
||||
|
||||
find('.js-cancel').click
|
||||
end
|
||||
|
||||
in_modal disappears: false do
|
||||
expect(page).to have_text 'Tab has changed'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when uploading attachment' do
|
||||
it 'shows an error if server throws an error' do
|
||||
allow(Store).to receive(:add) { raise 'Error' }
|
||||
visit 'ticket/create'
|
||||
|
||||
within :active_content do
|
||||
page.find('input#fileUpload_1', visible: :all).set(Rails.root.join('test/data/mail/mail001.box'))
|
||||
end
|
||||
|
||||
in_modal disappears: false do
|
||||
expect(page).to have_text 'Error'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when closing taskbar tab for new ticket creation' do
|
||||
|
|
Loading…
Reference in a new issue