From c6bb20039b708ed3316afab8a3c520a7a5f25135 Mon Sep 17 00:00:00 2001 From: zmagod Date: Wed, 7 Jun 2017 13:29:39 +0200 Subject: [PATCH] adds drag'n drop feature in task results [fixes SCI-1011], [fixes SCI-1310] and [fixes SCI-285] --- .../javascripts/my_modules/results.js.erb | 17 -- app/assets/javascripts/protocols/steps.js.erb | 4 +- .../javascripts/results/result_assets.js | 6 +- .../javascripts/sitewide/drag_n_drop.js.erb | 231 ++++++++++++++++-- app/controllers/result_assets_controller.rb | 82 ++++--- app/views/my_modules/_results.html.erb | 5 + app/views/my_modules/results.html.erb | 1 + app/views/result_assets/_new.html.erb | 32 ++- app/views/steps/_empty_step.html.erb | 4 +- config/locales/en.yml | 3 +- 10 files changed, 300 insertions(+), 85 deletions(-) create mode 100644 app/views/my_modules/_results.html.erb diff --git a/app/assets/javascripts/my_modules/results.js.erb b/app/assets/javascripts/my_modules/results.js.erb index ad3608600..079815e61 100644 --- a/app/assets/javascripts/my_modules/results.js.erb +++ b/app/assets/javascripts/my_modules/results.js.erb @@ -157,23 +157,6 @@ $form.clearFormErrors(); switch(resultTypeEnum) { - case ResultTypeEnum.FILE: - var $nameInput = $form.find('#result_name'); - var nameValid = textValidator(ev, $nameInput, 0, - <%= Constants::NAME_MAX_LENGTH %>); - var $fileInput = $form.find('#result_asset_attributes_file'); - var filesValid = true; - if ($fileInput.val()) { - filesValid = filesValidator(ev, $fileInput, FileTypeEnum.FILE, - editMode); - } - - if(nameValid && filesValid) { - // Local file uploading - animateSpinner(); - - } - break; case ResultTypeEnum.TABLE: var $nameInput = $form.find('#result_name'); var nameValid = textValidator(ev, $nameInput, 0, diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index ded284317..0ed7cf01b 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -543,7 +543,7 @@ var descriptionValid = textValidator(ev, $descrTextarea, 0, <%= Constants::TEXT_MAX_LENGTH %>); - if (DragNDrop.filesStatus() && + if (DragNDropSteps.filesStatus() && checklistsValid && nameValid && descriptionValid) { @@ -561,7 +561,7 @@ }, 1000); animateSpinner(null, true); - var data = DragNDrop.appendFilesToForm(ev); + var data = DragNDropSteps.appendFilesToForm(ev); data.append('step[description]', TinyMCE.getContent()); $.ajax({ url: $form.attr('action'), diff --git a/app/assets/javascripts/results/result_assets.js b/app/assets/javascripts/results/result_assets.js index 47a23013e..7ab31c493 100644 --- a/app/assets/javascripts/results/result_assets.js +++ b/app/assets/javascripts/results/result_assets.js @@ -1,7 +1,7 @@ (function(global) { 'use strict'; - var ResutlAssets = (function() { + global.ResutlAssets = (function() { // New result asset behaviour function initNewResultAsset() { $('#new-result-asset').on('click', function(event) { @@ -19,11 +19,11 @@ success: function(data) { var $form = $(data.html); animateSpinner(null, false); - $('#results').prepend($form); + $('#results').prepend($form) _formAjaxResultAsset($form); Results.initCancelFormButton($form, initNewResultAsset); Results.toggleResultEditButtons(false); - $('#result_name').focus(); + dragNdropAssetsInit('results'); }, error: function(xhr, status, e) { $(this).renderFormErrors('result', xhr.responseJSON, true, e); diff --git a/app/assets/javascripts/sitewide/drag_n_drop.js.erb b/app/assets/javascripts/sitewide/drag_n_drop.js.erb index d11098ad0..4fcb32ba8 100644 --- a/app/assets/javascripts/sitewide/drag_n_drop.js.erb +++ b/app/assets/javascripts/sitewide/drag_n_drop.js.erb @@ -1,7 +1,8 @@ (function(global) { 'use strict'; - global.DragNDrop = (function() { + // Module to handle file uploading in Results + global.DragNDropSteps = (function() { var droppedFiles = []; var filesValid = true; var totalSize = 0; @@ -22,18 +23,19 @@ // loops through a list of files and display each file in a separate panel function listItems() { + droppedFiles = droppedFiles.filter(Boolean); $('.panel-step-attachment-new').remove(); _dragNdropAssetsOff(); for(var i = 0; i < droppedFiles.length; i++) { - $('#new-step-assets') - .append(_uploadedAseetPreview(droppedFiles[i], i)) - .promise() - .done(function() { - _removeItemHandler(i); - }); + $('#new-step-assets') + .append(_uploadedAseetPreview(droppedFiles[i], i)) + .promise() + .done(function() { + _removeItemHandler(i); + }); } _validateTotalSize(); - dragNdropAssetsInit(); + dragNdropAssetsInit('steps'); } // appent the files to the form before submit @@ -42,7 +44,7 @@ var prevEls = $('input').filter(function() { return this.name.match(regex); }); - + droppedFiles = droppedFiles.filter(Boolean); var fd = new FormData($(ev.target).closest('form').get(0)); for(var i = 0; i < droppedFiles.length; i++) { var index = i + prevEls.length; @@ -82,13 +84,13 @@ var html = '
'; html += '
'; html += ''; - html += 'File'; + html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>'; html += '
'; html += ''; html += '
'; html += '
'; html += ' '; + html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>: '; html += truncateLongString(asset.name, <%= Constants::FILENAME_TRUNCATION_LENGTH %>); html += _validateFilesSize(asset); @@ -104,7 +106,7 @@ var $el = $(this); var index = $el.data('item-id'); totalSize -= parseInt(droppedFiles[index]/1048576); - droppedFiles.splice(parseInt(index), 1); + droppedFiles[index] = null; $el.closest('.panel-step-attachment-new').remove(); _validateTotalSize(); }); @@ -120,10 +122,202 @@ appendFilesToForm: appendFilesToForm, listItems: listItems, filesStatus: filesStatus - }) + }); })(); - global.dragNdropAssetsInit = function() { + // Module to handle file uploading in Results + global.DragNDropResults = (function() { + var droppedFiles = []; + var isValid = true; + var totalSize = 0; + + function init(files) { + var filesPresent = droppedFiles.length; + for(var i = 0; i < files.length; i++) { + droppedFiles.push(files[i]); + } + listItems(filesPresent); + } + + // return the status of files if they are ready to submit + function filesStatus() { + return isValid; + } + + // loops through a list of files and display each file in a separate panel + function listItems(index) { + _dragNdropAssetsOff(); + for(var i = index; i < droppedFiles.length; i++) { + $('#new-result-assets-select') + .after(_uploadedAseetPreview(droppedFiles[i], i)) + .promise() + .done(function() { + _removeItemHandler(i); + }); + } + _validateTotalSize(); + dragNdropAssetsInit('results'); + } + + // appent the files to the form before submit + function _appendFilesToForm() { + var regex = /result\[assets_attributes\]\[[0-9]*\]\[id\]/; + var prevEls = $('input').filter(function() { + return this.name.match(regex); + }); + + var fd = new FormData(); + var result_names = []; + $.each($('input[rel="results[name]"'), function() { + result_names.push($(this).val()); + }); + result_names.reverse(); + for(var i = 0; i < droppedFiles.length; i++) { + var index = i + prevEls.length; + var file_name = 'results_files[' + index + ']'; + fd.append(file_name, droppedFiles[i]); + fd.append('results_names[' + i + ']', result_names[i]); + } + droppedFiles = []; + isValid = true; + totalSize = 0; + _dragNdropAssetsOff(); + return fd; + } + + function _filerAndCheckFiles() { + droppedFiles = droppedFiles.filter(Boolean); + return (droppedFiles.length > 0); + } + + function _validateFilesSize(file) { + if((file.size/1048576) > <%= Constants::FILE_MAX_SIZE_MB %> && isValid) { + return "

<%= I18n.t 'general.file.size_exceeded', file_size: Constants::FILE_MAX_SIZE_MB %>

"; + } + totalSize += parseInt(file.size/1048576); + return ''; + } + + function _validateTotalSize() { + if(totalSize > <%= Constants::FILE_MAX_SIZE_MB %>) { + isValid = false; + $.each($('.panel-result-attachment-new'), function() { + $(this) + .find('.panel-body') + .append("

<%= I18n.t('general.file.total_size', size: Constants::FILE_MAX_SIZE_MB) %>

"); + }); + } else { + $('.dnd-error').remove(); + isValid = true; + } + } + + function validateTextSize(input) { + if(input.value.length > <%= Constants::NAME_MAX_LENGTH %>) { + $(input).parent().find('.dnd-error').remove(); + $(input).after("

<%= I18n.t('general.text.length_too_long', max_length: Constants::NAME_MAX_LENGTH) %>

"); + isValid = false; + } else { + $(input).parent().find('.dnd-error').remove(); + isValid = true; + } + } + + function _uploadedAseetPreview(asset, i) { + var html = '
'; + html += '
'; + html += ''; + html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>'; + html += '
'; + html += ''; + html += '
'; + html += '
'; + html += ''; + html += ''; + html += '
'; + html += truncateLongString(asset.name, + <%= Constants::FILENAME_TRUNCATION_LENGTH %>); + html += _validateFilesSize(asset); + html += '
'; + return html; + } + + function _removeItemHandler(id) { + $('[data-item-id="' + id +'"]').off('click').on('click', function(e) { + e.preventDefault(); + e.stopImmediatePropagation(); + e.stopPropagation(); + var $el = $(this); + var index = $el.data('item-id'); + totalSize -= parseInt(droppedFiles[index]/1048576); + droppedFiles[index] = null; + $el.closest('.panel-result-attachment-new').remove(); + _validateTotalSize(); + }); + } + + function uploadResultAssets(button) { + if(isValid && _filerAndCheckFiles()) { + animateSpinner(); + $.ajax({ + url: $(button).attr('data-href'), + method: 'POST', + data: _appendFilesToForm(), + contentType: false, + processData: false, + success: function(data) { + animateSpinner(null, false); + $('#new-result-assets-select').parent().remove(); + $(data.html).prependTo('#results').promise().done(function() { + $.each($('[data-container="new-reports"]').find('.result'), + function() { + initFormSubmitLinks($(this)); + ResutlAssets.applyEditResultAssetCallback(); + Results.applyCollapseLinkCallBack(); + Results.toggleResultEditButtons(true); + initPreviewModal(); + Comments.initialize(); + ResutlAssets.initNewResultAsset(); + Results.expandResult($(this)); + }); + + }); + $('#results-toolbar').show(); + }, + error: function() { + animateSpinner(); + location.reload(); + } + }) + } + } + + function destroyAll() { + _dragNdropAssetsOff(); + droppedFiles = []; + isValid = true; + totalSize = 0; + } + + function _dragNdropAssetsOff() { + $('body').off('drag dragstart dragend dragover dragenter dragleave drop'); + $('.is-dragover').hide(); + } + + return Object.freeze({ + init: init, + listItems: listItems, + destroyAll: destroyAll, + filesStatus: filesStatus, + validateTextSize: validateTextSize, + uploadResultAssets: uploadResultAssets + }); + })(); + + global.dragNdropAssetsInit = function(location) { var in_window = true; $('body').on('drag dragstart dragend dragover dragenter dragleave drop', @@ -143,8 +337,13 @@ }).on('drop', function(e) { $('.is-dragover').hide(); - DragNDrop.init(e.originalEvent.dataTransfer.files); - DragNDrop.listItems(); + var files = e.originalEvent.dataTransfer.files; + if(location === 'steps') { + DragNDropSteps.init(files); + DragNDropSteps.listItems(); + } else { + DragNDropResults.init(files); + } }); } diff --git a/app/controllers/result_assets_controller.rb b/app/controllers/result_assets_controller.rb index 7bae4741b..faeb069e2 100644 --- a/app/controllers/result_assets_controller.rb +++ b/app/controllers/result_assets_controller.rb @@ -26,53 +26,27 @@ class ResultAssetsController < ApplicationController end def create - @asset = Asset.new(result_params[:asset_attributes]) - @asset.created_by = current_user - @asset.last_modified_by = current_user - @asset.team = current_team - @result = Result.new( - user: current_user, - my_module: @my_module, - name: result_params[:name], - asset: @asset - ) - @result.last_modified_by = current_user - + obj = create_multiple_results respond_to do |format| - if (@result.save and @asset.save) then - # Post process file here - @asset.post_process_file(@my_module.experiment.project.team) - - # Generate activity - Activity.create( - type_of: :add_result, - user: current_user, - project: @my_module.experiment.project, - experiment: @my_module.experiment, - my_module: @my_module, - message: t( - "activities.add_asset_result", - user: current_user.full_name, - result: @result.name, - ) - ) - + if obj.fetch(:status) format.html do - flash[:success] = t( - "result_assets.create.success_flash", - module: @my_module.name) + flash[:success] = t('result_assets.create.success_flash', + module: @my_module.name) redirect_to results_my_module_path(@my_module) end format.json do render json: { - status: 'ok', html: render_to_string( - partial: 'my_modules/result.html.erb', locals: { result: @result } + partial: 'my_modules/results.html.erb', + locals: { results: obj.fetch(:results) } ) }, status: :ok end else - format.json { render json: @result.errors, status: :bad_request } + flash[:error] = t('result_assets.error_flash') + format.json do + render json: {}, status: :bad_request + end end end end @@ -249,4 +223,40 @@ class ResultAssetsController < ApplicationController ] ) end + + def create_multiple_results + success = true + results = [] + params[:results_files].each_with_index do |file, index| + asset = Asset.new(file: file.second, + created_by: current_user, + last_modified_by: current_user, + team: current_team) + result = Result.new(user: current_user, + my_module: @my_module, + name: params[:results_names][index.to_s], + asset: asset, + last_modified_by: current_user) + if result.save && asset.save + results << result + # Post process file here + asset.post_process_file(@my_module.experiment.project.team) + + # Generate activity + Activity.create( + type_of: :add_result, + user: current_user, + project: @my_module.experiment.project, + experiment: @my_module.experiment, + my_module: @my_module, + message: t('activities.add_asset_result', + user: current_user.full_name, + result: result.name) + ) + else + success = false + end + end + { status: success, results: results } + end end diff --git a/app/views/my_modules/_results.html.erb b/app/views/my_modules/_results.html.erb new file mode 100644 index 000000000..83af69b62 --- /dev/null +++ b/app/views/my_modules/_results.html.erb @@ -0,0 +1,5 @@ +
+ <% results.reverse_each do |result| %> + <%= render partial: 'my_modules/result.html.erb', locals: { result: result } %> + <% end %> +
diff --git a/app/views/my_modules/results.html.erb b/app/views/my_modules/results.html.erb index f03a6b250..363764273 100644 --- a/app/views/my_modules/results.html.erb +++ b/app/views/my_modules/results.html.erb @@ -1,6 +1,7 @@ <% provide(:head_title, t("my_modules.results.head_title", project: h(@project.name), module: h(@my_module.name)).html_safe) %> <%= render partial: "shared/sidebar" %> <%= render partial: "shared/secondary_navigation" %> +<%= render partial: 'shared/drag_n_drop_overlay' %>
diff --git a/app/views/result_assets/_new.html.erb b/app/views/result_assets/_new.html.erb index d4fa8e0bf..5fa9bb19e 100644 --- a/app/views/result_assets/_new.html.erb +++ b/app/views/result_assets/_new.html.erb @@ -1,14 +1,30 @@
- <%= bootstrap_form_for(@result, url: my_module_result_assets_path(format: :json), remote: true, multipart: true, data: { type: :json }) do |f| %> - <%= f.text_field :name, style: "margin-top: 10px;" %>
- <%= f.fields_for :asset do |ff| %> - <%= ff.file_field :file %> - <% end %> - <%= f.submit t("result_assets.new.create"), +
+ <%=t 'assets.drag_n_drop.label_html' %> +
+
+ <%#= bootstrap_form_for(@result, url: my_module_result_assets_path(format: :json), remote: true, multipart: true, data: { type: :json }) do |f| %> + <%#= f.text_field :name, style: "margin-top: 10px;" %> + <%#= f.fields_for :asset do |ff| %> + <%#= ff.file_field :file %> + <%# end %> + <%#= f.submit t("result_assets.new.create"), class: 'btn btn-primary save-result', onclick: "Results.processResult(event, Results.ResultTypeEnum.FILE, false);" %> - + - <% end %> + <%# end %>
diff --git a/app/views/steps/_empty_step.html.erb b/app/views/steps/_empty_step.html.erb index 3925da662..4718e9109 100644 --- a/app/views/steps/_empty_step.html.erb +++ b/app/views/steps/_empty_step.html.erb @@ -12,7 +12,7 @@