mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-12-26 01:35:34 +08:00
Implement DirectUpload for Steps, Results and Inventories [SCI-3679]
This commit is contained in:
parent
64b7a5646a
commit
5e65b07bdc
22 changed files with 1183 additions and 1049 deletions
24
Dockerfile
24
Dockerfile
|
@ -1,30 +1,21 @@
|
|||
FROM ruby:2.6.3
|
||||
FROM ruby:2.6.3-buster
|
||||
MAINTAINER BioSistemika <info@biosistemika.com>
|
||||
|
||||
# Get version of Debian (lsb_release substitute) and save it to /tmp/lsb_release for further commands
|
||||
RUN cat /etc/os-release | grep -Po "VERSION=.*\(\K\w+" | tee /tmp/lsb_release
|
||||
|
||||
# Add Debian stretch backports repository
|
||||
RUN echo "deb http://http.debian.net/debian $(cat /tmp/lsb_release)-backports main" \
|
||||
| tee /etc/apt/sources.list.d/$(cat /tmp/lsb_release)-backports.list
|
||||
|
||||
# additional dependecies
|
||||
# libSSL-1.0 is required by wkhtmltopdf binary
|
||||
# libreoffice for file preview generation
|
||||
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
|
||||
apt-get update -qq && \
|
||||
RUN apt-get update -qq && \
|
||||
apt-get install -y \
|
||||
libjemalloc1 \
|
||||
libssl1.0-dev \
|
||||
libjemalloc2 \
|
||||
libssl-dev \
|
||||
nodejs \
|
||||
yarnpkg \
|
||||
postgresql-client \
|
||||
default-jre-headless \
|
||||
unison \
|
||||
sudo graphviz --no-install-recommends \
|
||||
poppler-utils \
|
||||
sudo graphviz --no-install-recommends \
|
||||
libreoffice \
|
||||
libfile-mimeinfo-perl && \
|
||||
apt-get install -y --no-install-recommends -t $(cat /tmp/lsb_release)-backports libreoffice && \
|
||||
npm install -g yarn && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# heroku tools
|
||||
|
@ -34,6 +25,7 @@ ENV BUNDLE_PATH /usr/local/bundle/
|
|||
|
||||
# create app directory
|
||||
ENV APP_HOME /usr/src/app
|
||||
ENV PATH $APP_HOME/bin:$PATH
|
||||
RUN mkdir $APP_HOME
|
||||
WORKDIR $APP_HOME
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
FROM ruby:2.6.3
|
||||
FROM ruby:2.6.3-buster
|
||||
MAINTAINER BioSistemika <info@biosistemika.com>
|
||||
|
||||
RUN echo deb "http://http.debian.net/debian stretch-backports main" >> /etc/apt/sources.list
|
||||
RUN echo deb "http://http.debian.net/debian buster-backports main" >> /etc/apt/sources.list
|
||||
|
||||
# additional dependecies
|
||||
# libSSL-1.0 is required by wkhtmltopdf binary
|
||||
|
@ -9,19 +9,19 @@ RUN echo deb "http://http.debian.net/debian stretch-backports main" >> /etc/apt/
|
|||
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
|
||||
apt-get update -qq && \
|
||||
apt-get install -y \
|
||||
libjemalloc1 \
|
||||
libssl1.0-dev \
|
||||
libjemalloc2 \
|
||||
libssl-dev \
|
||||
nodejs \
|
||||
yarnpkg \
|
||||
groff-base \
|
||||
awscli \
|
||||
postgresql-client \
|
||||
netcat \
|
||||
default-jre-headless \
|
||||
sudo graphviz --no-install-recommends \
|
||||
poppler-utils \
|
||||
sudo graphviz --no-install-recommends \
|
||||
libfile-mimeinfo-perl && \
|
||||
apt-get install -y --no-install-recommends -t stretch-backports libreoffice && \
|
||||
npm install -g yarn && \
|
||||
apt-get install -y --no-install-recommends -t buster-backports libreoffice && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV RAILS_ENV production
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
//= require perfect-scrollbar.min
|
||||
//= require select2_customization
|
||||
//= require shared/inline_editing
|
||||
//= require activestorage
|
||||
//= require turbolinks
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
global Results ActiveStorage animateSpinner Comments ResultAssets FilePreviewModal
|
||||
TinyMCE getParam applyCreateWopiFileCallback initFormSubmitLinks textValidator
|
||||
*/
|
||||
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
|
@ -5,40 +10,11 @@
|
|||
var ResultTypeEnum = Object.freeze({
|
||||
FILE: 0,
|
||||
TABLE: 1,
|
||||
TEXT: 2,
|
||||
COMMENT: 3
|
||||
TEXT: 2
|
||||
});
|
||||
|
||||
function init() {
|
||||
initHandsOnTables($(document));
|
||||
_expandAllResults();
|
||||
applyCollapseLinkCallBack();
|
||||
applyCreateWopiFileCallback();
|
||||
|
||||
|
||||
|
||||
$(function () {
|
||||
$('#results-collapse-btn').click(function () {
|
||||
$('.result .panel-collapse').collapse('hide');
|
||||
$(document).find('span.collapse-result-icon').each(function() {
|
||||
$(this).addClass('fa-caret-square-down');
|
||||
$(this).removeClass('fa-caret-square-up');
|
||||
});
|
||||
});
|
||||
|
||||
$('#results-expand-btn').click(_expandAllResults);
|
||||
});
|
||||
|
||||
// This checks if the ctarget param exist in the rendered url and opens the
|
||||
// comment tab
|
||||
if( getParam('ctarget') ){
|
||||
var target = '#'+ getParam('ctarget');
|
||||
$(target).find('a.comment-tab-link').click();
|
||||
}
|
||||
}
|
||||
|
||||
function initHandsOnTables(root) {
|
||||
root.find('div.hot-table').each(function() {
|
||||
root.find('div.hot-table').each(function() {
|
||||
var $container = $(this).find('.step-result-hot-table');
|
||||
var contents = $(this).find('.hot-contents');
|
||||
|
||||
|
@ -50,19 +26,19 @@
|
|||
colHeaders: true,
|
||||
fillHandle: false,
|
||||
formulas: true,
|
||||
cells: function (row, col, prop) {
|
||||
cells: function(row, col) {
|
||||
var cellProperties = {};
|
||||
|
||||
if (col >= 0)
|
||||
if (col >= 0) {
|
||||
cellProperties.readOnly = true;
|
||||
else
|
||||
} else {
|
||||
cellProperties.readOnly = false;
|
||||
|
||||
}
|
||||
return cellProperties;
|
||||
}
|
||||
});
|
||||
var hot = $container.handsontable('getInstance');
|
||||
var data = JSON.parse(contents.attr('value'));
|
||||
let hot = $container.handsontable('getInstance');
|
||||
let data = JSON.parse(contents.attr('value'));
|
||||
hot.loadData(data.data);
|
||||
});
|
||||
}
|
||||
|
@ -80,7 +56,7 @@
|
|||
|
||||
// Toggle editing buttons
|
||||
function toggleResultEditButtons(show) {
|
||||
if(show) {
|
||||
if (show) {
|
||||
$('#results-toolbar').show();
|
||||
$('.edit-result-button').show();
|
||||
} else {
|
||||
|
@ -89,117 +65,113 @@
|
|||
}
|
||||
}
|
||||
|
||||
function renderTable(table) {
|
||||
$(table).handsontable('render');
|
||||
// Yet another dirty hack to solve HandsOnTable problems
|
||||
if (parseInt($(table).css('height'), 10) < parseInt($(table).css('max-height'), 10) - 30) {
|
||||
$(table).find('.ht_master .wtHolder').css({ height: '100%', width: '100%' });
|
||||
}
|
||||
}
|
||||
|
||||
// Expand all results
|
||||
function _expandAllResults() {
|
||||
function expandAllResults() {
|
||||
$('.result .panel-collapse').collapse('show');
|
||||
$(document).find('span.collapse-result-icon').each(function() {
|
||||
$(document).find('span.collapse-result-icon').each(function() {
|
||||
$(this).addClass('fa-caret-square-up');
|
||||
$(this).removeClass('fa-caret-square-down');
|
||||
});
|
||||
$(document).find('div.step-result-hot-table').each(function() {
|
||||
_renderTable(this);
|
||||
$(document).find('div.step-result-hot-table').each(function() {
|
||||
renderTable(this);
|
||||
});
|
||||
}
|
||||
|
||||
function expandResult(result) {
|
||||
$('.panel-collapse', result).collapse('show');
|
||||
$(result).find('span.collapse-result-icon').each(function() {
|
||||
$(result).find('span.collapse-result-icon').each(function() {
|
||||
$(this).addClass('fa-caret-square-up');
|
||||
$(this).removeClass('fa-caret-square-down');
|
||||
});
|
||||
_renderTable($(result).find('div.step-result-hot-table'));
|
||||
renderTable($(result).find('div.step-result-hot-table'));
|
||||
animateSpinner(null, false);
|
||||
}
|
||||
|
||||
function _renderTable(table) {
|
||||
$(table).handsontable('render');
|
||||
// Yet another dirty hack to solve HandsOnTable problems
|
||||
if (parseInt($(table).css('height'), 10) <
|
||||
parseInt($(table).css('max-height'), 10) - 30) {
|
||||
$(table).find('.ht_master .wtHolder').css({ 'height': '100%',
|
||||
'width': '100%' });
|
||||
}
|
||||
// create custom ajax request in order to fix issuses with remote: true from
|
||||
function handleResultFileSubmit(form, ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const url = form.find('#result_asset_attributes_file').data('directUploadUrl');
|
||||
const file = form.find('#result_asset_attributes_file')[0].files[0];
|
||||
const upload = new ActiveStorage.DirectUpload(file, url);
|
||||
|
||||
animateSpinner();
|
||||
|
||||
upload.create((error, blob) => {
|
||||
if (error) {
|
||||
// Handle the error
|
||||
} else {
|
||||
let formData = new FormData();
|
||||
formData.append('result[name]', form.find('#result_name').val());
|
||||
formData.append('result[asset_attributes][id]', form.find('#result_asset_attributes_id').val());
|
||||
formData.append('result[asset_attributes][signed_blob_id]', blob.signed_id);
|
||||
|
||||
$.ajax({
|
||||
type: 'PUT',
|
||||
url: form.attr('action'),
|
||||
data: formData,
|
||||
success: function(data) {
|
||||
animateSpinner(null, false);
|
||||
$('.edit_result').parent().remove();
|
||||
$(data.html).prependTo('#results').promise().done(() => {
|
||||
$.each($('#results').find('.result'), function() {
|
||||
initFormSubmitLinks($(this));
|
||||
ResultAssets.applyEditResultAssetCallback();
|
||||
applyCollapseLinkCallBack();
|
||||
applyCreateWopiFileCallback();
|
||||
toggleResultEditButtons(true);
|
||||
FilePreviewModal.init();
|
||||
Comments.init();
|
||||
ResultAssets.initNewResultAsset();
|
||||
expandResult($(this));
|
||||
});
|
||||
});
|
||||
|
||||
$('#results-toolbar').show();
|
||||
},
|
||||
error: function(XHR) {
|
||||
animateSpinner(null, false);
|
||||
$('.edit_result').renderFormErrors('result', XHR.responseJSON.errors);
|
||||
},
|
||||
processData: false,
|
||||
contentType: false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function processResult(ev, resultTypeEnum, editMode) {
|
||||
function processResult(ev, resultTypeEnum) {
|
||||
var $form = $(ev.target.form);
|
||||
$form.clearFormErrors();
|
||||
|
||||
switch(resultTypeEnum) {
|
||||
switch (resultTypeEnum) {
|
||||
case ResultTypeEnum.FILE:
|
||||
_handleResultFileSubmit($form, ev);
|
||||
handleResultFileSubmit($form, ev);
|
||||
break;
|
||||
case ResultTypeEnum.TABLE:
|
||||
var $nameInput = $form.find('#result_name');
|
||||
var nameValid = textValidator(ev, $nameInput, 0,
|
||||
<%= Constants::NAME_MAX_LENGTH %>);
|
||||
textValidator(ev, $form.find('#result_name'), 0, $form.data('name-max-length'));
|
||||
break;
|
||||
case ResultTypeEnum.TEXT:
|
||||
var $nameInput = $form.find('#result_name');
|
||||
var nameValid = textValidator(ev, $nameInput, 0,
|
||||
<%= Constants::NAME_MAX_LENGTH %>);
|
||||
var $descrTextarea = $form.find("#result_text_attributes_textarea");
|
||||
var $tinyMCEInput = TinyMCE.getContent();
|
||||
textValidator(ev, $descrTextarea, 1, <%= Constants::RICH_TEXT_MAX_LENGTH %>, false, $tinyMCEInput);
|
||||
break;
|
||||
case ResultTypeEnum.COMMENT:
|
||||
var $commentInput = $form.find('#comment_message');
|
||||
var commentValid = textValidator(ev, $commentInput, 1,
|
||||
<%= Constants::TEXT_MAX_LENGTH %>);
|
||||
textValidator(ev, $form.find('#result_name'), 0, $form.data('name-max-length'));
|
||||
textValidator(
|
||||
ev, $form.find('#result_text_attributes_textarea'), 1,
|
||||
$form.data('rich-text-max-length'), false, TinyMCE.getContent()
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// create custom ajax request in order to fix issuses with remote: true from
|
||||
function _handleResultFileSubmit(form, ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
animateSpinner();
|
||||
var data = new FormData();
|
||||
var file = document.getElementById('result_asset_attributes_file')
|
||||
.files[0];
|
||||
data.append('result[name]', form.find('#result_name').val());
|
||||
data.append('result[asset_attributes][id]',
|
||||
form.find('#result_asset_attributes_id').val());
|
||||
if( file ) {
|
||||
data.append('result[asset_attributes][file]', file);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'PUT',
|
||||
url: form.attr('action'),
|
||||
data: data,
|
||||
success: function(data) {
|
||||
animateSpinner(null, false);
|
||||
$('.edit_result').parent().remove();
|
||||
$(data.html).prependTo('#results').promise().done(function() {
|
||||
$.each($('#results').find('.result'),
|
||||
function() {
|
||||
initFormSubmitLinks($(this));
|
||||
ResutlAssets.applyEditResultAssetCallback();
|
||||
applyCollapseLinkCallBack();
|
||||
applyCreateWopiFileCallback();
|
||||
toggleResultEditButtons(true);
|
||||
FilePreviewModal.init();
|
||||
Comments.init();
|
||||
ResutlAssets.initNewResultAsset();
|
||||
expandResult($(this));
|
||||
});
|
||||
|
||||
});
|
||||
$('#results-toolbar').show();
|
||||
},
|
||||
error: function(XHR) {
|
||||
animateSpinner(null, false)
|
||||
$('.edit_result').renderFormErrors('result',
|
||||
XHR.responseJSON['errors']);
|
||||
|
||||
},
|
||||
processData: false,
|
||||
contentType: false,
|
||||
});
|
||||
}
|
||||
|
||||
// init cancel button
|
||||
function initCancelFormButton(form, callback) {
|
||||
$(form).find('.cancel-new').click(function(event) {
|
||||
|
@ -217,14 +189,40 @@
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
var el = $(element);
|
||||
if(confirm(el.data('confirm-text'))) {
|
||||
let el = $(element);
|
||||
if (confirm(el.data('confirm-text'))) {
|
||||
animateSpinner();
|
||||
$('#' + el.data('form-id')).submit();
|
||||
}
|
||||
}
|
||||
|
||||
var publicAPI = Object.freeze({
|
||||
function init() {
|
||||
initHandsOnTables($(document));
|
||||
expandAllResults();
|
||||
applyCollapseLinkCallBack();
|
||||
applyCreateWopiFileCallback();
|
||||
|
||||
$(function() {
|
||||
$('#results-collapse-btn').click(function() {
|
||||
$('.result .panel-collapse').collapse('hide');
|
||||
$(document).find('span.collapse-result-icon').each(function() {
|
||||
$(this).addClass('fa-caret-square-down');
|
||||
$(this).removeClass('fa-caret-square-up');
|
||||
});
|
||||
});
|
||||
|
||||
$('#results-expand-btn').click(expandAllResults);
|
||||
});
|
||||
|
||||
// This checks if the ctarget param exist in the rendered url and opens the
|
||||
// comment tab
|
||||
if (getParam('ctarget')) {
|
||||
let target = '#' + getParam('ctarget');
|
||||
$(target).find('a.comment-tab-link').click();
|
||||
}
|
||||
}
|
||||
|
||||
let publicAPI = Object.freeze({
|
||||
init: init,
|
||||
initHandsOnTables: initHandsOnTables,
|
||||
applyCollapseLinkCallBack: applyCollapseLinkCallBack,
|
||||
|
@ -237,7 +235,7 @@
|
|||
});
|
||||
|
||||
return publicAPI;
|
||||
})();
|
||||
}());
|
||||
|
||||
Results.init();
|
||||
})(window);
|
||||
}(window));
|
|
@ -564,10 +564,10 @@
|
|||
tableNamesValid ) {
|
||||
|
||||
$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);
|
||||
let hot = $(this).find(".hot").handsontable('getInstance');
|
||||
let contents = $(this).find('.hot-contents');
|
||||
let tableData = JSON.stringify({tableData: hot.getData()});
|
||||
contents.attr("value", tableData);
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
|
@ -576,55 +576,56 @@
|
|||
}, 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');
|
||||
|
||||
tinyMCE.editors.step_description_textarea.remove();
|
||||
|
||||
//Rerender tables
|
||||
$new_step.find("div.step-result-hot-table").each(function() {
|
||||
$(this).handsontable("render");
|
||||
});
|
||||
animateSpinner(null, false);
|
||||
DragNDropSteps.clearFiles();
|
||||
FilePreviewModal.init();
|
||||
$.initTooltips();
|
||||
if (typeof refreshProtocolStatusBar === 'function') refreshProtocolStatusBar();
|
||||
},
|
||||
error: function(xhr) {
|
||||
if (xhr.responseJSON['assets.file']) {
|
||||
$('#new-step-assets-group').addClass('has-error');
|
||||
$('#new-step-assets-tab').addClass('has-error');
|
||||
$.each(xhr.responseJSON['assets.file'], function(_, value) {
|
||||
$('#new-step-assets-group').prepend('<span class="help-block">' + value + '</span>');
|
||||
DragNDropSteps.appendFilesToForm(ev).then(formData => {
|
||||
// formData.append('step[description]', TinyMCE.getContent());
|
||||
$.ajax({
|
||||
url: $form.attr('action'),
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
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');
|
||||
|
||||
tinyMCE.editors.step_description_textarea.remove();
|
||||
|
||||
//Rerender tables
|
||||
$new_step.find("div.step-result-hot-table").each(function() {
|
||||
$(this).handsontable("render");
|
||||
});
|
||||
animateSpinner(null, false);
|
||||
DragNDropSteps.clearFiles();
|
||||
FilePreviewModal.init();
|
||||
$.initTooltips();
|
||||
if (typeof refreshProtocolStatusBar === 'function') refreshProtocolStatusBar();
|
||||
},
|
||||
error: function(xhr) {
|
||||
if (xhr.responseJSON['assets.file']) {
|
||||
$('#new-step-assets-group').addClass('has-error');
|
||||
$('#new-step-assets-tab').addClass('has-error');
|
||||
$.each(xhr.responseJSON['assets.file'], function(_, value) {
|
||||
$('#new-step-assets-group').prepend('<span class="help-block">' + value + '</span>');
|
||||
});
|
||||
}
|
||||
animateSpinner(null, false);
|
||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||
}
|
||||
animateSpinner(null, false);
|
||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||
}
|
||||
});
|
||||
newStepHandler();
|
||||
});
|
||||
newStepHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* global Promise _ ActiveStorage RepositoryItemEditForm */
|
||||
|
||||
//= require sugar.min
|
||||
//= require jquerymy-1.2.14.min
|
||||
|
||||
|
@ -116,55 +118,78 @@
|
|||
var formData = this.formData;
|
||||
var formDataObj = new FormData();
|
||||
var removeFileColumns = [];
|
||||
var filesToUploadCntr = 0;
|
||||
var filesUploadedCntr = 0;
|
||||
const directUploadUrl = $(tableID).data('directUploadUrl');
|
||||
|
||||
formDataObj.append('request_url', $(tableID).data('current-uri'));
|
||||
formDataObj.append('repository_row_id', $(selectedRecord).attr('id'));
|
||||
|
||||
$(_.keys(this.formData)).each(function(_, element) {
|
||||
var value = formData[element];
|
||||
if (element === "rowName") {
|
||||
formDataObj.append('repository_row_name', value);
|
||||
} else {
|
||||
var colId = element.replace('colId-', '');
|
||||
var $el = $('#' + element);
|
||||
// don't save anything if element is not visible
|
||||
if($el.length == 0) {
|
||||
return true;
|
||||
}
|
||||
if($el.attr('type') === 'file') {
|
||||
// handle deleting of element
|
||||
if($el.attr('remove') === "true") {
|
||||
removeFileColumns.push(colId);
|
||||
formDataObj.append('repository_cells[' + colId + ']', null);
|
||||
} else {
|
||||
formDataObj.append('repository_cells[' + colId + ']',
|
||||
getFileValue($el));
|
||||
return new Promise((resolve, reject) => {
|
||||
$(_.keys(this.formData)).each(function(_, element) {
|
||||
var value = formData[element];
|
||||
if (element === 'rowName') {
|
||||
formDataObj.append('repository_row_name', value);
|
||||
} else {
|
||||
let colId = element.replace('colId-', '');
|
||||
let $el = $('#' + element);
|
||||
// don't save anything if element is not visible
|
||||
if ($el.length === 0) {
|
||||
return;
|
||||
}
|
||||
if ($el.attr('type') === 'file') {
|
||||
// handle deleting of element
|
||||
if ($el.attr('remove') === 'true') {
|
||||
removeFileColumns.push(colId);
|
||||
formDataObj.append('repository_cells[' + colId + ']', null);
|
||||
} else if ($el[0].files.length > 0) {
|
||||
filesToUploadCntr += 1;
|
||||
}
|
||||
} else if (value.length >= 0) {
|
||||
formDataObj.append('repository_cells[' + colId + ']', value);
|
||||
}
|
||||
} else if(value.length >= 0) {
|
||||
formDataObj.append('repository_cells[' + colId + ']', value);
|
||||
}
|
||||
});
|
||||
|
||||
formDataObj.append('remove_file_columns', JSON.stringify(removeFileColumns));
|
||||
|
||||
// No files for upload, so return earlier
|
||||
if (filesToUploadCntr === 0) {
|
||||
resolve(formDataObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Second run, just for files
|
||||
$(_.keys(this.formData)).each(function(_, element) {
|
||||
let $el = $('#' + element);
|
||||
let colId = element.replace('colId-', '');
|
||||
|
||||
if ($el.attr('type') === 'file' && $el.attr('remove') !== 'true') {
|
||||
let upload = new ActiveStorage.DirectUpload($el[0].files[0], directUploadUrl);
|
||||
|
||||
upload.create(function(error, blob) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
formDataObj.append('repository_cells[' + colId + ']', blob.signed_id);
|
||||
filesUploadedCntr += 1;
|
||||
|
||||
if (filesUploadedCntr === filesToUploadCntr) {
|
||||
resolve(formDataObj);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
formDataObj.append('remove_file_columns', JSON.stringify(removeFileColumns));
|
||||
return formDataObj;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* |-----------------|
|
||||
* | Private methods |
|
||||
* |-----------------|
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves the file cell on FormData creation
|
||||
*
|
||||
* @param {Object} elementId
|
||||
*
|
||||
* @returns (String | Object)
|
||||
*/
|
||||
function getFileValue(element) {
|
||||
var file = element[0].files[0];
|
||||
return (file) ? file : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes object and surrounds it with input
|
||||
*
|
||||
|
@ -176,8 +201,8 @@
|
|||
* @returns (String)
|
||||
*/
|
||||
function changeToInputField(object, name, value, id) {
|
||||
return "<div class='form-group'><input class='form-control' data-object='" +
|
||||
object + "' name='" + name + "' value='" + value + "' id='" + id + "'></input></div>";
|
||||
return "<div class='form-group'><input class='form-control' data-object='"
|
||||
+ object + "' name='" + name + "' value='" + value + "' id='" + id + "'></input></div>";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,4 +393,4 @@
|
|||
formData[generateInputFieldReference(columnId)] = undefined;
|
||||
}
|
||||
}
|
||||
})(window);
|
||||
}(window));
|
||||
|
|
|
@ -695,65 +695,13 @@ var RepositoryDatatable = (function(global) {
|
|||
});
|
||||
}
|
||||
|
||||
// Save record
|
||||
global.onClickSave = function() {
|
||||
var node;
|
||||
var rowData;
|
||||
var formData;
|
||||
if (saveAction === 'update') {
|
||||
var row = TABLE.row(selectedRecord);
|
||||
node = row.node();
|
||||
rowData = row.data();
|
||||
formData = SCINOTE_REPOSITORY_EDITED_ROWS[0].parseToFormObject(
|
||||
TABLE_ID, selectedRecord
|
||||
);
|
||||
|
||||
} else if (saveAction === 'create') {
|
||||
node = selectedRecord;
|
||||
|
||||
// First fetch all the data in input fields
|
||||
formData = new FormData();
|
||||
formData.append('request_url', $(TABLE_ID).data('current-uri'));
|
||||
formData.append('repository_row_id', $(selectedRecord).attr('id'));
|
||||
|
||||
// Direct record attributes
|
||||
// Record name
|
||||
formData.append('repository_row_name', $('td input[data-object = repository_row]').val());
|
||||
|
||||
// Custom cells text type
|
||||
$(node).find('td input[data-object = repository_cell]').each(function() {
|
||||
// Send data only and only if cell is not empty
|
||||
if ($(this).val().trim()) {
|
||||
formData.append('repository_cells[' + $(this).attr('name') + ']', $(this).val());
|
||||
}
|
||||
});
|
||||
// Custom cells file type
|
||||
$(node).find('td input[data-object = repository_cell_file]').each(function() {
|
||||
// Send data only and only if cell is not empty
|
||||
if ($(this).context.files.length == 1 ) {
|
||||
if ($(this).data('changed')) {
|
||||
formData.append('repository_cells[' + $(this).attr('name') + ']',
|
||||
$(this).context.files[0]);
|
||||
} else {
|
||||
formData.append('repository_cells[' + $(this).attr('name') + ']', '');
|
||||
}
|
||||
}
|
||||
});
|
||||
// Custom cells list type
|
||||
$(node).find('td[column_id]').each(function(index, el) {
|
||||
var value = $(el).attr('list_item_id');
|
||||
formData.append('repository_cells[' + $(el).attr('column_id') + ']', value);
|
||||
});
|
||||
}
|
||||
|
||||
function submitForm(url, formData) {
|
||||
var url;
|
||||
var type;
|
||||
if (saveAction === 'update') {
|
||||
url = rowData.recordUpdateUrl;
|
||||
type = 'PUT';
|
||||
} else {
|
||||
type = 'POST';
|
||||
url = $('table' + TABLE_ID).data('create-record');
|
||||
}
|
||||
$.ajax({
|
||||
url: url,
|
||||
|
@ -767,8 +715,10 @@ var RepositoryDatatable = (function(global) {
|
|||
SmartAnnotation.closePopup();
|
||||
SCINOTE_REPOSITORY_EDITED_ROWS = [];
|
||||
onClickCancel();
|
||||
animateSpinner(null, false);
|
||||
},
|
||||
error: function(e) {
|
||||
animateSpinner(null, false);
|
||||
SmartAnnotation.closePopup();
|
||||
var data = e.responseJSON;
|
||||
clearAllErrors();
|
||||
|
@ -825,6 +775,92 @@ var RepositoryDatatable = (function(global) {
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildNewFormData() {
|
||||
return new Promise((resolve, reject) => {
|
||||
var node = selectedRecord;
|
||||
var formData = new FormData();
|
||||
var filesToUploadCntr = 0;
|
||||
var filesUploadedCntr = 0;
|
||||
const directUploadUrl = $(TABLE_ID).data('directUploadUrl');
|
||||
|
||||
// First fetch all the data in input fields
|
||||
formData.append('request_url', $(TABLE_ID).data('current-uri'));
|
||||
formData.append('repository_row_id', $(selectedRecord).attr('id'));
|
||||
|
||||
// Direct record attributes
|
||||
// Record name
|
||||
formData.append('repository_row_name', $('td input[data-object = repository_row]').val());
|
||||
|
||||
// Custom cells text type
|
||||
$(node).find('td input[data-object = repository_cell]').each(function() {
|
||||
// Send data only and only if cell is not empty
|
||||
if ($(this).val().trim()) {
|
||||
formData.append('repository_cells[' + $(this).attr('name') + ']', $(this).val());
|
||||
}
|
||||
});
|
||||
|
||||
// Custom cells list type
|
||||
$(node).find('td[column_id]').each(function(index, el) {
|
||||
var value = $(el).attr('list_item_id');
|
||||
formData.append('repository_cells[' + $(el).attr('column_id') + ']', value);
|
||||
});
|
||||
|
||||
// Custom cells file type, first run, count files ready for upload
|
||||
$(node).find('td input[data-object = repository_cell_file]').each(function() {
|
||||
// Send data only and only if cell is not empty
|
||||
if ($(this)[0].files.length == 1 ) {
|
||||
filesToUploadCntr += 1;
|
||||
}
|
||||
});
|
||||
|
||||
// No files for upload, so return earlier
|
||||
if (filesToUploadCntr === 0) {
|
||||
resolve(formData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Custom cells file type, second run, upload files
|
||||
$(node).find('td input[data-object = repository_cell_file]').each(function() {
|
||||
// Send data only and only if cell is not empty
|
||||
if ($(this)[0].files.length == 1 ) {
|
||||
let upload = new ActiveStorage.DirectUpload($(this)[0].files[0], directUploadUrl);
|
||||
let colId = $(this).attr('name');
|
||||
|
||||
upload.create(function(error, blob) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
formData.append('repository_cells[' + colId + ']', blob.signed_id);
|
||||
filesUploadedCntr += 1;
|
||||
|
||||
if (filesUploadedCntr === filesToUploadCntr) {
|
||||
resolve(formData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Save record
|
||||
global.onClickSave = function() {
|
||||
animateSpinner(null, true);
|
||||
if (saveAction === 'update') {
|
||||
let row = TABLE.row(selectedRecord);
|
||||
let rowData = row.data();
|
||||
SCINOTE_REPOSITORY_EDITED_ROWS[0].parseToFormObject(
|
||||
TABLE_ID, selectedRecord
|
||||
).then(formData => {
|
||||
submitForm(rowData.recordUpdateUrl, formData);
|
||||
});
|
||||
} else if (saveAction === 'create') {
|
||||
buildNewFormData().then(formData => {
|
||||
submitForm($('table' + TABLE_ID).data('create-record'), formData);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Delete record
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
global.ResutlAssets = (function() {
|
||||
global.ResultAssets = (function() {
|
||||
// New result asset behaviour
|
||||
function initNewResultAsset() {
|
||||
$('#new-result-asset').on('click', function(event) {
|
||||
|
@ -96,7 +96,7 @@
|
|||
return publicAPI;
|
||||
})();
|
||||
|
||||
ResutlAssets.initNewResultAsset();
|
||||
ResutlAssets.applyEditResultAssetCallback();
|
||||
ResultAssets.initNewResultAsset();
|
||||
ResultAssets.applyEditResultAssetCallback();
|
||||
FilePreviewModal.init();
|
||||
}(window));
|
||||
|
|
630
app/assets/javascripts/sitewide/drag_n_drop.js
Normal file
630
app/assets/javascripts/sitewide/drag_n_drop.js
Normal file
|
@ -0,0 +1,630 @@
|
|||
/* global Promise ActiveStorage animateSpinner copyFromClipboard I18n
|
||||
Results ResultAssets FilePreviewModal Comments truncateLongString
|
||||
DragNDropSteps DragNDropResults initFormSubmitLinks dragNdropAssetsInit */
|
||||
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
// Copy from clipboard
|
||||
global.copyFromClipboard = (function() {
|
||||
var UPLOADED_IMAGE = {};
|
||||
var LOCATION = '';
|
||||
|
||||
function retrieveImageFromClipboardAsBlob(pasteEvent, callback) {
|
||||
if (pasteEvent.clipboardData === false) {
|
||||
if ((typeof callback) === 'function') {
|
||||
callback(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
let items = pasteEvent.clipboardData.items;
|
||||
if (items === undefined) {
|
||||
if ((typeof callback) === 'function') {
|
||||
callback(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i += 1) {
|
||||
if (items[i].type.indexOf('image') !== -1) {
|
||||
let blob = items[i].getAsFile();
|
||||
|
||||
if ((typeof callback) === 'function') {
|
||||
callback(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// $(..).modal('hide') don't work properly so here we manually remove the
|
||||
// displayed modal
|
||||
function hideModalForGood() {
|
||||
$('#clipboardPreviewModal').removeClass('in');
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open');
|
||||
$('body').css('padding-right', '');
|
||||
$('#clipboardPreviewModal').hide();
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
hideModalForGood();
|
||||
$('#clipboardPreviewModal').remove();
|
||||
}
|
||||
|
||||
function addImageCallback() {
|
||||
$('[data-action="addImageFormClipboard"]').on('click', function() {
|
||||
let inputArray = [];
|
||||
let newName = $('#clipboardImageName').val();
|
||||
// check if the name is set
|
||||
if (newName && newName.length > 0) {
|
||||
let extension = UPLOADED_IMAGE.name.slice(
|
||||
(Math.max(0, UPLOADED_IMAGE.name.lastIndexOf('.')) || Infinity) + 1
|
||||
);
|
||||
// hack to inject custom name in File object
|
||||
let name = newName + '.' + extension;
|
||||
let blob = UPLOADED_IMAGE.slice(0, UPLOADED_IMAGE.size, UPLOADED_IMAGE.type);
|
||||
// make new blob with the correct name;
|
||||
let newFile = new File([blob], name, { type: UPLOADED_IMAGE.type });
|
||||
inputArray.push(newFile);
|
||||
} else { // return the default name
|
||||
inputArray.push(UPLOADED_IMAGE);
|
||||
}
|
||||
|
||||
// close modal
|
||||
closeModal();
|
||||
// reuse file upload from drag'n drop :)
|
||||
if (LOCATION === 'steps') {
|
||||
DragNDropSteps.init(inputArray);
|
||||
} else {
|
||||
DragNDropResults.init(inputArray);
|
||||
}
|
||||
// clear all uploaded images
|
||||
UPLOADED_IMAGE = {};
|
||||
});
|
||||
}
|
||||
|
||||
// removes modal from dom
|
||||
function destroyModalCallback() {
|
||||
let modal = $('#clipboardPreviewModal');
|
||||
modal.on('hidden.bs.modal', function() {
|
||||
modal.modal('hide').promise().done(function() {
|
||||
modal.remove();
|
||||
});
|
||||
UPLOADED_IMAGE = {};
|
||||
});
|
||||
}
|
||||
|
||||
// Generate modal html and hook callbacks
|
||||
function clipboardPasteModal() {
|
||||
var html = '<div id="clipboardPreviewModal" class="modal fade" ';
|
||||
html += 'tabindex="-1" role="dialog" aria-hidden="true">';
|
||||
html += '<div class="modal-dialog" role="document">';
|
||||
html += '<div class="modal-content"><div class="modal-header">';
|
||||
html += '<button type="button" class="close" data-dismiss="modal"';
|
||||
html += ' aria-label="Close"><span aria-hidden="true">×</span>';
|
||||
html += '</button><h4 class="modal-title">' + I18n.t('assets.from_clipboard.modal_title') + '</h4>';
|
||||
html += '</div><div class="modal-body"><p><strong>' + I18n.t('assets.from_clipboard.image_preview') + '</strong></p>';
|
||||
html += '<canvas style="border:1px solid grey;max-width:400px;max-height:300px" id="clipboardPreview" />';
|
||||
html += '<p><strong>' + I18n.t('assets.from_clipboard.file_name') + '</strong></p>';
|
||||
html += '<div class="input-group">';
|
||||
html += '<input id="clipboardImageName" type="text" class="form-control" ';
|
||||
html += 'placeholder="' + I18n.t('assets.from_clipboard.file_name_placeholder') + '" aria-describedby="image-name">';
|
||||
html += '<span class="input-group-addon" id="image-name"></span></div>';
|
||||
html += '</div><div class="modal-footer">';
|
||||
html += '<button type="button" class="btn btn-default" data-dismiss="modal">' + I18n.t('general.cancel') + '</button>';
|
||||
html += '<button type="button" class="btn btn-success" data-action="addImageFormClipboard">' + I18n.t('assets.from_clipboard.add_image') + '</button>';
|
||||
html += '</div></div></div></div><!-- /.modal -->';
|
||||
return $(html).appendTo($('body')).promise().done(function() {
|
||||
// display modal
|
||||
$('#clipboardPreviewModal').modal('show');
|
||||
// add callback to remove modal from DOM
|
||||
destroyModalCallback();
|
||||
// add callback on image submit
|
||||
addImageCallback();
|
||||
});
|
||||
}
|
||||
|
||||
function listener(pasteEvent) {
|
||||
retrieveImageFromClipboardAsBlob(pasteEvent, function(imageBlob) {
|
||||
if (imageBlob) {
|
||||
clipboardPasteModal().promise().done(function() {
|
||||
var canvas = document.getElementById('clipboardPreview');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
};
|
||||
let URLObj = window.URL || window.webkitURL;
|
||||
img.src = URLObj.createObjectURL(imageBlob);
|
||||
let extension = imageBlob.name.slice(
|
||||
(Math.max(0, imageBlob.name.lastIndexOf('.')) || Infinity) + 1
|
||||
);
|
||||
$('#image-name').html('.' + extension); // add extension near file name
|
||||
// temporary store image blob
|
||||
UPLOADED_IMAGE = imageBlob;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function init(location) {
|
||||
LOCATION = location;
|
||||
global.addEventListener('paste', listener, false);
|
||||
$.initTooltips();
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
global.removeEventListener('paste', listener, false);
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
init: init,
|
||||
destroy: destroy
|
||||
});
|
||||
}());
|
||||
|
||||
// Module to handle file uploading in Steps
|
||||
global.DragNDropSteps = (function() {
|
||||
var droppedFiles = [];
|
||||
var filesValid = true;
|
||||
var totalSize = 0;
|
||||
var fileMaxSizeMb;
|
||||
var fileMaxSize;
|
||||
var uploadedFilesCounter = 0;
|
||||
|
||||
// return the status of files if they are ready to submit
|
||||
function filesStatus() {
|
||||
return filesValid;
|
||||
}
|
||||
|
||||
function clearFiles() {
|
||||
droppedFiles = [];
|
||||
}
|
||||
|
||||
function incrementUploadedFilesCounter() {
|
||||
uploadedFilesCounter += 1;
|
||||
}
|
||||
|
||||
function getUploadedFilesCounter() {
|
||||
return uploadedFilesCounter;
|
||||
}
|
||||
|
||||
function dragNdropAssetsOff() {
|
||||
$('body').off('drag dragstart dragend dragover dragenter dragleave drop');
|
||||
$('.is-dragover').hide();
|
||||
// remove listeners for clipboard images
|
||||
copyFromClipboard.destroy();
|
||||
}
|
||||
|
||||
// append the files to the form before submit
|
||||
function appendFilesToForm(ev) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const form = $(ev.target).closest('form').get(0);
|
||||
const url = $(form).find('#drag-n-drop-assets').data('directUploadUrl');
|
||||
const regex = /step\[assets_attributes\]\[[0-9]*\]\[id\]/;
|
||||
const numberOfFiles = droppedFiles.length;
|
||||
let prevEls = $('input').filter(function() {
|
||||
return this.name.match(regex);
|
||||
});
|
||||
|
||||
let fd = new FormData(form);
|
||||
|
||||
uploadedFilesCounter = 0;
|
||||
fd.delete('step[file][]');
|
||||
|
||||
if (droppedFiles.length === 0) {
|
||||
resolve(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < droppedFiles.length; i += 1) {
|
||||
let upload = new ActiveStorage.DirectUpload(droppedFiles[i], url);
|
||||
let index = i + prevEls.length;
|
||||
|
||||
upload.create(function(error, blob) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
fd.append('step[assets_attributes][' + index + '][signed_blob_id]', blob.signed_id);
|
||||
incrementUploadedFilesCounter();
|
||||
if (getUploadedFilesCounter() === numberOfFiles) {
|
||||
resolve(fd);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filesValid = true;
|
||||
totalSize = 0;
|
||||
dragNdropAssetsOff();
|
||||
});
|
||||
}
|
||||
|
||||
function disableSubmitButton() {
|
||||
$('.step-save').prop('disabled', true);
|
||||
}
|
||||
|
||||
function enableSubmitButton() {
|
||||
$('.step-save').prop('disabled', false);
|
||||
}
|
||||
|
||||
function filerAndCheckFiles() {
|
||||
for (let i = 0; i < droppedFiles.length; i += 1) {
|
||||
if (droppedFiles[i].isValid === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (droppedFiles.length > 0);
|
||||
}
|
||||
|
||||
function validateFilesSize(file) {
|
||||
var fileSize = file.size;
|
||||
totalSize += parseInt(fileSize, 10);
|
||||
if (fileSize > fileMaxSize) {
|
||||
file.isValid = false;
|
||||
disableSubmitButton();
|
||||
return "<p class='dnd-error'>" + I18n.t('general.file.size_exceeded', { file_size: fileMaxSizeMb }) + '</p>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function validateTotalSize() {
|
||||
if (totalSize > fileMaxSize) {
|
||||
filesValid = false;
|
||||
disableSubmitButton();
|
||||
$.each($('.panel-step-attachment-new'), function() {
|
||||
if (!$(this).find('p').hasClass('dnd-total-error')) {
|
||||
$(this)
|
||||
.find('.panel-body')
|
||||
.append("<p class='dnd-total-error'>" + I18n.t('general.file.total_size', { size: fileMaxSizeMb }) + '</p>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('.dnd-total-error').remove();
|
||||
if (filerAndCheckFiles()) {
|
||||
filesValid = true;
|
||||
enableSubmitButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uploadedAssetPreview(asset, i) {
|
||||
var html = '<div class="attachment-placeholder pull-left new">';
|
||||
html += '<div class="attachment-thumbnail no-shadow new %>">';
|
||||
html += '<i class="fas fa-image"></i>';
|
||||
html += '</div>';
|
||||
html += '<div class="attachment-label">';
|
||||
html += truncateLongString(asset.name, $(document.body).data('filename-max-length'));
|
||||
html += '</div>';
|
||||
html += '<div class="spencer-bonnet-modif">';
|
||||
html += '</div>';
|
||||
html += '<div class="remove-icon pull-right">';
|
||||
html += '<a data-item-id="' + i + '" href="#">';
|
||||
html += '<span class="fas fa-trash"></span>';
|
||||
html += '</a> </div>';
|
||||
html += validateFilesSize(asset);
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function removeItemHandler(id, callback) {
|
||||
$('[data-item-id="' + id + '"]').off('click').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
e.stopPropagation();
|
||||
let $el = $(this);
|
||||
let index = $el.data('item-id');
|
||||
totalSize -= parseInt(droppedFiles[index].size, 10);
|
||||
droppedFiles.splice(index, 1);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// loops through a list of files and display each file in a separate panel
|
||||
function listItems() {
|
||||
totalSize = 0;
|
||||
enableSubmitButton();
|
||||
$('.attachment-placeholder.new').remove();
|
||||
dragNdropAssetsOff();
|
||||
|
||||
for (let i = 0; i < droppedFiles.length; i += 1) {
|
||||
$('.attachments.edit')
|
||||
.append(uploadedAssetPreview(droppedFiles[i], i))
|
||||
.promise()
|
||||
.done(function() {
|
||||
removeItemHandler(i, listItems);
|
||||
});
|
||||
}
|
||||
validateTotalSize();
|
||||
dragNdropAssetsInit('steps');
|
||||
}
|
||||
|
||||
function init(files) {
|
||||
fileMaxSizeMb = $(document.body).data('file-max-size-mb');
|
||||
fileMaxSize = fileMaxSizeMb * 1024 * 1024;
|
||||
for (let i = 0; i < files.length; i += 1) {
|
||||
droppedFiles.push(files[i]);
|
||||
}
|
||||
listItems();
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
init: init,
|
||||
appendFilesToForm: appendFilesToForm,
|
||||
listItems: listItems,
|
||||
filesStatus: filesStatus,
|
||||
clearFiles: clearFiles
|
||||
});
|
||||
}());
|
||||
|
||||
// Module to handle file uploading in Results
|
||||
global.DragNDropResults = (function() {
|
||||
var droppedFiles = [];
|
||||
var isValid = true;
|
||||
var totalSize = 0;
|
||||
var fileMaxSizeMb;
|
||||
var fileMaxSize;
|
||||
|
||||
function disableSubmitButton() {
|
||||
$('.save-result').prop('disabled', true);
|
||||
}
|
||||
|
||||
function enableSubmitButton() {
|
||||
$('.save-result').prop('disabled', false);
|
||||
}
|
||||
|
||||
function filerAndCheckFiles() {
|
||||
for (let i = 0; i < droppedFiles.length; i += 1) {
|
||||
if (droppedFiles[i].isValid === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (droppedFiles.length > 0);
|
||||
}
|
||||
|
||||
function dragNdropAssetsOff() {
|
||||
$('body').off('drag dragstart dragend dragover dragenter dragleave drop');
|
||||
$('.is-dragover').hide();
|
||||
}
|
||||
|
||||
function destroyAll() {
|
||||
dragNdropAssetsOff();
|
||||
droppedFiles = [];
|
||||
isValid = true;
|
||||
totalSize = 0;
|
||||
}
|
||||
|
||||
// return the status of files if they are ready to submit
|
||||
function filesStatus() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function validateTotalSize() {
|
||||
if (totalSize > fileMaxSize) {
|
||||
isValid = false;
|
||||
disableSubmitButton();
|
||||
$.each($('.panel-result-attachment-new'), function() {
|
||||
if (!$(this).find('p').hasClass('dnd-total-error')) {
|
||||
$(this)
|
||||
.find('.panel-body')
|
||||
.append("<p class='dnd-total-error'>" + I18n.t('general.file.total_size', { size: fileMaxSizeMb }) + '</p>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('.dnd-total-error').remove();
|
||||
if (filerAndCheckFiles()) {
|
||||
isValid = true;
|
||||
enableSubmitButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function submitResultForm(url, formData) {
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
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));
|
||||
ResultAssets.applyEditResultAssetCallback();
|
||||
Results.applyCollapseLinkCallBack();
|
||||
Results.toggleResultEditButtons(true);
|
||||
FilePreviewModal.init();
|
||||
Comments.init();
|
||||
ResultAssets.initNewResultAsset();
|
||||
Results.expandResult($(this));
|
||||
});
|
||||
});
|
||||
$('#results-toolbar').show();
|
||||
},
|
||||
error: function() {
|
||||
animateSpinner();
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// appent the files to the form before submit
|
||||
function appendFilesToForm(ev, fd) {
|
||||
const form = $(ev.target.form);
|
||||
const url = form.find('#drag-n-drop-assets').data('directUploadUrl');
|
||||
const numberOfFiles = droppedFiles.length;
|
||||
|
||||
let resultNames = [];
|
||||
|
||||
$.each($('input[rel="results[name]"]'), function() {
|
||||
resultNames.push($(this).val());
|
||||
});
|
||||
|
||||
resultNames.reverse();
|
||||
|
||||
for (let i = 0; i < numberOfFiles; i += 1) {
|
||||
let upload = new ActiveStorage.DirectUpload(droppedFiles[i], url);
|
||||
|
||||
upload.create(function(error, blob) {
|
||||
if (error) {
|
||||
// Handle the error
|
||||
} else {
|
||||
fd.append('results_names[' + i + ']', resultNames[i]);
|
||||
fd.append('results_files[' + i + '][signed_blob_id]', blob.signed_id);
|
||||
if ((i + 1) === numberOfFiles) {
|
||||
submitResultForm($(ev.target).attr('data-href'), fd);
|
||||
destroyAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
||||
function validateFilesSize(file) {
|
||||
var fileSize = file.size;
|
||||
totalSize += parseInt(fileSize, 10);
|
||||
if (fileSize > fileMaxSize) {
|
||||
file.isValid = false;
|
||||
disableSubmitButton();
|
||||
return "<p class='dnd-error'>" + I18n.t('general.file.size_exceeded', { file_size: fileMaxSizeMb }) + '</p>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function validateTextSize(input) {
|
||||
if (input.value.length > $(document.body).data('name-max-length')) {
|
||||
$(input).parent().find('.dnd-error').remove();
|
||||
$(input).after("<p class='dnd-error'>" + I18n.t('general.text.length_too_long', { max_length: $(document.body).data('name-max-length') }) + '</p>');
|
||||
isValid = false;
|
||||
} else {
|
||||
$(input).parent().find('.dnd-error').remove();
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
function uploadedAssetPreview(asset, i) {
|
||||
var html = '<div class="panel panel-default panel-result-attachment-new">';
|
||||
html += '<div class="panel-heading">';
|
||||
html += '<span class="fas fa-paperclip"></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="fas fa-times"></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, $(document.body).data('filename-max-length'));
|
||||
html += validateFilesSize(asset);
|
||||
html += '</div></div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function processResult(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (isValid && filerAndCheckFiles()) {
|
||||
animateSpinner();
|
||||
|
||||
let formData = new FormData();
|
||||
|
||||
appendFilesToForm(ev, formData);
|
||||
}
|
||||
}
|
||||
|
||||
function removeItemHandler(id, callback) {
|
||||
$('[data-item-id="' + id + '"]').off('click').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
e.stopPropagation();
|
||||
let $el = $(this);
|
||||
let index = $el.data('item-id');
|
||||
totalSize -= parseInt(droppedFiles[index].size, 10);
|
||||
droppedFiles.splice(index, 1);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// loops through a list of files and display each file in a separate panel
|
||||
function listItems() {
|
||||
totalSize = 0;
|
||||
$('.panel-result-attachment-new').remove();
|
||||
if (droppedFiles.length < 1) {
|
||||
disableSubmitButton();
|
||||
} else {
|
||||
dragNdropAssetsOff();
|
||||
|
||||
for (let i = 0; i < droppedFiles.length; i += 1) {
|
||||
$('#new-result-assets-select')
|
||||
.after(uploadedAssetPreview(droppedFiles[i], i))
|
||||
.promise()
|
||||
.done(function() {
|
||||
removeItemHandler(i, listItems);
|
||||
});
|
||||
}
|
||||
validateTotalSize();
|
||||
dragNdropAssetsInit('results');
|
||||
}
|
||||
}
|
||||
|
||||
function init(files) {
|
||||
fileMaxSizeMb = $(document.body).data('file-max-size-mb');
|
||||
fileMaxSize = fileMaxSizeMb * 1024 * 1024;
|
||||
for (let i = 0; i < files.length; i += 1) {
|
||||
droppedFiles.push(files[i]);
|
||||
}
|
||||
listItems();
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
init: init,
|
||||
listItems: listItems,
|
||||
destroyAll: destroyAll,
|
||||
filesStatus: filesStatus,
|
||||
validateTextSize: validateTextSize,
|
||||
processResult: processResult
|
||||
});
|
||||
}());
|
||||
|
||||
global.dragNdropAssetsInit = function(location) {
|
||||
var inWindow = true;
|
||||
|
||||
$('body')
|
||||
.on('drag dragstart dragend dragover dragenter dragleave drop', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
})
|
||||
.on('dragover', function() {
|
||||
inWindow = true;
|
||||
$('.is-dragover').show();
|
||||
})
|
||||
.on('dragleave', function() {
|
||||
inWindow = false;
|
||||
setTimeout(function() {
|
||||
if (!inWindow) {
|
||||
$('.is-dragover').hide();
|
||||
}
|
||||
}, 5000);
|
||||
})
|
||||
.on('drop', function(e) {
|
||||
$('.is-dragover').hide();
|
||||
let files = e.originalEvent.dataTransfer.files;
|
||||
if (location === 'steps') {
|
||||
DragNDropSteps.init(files);
|
||||
} else {
|
||||
DragNDropResults.init(files);
|
||||
}
|
||||
});
|
||||
|
||||
copyFromClipboard.init(location);
|
||||
};
|
||||
}(window));
|
|
@ -1,568 +0,0 @@
|
|||
(function(global) {
|
||||
'use strict';
|
||||
// Copy from clipboard
|
||||
global.copyFromClipboard = (function() {
|
||||
var UPLOADED_IMAGE = {};
|
||||
var LOCATION = '';
|
||||
function init(location) {
|
||||
LOCATION = location;
|
||||
global.addEventListener('paste', _listener, false);
|
||||
$.initTooltips();
|
||||
};
|
||||
|
||||
function destroy() {
|
||||
global.removeEventListener('paste', _listener, false);
|
||||
}
|
||||
|
||||
function _listener(pasteEvent) {
|
||||
_retrieveImageFromClipboardAsBlob(pasteEvent, function(imageBlob) {
|
||||
if(imageBlob){
|
||||
_clipboardPasteModal().promise().done(function() {
|
||||
var canvas = document.getElementById('clipboardPreview');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
};
|
||||
var URLObj = window.URL || window.webkitURL;
|
||||
img.src = URLObj.createObjectURL(imageBlob);
|
||||
var extension = imageBlob.name.slice(
|
||||
(Math.max(0, imageBlob.name.lastIndexOf(".")) || Infinity) + 1
|
||||
);
|
||||
$('#image-name').html('.' + extension); // add extension near file name
|
||||
// temporary store image blob
|
||||
UPLOADED_IMAGE = imageBlob
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _retrieveImageFromClipboardAsBlob(pasteEvent, callback){
|
||||
if(pasteEvent.clipboardData == false) {
|
||||
if(typeof(callback) == "function"){
|
||||
callback(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
var items = pasteEvent.clipboardData.items;
|
||||
if(items == undefined){
|
||||
if(typeof(callback) == "function"){
|
||||
callback(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].type.indexOf("image") == -1) continue;
|
||||
var blob = items[i].getAsFile();
|
||||
|
||||
if(typeof(callback) == "function") {
|
||||
callback(blob);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// removes modal from dom
|
||||
function _destroyModalCallback() {
|
||||
var modal = $('#clipboardPreviewModal');
|
||||
modal.on('hidden.bs.modal', function() {
|
||||
modal.modal('hide').promise().done(function() {
|
||||
modal.remove();
|
||||
});
|
||||
UPLOADED_IMAGE = {};
|
||||
});
|
||||
}
|
||||
|
||||
function _closeModal() {
|
||||
_hideModalForGood();
|
||||
$("#clipboardPreviewModal").remove();
|
||||
}
|
||||
|
||||
// $(..).modal('hide') don't work properly so here we manually remove the
|
||||
// displayed modal
|
||||
function _hideModalForGood(){
|
||||
$("#clipboardPreviewModal").removeClass("in");
|
||||
$(".modal-backdrop").remove();
|
||||
$('body').removeClass('modal-open');
|
||||
$('body').css('padding-right', '');
|
||||
$("#clipboardPreviewModal").hide();
|
||||
}
|
||||
|
||||
function _addImageCallback() {
|
||||
$('[data-action="addImageFormClipboard"]').on('click', function() {
|
||||
var inputArray = [];
|
||||
var newName = $('#clipboardImageName').val();
|
||||
// check if the name is set
|
||||
if( newName && newName.length > 0 ) {
|
||||
var extension = UPLOADED_IMAGE.name.slice(
|
||||
(Math.max(0, UPLOADED_IMAGE.name.lastIndexOf(".")) || Infinity) + 1
|
||||
);
|
||||
// hack to inject custom name in File object
|
||||
var name = newName + '.' + extension;
|
||||
var blob = UPLOADED_IMAGE.slice(0, UPLOADED_IMAGE.size, UPLOADED_IMAGE.type);
|
||||
// make new blob with the correct name;
|
||||
var newFile = new File([blob], name, { type: UPLOADED_IMAGE.type });
|
||||
inputArray.push(newFile);
|
||||
} else { // return the default name
|
||||
inputArray.push(UPLOADED_IMAGE);
|
||||
}
|
||||
|
||||
// close modal
|
||||
_closeModal();
|
||||
// reuse file upload from drag'n drop :)
|
||||
if(LOCATION === 'steps') {
|
||||
DragNDropSteps.init(inputArray);
|
||||
} else {
|
||||
DragNDropResults.init(inputArray);
|
||||
}
|
||||
// clear all uploaded images
|
||||
UPLOADED_IMAGE = {};
|
||||
});
|
||||
}
|
||||
|
||||
// Generate modal html and hook callbacks
|
||||
function _clipboardPasteModal() {
|
||||
var html = '<div id="clipboardPreviewModal" class="modal fade" ';
|
||||
html += 'tabindex="-1" role="dialog" aria-hidden="true">';
|
||||
html += '<div class="modal-dialog" role="document">';
|
||||
html += '<div class="modal-content"><div class="modal-header">';
|
||||
html += '<button type="button" class="close" data-dismiss="modal"';
|
||||
html += ' aria-label="Close"><span aria-hidden="true">×</span>';
|
||||
html += '</button><h4 class="modal-title"><%= I18n.t('assets.from_clipboard.modal_title') %></h4>';
|
||||
html += '</div><div class="modal-body"><p><strong><%= I18n.t('assets.from_clipboard.image_preview') %></strong></p>';
|
||||
html += '<canvas style="border:1px solid grey;max-width:400px;max-height:300px" id="clipboardPreview" />';
|
||||
html += '<p><strong><%= I18n.t('assets.from_clipboard.file_name') %></strong></p>';
|
||||
html += '<div class="input-group">';
|
||||
html += '<input id="clipboardImageName" type="text" class="form-control" ';
|
||||
html += 'placeholder="<%= I18n.t('assets.from_clipboard.file_name_placeholder') %>" aria-describedby="image-name">';
|
||||
html += '<span class="input-group-addon" id="image-name"></span></div>';
|
||||
html += '</div><div class="modal-footer">';
|
||||
html += '<button type="button" class="btn btn-default" data-dismiss="modal"><%= I18n.t('general.cancel') %></button>';
|
||||
html += '<button type="button" class="btn btn-success" data-action="addImageFormClipboard"><%= I18n.t('assets.from_clipboard.add_image') %></button>';
|
||||
html += '</div></div></div></div><!-- /.modal -->';
|
||||
return $(html).appendTo($('body')).promise().done(function() {
|
||||
// display modal
|
||||
$('#clipboardPreviewModal').modal('show');
|
||||
// add callback to remove modal from DOM
|
||||
_destroyModalCallback();
|
||||
// add callback on image submit
|
||||
_addImageCallback();
|
||||
});
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
init: init,
|
||||
destroy: destroy
|
||||
});
|
||||
})();
|
||||
|
||||
// Module to handle file uploading in Steps
|
||||
global.DragNDropSteps = (function() {
|
||||
var droppedFiles = [];
|
||||
var filesValid = true;
|
||||
var totalSize = 0;
|
||||
var fileMaxSizeMb;
|
||||
var fileMaxSize;
|
||||
|
||||
function init(files) {
|
||||
fileMaxSizeMb = $(document.body).data('file-max-size-mb');
|
||||
fileMaxSize = fileMaxSizeMb * 1024 * 1024;
|
||||
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;
|
||||
}
|
||||
|
||||
function clearFiles() {
|
||||
droppedFiles = [];
|
||||
}
|
||||
|
||||
// loops through a list of files and display each file in a separate panel
|
||||
function listItems() {
|
||||
totalSize = 0;
|
||||
_enableSubmitButton();
|
||||
$('.attachment-placeholder.new').remove();
|
||||
_dragNdropAssetsOff();
|
||||
for(var i = 0; i < droppedFiles.length; i++) {
|
||||
$('.attachments.edit')
|
||||
.append(_uploadedAssetPreview(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);
|
||||
});
|
||||
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]);
|
||||
}
|
||||
|
||||
filesValid = true;
|
||||
totalSize = 0;
|
||||
_dragNdropAssetsOff();
|
||||
return fd;
|
||||
}
|
||||
|
||||
function _disableSubmitButton() {
|
||||
$('.step-save').prop('disabled', true);
|
||||
}
|
||||
|
||||
function _enableSubmitButton() {
|
||||
$('.step-save').prop('disabled', false);
|
||||
}
|
||||
|
||||
function _filerAndCheckFiles() {
|
||||
for(var i = 0; i < droppedFiles.length; i++) {
|
||||
if(droppedFiles[i].isValid == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (droppedFiles.length > 0);
|
||||
}
|
||||
|
||||
function _validateFilesSize(file) {
|
||||
var fileSize = file.size;
|
||||
totalSize += parseInt(fileSize);
|
||||
if(fileSize > fileMaxSize) {
|
||||
file.isValid = false;
|
||||
_disableSubmitButton();
|
||||
return "<p class='dnd-error'>" + I18n.t('general.file.size_exceeded', { file_size: fileMaxSizeMb }) + '</p>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function _validateTotalSize() {
|
||||
if(totalSize > fileMaxSize) {
|
||||
filesValid = false;
|
||||
_disableSubmitButton();
|
||||
$.each($('.panel-step-attachment-new'), function() {
|
||||
if(!$(this).find('p').hasClass('dnd-total-error')) {
|
||||
$(this)
|
||||
.find('.panel-body')
|
||||
.append("<p class='dnd-total-error'>" + I18n.t('general.file.total_size', { size: fileMaxSizeMb }) + '</p>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('.dnd-total-error').remove();
|
||||
if(_filerAndCheckFiles()) {
|
||||
filesValid = true;
|
||||
_enableSubmitButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _uploadedAssetPreview(asset, i) {
|
||||
var html = '<div class="attachment-placeholder pull-left new">';
|
||||
html +='<div class="attachment-thumbnail no-shadow new %>">';
|
||||
html +='<i class="fas fa-image"></i>';
|
||||
html +='</div>';
|
||||
html +='<div class="attachment-label">' + truncateLongString(asset.name, <%= Constants::FILENAME_TRUNCATION_LENGTH %>);
|
||||
html += '</div>';
|
||||
html +='<div class="spencer-bonnet-modif">';
|
||||
html +='</div>';
|
||||
html +='<div class="remove-icon pull-right">';
|
||||
html +='<a data-item-id="' + i + '" href="#">';
|
||||
html +='<span class="fas fa-trash"></span>';
|
||||
html +='</a> </div>';
|
||||
html += _validateFilesSize(asset);
|
||||
html +='</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].size);
|
||||
droppedFiles.splice(index, 1);
|
||||
listItems();
|
||||
});
|
||||
}
|
||||
|
||||
function _dragNdropAssetsOff() {
|
||||
$('body').off('drag dragstart dragend dragover dragenter dragleave drop');
|
||||
$('.is-dragover').hide();
|
||||
// remove listeners for clipboard images
|
||||
copyFromClipboard.destroy();
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
init: init,
|
||||
appendFilesToForm: appendFilesToForm,
|
||||
listItems: listItems,
|
||||
filesStatus: filesStatus,
|
||||
clearFiles: clearFiles
|
||||
});
|
||||
})();
|
||||
|
||||
// Module to handle file uploading in Results
|
||||
global.DragNDropResults = (function() {
|
||||
var droppedFiles = [];
|
||||
var isValid = true;
|
||||
var totalSize = 0;
|
||||
var fileMaxSizeMb;
|
||||
var fileMaxSize;
|
||||
|
||||
function init(files) {
|
||||
fileMaxSizeMb = $(document.body).data('file-max-size-mb');
|
||||
fileMaxSize = fileMaxSizeMb * 1024 * 1024;
|
||||
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 isValid;
|
||||
}
|
||||
|
||||
// loops through a list of files and display each file in a separate panel
|
||||
function listItems() {
|
||||
totalSize = 0;
|
||||
$('.panel-result-attachment-new').remove();
|
||||
if(droppedFiles.length < 1) {
|
||||
_disableSubmitButton();
|
||||
} else {
|
||||
_dragNdropAssetsOff();
|
||||
for(var i = 0; i < droppedFiles.length; i++) {
|
||||
$('#new-result-assets-select')
|
||||
.after(_uploadedAssetPreview(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 _disableSubmitButton() {
|
||||
$('.save-result').prop('disabled', true);
|
||||
}
|
||||
|
||||
function _enableSubmitButton() {
|
||||
$('.save-result').prop('disabled', false);
|
||||
}
|
||||
|
||||
function _filerAndCheckFiles() {
|
||||
for(var i = 0; i < droppedFiles.length; i++) {
|
||||
if(droppedFiles[i].isValid == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (droppedFiles.length > 0);
|
||||
}
|
||||
|
||||
function _validateFilesSize(file) {
|
||||
var fileSize = file.size;
|
||||
totalSize += parseInt(fileSize);
|
||||
if(fileSize > fileMaxSize) {
|
||||
file.isValid = false;
|
||||
_disableSubmitButton();
|
||||
return "<p class='dnd-error'>" + I18n.t('general.file.size_exceeded', { file_size: fileMaxSizeMb }) + '</p>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function _validateTotalSize() {
|
||||
if(totalSize > fileMaxSize) {
|
||||
isValid = false;
|
||||
_disableSubmitButton();
|
||||
$.each($('.panel-result-attachment-new'), function() {
|
||||
if(!$(this).find('p').hasClass('dnd-total-error')) {
|
||||
$(this)
|
||||
.find('.panel-body')
|
||||
.append("<p class='dnd-total-error'>" + I18n.t('general.file.total_size', { size: fileMaxSizeMb }) + '</p>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('.dnd-total-error').remove();
|
||||
if(_filerAndCheckFiles()) {
|
||||
isValid = true;
|
||||
_enableSubmitButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 _uploadedAssetPreview(asset, i) {
|
||||
var html = '<div class="panel panel-default panel-result-attachment-new">';
|
||||
html += '<div class="panel-heading">';
|
||||
html += '<span class="fas fa-paperclip"></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="fas fa-times"></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].size);
|
||||
droppedFiles.splice(index, 1);
|
||||
listItems();
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
FilePreviewModal.init();
|
||||
Comments.init();
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
copyFromClipboard.init(location);
|
||||
}
|
||||
|
||||
})(window);
|
|
@ -1078,6 +1078,10 @@ ul.content-activities {
|
|||
}
|
||||
}
|
||||
|
||||
.drag-n-drop-file-input {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.dnd-error,
|
||||
.dnd-total-error {
|
||||
color: $brand-danger;
|
||||
|
|
|
@ -61,70 +61,55 @@ class ResultAssetsController < ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
update_params = result_params
|
||||
previous_size = @result.space_taken
|
||||
previous_asset = @result.asset
|
||||
success_flash = nil
|
||||
saved = false
|
||||
|
||||
if update_params.key? :asset_attributes
|
||||
asset = Asset.find_by_id(update_params[:asset_attributes][:id])
|
||||
asset.created_by = current_user
|
||||
asset.last_modified_by = current_user
|
||||
asset.team = current_team
|
||||
@result.asset = asset
|
||||
end
|
||||
@result.transaction do
|
||||
update_params = result_params
|
||||
previous_size = @result.space_taken
|
||||
|
||||
@result.last_modified_by = current_user
|
||||
@result.assign_attributes(update_params)
|
||||
success_flash = t('result_assets.update.success_flash',
|
||||
module: @my_module.name)
|
||||
if update_params.dig(:asset_attributes, :signed_blob_id)
|
||||
@result.asset.last_modified_by = current_user
|
||||
@result.asset.update(file: update_params[:asset_attributes][:signed_blob_id])
|
||||
update_params.delete(:asset_attributes)
|
||||
end
|
||||
|
||||
if @result.archived_changed?(from: false, to: true)
|
||||
if previous_asset.locked?
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
flash[:error] = t('result_assets.archive.error_flash')
|
||||
redirect_to results_my_module_path(@my_module)
|
||||
return
|
||||
end
|
||||
@result.last_modified_by = current_user
|
||||
@result.assign_attributes(update_params)
|
||||
success_flash = t('result_assets.update.success_flash', module: @my_module.name)
|
||||
|
||||
if @result.archived_changed?(from: false, to: true)
|
||||
if @result.asset.locked?
|
||||
@result.errors.add(:asset_attributes, t('result_assets.archive.error_flash'))
|
||||
raise ActiveRecord:: Rollback
|
||||
end
|
||||
end
|
||||
|
||||
saved = @result.archive(current_user)
|
||||
success_flash = t('result_assets.archive.success_flash',
|
||||
module: @my_module.name)
|
||||
if saved
|
||||
log_activity(:archive_result)
|
||||
end
|
||||
elsif @result.archived_changed?(from: true, to: false)
|
||||
render_403
|
||||
else
|
||||
if previous_asset.locked?
|
||||
@result.errors.add(:asset_attributes,
|
||||
t('result_assets.edit.locked_file_error'))
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
status: 'error',
|
||||
errors: @result.errors
|
||||
}, status: :bad_request
|
||||
return
|
||||
end
|
||||
saved = @result.archive(current_user)
|
||||
success_flash = t('result_assets.archive.success_flash', module: @my_module.name)
|
||||
log_activity(:archive_result) if saved
|
||||
elsif @result.archived_changed?(from: true, to: false)
|
||||
@result.errors.add(:asset_attributes, t('result_assets.archive.error_flash'))
|
||||
raise ActiveRecord:: Rollback
|
||||
else
|
||||
if @result.asset.locked?
|
||||
@result.errors.add(:asset_attributes, t('result_assets.edit.locked_file_error'))
|
||||
raise ActiveRecord:: Rollback
|
||||
end
|
||||
end
|
||||
# Asset (file) and/or name has been changed
|
||||
saved = @result.save
|
||||
# Asset (file) and/or name has been changed
|
||||
saved = @result.save
|
||||
|
||||
if saved
|
||||
# Release team's space taken due to
|
||||
# previous asset being removed
|
||||
team = @result.my_module.experiment.project.team
|
||||
team.release_space(previous_size)
|
||||
team.save
|
||||
if saved
|
||||
# Release team's space taken due to
|
||||
# previous asset being removed
|
||||
team = @result.my_module.experiment.project.team
|
||||
team.release_space(previous_size)
|
||||
team.save
|
||||
|
||||
# Post process new file if neccesary
|
||||
@result.asset.post_process_file(team) if @result.asset.present?
|
||||
# Post process new file if neccesary
|
||||
@result.asset.post_process_file(team) if @result.asset.present?
|
||||
|
||||
log_activity(:edit_result)
|
||||
log_activity(:edit_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -181,37 +166,29 @@ class ResultAssetsController < ApplicationController
|
|||
end
|
||||
|
||||
def result_params
|
||||
params.require(:result).permit(
|
||||
:name, :archived,
|
||||
asset_attributes: [
|
||||
:id,
|
||||
:file
|
||||
]
|
||||
)
|
||||
params.require(:result).permit(:name, :archived, asset_attributes: :signed_blob_id)
|
||||
end
|
||||
|
||||
def create_multiple_results
|
||||
success = true
|
||||
success = false
|
||||
results = []
|
||||
params[:results_files].values.each_with_index do |file, index|
|
||||
asset = Asset.new(created_by: current_user,
|
||||
last_modified_by: current_user,
|
||||
team: current_team)
|
||||
asset.file.attach(file)
|
||||
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
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
params[:results_files].values.each_with_index do |file, index|
|
||||
asset = Asset.create!(created_by: current_user, last_modified_by: current_user, team: current_team)
|
||||
asset.file.attach(file[:signed_blob_id])
|
||||
result = Result.create!(user: current_user,
|
||||
my_module: @my_module,
|
||||
name: params[:results_names][index.to_s],
|
||||
asset: asset,
|
||||
last_modified_by: current_user)
|
||||
results << result
|
||||
# Post process file here
|
||||
asset.post_process_file(@my_module.experiment.project.team)
|
||||
|
||||
log_activity(:add_result, result)
|
||||
else
|
||||
success = false
|
||||
end
|
||||
|
||||
success = true
|
||||
end
|
||||
{ status: success, results: results }
|
||||
end
|
||||
|
|
|
@ -29,50 +29,63 @@ class StepsController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@step = Step.new(step_params)
|
||||
# gerate a tag that replaces img tag in database
|
||||
@step.completed = false
|
||||
@step.position = @protocol.number_of_steps
|
||||
@step.protocol = @protocol
|
||||
@step.user = current_user
|
||||
@step.last_modified_by = current_user
|
||||
@step.assets.each do |asset|
|
||||
asset.created_by = current_user
|
||||
asset.team = current_team
|
||||
end
|
||||
@step.tables.each do |table|
|
||||
table.created_by = current_user
|
||||
table.team = current_team
|
||||
end
|
||||
# Update default checked state
|
||||
@step.checklists.each do |checklist|
|
||||
checklist.checklist_items.each do |checklist_item|
|
||||
checklist_item.checked = false
|
||||
@step = Step.new
|
||||
@step.transaction do
|
||||
new_step_params = step_params
|
||||
|
||||
# Attach newly uploaded files, and than remove their blob ids from the parameters
|
||||
new_step_params[:assets_attributes]&.each do |key, value|
|
||||
next unless value[:signed_blob_id]
|
||||
|
||||
asset = Asset.create!(created_by: current_user, last_modified_by: current_user, team: current_team)
|
||||
asset.file.attach(value[:signed_blob_id])
|
||||
@step.assets << asset
|
||||
new_step_params[:assets_attributes].delete(key)
|
||||
end
|
||||
|
||||
@step.assign_attributes(new_step_params)
|
||||
# gerate a tag that replaces img tag in database
|
||||
@step.completed = false
|
||||
@step.position = @protocol.number_of_steps
|
||||
@step.protocol = @protocol
|
||||
@step.user = current_user
|
||||
@step.last_modified_by = current_user
|
||||
@step.tables.each do |table|
|
||||
table.created_by = current_user
|
||||
table.team = current_team
|
||||
end
|
||||
# Update default checked state
|
||||
@step.checklists.each do |checklist|
|
||||
checklist.checklist_items.each do |checklist_item|
|
||||
checklist_item.checked = false
|
||||
end
|
||||
end
|
||||
|
||||
@step.save!
|
||||
|
||||
# Post process all assets
|
||||
@step.assets.each do |asset|
|
||||
asset.post_process_file(@protocol.team)
|
||||
end
|
||||
|
||||
# link tiny_mce_assets to the step
|
||||
TinyMceAsset.update_images(@step, params[:tiny_mce_images])
|
||||
|
||||
create_annotation_notifications(@step)
|
||||
|
||||
# Generate activity
|
||||
if @protocol.in_module?
|
||||
log_activity(:create_step, @my_module.experiment.project, my_module: @my_module.id)
|
||||
else
|
||||
log_activity(:add_step_to_protocol_repository, nil, protocol: @protocol.id)
|
||||
end
|
||||
|
||||
# Update protocol timestamp
|
||||
update_protocol_ts(@step)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if @step.save
|
||||
# Post process all assets
|
||||
@step.assets.each do |asset|
|
||||
asset.post_process_file(@protocol.team)
|
||||
end
|
||||
|
||||
# link tiny_mce_assets to the step
|
||||
TinyMceAsset.update_images(@step, params[:tiny_mce_images])
|
||||
|
||||
create_annotation_notifications(@step)
|
||||
|
||||
# Generate activity
|
||||
if @protocol.in_module?
|
||||
log_activity(:create_step, @my_module.experiment.project, my_module: @my_module.id)
|
||||
else
|
||||
log_activity(:add_step_to_protocol_repository, nil, protocol: @protocol.id)
|
||||
end
|
||||
|
||||
# Update protocol timestamp
|
||||
update_protocol_ts(@step)
|
||||
|
||||
if @step.errors.empty?
|
||||
format.json do
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
|
@ -129,6 +142,17 @@ class StepsController < ApplicationController
|
|||
# NOTE - step_params_all variable is updated
|
||||
destroy_attributes(step_params_all)
|
||||
|
||||
# Attach newly uploaded files, and than remove their blob ids from the parameters
|
||||
step_params_all[:assets_attributes]&.each do |key, value|
|
||||
next unless value[:signed_blob_id]
|
||||
|
||||
@step.assets
|
||||
.create!(created_by: current_user, last_modified_by: current_user, team: current_team)
|
||||
.file
|
||||
.attach(value[:signed_blob_id])
|
||||
step_params_all[:assets_attributes].delete(key)
|
||||
end
|
||||
|
||||
@step.assign_attributes(step_params_all)
|
||||
@step.last_modified_by = current_user
|
||||
|
||||
|
@ -161,7 +185,7 @@ class StepsController < ApplicationController
|
|||
|
||||
# Post process step assets
|
||||
@step.assets.each do |asset|
|
||||
asset.post_process_file(team)
|
||||
asset.post_process_file(team) if asset.changed?
|
||||
end
|
||||
|
||||
# Generate activity
|
||||
|
@ -469,14 +493,12 @@ class StepsController < ApplicationController
|
|||
|
||||
# Checks if hash contains destroy parameter '_destroy' and returns
|
||||
# boolean value.
|
||||
def has_destroy_params(params)
|
||||
for key, values in params do
|
||||
if values.respond_to?(:each)
|
||||
for pos, attrs in params[key] do
|
||||
if attrs[:_destroy] == '1'
|
||||
return true
|
||||
end
|
||||
end
|
||||
def has_destroy_params?(params)
|
||||
params.each do |key, values|
|
||||
next unless values.respond_to?(:each)
|
||||
|
||||
params[key].each do |_, attrs|
|
||||
return true if attrs[:_destroy] == '1'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -487,26 +509,26 @@ class StepsController < ApplicationController
|
|||
# values that contains destroy parameters from original variable and
|
||||
# puts them into update_params variable.
|
||||
def extract_destroy_params(params, update_params)
|
||||
for key, values in params do
|
||||
if values.respond_to?(:each)
|
||||
update_params[key] = {} unless update_params[key]
|
||||
attr_params = update_params[key]
|
||||
params.each do |key, values|
|
||||
next unless values.respond_to?(:each)
|
||||
|
||||
for pos, attrs in params[key] do
|
||||
if attrs[:_destroy] == '1'
|
||||
if attrs[:id].present?
|
||||
asset = Asset.find_by_id(attrs[:id])
|
||||
if asset.try(&:locked?)
|
||||
asset.errors.add(:base, 'This file is locked.')
|
||||
else
|
||||
attr_params[pos] = { id: attrs[:id], _destroy: '1' }
|
||||
end
|
||||
update_params[key] = {} unless update_params[key]
|
||||
attr_params = update_params[key]
|
||||
|
||||
params[key].each do |pos, attrs|
|
||||
if attrs[:_destroy] == '1'
|
||||
if attrs[:id].present?
|
||||
asset = Asset.find_by_id(attrs[:id])
|
||||
if asset.try(&:locked?)
|
||||
asset.errors.add(:base, 'This file is locked.')
|
||||
else
|
||||
attr_params[pos] = { id: attrs[:id], _destroy: '1' }
|
||||
end
|
||||
params[key].delete(pos)
|
||||
elsif has_destroy_params(params[key][pos])
|
||||
attr_params[pos] = { id: attrs[:id] }
|
||||
extract_destroy_params(params[key][pos], attr_params[pos])
|
||||
end
|
||||
params[key].delete(pos)
|
||||
elsif has_destroy_params?(params[key][pos])
|
||||
attr_params[pos] = { id: attrs[:id] }
|
||||
extract_destroy_params(params[key][pos], attr_params[pos])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -587,8 +609,8 @@ class StepsController < ApplicationController
|
|||
],
|
||||
assets_attributes: [
|
||||
:id,
|
||||
:file,
|
||||
:_destroy
|
||||
:_destroy,
|
||||
:signed_blob_id
|
||||
],
|
||||
tables_attributes: [
|
||||
:id,
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
data-atwho-rep-items-url="<%= atwho_rep_items_team_path(current_team) %>"
|
||||
data-atwho-menu-items="<%= atwho_menu_items_team_path(current_team) %>"
|
||||
data-file-max-size-mb="<%= Rails.configuration.x.file_max_size_mb %>"
|
||||
data-name-max-length="<%= Constants::NAME_MAX_LENGTH %>"
|
||||
data-filename-max-length="<%= Constants::FILENAME_TRUNCATION_LENGTH %>"
|
||||
data-tooltips-enabled="<%= current_user.settings[:tooltips_enabled] %>"
|
||||
<% end %>
|
||||
>
|
||||
|
@ -45,7 +47,7 @@
|
|||
<%= render "shared/file_preview_modal.html.erb" %>
|
||||
<%= render "shared/file_edit_modal.html.erb" %>
|
||||
<%= render "shared/navigation" %>
|
||||
|
||||
|
||||
<% if user_signed_in? && flash[:system_notification_modal] && current_user.show_login_system_notification? %>
|
||||
<%= render partial: "/system_notifications/system_notification_modal", locals: { notification: current_user.user_system_notifications.show_on_login(true) } %>
|
||||
<% else %>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
data-create-record="<%= repository_repository_rows_path(repository) %>"
|
||||
data-delete-record="<%= repository_delete_records_path(repository) %>"
|
||||
data-copy-records="<%= repository_copy_records_path(repository) %>"
|
||||
data-direct-upload-url="<%= rails_direct_uploads_url %>"
|
||||
data-max-dropdown-length="<%= Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>"
|
||||
data-repository-columns-ids="<%= repository.available_columns_ids %>"
|
||||
data-save-text="<%= I18n.t('general.save') %>"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<%= file_extension_icon(ff.object) %>
|
||||
<%= ff.object.file_name %>
|
||||
</p>
|
||||
<%= ff.file_field :file %>
|
||||
<%= ff.file_field :file, direct_upload: true %>
|
||||
<% end %>
|
||||
<hr>
|
||||
<div class="align-right">
|
||||
|
@ -16,7 +16,7 @@
|
|||
</button>
|
||||
<%= f.submit t("general.save"),
|
||||
class: 'btn btn-success save-result',
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.FILE, true);" %>
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.FILE);" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
<div class="well">
|
||||
<div id="new-result-assets-select" class="text-center new-asset-box">
|
||||
<span class="help_tooltips"
|
||||
data-tooltiplink="<%= I18n.t('tooltips.link.protocol.step_add_files') %>"
|
||||
data-tooltipcontent="<%= I18n.t('tooltips.text.protocol.step_add_files') %>">
|
||||
<%=t 'assets.drag_n_drop.label_html' %>
|
||||
<label>
|
||||
<span class="btn btn-default new-asset-upload-button">
|
||||
<span class="fas fa-file-upload new-asset-upload-icon"></span>
|
||||
<%=t 'assets.drag_n_drop.browse_label' %>
|
||||
<%= form_for(@result, url: my_module_result_assets_path(format: :json), data: { type: :json }) do |f| %>
|
||||
<div id="new-result-assets-select" class="text-center new-asset-box">
|
||||
<span class="help_tooltips"
|
||||
data-tooltiplink="<%= I18n.t('tooltips.link.protocol.step_add_files') %>"
|
||||
data-tooltipcontent="<%= I18n.t('tooltips.text.protocol.step_add_files') %>">
|
||||
<%= t('assets.drag_n_drop.label_html') %>
|
||||
<label>
|
||||
<span class="btn btn-default new-asset-upload-button">
|
||||
<span class="fas fa-file-upload new-asset-upload-icon"></span>
|
||||
<%= t('assets.drag_n_drop.browse_label') %>
|
||||
</span>
|
||||
<%= f.file_field :file,
|
||||
id: 'drag-n-drop-assets',
|
||||
class: 'drag-n-drop-file-input',
|
||||
direct_upload: true,
|
||||
multiple: true,
|
||||
onchange: "DragNDropResults.init(this.files);" %>
|
||||
</label>
|
||||
</span>
|
||||
<input type="file"
|
||||
onchange="DragNDropResults.init(this.files, 'select')"
|
||||
id="drag-n-drop-assets"
|
||||
style="display: none" multiple>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<br />
|
||||
<div class="align-right">
|
||||
<button type="button"
|
||||
class="btn btn-default cancel-new"
|
||||
onClick="DragNDropResults.destroyAll()">
|
||||
<%= t("general.cancel")%>
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-success save-result"
|
||||
disabled="true"
|
||||
data-href="<%= my_module_result_assets_path(format: :json) %>"
|
||||
onClick="DragNDropResults.processResult(this)"><%=t 'result_assets.new.create' %></button>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="align-right">
|
||||
<%= f.submit t('general.cancel'),
|
||||
class: 'btn btn-default cancel-new',
|
||||
onclick: 'DragNDropResults.destroyAll();' %>
|
||||
<%= f.submit t('result_assets.new.create'),
|
||||
class: 'btn btn-success save-result',
|
||||
onclick: 'DragNDropResults.processResult(event);',
|
||||
disabled: true,
|
||||
data: { href: my_module_result_assets_path(format: :json) } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<div id="table-form" class="well">
|
||||
<%= bootstrap_form_for(@result, url: result_table_path(format: :json), remote: true) do |f| %>
|
||||
<%= bootstrap_form_for(@result, url: result_table_path(format: :json),
|
||||
data: { 'name-max-length': Constants::NAME_MAX_LENGTH },
|
||||
remote: true) do |f| %>
|
||||
<%= f.text_field :name, style: "margin-top: 10px;" %><br />
|
||||
<div class="editable-table">
|
||||
<%= f.fields_for :table do |ff| %>
|
||||
|
@ -15,7 +17,7 @@
|
|||
</button>
|
||||
<%= f.submit t("general.save"),
|
||||
class: 'btn btn-success save-result',
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TABLE, true);" %>
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TABLE);" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<div id="table-form" class="well">
|
||||
<%= bootstrap_form_for(@result, url: my_module_result_tables_path(format: :json), remote: true) do |f| %>
|
||||
<%= bootstrap_form_for(@result, url: my_module_result_tables_path(format: :json),
|
||||
data: { 'name-max-length': Constants::NAME_MAX_LENGTH },
|
||||
remote: true) do |f| %>
|
||||
<%= f.text_field :name, style: "margin-top: 10px;" %><br />
|
||||
<div class="editable-table" style="margin-bottom: 25px;">
|
||||
<%= f.fields_for :table do |ff| %>
|
||||
|
@ -14,7 +16,7 @@
|
|||
</button>
|
||||
<%= f.submit t("result_tables.new.create"),
|
||||
class: 'btn btn-success save-result',
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TABLE, false);" %>
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TABLE);" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<div class="well">
|
||||
<%= bootstrap_form_for(@result, url: result_text_path(format: :json), remote: :true) do |f| %>
|
||||
<%= bootstrap_form_for(@result, url: result_text_path(format: :json),
|
||||
data: { 'name-max-length': Constants::NAME_MAX_LENGTH, 'rich-text-max-length': Constants::RICH_TEXT_MAX_LENGTH },
|
||||
remote: :true) do |f| %>
|
||||
<%= f.text_field :name, style: "margin-top: 10px;" %><br />
|
||||
<%= f.fields_for :result_text do |ff| %>
|
||||
<%= ff.tiny_mce_editor(:text,
|
||||
|
@ -15,7 +17,7 @@
|
|||
</button>
|
||||
<%= f.submit t("general.save"),
|
||||
class: 'btn btn-success save-result',
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TEXT, true);" %>
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TEXT);" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<div class="well">
|
||||
<%= bootstrap_form_for(@result, url: my_module_result_texts_path(format: :json), remote: true) do |f| %>
|
||||
<%= bootstrap_form_for(@result, url: my_module_result_texts_path(format: :json),
|
||||
data: { 'name-max-length': Constants::NAME_MAX_LENGTH, 'rich-text-max-length': Constants::RICH_TEXT_MAX_LENGTH },
|
||||
remote: true) do |f| %>
|
||||
<%= f.text_field :name, style: "margin-top: 10px;" %><br />
|
||||
<%= f.fields_for :result_text do |ff| %>
|
||||
<%= ff.tiny_mce_editor(:text,
|
||||
|
@ -14,7 +16,7 @@
|
|||
</button>
|
||||
<%= f.submit t("result_texts.new.create"),
|
||||
class: 'btn btn-success save-result',
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TEXT, false);" %>
|
||||
onclick: "Results.processResult(event, Results.ResultTypeEnum.TEXT);" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -59,10 +59,13 @@
|
|||
<span class="fas fa-file-upload new-asset-upload-icon"></span>
|
||||
<%=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>
|
||||
<span style="display:none;">
|
||||
<%= f.file_field :file,
|
||||
id: "drag-n-drop-assets",
|
||||
multiple: true,
|
||||
direct_upload: true,
|
||||
onchange: "DragNDropSteps.init(this.files)" %>
|
||||
</span>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue