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:
Mantas Masalskis 2021-10-04 21:05:32 +02:00 committed by Thorsten Eckel
parent 09fc94cf72
commit 94011846d6
8 changed files with 195 additions and 139 deletions

View file

@ -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

View file

@ -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))

View file

@ -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()

View 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')

View file

@ -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:

View file

@ -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>

View file

@ -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 {

View file

@ -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