adds drag'n drop feature in task results [fixes SCI-1011], [fixes SCI-1310] and [fixes SCI-285]

This commit is contained in:
zmagod 2017-06-07 13:29:39 +02:00
parent ab4961ddb1
commit c6bb20039b
10 changed files with 300 additions and 85 deletions

View file

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

View file

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

View file

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

View file

@ -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 = '<div class="panel panel-default panel-step-attachment-new">';
html += '<div class="panel-heading">';
html += '<span class="glyphicon glyphicon-file"></span>';
html += 'File';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>';
html += '<div class="pull-right">';
html += '<a data-item-id="' + i + '" href="#">';
html += '<span class="glyphicon glyphicon-remove"></span></a></div></div>';
html += '<div class="panel-body">';
html += '<label class="control-label">';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %></label> ';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>:</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 "<p><%= I18n.t 'general.file.size_exceeded', file_size: Constants::FILE_MAX_SIZE_MB %></p>";
}
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("<p class='dnd-error'><%= I18n.t('general.file.total_size', size: Constants::FILE_MAX_SIZE_MB) %></p>");
});
} 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("<p class='dnd-error'><%= I18n.t('general.text.length_too_long', max_length: Constants::NAME_MAX_LENGTH) %></p>");
isValid = false;
} else {
$(input).parent().find('.dnd-error').remove();
isValid = true;
}
}
function _uploadedAseetPreview(asset, i) {
var html = '<div class="panel panel-default panel-result-attachment-new">';
html += '<div class="panel-heading">';
html += '<span class="glyphicon glyphicon-file"></span>';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>';
html += '<div class="pull-right">';
html += '<a data-item-id="' + i + '" href="#">';
html += '<span class="glyphicon glyphicon-remove"></span></a></div></div>';
html += '<div class="panel-body"><div class="form-group">';
html += '<label class="control-label">Name</label>';
html += '<input type="text" class="form-control" ';
html += 'onChange="DragNDropResults.validateTextSize(this)"';
html += ' rel="results[name]" name="results[name][' + i + ']">';
html += '</div><div class="form-group"><label class="control-label">';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>:</label> ';
html += truncateLongString(asset.name,
<%= Constants::FILENAME_TRUNCATION_LENGTH %>);
html += _validateFilesSize(asset);
html += '</div></div>';
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);
}
});
}

View file

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

View file

@ -0,0 +1,5 @@
<div data-container="new-reports">
<% results.reverse_each do |result| %>
<%= render partial: 'my_modules/result.html.erb', locals: { result: result } %>
<% end %>
</div>

View file

@ -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' %>
<div id="results-toolbar">
<div class="pull-right">

View file

@ -1,14 +1,30 @@
<div class="well">
<%= 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;" %><br />
<%= f.fields_for :asset do |ff| %>
<%= ff.file_field :file %>
<% end %>
<%= f.submit t("result_assets.new.create"),
<div id="new-result-assets-select"
class="text-center new-asset-box">
<%=t 'assets.drag_n_drop.label_html' %> <label><span class="btn btn-primary"><%=t 'assets.drag_n_drop.browse_label' %></span>
<input type="file"
onchange="DragNDropResults.init(this.files, 'select')"
id="drag-n-drop-assets"
style="display: none" multiple>
</label>
</div>
<br />
<%#= 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);" %>
<button type="button" class="btn btn-default cancel-new">
<button type="button"
class="btn btn-primary save-result"
data-href="<%= my_module_result_assets_path(format: :json) %>"
onClick="DragNDropResults.uploadResultAssets(this)"><%=t 'result_assets.new.create' %></button>
<button type="button"
class="btn btn-default cancel-new"
onClick="DragNDropResults.destroyAll()">
<%= t("general.cancel")%>
</button>
<% end %>
<%# end %>
</div>

View file

@ -12,7 +12,7 @@
</li>
<li role="presentation"
id="new-step-assets-tab"
onClick="dragNdropAssetsInit()">
onClick="dragNdropAssetsInit('steps')">
<a href="#new-step-assets" data-toggle="tab">
<span class="glyphicon glyphicon-file"></span>
<%= t("protocols.steps.new.tab_assets") %>
@ -45,7 +45,7 @@
<div class="text-center new-asset-box">
<%=t 'assets.drag_n_drop.label_html' %> <label><span class="btn btn-primary"><%=t 'assets.drag_n_drop.browse_label' %></span>
<input type="file"
onchange="DragNDrop.init(this.files, 'select')"
onchange="DragNDropSteps.init(this.files, 'select')"
id="drag-n-drop-assets"
style="display: none" multiple>
</label>

View file

@ -813,6 +813,7 @@ en:
success_flash: "File result successfully deleted."
wopi_open_file: "Open in %{app}"
wopi_edit_file: "Edit in %{app}"
error_flash: 'Something went wrong! Please try again later.'
result_tables:
new:
@ -1568,7 +1569,7 @@ en:
label_html: 'Drag &amp; drop files here or '
browse_label: 'Browse to add'
drop_label: 'Drop to add to Step'
file_label: 'File:'
file_label: 'File'
atwho:
no_results: "No results found"
users: