mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-29 00:14:41 +08:00
Merge pull request #658 from ZmagoD/zd_SCI_1011
Multiple file upload with drag & drop capability in steps and results
This commit is contained in:
commit
4873624cec
17 changed files with 553 additions and 138 deletions
|
@ -157,23 +157,6 @@
|
||||||
$form.clearFormErrors();
|
$form.clearFormErrors();
|
||||||
|
|
||||||
switch(resultTypeEnum) {
|
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:
|
case ResultTypeEnum.TABLE:
|
||||||
var $nameInput = $form.find('#result_name');
|
var $nameInput = $form.find('#result_name');
|
||||||
var nameValid = textValidator(ev, $nameInput, 0,
|
var nameValid = textValidator(ev, $nameInput, 0,
|
||||||
|
|
|
@ -214,23 +214,6 @@
|
||||||
$(e.target).find("input[type='file']").val("");
|
$(e.target).find("input[type='file']").val("");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add hidden fields for tables
|
|
||||||
$form.submit(function(){
|
|
||||||
$(this).find("[data-role='editable-table']").each(function() {
|
|
||||||
var hot = $(this).find(".hot").handsontable('getInstance');
|
|
||||||
var contents = $(this).find('.hot-contents');
|
|
||||||
var data = JSON.stringify({data: hot.getData()});
|
|
||||||
contents.attr("value", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
initStepsComments();
|
|
||||||
animateSpinner(null, false);
|
|
||||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
|
||||||
}, 1000);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init ajax success/error for edit form
|
// Init ajax success/error for edit form
|
||||||
|
@ -279,49 +262,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function formNewAjax($form) {
|
|
||||||
$form
|
|
||||||
.on("ajax:beforeSend", function () {
|
|
||||||
$(".nested_step_checklists ul").each(function () {
|
|
||||||
reorderCheckboxData(this);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on("ajax:success", function(e, data) {
|
|
||||||
$(this).after(data.html);
|
|
||||||
var $new_step = $(this).next();
|
|
||||||
$(this).remove();
|
|
||||||
|
|
||||||
initCallBacks();
|
|
||||||
initHandsOnTable($new_step);
|
|
||||||
expandStep($new_step);
|
|
||||||
toggleButtons(true);
|
|
||||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
|
||||||
|
|
||||||
//Rerender tables
|
|
||||||
$new_step.find("div.step-result-hot-table").each(function() {
|
|
||||||
$(this).handsontable("render");
|
|
||||||
});
|
|
||||||
animateSpinner(null, false);
|
|
||||||
setupAssetsLoading();
|
|
||||||
})
|
|
||||||
.on("ajax:error", function(e, xhr, status, error) {
|
|
||||||
$(this).after(xhr.responseJSON.html);
|
|
||||||
var $form = $(this).next();
|
|
||||||
$(this).remove();
|
|
||||||
|
|
||||||
$errInput = $form.find(".form-group.has-error").first().find("input");
|
|
||||||
renderFormError(e, $errInput);
|
|
||||||
|
|
||||||
formCallback($form);
|
|
||||||
formNewAjax($form);
|
|
||||||
applyCancelOnNew();
|
|
||||||
animateSpinner(null, false);
|
|
||||||
|
|
||||||
TinyMCE.destroyAll();
|
|
||||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleButtons(show) {
|
function toggleButtons(show) {
|
||||||
if (show) {
|
if (show) {
|
||||||
$("[data-action='new-step']").show();
|
$("[data-action='new-step']").show();
|
||||||
|
@ -562,7 +502,6 @@
|
||||||
scrollTop: $(document).height() - $(window).height()
|
scrollTop: $(document).height() - $(window).height()
|
||||||
});
|
});
|
||||||
formCallback($form);
|
formCallback($form);
|
||||||
formNewAjax($form);
|
|
||||||
applyCancelOnNew();
|
applyCancelOnNew();
|
||||||
toggleButtons(false);
|
toggleButtons(false);
|
||||||
initializeCheckboxSorting();
|
initializeCheckboxSorting();
|
||||||
|
@ -588,13 +527,13 @@
|
||||||
// experience is improved
|
// experience is improved
|
||||||
global.processStep = function processStep(ev, editMode) {
|
global.processStep = function processStep(ev, editMode) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopImmediatePropagation();
|
||||||
|
|
||||||
var $form = $(ev.target.form);
|
var $form = $(ev.target.form);
|
||||||
$form.clearFormErrors();
|
$form.clearFormErrors();
|
||||||
$form.removeBlankFileForms();
|
$form.removeBlankFileForms();
|
||||||
|
|
||||||
var $fileInputs = $form.find("input[type=file]");
|
|
||||||
var filesValid = filesValidator(ev, $fileInputs, FileTypeEnum.FILE);
|
|
||||||
var $checklists = $form.find(".nested_step_checklists");
|
var $checklists = $form.find(".nested_step_checklists");
|
||||||
var checklistsValid = checklistsValidator(ev, $checklists, editMode);
|
var checklistsValid = checklistsValidator(ev, $checklists, editMode);
|
||||||
var $nameInput = $form.find("#step_name");
|
var $nameInput = $form.find("#step_name");
|
||||||
|
@ -604,8 +543,73 @@
|
||||||
var descriptionValid = textValidator(ev, $descrTextarea, 0,
|
var descriptionValid = textValidator(ev, $descrTextarea, 0,
|
||||||
<%= Constants::TEXT_MAX_LENGTH %>);
|
<%= Constants::TEXT_MAX_LENGTH %>);
|
||||||
|
|
||||||
if (filesValid && checklistsValid && nameValid && descriptionValid) {
|
if (DragNDropSteps.filesStatus() &&
|
||||||
// Local file uploading
|
checklistsValid &&
|
||||||
|
nameValid &&
|
||||||
|
descriptionValid) {
|
||||||
|
|
||||||
|
$form.find("[data-role='editable-table']").each(function() {
|
||||||
|
var hot = $(this).find(".hot").handsontable('getInstance');
|
||||||
|
var contents = $(this).find('.hot-contents');
|
||||||
|
var data = JSON.stringify({data: hot.getData()});
|
||||||
|
contents.attr("value", data);
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
initStepsComments();
|
||||||
|
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
animateSpinner(null, true);
|
||||||
|
var data = DragNDropSteps.appendFilesToForm(ev);
|
||||||
|
data.append('step[description]', TinyMCE.getContent());
|
||||||
|
$.ajax({
|
||||||
|
url: $form.attr('action'),
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
beforeSend: function() {
|
||||||
|
$(".nested_step_checklists ul").each(function () {
|
||||||
|
reorderCheckboxData(this);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$($form.closest('.well')).after(data.html);
|
||||||
|
var $new_step = $($form.closest('.well')).next();
|
||||||
|
$($form.closest('.well')).remove();
|
||||||
|
|
||||||
|
initCallBacks();
|
||||||
|
initHandsOnTable($new_step);
|
||||||
|
expandStep($new_step);
|
||||||
|
toggleButtons(true);
|
||||||
|
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||||
|
|
||||||
|
//Rerender tables
|
||||||
|
$new_step.find("div.step-result-hot-table").each(function() {
|
||||||
|
$(this).handsontable("render");
|
||||||
|
});
|
||||||
|
animateSpinner(null, false);
|
||||||
|
setupAssetsLoading();
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
$form.after(xhr.responseJSON.html);
|
||||||
|
var $new_form = $form.next();
|
||||||
|
$form.remove();
|
||||||
|
|
||||||
|
$errInput = $new_form.find(".form-group.has-error")
|
||||||
|
.first()
|
||||||
|
.find("input");
|
||||||
|
renderFormError(e, $errInput);
|
||||||
|
|
||||||
|
formCallback($form);
|
||||||
|
applyCancelOnNew();
|
||||||
|
animateSpinner(null, false);
|
||||||
|
|
||||||
|
TinyMCE.destroyAll();
|
||||||
|
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||||
|
}
|
||||||
|
});
|
||||||
newStepHandler();
|
newStepHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
(function(global) {
|
(function(global) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ResutlAssets = (function() {
|
global.ResutlAssets = (function() {
|
||||||
// New result asset behaviour
|
// New result asset behaviour
|
||||||
function initNewResultAsset() {
|
function initNewResultAsset() {
|
||||||
$('#new-result-asset').on('click', function(event) {
|
$('#new-result-asset').on('click', function(event) {
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
_formAjaxResultAsset($form);
|
_formAjaxResultAsset($form);
|
||||||
Results.initCancelFormButton($form, initNewResultAsset);
|
Results.initCancelFormButton($form, initNewResultAsset);
|
||||||
Results.toggleResultEditButtons(false);
|
Results.toggleResultEditButtons(false);
|
||||||
$('#result_name').focus();
|
dragNdropAssetsInit('results');
|
||||||
},
|
},
|
||||||
error: function(xhr, status, e) {
|
error: function(xhr, status, e) {
|
||||||
$(this).renderFormErrors('result', xhr.responseJSON, true, e);
|
$(this).renderFormErrors('result', xhr.responseJSON, true, e);
|
||||||
|
|
346
app/assets/javascripts/sitewide/drag_n_drop.js.erb
Normal file
346
app/assets/javascripts/sitewide/drag_n_drop.js.erb
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
(function(global) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Module to handle file uploading in Steps
|
||||||
|
global.DragNDropSteps = (function() {
|
||||||
|
var droppedFiles = [];
|
||||||
|
var filesValid = true;
|
||||||
|
var totalSize = 0;
|
||||||
|
|
||||||
|
function init(files) {
|
||||||
|
for(var i = 0; i < files.length; i++) {
|
||||||
|
droppedFiles.push(files[i]);
|
||||||
|
}
|
||||||
|
listItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the status of files if they are ready to submit
|
||||||
|
function filesStatus() {
|
||||||
|
return filesValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_validateTotalSize();
|
||||||
|
dragNdropAssetsInit('steps');
|
||||||
|
}
|
||||||
|
|
||||||
|
// append the files to the form before submit
|
||||||
|
function appendFilesToForm(ev) {
|
||||||
|
var regex = /step\[assets_attributes\]\[[0-9]*\]\[id\]/;
|
||||||
|
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;
|
||||||
|
var name = 'step[assets_attributes][' + index + '][file]';
|
||||||
|
fd.append(name, droppedFiles[i]);
|
||||||
|
}
|
||||||
|
droppedFiles = [];
|
||||||
|
filesValid = true;
|
||||||
|
totalSize = 0;
|
||||||
|
_dragNdropAssetsOff();
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _validateFilesSize(file) {
|
||||||
|
var maxSize = file.size/1048576;
|
||||||
|
if(maxSize > <%= Constants::FILE_MAX_SIZE_MB %> && filesValid) {
|
||||||
|
return "<p><%= I18n.t 'general.file.size_exceeded', file_size: Constants::FILE_MAX_SIZE_MB %></p>";
|
||||||
|
}
|
||||||
|
totalSize += parseInt(maxSize);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function _validateTotalSize() {
|
||||||
|
if(totalSize > <%= Constants::FILE_MAX_SIZE_MB %>) {
|
||||||
|
filesValid = false;
|
||||||
|
$.each($('.panel-step-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();
|
||||||
|
filesValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _uploadedAseetPreview(asset, i) {
|
||||||
|
var html = '<div class="panel panel-default panel-step-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">';
|
||||||
|
html += '<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-step-attachment-new').remove();
|
||||||
|
_validateTotalSize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _dragNdropAssetsOff() {
|
||||||
|
$('body').off('drag dragstart dragend dragover dragenter dragleave drop');
|
||||||
|
$('.is-dragover').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.freeze({
|
||||||
|
init: init,
|
||||||
|
appendFilesToForm: appendFilesToForm,
|
||||||
|
listItems: listItems,
|
||||||
|
filesStatus: filesStatus
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
}
|
||||||
|
destroyAll();
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _filerAndCheckFiles() {
|
||||||
|
droppedFiles = droppedFiles.filter(Boolean);
|
||||||
|
return (droppedFiles.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _validateFilesSize(file) {
|
||||||
|
var maxSize = file.size/1048576;
|
||||||
|
if(maxSize > <%= Constants::FILE_MAX_SIZE_MB %> && isValid) {
|
||||||
|
return "<p><%= I18n.t 'general.file.size_exceeded', file_size: Constants::FILE_MAX_SIZE_MB %></p>";
|
||||||
|
}
|
||||||
|
totalSize += parseInt(maxSize);
|
||||||
|
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 processResult(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,
|
||||||
|
processResult: processResult
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
global.dragNdropAssetsInit = function(location) {
|
||||||
|
var in_window = true;
|
||||||
|
|
||||||
|
$('body').on('drag dragstart dragend dragover dragenter dragleave drop',
|
||||||
|
function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}).on('dragover', function() {
|
||||||
|
in_window = true;
|
||||||
|
$('.is-dragover').show();
|
||||||
|
}).on('dragleave', function() {
|
||||||
|
in_window = false;
|
||||||
|
setTimeout(function() {
|
||||||
|
if(!in_window) {
|
||||||
|
$('.is-dragover').hide();
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
}).on('drop', function(e) {
|
||||||
|
$('.is-dragover').hide();
|
||||||
|
var files = e.originalEvent.dataTransfer.files;
|
||||||
|
if(location === 'steps') {
|
||||||
|
DragNDropSteps.init(files);
|
||||||
|
} else {
|
||||||
|
DragNDropResults.init(files);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
})(window);
|
|
@ -44,6 +44,9 @@ $color-milano-red: #a70b05;
|
||||||
// Colors for specific intents
|
// Colors for specific intents
|
||||||
$color-visited-link: #23527c;
|
$color-visited-link: #23527c;
|
||||||
|
|
||||||
|
// Overlay shade for drag'n dropdown
|
||||||
|
$color-drag-overlay: rgba(0, 0, 0, .4);
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Other
|
// Other
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
@ -1265,6 +1265,45 @@ ul.content-module-activities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.new-asset-box {
|
||||||
|
border: 1px solid $color-silver;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dnd-error {
|
||||||
|
color: $color-milano-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-dragover {
|
||||||
|
background: $color-drag-overlay;
|
||||||
|
bottom: 0;
|
||||||
|
display: none;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 999999;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $color-white;
|
||||||
|
display: block;
|
||||||
|
font-size: 4em;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-top: 25%;
|
||||||
|
pointer-events: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.step,
|
.step,
|
||||||
.result {
|
.result {
|
||||||
.panel {
|
.panel {
|
||||||
|
|
|
@ -26,53 +26,27 @@ class ResultAssetsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@asset = Asset.new(result_params[:asset_attributes])
|
obj = create_multiple_results
|
||||||
@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
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if (@result.save and @asset.save) then
|
if obj.fetch(:status)
|
||||||
# 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,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
format.html do
|
format.html do
|
||||||
flash[:success] = t(
|
flash[:success] = t('result_assets.create.success_flash',
|
||||||
"result_assets.create.success_flash",
|
module: @my_module.name)
|
||||||
module: @my_module.name)
|
|
||||||
redirect_to results_my_module_path(@my_module)
|
redirect_to results_my_module_path(@my_module)
|
||||||
end
|
end
|
||||||
format.json do
|
format.json do
|
||||||
render json: {
|
render json: {
|
||||||
status: 'ok',
|
|
||||||
html: render_to_string(
|
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
|
}, status: :ok
|
||||||
end
|
end
|
||||||
else
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -249,4 +223,40 @@ class ResultAssetsController < ApplicationController
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -44,7 +44,6 @@ class StepsController < ApplicationController
|
||||||
table.created_by = current_user
|
table.created_by = current_user
|
||||||
table.team = current_team
|
table.team = current_team
|
||||||
end
|
end
|
||||||
|
|
||||||
# Update default checked state
|
# Update default checked state
|
||||||
@step.checklists.each do |checklist|
|
@step.checklists.each do |checklist|
|
||||||
checklist.checklist_items.each do |checklist_item|
|
checklist.checklist_items.each do |checklist_item|
|
||||||
|
|
5
app/views/my_modules/_results.html.erb
Normal file
5
app/views/my_modules/_results.html.erb
Normal 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>
|
|
@ -1,4 +1,6 @@
|
||||||
<% provide(:head_title, t("my_modules.protocols.head_title", project: h(@project.name), module: h(@my_module.name)).html_safe) %>
|
<% provide(:head_title, t("my_modules.protocols.head_title", project: h(@project.name), module: h(@my_module.name)).html_safe) %>
|
||||||
|
|
||||||
|
<%= render partial: 'shared/drag_n_drop_overlay' %>
|
||||||
<%= render partial: "shared/sidebar" %>
|
<%= render partial: "shared/sidebar" %>
|
||||||
<%= render partial: "shared/secondary_navigation" %>
|
<%= render partial: "shared/secondary_navigation" %>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<% provide(:head_title, t("my_modules.results.head_title", project: h(@project.name), module: h(@my_module.name)).html_safe) %>
|
<% 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/sidebar" %>
|
||||||
<%= render partial: "shared/secondary_navigation" %>
|
<%= render partial: "shared/secondary_navigation" %>
|
||||||
|
<%= render partial: 'shared/drag_n_drop_overlay' %>
|
||||||
|
|
||||||
<div id="results-toolbar">
|
<div id="results-toolbar">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<% provide(:head_title, t("protocols.edit.head_title")) %>
|
<% provide(:head_title, t("protocols.edit.head_title")) %>
|
||||||
|
|
||||||
|
<%= render partial: 'shared/drag_n_drop_overlay' %>
|
||||||
<%= render partial: "protocols/breadcrumbs.html.erb",
|
<%= render partial: "protocols/breadcrumbs.html.erb",
|
||||||
locals: { teams: @teams,
|
locals: { teams: @teams,
|
||||||
current_team: @protocol.team,
|
current_team: @protocol.team,
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<%= bootstrap_form_for(@result, url: my_module_result_assets_path(format: :json), remote: true, multipart: true, data: { type: :json }) do |f| %>
|
<div id="new-result-assets-select"
|
||||||
<%= f.text_field :name, style: "margin-top: 10px;" %><br />
|
class="text-center new-asset-box">
|
||||||
<%= f.fields_for :asset do |ff| %>
|
<%=t 'assets.drag_n_drop.label_html' %> <label><span class="btn btn-primary"><%=t 'assets.drag_n_drop.browse_label' %></span>
|
||||||
<%= ff.file_field :file %>
|
<input type="file"
|
||||||
<% end %>
|
onchange="DragNDropResults.init(this.files, 'select')"
|
||||||
<%= f.submit t("result_assets.new.create"),
|
id="drag-n-drop-assets"
|
||||||
class: 'btn btn-primary save-result',
|
style="display: none" multiple>
|
||||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.FILE, false);" %>
|
</label>
|
||||||
<button type="button" class="btn btn-default cancel-new">
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-primary save-result"
|
||||||
|
data-href="<%= my_module_result_assets_path(format: :json) %>"
|
||||||
|
onClick="DragNDropResults.processResult(this)"><%=t 'result_assets.new.create' %></button>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-default cancel-new"
|
||||||
|
onClick="DragNDropResults.destroyAll()">
|
||||||
<%= t("general.cancel")%>
|
<%= t("general.cancel")%>
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
<%# end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
3
app/views/shared/_drag_n_drop_overlay.html.erb
Normal file
3
app/views/shared/_drag_n_drop_overlay.html.erb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="is-dragover">
|
||||||
|
<span><%=t 'assets.drag_n_drop.drop_label' %></span>
|
||||||
|
</div>
|
|
@ -10,7 +10,9 @@
|
||||||
<%= t("protocols.steps.new.tab_checklists") %>
|
<%= t("protocols.steps.new.tab_checklists") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" id="new-step-assets-tab">
|
<li role="presentation"
|
||||||
|
id="new-step-assets-tab"
|
||||||
|
onClick="dragNdropAssetsInit('steps')">
|
||||||
<a href="#new-step-assets" data-toggle="tab">
|
<a href="#new-step-assets" data-toggle="tab">
|
||||||
<span class="glyphicon glyphicon-file"></span>
|
<span class="glyphicon glyphicon-file"></span>
|
||||||
<%= t("protocols.steps.new.tab_assets") %>
|
<%= t("protocols.steps.new.tab_assets") %>
|
||||||
|
@ -40,13 +42,17 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" role="tabpanel" id="new-step-assets">
|
<div class="tab-pane" role="tabpanel" id="new-step-assets">
|
||||||
|
<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="DragNDropSteps.init(this.files)"
|
||||||
|
id="drag-n-drop-assets"
|
||||||
|
style="display: none" multiple>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<%= f.nested_fields_for :assets do |ff| %>
|
<%= f.nested_fields_for :assets do |ff| %>
|
||||||
<%= render "form_assets.html.erb", ff: ff, step: step %>
|
<%= render "form_assets.html.erb", ff: ff, step: step %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= f.add_nested_fields_link :assets do %>
|
|
||||||
<span class="glyphicon glyphicon-plus"></span>
|
|
||||||
<%= t("protocols.steps.new.add_asset") %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" role="tabpanel" id="new-step-tables">
|
<div class="tab-pane" role="tabpanel" id="new-step-tables">
|
||||||
<%= f.nested_fields_for :tables do |ff| %>
|
<%= f.nested_fields_for :tables do |ff| %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<%= bootstrap_form_for(@step, url: protocol_steps_path(@protocol, format: :json), remote: true, authenticity_token: true, multipart: true, data: { role: "new-step-form", type: :json }) do |f| %>
|
<%= bootstrap_form_for(@step, url: protocol_steps_path(@protocol, format: :json), authenticity_token: true, multipart: true, data: { role: "new-step-form", type: :json }) do |f| %>
|
||||||
<h4><%= t("protocols.steps.new.add_step_title") %></h4>
|
<h4><%= t("protocols.steps.new.add_step_title") %></h4>
|
||||||
<hr>
|
<hr>
|
||||||
<%= render partial: "empty_step.html.erb", locals: {step: @step, f: f} %>
|
<%= render partial: "empty_step.html.erb", locals: {step: @step, f: f} %>
|
||||||
|
|
|
@ -813,6 +813,7 @@ en:
|
||||||
success_flash: "File result successfully deleted."
|
success_flash: "File result successfully deleted."
|
||||||
wopi_open_file: "Open in %{app}"
|
wopi_open_file: "Open in %{app}"
|
||||||
wopi_edit_file: "Edit in %{app}"
|
wopi_edit_file: "Edit in %{app}"
|
||||||
|
error_flash: 'Something went wrong! Please try again later.'
|
||||||
|
|
||||||
result_tables:
|
result_tables:
|
||||||
new:
|
new:
|
||||||
|
@ -1566,7 +1567,11 @@ en:
|
||||||
head_title:
|
head_title:
|
||||||
edit: "sciNote | %{file_name} | Edit"
|
edit: "sciNote | %{file_name} | Edit"
|
||||||
view: "sciNote | %{file_name} | View"
|
view: "sciNote | %{file_name} | View"
|
||||||
|
drag_n_drop:
|
||||||
|
label_html: 'Drag & drop files here or '
|
||||||
|
browse_label: 'Browse to add'
|
||||||
|
drop_label: 'Drop to add to Step'
|
||||||
|
file_label: 'File'
|
||||||
atwho:
|
atwho:
|
||||||
no_results: "No results found"
|
no_results: "No results found"
|
||||||
users:
|
users:
|
||||||
|
|
Loading…
Add table
Reference in a new issue