Merge branch 'master' of https://github.com/biosistemika/scinote-web into zd_SCI_1336

This commit is contained in:
zmagod 2017-06-21 15:56:07 +02:00
commit bad52ceff1
67 changed files with 1283 additions and 487 deletions

View file

@ -55,7 +55,7 @@ gem 'faker' # Generate fake data
gem 'auto_strip_attributes', '~> 2.1' # Removes unnecessary whitespaces from ActiveRecord or ActiveModel attributes
gem 'deface', '~> 1.0'
gem 'nokogiri' # HTML/XML parser
gem 'sneaky-save', git: 'git://github.com/einzige/sneaky-save.git'
gem 'sneaky-save', git: 'https://github.com/einzige/sneaky-save'
gem 'rails_autolink', '~> 1.1', '>= 1.1.6'
gem 'delayed_paperclip'
gem 'rubyzip'

View file

@ -1,8 +1,8 @@
GIT
remote: git://github.com/einzige/sneaky-save.git
revision: 03866e838f62a4b13e15784974fcc13e14cd9502
remote: https://github.com/einzige/sneaky-save
revision: e7c77674abe74d598dfd58db7c680dd85936f207
specs:
sneaky-save (0.1.1)
sneaky-save (0.1.2)
activerecord (>= 3.2.0)
GEM

View file

@ -1,5 +1,5 @@
APP_HOME="/usr/src/app"
DB_IP=$(shell docker inspect web_db_1 | grep -m 1 "\"IPAddress\": " | awk '{ match($$0, /"IPAddress": "([0-9\.]*)",/, a); print a[1] }')
DB_IP=$(shell docker inspect scinote_db_development | grep "\"IPAddress\": " | awk '{ match($$0, /"IPAddress": "([0-9\.]*)",/, a); print a[1] }')
define PRODUCTION_CONFIG_BODY
SECRET_KEY_BASE=$(shell openssl rand -hex 64)

View file

@ -157,23 +157,6 @@
$form.clearFormErrors();
switch(resultTypeEnum) {
case ResultTypeEnum.FILE:
var $nameInput = $form.find('#result_name');
var nameValid = textValidator(ev, $nameInput, 0,
<%= Constants::NAME_MAX_LENGTH %>);
var $fileInput = $form.find('#result_asset_attributes_file');
var filesValid = true;
if ($fileInput.val()) {
filesValid = filesValidator(ev, $fileInput, FileTypeEnum.FILE,
editMode);
}
if(nameValid && filesValid) {
// Local file uploading
animateSpinner();
}
break;
case ResultTypeEnum.TABLE:
var $nameInput = $form.find('#result_name');
var nameValid = textValidator(ev, $nameInput, 0,

View file

@ -214,23 +214,6 @@
$(e.target).find("input[type='file']").val("");
}
});
// Add hidden fields for tables
$form.submit(function(){
$(this).find("[data-role='editable-table']").each(function() {
var hot = $(this).find(".hot").handsontable('getInstance');
var contents = $(this).find('.hot-contents');
var data = JSON.stringify({data: hot.getData()});
contents.attr("value", data);
});
setTimeout(function() {
initStepsComments();
animateSpinner(null, false);
SmartAnnotation.preventPropagation('.atwho-user-popover');
}, 1000);
return true;
});
}
// Init ajax success/error for edit form
@ -279,49 +262,6 @@
});
}
function formNewAjax($form) {
$form
.on("ajax:beforeSend", function () {
$(".nested_step_checklists ul").each(function () {
reorderCheckboxData(this);
});
})
.on("ajax:success", function(e, data) {
$(this).after(data.html);
var $new_step = $(this).next();
$(this).remove();
initCallBacks();
initHandsOnTable($new_step);
expandStep($new_step);
toggleButtons(true);
SmartAnnotation.preventPropagation('.atwho-user-popover');
//Rerender tables
$new_step.find("div.step-result-hot-table").each(function() {
$(this).handsontable("render");
});
animateSpinner(null, false);
setupAssetsLoading();
})
.on("ajax:error", function(e, xhr, status, error) {
$(this).after(xhr.responseJSON.html);
var $form = $(this).next();
$(this).remove();
$errInput = $form.find(".form-group.has-error").first().find("input");
renderFormError(e, $errInput);
formCallback($form);
formNewAjax($form);
applyCancelOnNew();
animateSpinner(null, false);
TinyMCE.destroyAll();
SmartAnnotation.preventPropagation('.atwho-user-popover');
});
}
function toggleButtons(show) {
if (show) {
$("[data-action='new-step']").show();
@ -562,7 +502,6 @@
scrollTop: $(document).height() - $(window).height()
});
formCallback($form);
formNewAjax($form);
applyCancelOnNew();
toggleButtons(false);
initializeCheckboxSorting();
@ -588,13 +527,13 @@
// experience is improved
global.processStep = function processStep(ev, editMode) {
ev.stopPropagation();
ev.preventDefault();
ev.stopImmediatePropagation();
var $form = $(ev.target.form);
$form.clearFormErrors();
$form.removeBlankFileForms();
var $fileInputs = $form.find("input[type=file]");
var filesValid = filesValidator(ev, $fileInputs, FileTypeEnum.FILE);
var $checklists = $form.find(".nested_step_checklists");
var checklistsValid = checklistsValidator(ev, $checklists, editMode);
var $nameInput = $form.find("#step_name");
@ -604,8 +543,73 @@
var descriptionValid = textValidator(ev, $descrTextarea, 0,
<%= Constants::TEXT_MAX_LENGTH %>);
if (filesValid && checklistsValid && nameValid && descriptionValid) {
// Local file uploading
if (DragNDropSteps.filesStatus() &&
checklistsValid &&
nameValid &&
descriptionValid) {
$form.find("[data-role='editable-table']").each(function() {
var hot = $(this).find(".hot").handsontable('getInstance');
var contents = $(this).find('.hot-contents');
var data = JSON.stringify({data: hot.getData()});
contents.attr("value", data);
});
setTimeout(function() {
initStepsComments();
SmartAnnotation.preventPropagation('.atwho-user-popover');
}, 1000);
animateSpinner(null, true);
var data = DragNDropSteps.appendFilesToForm(ev);
data.append('step[description]', TinyMCE.getContent());
$.ajax({
url: $form.attr('action'),
method: 'POST',
data: data,
contentType: false,
processData: false,
beforeSend: function() {
$(".nested_step_checklists ul").each(function () {
reorderCheckboxData(this);
});
},
success: function(data) {
$($form.closest('.well')).after(data.html);
var $new_step = $($form.closest('.well')).next();
$($form.closest('.well')).remove();
initCallBacks();
initHandsOnTable($new_step);
expandStep($new_step);
toggleButtons(true);
SmartAnnotation.preventPropagation('.atwho-user-popover');
//Rerender tables
$new_step.find("div.step-result-hot-table").each(function() {
$(this).handsontable("render");
});
animateSpinner(null, false);
setupAssetsLoading();
},
error: function(e) {
$form.after(xhr.responseJSON.html);
var $new_form = $form.next();
$form.remove();
$errInput = $new_form.find(".form-group.has-error")
.first()
.find("input");
renderFormError(e, $errInput);
formCallback($form);
applyCancelOnNew();
animateSpinner(null, false);
TinyMCE.destroyAll();
SmartAnnotation.preventPropagation('.atwho-user-popover');
}
});
newStepHandler();
}
}
@ -635,8 +639,9 @@
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%");
if (parseInt($(table).css('height'), 10) < parseInt($(table).css('max-height'), 10) - 30) {
$(table).find('.ht_master .wtHolder').css({ 'height': '100%',
'width': '100%' });
}
}
@ -672,4 +677,5 @@
});
})
global.initHandsOnTable = initHandsOnTable;
})(window);

View file

@ -564,7 +564,7 @@ function initSidebarElement(reportEl) {
var type = reportEl.data("type");
var name = reportEl.data("name");
var scrollId = reportEl.data("scroll-id");
var iconClass = "glyphicon " + reportEl.data("icon-class");
var iconClass = reportEl.data("icon-class");
// Generate list element
var newLi = $(document.createElement("li"));

View file

@ -0,0 +1,7 @@
(function() {
'use strict';
$('.delete-repo-option').initializeModal('#delete-repo-modal');
$('.rename-repo-option').initializeModal('#rename-repo-modal');
$('.copy-repo-option').initializeModal('#copy-repo-modal');
})();

View file

@ -1,49 +1,35 @@
//= require repositories/import/records_importer.js
(function() {
'use strict';
function showNewRepository() {
$('#create-repo-modal').on('ajax:success', function(data) {
var location = data.url;
window.location.replace(location);
function initImportRecordsModal() {
$('#importRecordsButton').off().on('click', function() {
$('#modal-import-records').modal('show');
_initParseRecordsModal();
});
}
function initCreateRepository() {
$('.create-repository').off().on('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
$.ajax({
url: $(this).attr('href'),
type: 'GET',
dataType: 'json',
success: function(data) {
$(data.html).appendTo('body').promise().done(function() {
$('#create-repo-modal').modal('show');
});
},
error: function() {
location.reload();
}
})
function _initParseRecordsModal() {
$('#form-records-file').on('ajax:success', function(ev, data) {
$('#modal-import-records').modal('hide');
$(data.html).appendTo('body').promise().done(function() {
$('#parse-records_modal').modal('show');
repositoryRecordsImporter();
});
});
}
function loadRepositoryTab() {
var param, pane;
var param;
$('#repository-tabs a').on("click", function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
pane = $(this);
var pane = $(this);
$.ajax({
url: pane.attr('data-url'),
type: 'GET',
dataType: 'json',
success: function(data) {
var tabBody = $(pane.context.hash).find('.tab-content-body');
url: $(this).attr("data-url"),
type: "GET",
dataType: "json",
success: function (data) {
var tabBody = $(pane.context.hash).find(".tab-content-body");
tabBody.html(data.html);
pane.tab('show').promise().done(function() {
initImportRecordsModal();
@ -58,7 +44,7 @@
// on page load
if( param = getParam('repository') ){
// load selected tab
$('a[href="#custom_repo_' + param + '"]').click();
$('a[href="#custom_repo_'+param+'"]').click();
}
else {
// load first tab content
@ -66,37 +52,15 @@
}
// clean tab content
$('a[data-toggle="tab"]').on('hide.bs.tab', function(e) {
$('a[data-toggle="tab"]').on('hide.bs.tab', function (e) {
$(".tab-content-body").html("");
})
}
function initImportRecordsModal() {
$('#importRecordsButton').off().on('click', function() {
$('#modal-import-records').modal('show');
initParseRecordsModal();
});
}
function initParseRecordsModal() {
$('#form-records-file').on('ajax:success', function(ev, data) {
$('#modal-import-records').modal('hide');
$(data.html).appendTo('body').promise().done(function() {
$('#parse-records_modal').modal('show');
repositoryRecordsImporter();
});
});
};
$('.delete-repo-option').initializeModal('#delete-repo-modal');
$('.rename-repo-option').initializeModal('#rename-repo-modal');
$('.copy-repo-option').initializeModal('#copy-repo-modal');
// $('.create-repository').initializeModal('#create-repo-modal');
$('.create-repository').initializeModal('#create-repo-modal');
$(document).ready(function() {
loadRepositoryTab();
// showParsedRecords();
initCreateRepository();
initImportRecordsModal();
});
})();

View file

@ -92,7 +92,6 @@ function dataTableInit() {
columns: (function() {
var numOfColumns = $('#repository-table').data('num-columns');
var columns = [];
for (var i = 0; i < numOfColumns; i++) {
var visible = (i <= 4);
var searchable = (i > 0 && i <= 4);
@ -119,6 +118,7 @@ function dataTableInit() {
rowsSelected.length +
' entries selected)');
initRowSelection();
initHeaderTooltip();
},
preDrawCallback: function() {
animateSpinner(this);
@ -154,16 +154,13 @@ function dataTableInit() {
type: 'POST'
});
loadFirstTime = false;
initHeaderTooltip();
},
fnInitComplete: function(oSettings) {
// Reload correct column order and visibility (if you refresh page)
// First two columns are fixed
table.column(0).visible(true);
if ($('#assigned').text().length === 0) {
table.column(1).visible(false);
} else {
table.column(1).visible(true);
}
table.column(1).visible(true);
for (var i = 2; i < table.columns()[0].length; i++) {
var visibility = false;
if (myData.columns[i]) {
@ -197,6 +194,10 @@ function dataTableInit() {
$(this).parent().find('.repository-row-selector').trigger('click');
});
table.on('column-reorder', function() {
initRowSelection();
});
$('#assignRepositories, #unassignRepositories').click(function() {
animateLoading();
});
@ -214,6 +215,59 @@ setTimeout(function() {
// Enables noSearchHidden plugin
$.fn.dataTable.defaults.noSearchHidden = true;
$('form#form-export').submit(function() {
var form = this;
if (currentMode === 'viewMode') {
// Remove all hidden fields
$(form).find('input[name=row_ids\\[\\]]').remove();
$(form).find('input[name=header_ids\\[\\]]').remove();
// Append visible column information
$('.active table#repository-table thead tr th').each(function() {
var th = $(this);
var val;
switch ($(th).attr('id')) {
case 'checkbox':
val = -1;
break;
case 'assigned':
val = -2;
break;
case 'row-name':
val = -3;
break;
case 'added-by':
val = -4;
break;
case 'added-on':
val = -5;
break;
default:
val = th.attr('id');
}
if (val) {
appendInput(form, val, 'header_ids[]');
}
});
// Append records
$.each(rowsSelected, function(index, rowId) {
appendInput(form, rowId, 'row_ids[]');
});
}
});
function appendInput(form, val, name) {
$(form).append(
$('<input>')
.attr('type', 'hidden')
.attr('name', name)
.val(val)
);
}
function initRowSelection() {
// Handle clicks on checkbox
$('.dt-body-center .repository-row-selector').change(function(e) {
@ -303,6 +357,13 @@ function initHeaderTooltip() {
offsetLeft -= 150;
}
var offsetTop = $tooltip.offset().top;
var width = 200;
// set tooltip params in the table body
if ( $(this).parents('#repository-table').length ) {
offsetLeft = $('#repository-table').offset().left + 100;
width = $('#repository-table').width() - 200;
}
$('body').append($tooltip);
$tooltip.css('background-color', '#d2d2d2');
$tooltip.css('border-radius', '6px');
@ -314,8 +375,9 @@ function initHeaderTooltip() {
$tooltip.css('text-align', 'center');
$tooltip.css('top', offsetTop + 'px');
$tooltip.css('visibility', 'visible');
$tooltip.css('width', '200px');
$tooltip.css('width', width + 'px');
$tooltip.css('word-wrap', 'break-word');
$tooltip.css('z-index', '4');
$(this).data('dropdown-tooltip', $tooltip);
}, function() {
$(this).append($(this).data('dropdown-tooltip'));
@ -399,10 +461,12 @@ function onClickAssignRecords() {
success: function(data) {
HelperModule.flashAlertMsg(data.flash, 'success');
onClickCancel();
clearRowSelection();
},
error: function(data) {
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
onClickCancel();
clearRowSelection();
}
});
}
@ -417,10 +481,12 @@ function onClickUnassignRecords() {
success: function(data) {
HelperModule.flashAlertMsg(data.flash, 'success');
onClickCancel();
clearRowSelection();
},
error: function(data) {
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
onClickCancel();
clearRowSelection();
}
});
}
@ -433,7 +499,8 @@ function onClickDeleteRecord() {
dataType: 'json',
data: {selected_rows: rowsSelected},
success: function(data) {
HelperModule.flashAlertMsg(data.flash, 'success');
HelperModule.flashAlertMsg(data.flash, data.color);
rowsSelected = [];
onClickCancel();
},
error: function(e) {
@ -615,7 +682,7 @@ function onClickSave() {
if (input) {
input.closest('.form-group').addClass('has-error');
input.parent().append("<span class='help-block'>" +
val.value[0] + '<br /></span>');
val.data[0] + '<br /></span>');
}
});
});
@ -637,29 +704,38 @@ function updateButtons() {
$('th').removeClass('disable-click');
$('.repository-row-selector').removeClass('disabled');
$('.repository-row-selector').prop('disabled', false);
if (rowsSelected.length === 1) {
$('#editRepositoryRecord').prop('disabled', false);
$('#editRepositoryRecord').removeClass('disabled');
$('#deleteRepositoryRecordsButton').prop('disabled', false);
$('#deleteRepositoryRecordsButton').removeClass('disabled');
$('#assignRepositoryRecords').removeClass('disabled');
$('#assignRepositoryRecords').prop('disabled', false);
$('#unassignRepositoryRecords').removeClass('disabled');
$('#unassignRepositoryRecords').prop('disabled', false);
} else if (rowsSelected.length === 0) {
if (rowsSelected.length === 0) {
$('#editRepositoryRecord').prop('disabled', true);
$('#editRepositoryRecord').addClass('disabled');
$('#deleteRepositoryRecordsButton').prop('disabled', true);
$('#deleteRepositoryRecordsButton').addClass('disabled');
$('#exportRepositoriesButton').addClass('disabled');
$('#exportRepositoriesButton').prop('disabled', true);
$('#exportRepositoriesButton').off('click');
$('#export-repositories').off('click');
$('#assignRepositoryRecords').addClass('disabled');
$('#assignRepositoryRecords').prop('disabled', true);
$('#unassignRepositoryRecords').addClass('disabled');
$('#unassignRepositoryRecords').prop('disabled', true);
} else {
$('#editRepositoryRecord').prop('disabled', true);
$('#editRepositoryRecord').addClass('disabled');
if (rowsSelected.length === 1) {
$('#editRepositoryRecord').prop('disabled', false);
$('#editRepositoryRecord').removeClass('disabled');
} else {
$('#editRepositoryRecord').prop('disabled', true);
$('#editRepositoryRecord').addClass('disabled');
}
$('#deleteRepositoryRecordsButton').prop('disabled', false);
$('#deleteRepositoryRecordsButton').removeClass('disabled');
$('#exportRepositoriesButton').removeClass('disabled');
$('#exportRepositoriesButton').prop('disabled', false);
$('#exportRepositoriesButton').on('click', function() {
$('#exportRepositoryModal').modal('show');
});
$('#export-repositories').on('click', function() {
animateSpinner(null, true);
$('#form-export').submit();
});
$('#assignRepositoryRecords').removeClass('disabled');
$('#assignRepositoryRecords').prop('disabled', false);
$('#unassignRepositoryRecords').removeClass('disabled');
@ -674,6 +750,9 @@ function updateButtons() {
$('#addNewColumn').prop('disabled', true);
$('#deleteRepositoryRecordsButton').addClass('disabled');
$('#deleteRepositoryRecordsButton').prop('disabled', true);
$('#exportRepositoriesButton').addClass('disabled');
$('#exportRepositoriesButton').off('click');
$('#export-repositories').off('click');
$('#assignRepositoryRecords').addClass('disabled');
$('#assignRepositoryRecords').prop('disabled', true);
$('#unassignRepositoryRecords').addClass('disabled');
@ -697,6 +776,13 @@ function clearAllErrors() {
$('#alert-container').find('div').remove();
}
function clearRowSelection() {
$('.dt-body-center .repository-row-selector').prop('checked', false);
$('.dt-body-center .repository-row-selector').closest('tr')
.removeClass('selected');
rowsSelected = [];
}
// Restore previous table
function onClickCancel() {
if ($('#assigned').text().length === 0) {
@ -759,6 +845,7 @@ function changeToEditMode() {
currentMode = 'editMode';
// Table specific stuff
table.button(0).enable(false);
initHeaderTooltip();
}
/*
@ -971,6 +1058,7 @@ function changeToEditMode() {
if (!_.isEmpty(searchText)) {
table.search(searchText).draw();
}
initRowSelection();
});
}
@ -1257,7 +1345,7 @@ function changeToEditMode() {
var maxLength = $('#repository-table').data('max-dropdown-length');
if ($.trim(name).length > maxLength) {
return '<div class="modal-tooltip">' +
maxLength +
truncateLongString(name, maxLength) +
'<span class="modal-tooltiptext">' + name + '</span></div>';
}
return name;

View file

@ -1,7 +1,7 @@
(function(global) {
'use strict';
var ResutlAssets = (function() {
global.ResutlAssets = (function() {
// New result asset behaviour
function initNewResultAsset() {
$('#new-result-asset').on('click', function(event) {
@ -23,7 +23,7 @@
_formAjaxResultAsset($form);
Results.initCancelFormButton($form, initNewResultAsset);
Results.toggleResultEditButtons(false);
$('#result_name').focus();
dragNdropAssetsInit('results');
},
error: function(xhr, status, e) {
$(this).renderFormErrors('result', xhr.responseJSON, true, e);

View file

@ -33,9 +33,12 @@ var loadFirstTime = true;
var table;
var originalHeader;
var view_assigned;
function dataTableInit() {
// Make a copy of original samples table header
originalHeader = $('#samples thead').children().clone();
view_assigned = 'assigned';
table = $('#samples').DataTable({
order: [[2, 'desc']],
dom: "R<'row'<'col-sm-9-custom toolbar'l><'col-sm-3-custom'f>>tpi",
@ -53,6 +56,9 @@ function dataTableInit() {
destroy: true,
ajax: {
url: $('#samples').data('source'),
data: function ( d ) {
d.assigned = view_assigned;
},
global: false,
type: 'POST'
},
@ -661,7 +667,14 @@ function updateButtons() {
$("#deleteSamplesButton").removeClass("disabled");
$("#exportSamplesButton").removeClass("disabled");
$("#exportSamplesButton").prop("disabled",false);
$("#exportSamplesButton").on("click", function() { $('#form-export').submit(); });
$("#exportSamplesButton").on("click", function() {
$('#modal-export-samples-success')
.modal('show')
.on('hidden.bs.modal', function() {
animateSpinner(null, true);
$('#form-export').submit();
});
});
$("#assignSamples").removeClass("disabled");
$("#assignSamples").prop("disabled", false);
$("#unassignSamples").removeClass("disabled");
@ -687,7 +700,14 @@ function updateButtons() {
$("#deleteSamplesButton").removeClass("disabled");
$("#exportSamplesButton").removeClass("disabled");
$("#exportSamplesButton").prop("disabled",false);
$("#exportSamplesButton").on("click", function() { $('#form-export').submit(); });
$("#exportSamplesButton").on("click", function() {
$('#modal-export-samples-success')
.modal('show')
.on('hidden.bs.modal', function() {
animateSpinner(null, true);
$('#form-export').submit();
});
});
$("#assignSamples").removeClass("disabled");
$("#assignSamples").prop("disabled", false);
$("#unassignSamples").removeClass("disabled");
@ -819,6 +839,15 @@ function onClickAddSample() {
});
}
$('#assignedSamples').on('click', function () {
view_assigned = 'assigned';
table.draw();
});
$('#allSamples').on('click', function () {
view_assigned = 'all';
table.draw();
});
// Handle enter key
$(document).off('keypress').keypress(function(event) {
var keycode = (event.keyCode ? event.keyCode : event.which);

View file

@ -17,6 +17,12 @@ $("form#form-samples-file")
$(this).find(".form-group").append("<span class='help-block'>" + data.responseJSON.message + "</span>");
});
$('.sample-assign-group > .btn').click(function() {
$('.btn-group > .btn').removeClass('active btn-primary');
$('.btn-group > .btn').addClass('btn-default');
$(this).addClass('active btn-primary');
});
// Fetch samples data and updates the select options fields for
// sample group and sample type column
function updateSamplesTypesandGroups() {

View file

@ -0,0 +1,346 @@
(function(global) {
'use strict';
// Module to handle file uploading in Steps
global.DragNDropSteps = (function() {
var droppedFiles = [];
var filesValid = true;
var totalSize = 0;
function init(files) {
for(var i = 0; i < files.length; i++) {
droppedFiles.push(files[i]);
}
listItems();
}
// return the status of files if they are ready to submit
function filesStatus() {
return filesValid;
}
// loops through a list of files and display each file in a separate panel
function listItems() {
droppedFiles = droppedFiles.filter(Boolean);
$('.panel-step-attachment-new').remove();
_dragNdropAssetsOff();
for(var i = 0; i < droppedFiles.length; i++) {
$('#new-step-assets')
.append(_uploadedAseetPreview(droppedFiles[i], i))
.promise()
.done(function() {
_removeItemHandler(i);
});
}
_validateTotalSize();
dragNdropAssetsInit('steps');
}
// append the files to the form before submit
function appendFilesToForm(ev) {
var regex = /step\[assets_attributes\]\[[0-9]*\]\[id\]/;
var prevEls = $('input').filter(function() {
return this.name.match(regex);
});
droppedFiles = droppedFiles.filter(Boolean);
var fd = new FormData($(ev.target).closest('form').get(0));
for(var i = 0; i < droppedFiles.length; i++) {
var index = i + prevEls.length;
var name = 'step[assets_attributes][' + index + '][file]';
fd.append(name, droppedFiles[i]);
}
droppedFiles = [];
filesValid = true;
totalSize = 0;
_dragNdropAssetsOff();
return fd;
}
function _validateFilesSize(file) {
var maxSize = file.size/1048576;
if(maxSize > <%= Constants::FILE_MAX_SIZE_MB %> && filesValid) {
return "<p><%= I18n.t 'general.file.size_exceeded', file_size: Constants::FILE_MAX_SIZE_MB %></p>";
}
totalSize += parseInt(maxSize);
return '';
}
function _validateTotalSize() {
if(totalSize > <%= Constants::FILE_MAX_SIZE_MB %>) {
filesValid = false;
$.each($('.panel-step-attachment-new'), function() {
$(this)
.find('.panel-body')
.append("<p class='dnd-error'><%= I18n.t('general.file.total_size', size: Constants::FILE_MAX_SIZE_MB) %></p>");
});
} else {
$('.dnd-error').remove();
filesValid = true;
}
}
function _uploadedAseetPreview(asset, i) {
var html = '<div class="panel panel-default panel-step-attachment-new">';
html += '<div class="panel-heading">';
html += '<span class="glyphicon glyphicon-file"></span>';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>';
html += '<div class="pull-right">';
html += '<a data-item-id="' + i + '" href="#">';
html += '<span class="glyphicon glyphicon-remove"></span></a></div></div>';
html += '<div class="panel-body">';
html += '<label class="control-label">';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>:</label> ';
html += truncateLongString(asset.name,
<%= Constants::FILENAME_TRUNCATION_LENGTH %>);
html += _validateFilesSize(asset);
html += '</div></div>';
return html;
}
function _removeItemHandler(id) {
$('[data-item-id="' + id +'"]').off('click').on('click', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
e.stopPropagation();
var $el = $(this);
var index = $el.data('item-id');
totalSize -= parseInt(droppedFiles[index]/1048576);
droppedFiles[index] = null;
$el.closest('.panel-step-attachment-new').remove();
_validateTotalSize();
});
}
function _dragNdropAssetsOff() {
$('body').off('drag dragstart dragend dragover dragenter dragleave drop');
$('.is-dragover').hide();
}
return Object.freeze({
init: init,
appendFilesToForm: appendFilesToForm,
listItems: listItems,
filesStatus: filesStatus
});
})();
// Module to handle file uploading in Results
global.DragNDropResults = (function() {
var droppedFiles = [];
var isValid = true;
var totalSize = 0;
function init(files) {
var filesPresent = droppedFiles.length;
for(var i = 0; i < files.length; i++) {
droppedFiles.push(files[i]);
}
listItems(filesPresent);
}
// return the status of files if they are ready to submit
function filesStatus() {
return isValid;
}
// loops through a list of files and display each file in a separate panel
function listItems(index) {
_dragNdropAssetsOff();
for(var i = index; i < droppedFiles.length; i++) {
$('#new-result-assets-select')
.after(_uploadedAseetPreview(droppedFiles[i], i))
.promise()
.done(function() {
_removeItemHandler(i);
});
}
_validateTotalSize();
dragNdropAssetsInit('results');
}
// appent the files to the form before submit
function _appendFilesToForm() {
var regex = /result\[assets_attributes\]\[[0-9]*\]\[id\]/;
var prevEls = $('input').filter(function() {
return this.name.match(regex);
});
var fd = new FormData();
var result_names = [];
$.each($('input[rel="results[name]"]'), function() {
result_names.push($(this).val());
});
result_names.reverse();
for(var i = 0; i < droppedFiles.length; i++) {
var index = i + prevEls.length;
var file_name = 'results_files[' + index + ']';
fd.append(file_name, droppedFiles[i]);
fd.append('results_names[' + i + ']', result_names[i]);
}
destroyAll();
return fd;
}
function _filerAndCheckFiles() {
droppedFiles = droppedFiles.filter(Boolean);
return (droppedFiles.length > 0);
}
function _validateFilesSize(file) {
var maxSize = file.size/1048576;
if(maxSize > <%= Constants::FILE_MAX_SIZE_MB %> && isValid) {
return "<p><%= I18n.t 'general.file.size_exceeded', file_size: Constants::FILE_MAX_SIZE_MB %></p>";
}
totalSize += parseInt(maxSize);
return '';
}
function _validateTotalSize() {
if(totalSize > <%= Constants::FILE_MAX_SIZE_MB %>) {
isValid = false;
$.each($('.panel-result-attachment-new'), function() {
$(this)
.find('.panel-body')
.append("<p class='dnd-error'><%= I18n.t('general.file.total_size', size: Constants::FILE_MAX_SIZE_MB) %></p>");
});
} else {
$('.dnd-error').remove();
isValid = true;
}
}
function validateTextSize(input) {
if(input.value.length > <%= Constants::NAME_MAX_LENGTH %>) {
$(input).parent().find('.dnd-error').remove();
$(input).after("<p class='dnd-error'><%= I18n.t('general.text.length_too_long', max_length: Constants::NAME_MAX_LENGTH) %></p>");
isValid = false;
} else {
$(input).parent().find('.dnd-error').remove();
isValid = true;
}
}
function _uploadedAseetPreview(asset, i) {
var html = '<div class="panel panel-default panel-result-attachment-new">';
html += '<div class="panel-heading">';
html += '<span class="glyphicon glyphicon-file"></span>';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>';
html += '<div class="pull-right">';
html += '<a data-item-id="' + i + '" href="#">';
html += '<span class="glyphicon glyphicon-remove"></span></a></div></div>';
html += '<div class="panel-body"><div class="form-group">';
html += '<label class="control-label">Name</label>';
html += '<input type="text" class="form-control" ';
html += 'onChange="DragNDropResults.validateTextSize(this)"';
html += ' rel="results[name]" name="results[name][' + i + ']">';
html += '</div><div class="form-group"><label class="control-label">';
html += '<%= I18n.t 'assets.drag_n_drop.file_label' %>:</label> ';
html += truncateLongString(asset.name,
<%= Constants::FILENAME_TRUNCATION_LENGTH %>);
html += _validateFilesSize(asset);
html += '</div></div>';
return html;
}
function _removeItemHandler(id) {
$('[data-item-id="' + id +'"]').off('click').on('click', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
e.stopPropagation();
var $el = $(this);
var index = $el.data('item-id');
totalSize -= parseInt(droppedFiles[index]/1048576);
droppedFiles[index] = null;
$el.closest('.panel-result-attachment-new').remove();
_validateTotalSize();
});
}
function processResult(button) {
if(isValid && _filerAndCheckFiles()) {
animateSpinner();
$.ajax({
url: $(button).attr('data-href'),
method: 'POST',
data: _appendFilesToForm(),
contentType: false,
processData: false,
success: function(data) {
animateSpinner(null, false);
$('#new-result-assets-select').parent().remove();
$(data.html).prependTo('#results').promise().done(function() {
$.each($('[data-container="new-reports"]').find('.result'),
function() {
initFormSubmitLinks($(this));
ResutlAssets.applyEditResultAssetCallback();
Results.applyCollapseLinkCallBack();
Results.toggleResultEditButtons(true);
initPreviewModal();
Comments.initialize();
ResutlAssets.initNewResultAsset();
Results.expandResult($(this));
});
});
$('#results-toolbar').show();
},
error: function() {
animateSpinner();
location.reload();
}
})
}
}
function destroyAll() {
_dragNdropAssetsOff();
droppedFiles = [];
isValid = true;
totalSize = 0;
}
function _dragNdropAssetsOff() {
$('body').off('drag dragstart dragend dragover dragenter dragleave drop');
$('.is-dragover').hide();
}
return Object.freeze({
init: init,
listItems: listItems,
destroyAll: destroyAll,
filesStatus: filesStatus,
validateTextSize: validateTextSize,
processResult: processResult
});
})();
global.dragNdropAssetsInit = function(location) {
var in_window = true;
$('body').on('drag dragstart dragend dragover dragenter dragleave drop',
function(e) {
e.preventDefault();
e.stopPropagation();
}).on('dragover', function() {
in_window = true;
$('.is-dragover').show();
}).on('dragleave', function() {
in_window = false;
setTimeout(function() {
if(!in_window) {
$('.is-dragover').hide();
}
}, 5000);
}).on('drop', function(e) {
$('.is-dragover').hide();
var files = e.originalEvent.dataTransfer.files;
if(location === 'steps') {
DragNDropSteps.init(files);
} else {
DragNDropResults.init(files);
}
});
}
})(window);

View file

@ -44,6 +44,9 @@ $color-milano-red: #a70b05;
// Colors for specific intents
$color-visited-link: #23527c;
// Overlay shade for drag'n dropdown
$color-drag-overlay: rgba(0, 0, 0, .4);
//==============================================================================
// Other
//==============================================================================

View file

@ -2,6 +2,7 @@
height: auto;
max-height: 400px;
overflow-x: hidden;
text-transform: initial;
}
.repository-table {
@ -12,3 +13,9 @@
display: inline-block;
}
}
.repository-cog {
display: inline-block;
float: right;
padding-left: 4px;
}

View file

@ -1265,6 +1265,45 @@ ul.content-module-activities {
}
}
.new-asset-box {
border: 1px solid $color-silver;
border-radius: 2px;
font-size: 2rem;
margin-bottom: 20px;
margin-top: 20px;
padding-bottom: 30px;
padding-top: 30px;
}
.dnd-error {
color: $color-milano-red;
}
.is-dragover {
background: $color-drag-overlay;
bottom: 0;
display: none;
height: 100%;
left: 0;
min-height: 100%;
pointer-events: none;
position: fixed;
right: 0;
top: 0;
width: 100%;
z-index: 999999;
span {
color: $color-white;
display: block;
font-size: 4em;
font-weight: bold;
padding-top: 25%;
pointer-events: none;
text-align: center;
}
}
.step,
.result {
.panel {
@ -1537,6 +1576,11 @@ table.dataTable {
visibility: visible;
}
// don't display tooltip, it's handeled with js
.repository-table .modal-tooltip:hover .modal-tooltiptext {
visibility: hidden;
}
// Comments
.comment-more {

View file

@ -27,6 +27,10 @@ class MyModulesController < ApplicationController
before_action :check_assign_samples_permissions, only: :assign_samples
before_action :check_unassign_samples_permissions, only: :unassign_samples
before_action :check_complete_my_module_perimission, only: :complete_my_module
before_action :check_assign_repository_records_permissions,
only: :assign_repository_records
before_action :check_unassign_repository_records_permissions,
only: :unassign_repository_records
layout 'fluid'.freeze
@ -386,8 +390,6 @@ class MyModulesController < ApplicationController
# Submit actions
def assign_repository_records
render_403 && return unless can_assign_repository_records(@my_module,
@repository)
if params[:selected_rows].present? && params[:repository_id].present?
records_names = []
@ -415,7 +417,7 @@ class MyModulesController < ApplicationController
'activities.assign_repository_records',
user: current_user.full_name,
task: @my_module.name,
repository: @repository,
repository: @repository.name,
records: records_names.join(', ')
)
)
@ -437,8 +439,6 @@ class MyModulesController < ApplicationController
end
def unassign_repository_records
render_403 && return unless can_unassign_repository_records(@my_module,
@repository)
if params[:selected_rows].present? && params[:repository_id].present?
records = []
@ -462,7 +462,7 @@ class MyModulesController < ApplicationController
'activities.unassign_repository_records',
user: current_user.full_name,
task: @my_module.name,
repository: @repository,
repository: @repository.name,
records: records.map(&:name).join(', ')
)
)
@ -641,12 +641,20 @@ class MyModulesController < ApplicationController
end
end
def check_assign_repository_records_permissions
render_403 unless can_assign_repository_records(@my_module, @repository)
end
def check_unassign_repository_records_permissions
render_403 unless can_unassign_repository_records(@my_module, @repository)
end
def check_complete_my_module_perimission
render_403 unless can_complete_module(@my_module)
end
def my_module_params
params.require(:my_module).permit(:name, :description, :due_date,
:archived)
:archived)
end
end

View file

@ -1,21 +1,23 @@
class RepositoriesController < ApplicationController
before_action :load_vars,
except: %i(repository_table_index parse_sheet import_records)
except: %i(index create create_modal parse_sheet import_records)
before_action :load_parent_vars, except:
%i(repository_table_index export_repository parse_sheet import_records)
before_action :check_team, only: %i(parse_sheet import_records)
before_action :check_view_all_permissions, only: :index
before_action :check_edit_and_destroy_permissions,
only: %i(destroy destroy_modal rename_modal update)
before_action :check_copy_permissions,
only: %i(copy_modal copy)
before_action :check_create_permissions,
only: %i(create_new_modal create)
before_action :check_view_permissions, only: :export_repository
before_action :check_edit_and_destroy_permissions, only:
%i(destroy destroy_modal rename_modal update)
before_action :check_copy_permissions, only:
%i(copy_modal copy)
before_action :check_create_permissions, only:
%i(create_new_modal create)
def index
render('repositories/index')
end
def show_tab
@repository = Repository.find_by_id(params[:repository_id])
respond_to do |format|
format.json do
render json: {
@ -64,7 +66,6 @@ class RepositoriesController < ApplicationController
end
def destroy_modal
@repository = Repository.find(params[:repository_id])
respond_to do |format|
format.json do
render json: {
@ -77,7 +78,6 @@ class RepositoriesController < ApplicationController
end
def destroy
@repository = Repository.find(params[:id])
flash[:success] = t('repositories.index.delete_flash',
name: @repository.name)
@repository.destroy
@ -85,7 +85,6 @@ class RepositoriesController < ApplicationController
end
def rename_modal
@repository = Repository.find(params[:repository_id])
respond_to do |format|
format.json do
render json: {
@ -98,7 +97,6 @@ class RepositoriesController < ApplicationController
end
def update
@repository = Repository.find(params[:id])
old_name = @repository.name
@repository.update_attributes(repository_params)
@ -118,7 +116,6 @@ class RepositoriesController < ApplicationController
end
def copy_modal
@repository = Repository.find(params[:repository_id])
@tmp_repository = Repository.new(
team: @team,
created_by: current_user,
@ -136,7 +133,6 @@ class RepositoriesController < ApplicationController
end
def copy
@repository = Repository.find(params[:repository_id])
@tmp_repository = Repository.new(
team: @team,
created_by: current_user
@ -171,7 +167,6 @@ class RepositoriesController < ApplicationController
# AJAX actions
def repository_table_index
@repository = Repository.find_by_id(params[:repository_id])
if @repository.nil? || !can_view_repository(@repository)
render_403
else
@ -247,6 +242,15 @@ class RepositoriesController < ApplicationController
end
end
def export_repository
if params[:row_ids] && params[:header_ids]
generate_zip
else
flash[:alert] = t('zip_export.export_error')
end
redirect_to :back
end
private
def repostiory_import_actions
@ -260,6 +264,12 @@ class RepositoriesController < ApplicationController
end
def load_vars
repository_id = params[:id] || params[:repository_id]
@repository = Repository.find_by_id(repository_id)
render_404 unless @repository
end
def load_parent_vars
@team = Team.find_by_id(params[:team_id])
render_404 unless @team
@repositories = @team.repositories.order(created_at: :asc)
@ -273,6 +283,10 @@ class RepositoriesController < ApplicationController
render_403 unless can_view_team_repositories(@team)
end
def check_view_permissions
render_403 unless can_view_repository(@repository)
end
def check_create_permissions
render_403 unless can_create_repository(@team)
end
@ -299,4 +313,66 @@ class RepositoriesController < ApplicationController
status: :unprocessable_entity
end
end
def generate_zip
# Fetch rows in the same order as in the currently viewed datatable
ordered_row_ids = params[:row_ids]
id_row_map = RepositoryRow.where(id: ordered_row_ids,
repository: @repository)
.index_by(&:id)
ordered_rows = ordered_row_ids.collect { |id| id_row_map[id.to_i] }
zip = ZipExport.create(user: current_user)
zip.generate_exportable_zip(
current_user,
to_csv(ordered_rows, params[:header_ids]),
:repositories
)
end
def to_csv(rows, column_ids)
require 'csv'
# Parse column names
csv_header = []
column_ids.each do |c_id|
csv_header << case c_id.to_i
when -1, -2
next
when -3
I18n.t('repositories.table.row_name')
when -4
I18n.t('repositories.table.added_by')
when -5
I18n.t('repositories.table.added_on')
else
column = RepositoryColumn.find_by_id(c_id)
column ? column.name : nil
end
end
CSV.generate do |csv|
csv << csv_header
rows.each do |row|
csv_row = []
column_ids.each do |c_id|
csv_row << case c_id.to_i
when -1, -2
next
when -3
row.name
when -4
row.created_by.full_name
when -5
I18n.l(row.created_at, format: :full)
else
cell = row.repository_cells
.find_by(repository_column_id: c_id)
cell ? cell.value.data : nil
end
end
csv << csv_row
end
end
end
end

View file

@ -113,11 +113,11 @@ class RepositoryColumnsController < ApplicationController
end
def check_update_permissions
render_403 unless can_edit_columns_in_repository(@repository)
render_403 unless can_edit_column_in_repository(@repository_column)
end
def check_destroy_permissions
render_403 unless can_delete_columns_in_repository(@repository)
render_403 unless can_delete_column_in_repository(@repository_column)
end
def repository_column_params

View file

@ -21,7 +21,6 @@ class RepositoryRowsController < ApplicationController
record.name = record_params[:name] unless record_params[:name].blank?
unless record.save
errors[:default_fields] = record.errors.messages
raise ActiveRecord::RecordInvalid
end
if params[:repository_cells]
params[:repository_cells].each do |key, value|
@ -37,29 +36,31 @@ class RepositoryRowsController < ApplicationController
repository_column: column
}
)
unless cell_value.save
if cell_value.save
record_annotation_notification(record, cell_value.repository_cell)
else
errors[:repository_cells] << {
"#{cell.repository_column.id}": cell_value.errors.messages
"#{column.id}": cell_value.errors.messages
}
raise ActiveRecord::RecordInvalid
end
record_annotation_notification(record, cell_value.repository_cell)
end
end
end
respond_to do |format|
format.json do
render json: { id: record.id,
flash: t('repositories.create.success_flash',
record: escape_input(record.name),
repository: escape_input(@repository.name)) },
status: :ok
if errors[:default_fields].empty? && errors[:repository_cells].empty?
render json: { id: record.id,
flash: t('repositories.create.success_flash',
record: escape_input(record.name),
repository: escape_input(@repository.name)) },
status: :ok
else
render json: errors,
status: :bad_request
end
end
end
rescue
respond_to do |format|
format.json { render json: errors, status: :bad_request }
end
end
def edit
@ -94,7 +95,6 @@ class RepositoryRowsController < ApplicationController
@record.name = record_params[:name].blank? ? nil : record_params[:name]
unless @record.save
errors[:default_fields] = @record.errors.messages
raise ActiveRecord::RecordInvalid
end
if params[:repository_cells]
params[:repository_cells].each do |key, value|
@ -104,13 +104,14 @@ class RepositoryRowsController < ApplicationController
if existing
# Cell exists and new value present, so update value
existing.value.data = value
unless existing.value.save
if existing.value.save
record_annotation_notification(@record, existing)
else
errors[:repository_cells] << {
"#{cell.repository_column_id}": existing.value.errors.messages
"#{existing.repository_column_id}":
existing.value.errors.messages
}
raise ActiveRecord::RecordInvalid
end
record_annotation_notification(@record, existing)
else
# Looks like it is a new cell, so we need to create new value, cell
# will be created automatically
@ -126,13 +127,13 @@ class RepositoryRowsController < ApplicationController
repository_column: column
}
)
unless value.save
if value.save
record_annotation_notification(@record, value.repository_cell)
else
errors[:repository_cells] << {
"#{cell.repository_column_id}": value.errors.messages
"#{column.id}": value.errors.messages
}
raise ActiveRecord::RecordInvalid
end
record_annotation_notification(@record, value.repository_cell)
end
end
# Clean up empty cells, not present in updated record
@ -145,24 +146,26 @@ class RepositoryRowsController < ApplicationController
end
end
# Row sucessfully updated, so sending response to client
respond_to do |format|
format.json do
render json: {
id: @record.id,
flash: t(
'repositories.update.success_flash',
record: escape_input(@record.name),
repository: escape_input(@repository.name)
)
},
status: :ok
if errors[:default_fields].empty? && errors[:repository_cells].empty?
# Row sucessfully updated, so sending response to client
render json: {
id: @record.id,
flash: t(
'repositories.update.success_flash',
record: escape_input(@record.name),
repository: escape_input(@repository.name)
)
},
status: :ok
else
# Errors
render json: errors,
status: :bad_request
end
end
end
rescue
respond_to do |format|
format.json { render json: errors, status: :bad_request }
end
end
def delete_records
@ -175,7 +178,8 @@ class RepositoryRowsController < ApplicationController
end
end
if deleted_count.zero?
flash = t('repositories.destroy.no_deleted_records_flash')
flash = t('repositories.destroy.no_deleted_records_flash',
other_records_number: params[:selected_rows].count)
elsif deleted_count != params[:selected_rows].count
not_deleted_count = params[:selected_rows].count - deleted_count
flash = t('repositories.destroy.contains_other_records_flash',
@ -186,7 +190,8 @@ class RepositoryRowsController < ApplicationController
records_number: deleted_count)
end
respond_to do |format|
format.json { render json: { flash: flash }, status: :ok }
color = deleted_count.zero? ? 'info' : 'success'
format.json { render json: { flash: flash, color: color }, status: :ok }
end
else
respond_to do |format|
@ -219,7 +224,7 @@ class RepositoryRowsController < ApplicationController
end
def check_edit_permissions
render_403 unless can_edit_repository_records(@repository)
render_403 unless can_edit_repository_record(@record)
end
def check_destroy_permissions

View file

@ -26,53 +26,27 @@ class ResultAssetsController < ApplicationController
end
def create
@asset = Asset.new(result_params[:asset_attributes])
@asset.created_by = current_user
@asset.last_modified_by = current_user
@asset.team = current_team
@result = Result.new(
user: current_user,
my_module: @my_module,
name: result_params[:name],
asset: @asset
)
@result.last_modified_by = current_user
obj = create_multiple_results
respond_to do |format|
if (@result.save and @asset.save) then
# Post process file here
@asset.post_process_file(@my_module.experiment.project.team)
# Generate activity
Activity.create(
type_of: :add_result,
user: current_user,
project: @my_module.experiment.project,
experiment: @my_module.experiment,
my_module: @my_module,
message: t(
"activities.add_asset_result",
user: current_user.full_name,
result: @result.name,
)
)
if obj.fetch(:status)
format.html do
flash[:success] = t(
"result_assets.create.success_flash",
module: @my_module.name)
flash[:success] = t('result_assets.create.success_flash',
module: @my_module.name)
redirect_to results_my_module_path(@my_module)
end
format.json do
render json: {
status: 'ok',
html: render_to_string(
partial: 'my_modules/result.html.erb', locals: { result: @result }
partial: 'my_modules/results.html.erb',
locals: { results: obj.fetch(:results) }
)
}, status: :ok
end
else
format.json { render json: @result.errors, status: :bad_request }
flash[:error] = t('result_assets.error_flash')
format.json do
render json: {}, status: :bad_request
end
end
end
end
@ -249,4 +223,40 @@ class ResultAssetsController < ApplicationController
]
)
end
def create_multiple_results
success = true
results = []
params[:results_files].each_with_index do |file, index|
asset = Asset.new(file: file.second,
created_by: current_user,
last_modified_by: current_user,
team: current_team)
result = Result.new(user: current_user,
my_module: @my_module,
name: params[:results_names][index.to_s],
asset: asset,
last_modified_by: current_user)
if result.save && asset.save
results << result
# Post process file here
asset.post_process_file(@my_module.experiment.project.team)
# Generate activity
Activity.create(
type_of: :add_result,
user: current_user,
project: @my_module.experiment.project,
experiment: @my_module.experiment,
my_module: @my_module,
message: t('activities.add_asset_result',
user: current_user.full_name,
result: result.name)
)
else
success = false
end
end
{ status: success, results: results }
end
end

View file

@ -44,7 +44,6 @@ class StepsController < ApplicationController
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|

View file

@ -262,26 +262,20 @@ class TeamsController < ApplicationController
end
def export_samples
require "csv"
respond_to do |format|
if params[:sample_ids].present? and params[:header_ids].present?
samples = []
params[:sample_ids].each do |id|
sample = Sample.find_by_id(id)
if sample
samples << sample
end
end
format.csv { send_data @team.to_csv(samples, params[:header_ids]) }
else
format.csv { render nothing: true }
end
if params[:sample_ids] && params[:header_ids]
generate_samples_zip
else
flash[:alert] = t('zip_export.export_error')
end
redirect_to :back
end
def routing_error(error = 'Routing error', status = :not_found, exception=nil)
redirect_to root_path
end
private
def load_vars
@team = Team.find_by_id(params[:id])
@ -302,8 +296,12 @@ class TeamsController < ApplicationController
end
end
def routing_error(error = 'Routing error', status = :not_found, exception=nil)
redirect_to root_path
def generate_samples_zip
zip = ZipExport.create(user: current_user)
zip.generate_exportable_zip(
current_user,
@team.to_csv(Sample.where(id: params[:sample_ids]), params[:header_ids]),
:samples
)
end
end

View file

@ -102,7 +102,7 @@ class RepositoryDatatable < AjaxDatatablesRails::Base
records.map do |record|
row = {
'DT_RowId': record.id,
'1': @my_module ? assigned_row(record) : '',
'1': assigned_row(record),
'2': escape_input(record.name),
'3': I18n.l(record.created_at, format: :full),
'4': escape_input(record.created_by.full_name),
@ -118,9 +118,12 @@ class RepositoryDatatable < AjaxDatatablesRails::Base
# Add custom columns
record.repository_cells.each do |cell|
row[@columns_mappings[cell.repository_column.id]] =
custom_auto_link(cell.value.data,
simple_format: true,
team: @team)
custom_auto_link(
display_tooltip(cell.value.data,
Constants::NAME_MAX_LENGTH),
simple_format: true,
team: @team
)
end
row
end
@ -138,18 +141,14 @@ class RepositoryDatatable < AjaxDatatablesRails::Base
# after that "data" function will return json
def get_raw_records
repository_rows = RepositoryRow
.includes(
.preload(
:repository_columns,
:created_by
# repository_cells: :value
).references(
:repository_columns,
:created_by
:created_by,
repository_cells: :value
)
.joins(:created_by)
.where(repository: @repository)
@assigned_rows = @my_module.repository_rows if @my_module
# Make mappings of custom columns, so we have same id for every column
i = 5
@columns_mappings = {}
@ -157,6 +156,24 @@ class RepositoryDatatable < AjaxDatatablesRails::Base
@columns_mappings[column.id] = i.to_s
i += 1
end
if @my_module
@assigned_rows = @my_module.repository_rows
.preload(
:repository_columns,
:created_by,
repository_cells: :value
)
.joins(:created_by)
.where(repository: @repository)
return @assigned_rows if params[:assigned] == 'assigned'
else
@assigned_rows = repository_rows.joins(
'INNER JOIN my_module_repository_rows ON
(repository_rows.id = my_module_repository_rows.repository_row_id)'
)
end
repository_rows
end
@ -166,32 +183,73 @@ class RepositoryDatatable < AjaxDatatablesRails::Base
# number of samples/all samples it's dependant upon sort_record query
def fetch_records
records = get_raw_records
records = @assigned_rows if @my_module && params[:assigned] == 'assigned'
records = filter_records(records) if params[:search].present?
records = sort_records(records) if params[:order].present?
records = paginate_records(records) unless params[:length].present? &&
params[:length] == '-1'
escape_special_chars
records = filter_records(records) if params[:search].present? &&
!sorting_by_custom_column
records = paginate_records(records) if !(params[:length].present? &&
params[:length] == '-1') &&
!sorting_by_custom_column
records
end
# Overriden to make it work for custom columns, because they are polymorphic
# NOTE: Function assumes the provided records/rows are only from the current
# repository!
def filter_records(repo_rows)
return repo_rows unless params[:search].present? &&
params[:search][:value].present?
search_val = params[:search][:value]
filtered_rows = repo_rows.find_by_sql(
"SELECT DISTINCT repository_rows.*
FROM repository_rows
INNER JOIN (
SELECT users.*
FROM users
) AS users
ON users.id = repository_rows.created_by_id
LEFT OUTER JOIN (
SELECT repository_cells.repository_row_id,
repository_text_values.data AS text_value,
to_char(repository_date_values.data, 'DD.MM.YYYY HH24:MI')
AS date_value
FROM repository_cells
INNER JOIN repository_text_values
ON repository_text_values.id = repository_cells.value_id
FULL OUTER JOIN repository_date_values
ON repository_date_values.id = repository_cells.value_id
) AS values
ON values.repository_row_id = repository_rows.id
WHERE repository_rows.repository_id = #{@repository.id}
AND (repository_rows.name ILIKE '%#{search_val}%'
OR to_char(repository_rows.created_at, 'DD.MM.YYYY HH24:MI')
ILIKE '%#{search_val}%'
OR users.full_name ILIKE '%#{search_val}%'
OR text_value ILIKE '%#{search_val}%'
OR date_value ILIKE '%#{search_val}%')"
)
repo_rows.where(id: filtered_rows)
end
# Override default sort method if needed
def sort_records(records)
if params[:order].present? && params[:order].length == 1
if sort_column(params[:order].values[0]) == ASSIGNED_SORT_COL
# If "assigned" column is sorted
direction = sort_null_direction(params[:order].values[0])
if @my_module
# Depending on the sort, order nulls first or
# nulls last on repository_cells association
direction = sort_null_direction(params[:order].values[0])
records.joins(
"LEFT OUTER JOIN my_module_repository_rows ON
(repository_rows.id = my_module_repository_rows.repository_row_id
AND (my_module_repository_rows.my_module_id = #{@my_module.id} OR
my_module_repository_rows.id IS NULL))"
).order("my_module_repository_rows.id NULLS #{direction}")
else
records.joins(
'LEFT OUTER JOIN my_module_repository_rows ON
(repository_rows.id = my_module_repository_rows.repository_row_id)'
).order("my_module_repository_rows.id NULLS #{direction}")
end
elsif sorting_by_custom_column
# Check if have to filter records first
@ -237,18 +295,14 @@ class RepositoryDatatable < AjaxDatatablesRails::Base
# as sq ORDER BY CASE WHEN sq.custom_field_id = #{column_id} THEN 1 ELSE 2 END #{dir}, sq.value #{dir}
# LIMIT #{per_page} OFFSET #{offset}")
RepositoryRow.find_by_sql(
"SELECT repository_rows.*, values.value AS value
FROM repository_rows
LEFT OUTER JOIN (SELECT repository_cells.*,
records.joins(
"LEFT OUTER JOIN (SELECT repository_cells.repository_row_id,
repository_text_values.data AS value FROM repository_cells
INNER JOIN repository_text_values
ON repository_text_values.id = repository_cells.value_id
WHERE repository_cells.repository_column_id = #{column_id}) AS values
ON values.repository_row_id = repository_rows.id
WHERE repository_rows.repository_id = #{@repository.id}
ORDER BY value #{dir} LIMIT #{per_page} OFFSET #{offset}"
)
ON values.repository_row_id = repository_rows.id"
).order("values.value #{dir}")
else
super(records)
end

View file

@ -21,6 +21,7 @@ class SampleDatatable < AjaxDatatablesRails::Base
'regex' => false,
'caseInsensitive' => true },
'columns' => [],
'assigned' => 'all',
'ColReorder' => [*0..6]
}
7.times do
@ -178,10 +179,14 @@ class SampleDatatable < AjaxDatatablesRails::Base
@assigned_samples = @my_module.samples
samples = samples.joins("LEFT OUTER JOIN sample_my_modules ON
(samples.id = sample_my_modules.sample_id AND
(sample_my_modules.my_module_id = #{@my_module.id.to_s} OR
sample_my_modules.id IS NULL))")
.references(:sample_my_modules)
(samples.id = sample_my_modules.sample_id AND
(sample_my_modules.my_module_id =
#{@my_module.id} OR
sample_my_modules.id IS NULL))")
.references(:sample_my_modules)
if params[:assigned] == 'assigned'
samples = samples.where('"sample_my_modules"."id" > 0')
end
elsif @project
@assigned_samples = @project.assigned_samples
ids = @project.my_modules_ids
@ -198,6 +203,16 @@ class SampleDatatable < AjaxDatatablesRails::Base
sample_my_modules.id IS NULL))")
.references(:sample_my_modules)
end
if params[:assigned] == 'assigned'
samples = samples.joins('LEFT OUTER JOIN "my_modules" ON
"my_modules"."id" =
"sample_my_modules"."my_module_id"')
.joins('LEFT OUTER JOIN "experiments" ON
"experiments"."id" =
"my_modules"."experiment_id"')
.where('"experiments"."project_id" = ?', @project.id)
.where('"my_modules"."nr_of_assigned_samples" > 0')
end
elsif @experiment
@assigned_samples = @experiment.assigned_samples
ids = @experiment.my_modules.select(:id)
@ -207,6 +222,14 @@ class SampleDatatable < AjaxDatatablesRails::Base
(sample_my_modules.my_module_id IN (#{ids.to_sql}) OR
sample_my_modules.id IS NULL))")
.references(:sample_my_modules)
if params[:assigned] == 'assigned'
samples = samples.joins('LEFT OUTER JOIN "my_modules" ON
"my_modules"."id" =
"sample_my_modules"."my_module_id"')
.where('"my_modules"."experiment_id" = ?',
@experiment.id)
.where('"my_modules"."nr_of_assigned_samples" > 0')
end
end
# Make mappings of custom fields, so we have same id for every column
@ -297,9 +320,9 @@ class SampleDatatable < AjaxDatatablesRails::Base
.distinct
# check the input param and merge the two arrays of ids
if params[:order].values[0]["dir"] == "asc"
if params[:order].values[0]['dir'] == 'asc'
ids = assigned + unassigned
elsif params[:order].values[0]["dir"] == "desc"
elsif params[:order].values[0]['dir'] == 'desc'
ids = unassigned + assigned
end
ids = ids.collect { |s| s.id }

View file

@ -1,13 +1,20 @@
module ActivityHelper
def activity_truncate(message, len = Constants::NAME_TRUNCATION_LENGTH)
activity_title = message.match(/<strong>(.*?)<\/strong>/)[1]
if activity_title.length > Constants::NAME_TRUNCATION_LENGTH
title = "<div class='modal-tooltip'>#{truncate(activity_title, length: len)}
<span class='modal-tooltiptext'>#{activity_title}</span></div>"
else
title = truncate(activity_title, length: len)
activity_titles = message.scan(/<strong>(.*?)<\/strong>/)
activity_titles.each do |activity_title|
activity_title = activity_title[0]
if activity_title.length > Constants::NAME_TRUNCATION_LENGTH
title = "<div class='modal-tooltip'>
#{truncate(activity_title, length: len)}
<span class='modal-tooltiptext'>
#{activity_title}
</span>
</div>"
else
title = truncate(activity_title, length: len)
end
message = message.gsub(/#{Regexp.escape(activity_title)}/, title)
end
message = message.gsub(/#{activity_title}/, title )
sanitize_input(message) if message
end

View file

@ -17,7 +17,7 @@ module ApplicationHelper
end
def display_tooltip(message, len = Constants::NAME_TRUNCATION_LENGTH)
if message.strip.length > Constants::NAME_TRUNCATION_LENGTH
if message.strip.length > len
sanitize_input("<div class='modal-tooltip'> \
#{truncate(message.strip, length: len)} \
<span class='modal-tooltiptext'> \
@ -233,11 +233,29 @@ module ApplicationHelper
raw(user_description) + raw('" >') + user_name + raw('</a>')
end
# Dirty, dirty hack for displaying images in reports
def user_avatar_absolute_url(user, style)
unless user.avatar(style) == '/images/icon_small/missing.png'
return user.avatar(style)
prefix = ''
if ENV['PAPERCLIP_STORAGE'].present? &&
ENV['MAIL_SERVER_URL'].present? &&
ENV['PAPERCLIP_STORAGE'] != 'filesystem'
prefix = ENV['MAIL_SERVER_URL']
end
URI.join(Rails.application.routes.url_helpers.root_url,
"/images/#{style}/missing.png").to_s
# for development
prefix = 'localhost:3000' if ENV['MAIL_SERVER_URL'] == 'localhost'
if !prefix.empty? &&
!prefix.include?('http://') &&
!prefix.include?('https://')
prefix = if respond_to?(:request) && request.ssl?
"https://#{prefix}"
else
"http://#{prefix}"
end
end
unless user.avatar(style) == '/images/icon_small/missing.png'
return user.avatar(style, timeout: Constants::URL_LONG_EXPIRE_TIME)
end
url_for(prefix + "/images/#{style}/missing.png")
end
end

View file

@ -1058,12 +1058,8 @@ module PermissionHelper
team.repositories.count < Constants::REPOSITORIES_LIMIT
end
def can_view_repositories(team)
is_normal_user_or_admin_of_team(team)
end
def can_view_repository(repository)
is_normal_user_or_admin_of_team(repository.team)
is_member_of_team(repository.team)
end
def can_edit_and_destroy_repository(repository)
@ -1078,12 +1074,12 @@ module PermissionHelper
is_normal_user_or_admin_of_team(repository.team)
end
def can_delete_columns_in_repository(repository)
is_normal_user_or_admin_of_team(repository.team)
def can_delete_column_in_repository(column)
is_normal_user_or_admin_of_team(column.repository.team)
end
def can_edit_columns_in_repository(repository)
is_normal_user_or_admin_of_team(repository.team)
def can_edit_column_in_repository(column)
is_normal_user_or_admin_of_team(column.repository.team)
end
def can_create_repository_records(repository)
@ -1094,8 +1090,8 @@ module PermissionHelper
is_normal_user_or_admin_of_team(repository.team)
end
def can_edit_repository_records(repository)
is_normal_user_or_admin_of_team(repository.team)
def can_edit_repository_record(record)
is_normal_user_or_admin_of_team(record.repository.team)
end
def can_delete_repository_records(repository)
@ -1109,12 +1105,12 @@ module PermissionHelper
end
def can_assign_repository_records(my_module, repository)
can_edit_repository_records(repository) &&
can_delete_repository_records(repository) &&
is_technician_or_higher_of_project(my_module.experiment.project)
end
def can_unassign_repository_records(my_module, repository)
can_edit_repository_records(repository) &&
can_delete_repository_records(repository) &&
is_technician_or_higher_of_project(my_module.experiment.project)
end
end

View file

@ -31,17 +31,21 @@ module SidebarHelper
end
def module_action_to_link_to(my_module)
case action_name
when 'results'
return results_my_module_url(my_module)
when 'activities'
return activities_my_module_url(my_module)
when 'samples'
return samples_my_module_url(my_module)
when 'archive', 'module_archive', 'experiment_archive'
return archive_my_module_url(my_module)
if action_name == 'results'
results_my_module_url(my_module)
elsif action_name == 'activities'
activities_my_module_url(my_module)
elsif action_name == 'samples'
samples_my_module_url(my_module)
elsif action_name.in?(%w(archive module_archive experiment_archive))
archive_my_module_url(my_module)
elsif action_name == 'repository' && @repository
repository_my_module_url(
id: my_module.id,
repository_id: @repository.id
)
else
return protocols_my_module_url(my_module)
protocols_my_module_url(my_module)
end
end
end

View file

@ -18,6 +18,7 @@ module TinyMceHelper
end
def generate_image_tag_from_token(text)
return unless text
regex = /\[~tiny_mce_id:([0-9a-zA-Z]+)\]/
text.gsub(regex) do |el|
match = el.match(regex)

View file

@ -84,8 +84,6 @@ class ReportElement < ActiveRecord::Base
if parent_model == 'experiment'
destroy unless send(parent_model).project == report.project
elsif parent_model == 'step'
destroy unless send(parent_model).completed
else
destroy unless (send(parent_model).active? rescue send(parent_model))
end

View file

@ -14,39 +14,41 @@ class RepositoryTableState < ActiveRecord::Base
end
def self.update_state(custom_column, column_index, user)
table_state = RepositoryTableState.where(
user: user,
# table state of every user having access to this repository needs udpating
table_states = RepositoryTableState.where(
repository: custom_column.repository
)
return if table_state.empty?
repository_state = table_state.first['state']
if column_index
# delete column
repository_state['columns'].delete(column_index)
repository_state['columns'].keys.each do |index|
if index.to_i > column_index.to_i
repository_state['columns'][(index.to_i - 1).to_s] =
repository_state['columns'].delete(index)
else
index
table_states.each do |table_state|
repository_state = table_state['state']
if column_index
# delete column
repository_state['columns'].delete(column_index)
repository_state['columns'].keys.each do |index|
if index.to_i > column_index.to_i
repository_state['columns'][(index.to_i - 1).to_s] =
repository_state['columns'].delete(index)
else
index
end
end
end
repository_state['ColReorder'].delete(column_index)
repository_state['ColReorder'].map! do |index|
if index.to_i > column_index.to_i
(index.to_i - 1).to_s
else
index
repository_state['ColReorder'].delete(column_index)
repository_state['ColReorder'].map! do |index|
if index.to_i > column_index.to_i
(index.to_i - 1).to_s
else
index
end
end
else
# add column
index = repository_state['columns'].count
repository_state['columns'][index] = RepositoryDatatable::
REPOSITORY_TABLE_DEFAULT_STATE['columns'].first
repository_state['ColReorder'].insert(2, index)
end
else
# add column
index = repository_state['columns'].count
repository_state['columns'][index] = RepositoryDatatable::
REPOSITORY_TABLE_DEFAULT_STATE['columns'].first
repository_state['ColReorder'].insert(2, index)
table_state.update(state: repository_state)
end
table_state.first.update(state: repository_state)
end
def self.create_state(user, repository)

View file

@ -101,4 +101,14 @@ class ZipExport < ActiveRecord::Base
end
end
end
def generate_samples_zip(tmp_dir, data, _options = {})
file = FileUtils.touch("#{tmp_dir}/export.csv").first
File.open(file, 'wb') { |f| f.write(data) }
end
def generate_repositories_zip(tmp_dir, data, _options = {})
file = FileUtils.touch("#{tmp_dir}/export.csv").first
File.open(file, 'wb') { |f| f.write(data) }
end
end

View file

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

View file

@ -1,4 +1,6 @@
<% provide(:head_title, t("my_modules.protocols.head_title", project: h(@project.name), module: h(@my_module.name)).html_safe) %>
<%= render partial: 'shared/drag_n_drop_overlay' %>
<%= render partial: "shared/sidebar" %>
<%= render partial: "shared/secondary_navigation" %>

View file

@ -1,6 +1,7 @@
<% provide(:head_title, t("my_modules.results.head_title", project: h(@project.name), module: h(@my_module.name)).html_safe) %>
<%= render partial: "shared/sidebar" %>
<%= render partial: "shared/secondary_navigation" %>
<%= render partial: 'shared/drag_n_drop_overlay' %>
<div id="results-toolbar">
<div class="pull-right">

View file

@ -1,5 +1,6 @@
<% provide(:head_title, t("protocols.edit.head_title")) %>
<%= render partial: 'shared/drag_n_drop_overlay' %>
<%= render partial: "protocols/breadcrumbs.html.erb",
locals: { teams: @teams,
current_team: @protocol.team,

View file

@ -6,7 +6,7 @@
<div class="report-element-header">
<div class="row">
<div class="pull-left activity-icon">
<span class="glyphicon glyphicon glyphicon-equalizer"></span>
<span class="glyphicon glyphicon-equalizer"></span>
</div>
<div class="pull-left activity-name">
<%=t "projects.reports.elements.module_activity.name", my_module: my_module.name %>

View file

@ -1,7 +1,7 @@
<% if my_module.blank? and @my_module.present? then my_module = @my_module end %>
<% timestamp = my_module.created_at %>
<% name = my_module.name %>
<div class="report-element report-module-element" data-ts="<%= timestamp.to_i %>" data-type="my_module" data-id='{ "my_module_id": <%= my_module.id %> }' data-scroll-id="<%= my_module.id %>" data-modal-title="<%=t "projects.reports.elements.modals.module_contents.head_title", module: my_module.name %>" data-name="<%= name %>" data-icon-class="glyphicon-credit-card">
<div class="report-element report-module-element" data-ts="<%= timestamp.to_i %>" data-type="my_module" data-id='{ "my_module_id": <%= my_module.id %> }' data-scroll-id="<%= my_module.id %>" data-modal-title="<%=t "projects.reports.elements.modals.module_contents.head_title", module: my_module.name %>" data-name="<%= name %>" data-icon-class="glyphicon glyphicon-credit-card">
<div class="report-element-header">
<div class="row">
<div class="pull-left user-time">

View file

@ -4,11 +4,11 @@
<% if order.blank? and @order.present? then order = @order end %>
<% timestamp = Time.current + 1.year - 1.days %>
<% rows_json = my_module.repository_json_hot(repository.id, order) %>
<div class="report-element report-module-repository-element" data-sort-hot="1" data-ts="<%= timestamp.to_i %>" data-type="my_module_repository" data-id='{ "my_module_id": <%= my_module.id %>, "repository_id": <%= repository.id %> }' data-scroll-id="<%= "#{my_module.id}_#{repository.id}" %>" data-order="<%= order == :asc ? "asc" : "desc" %>" data-name="<%= repository.name %>" data-icon-class="glyphicon-oil">
<div class="report-element report-module-repository-element" data-sort-hot="1" data-ts="<%= timestamp.to_i %>" data-type="my_module_repository" data-id='{ "my_module_id": <%= my_module.id %>, "repository_id": <%= repository.id %> }' data-scroll-id="<%= "#{my_module.id}_#{repository.id}" %>" data-order="<%= order == :asc ? "asc" : "desc" %>" data-name="<%= repository.name %>" data-icon-class="fa fa-cubes">
<div class="report-element-header">
<div class="row">
<div class="pull-left repository-icon">
<span class="glyphicon glyphicon-oil"></span>
<span class="fa fa-cubes"></span>
</div>
<div class="pull-left repository-name">
<%=t "projects.reports.elements.module_repository.name", repository: repository.name, my_module: my_module.name %>

View file

@ -4,7 +4,7 @@
<% comments = result.result_comments %>
<% timestamp = asset.created_at %>
<% name = result.name %>
<% icon_class = is_image ? "glyphicon-picture" : "glyphicon-file" %>
<% icon_class = 'glyphicon ' + (is_image ? 'glyphicon-picture' : 'glyphicon-file') %>
<div class="report-element report-result-element report-result-asset-element" data-ts="<%= timestamp.to_i %>" data-type="result_asset" data-id='{ "result_id": <%= result.id %> }' data-scroll-id="<%= result.id %>" data-modal-title="<%=t "projects.reports.elements.modals.result_contents.head_title", result: result.name %>" data-name="<%= name %>" data-icon-class="<%= icon_class %>">
<div class="report-element-header">
<div class="row">

View file

@ -3,7 +3,7 @@
<% comments = result.result_comments %>
<% timestamp = table.created_at %>
<% name = result.name %>
<div class="report-element report-result-element report-result-table-element" data-ts="<%= timestamp.to_i %>" data-type="result_table" data-id='{ "result_id": <%= result.id %> }' data-scroll-id="<%= result.id %>" data-modal-title="<%=t "projects.reports.elements.modals.result_contents.head_title", result: result.name %>" data-name="<%= name %>" data-icon-class="glyphicon-th">
<div class="report-element report-result-element report-result-table-element" data-ts="<%= timestamp.to_i %>" data-type="result_table" data-id='{ "result_id": <%= result.id %> }' data-scroll-id="<%= result.id %>" data-modal-title="<%=t "projects.reports.elements.modals.result_contents.head_title", result: result.name %>" data-name="<%= name %>" data-icon-class="glyphicon glyphicon-th">
<div class="report-element-header">
<div class="row">
<div class="pull-left result-name-container">

View file

@ -3,7 +3,7 @@
<% comments = result.result_comments %>
<% timestamp = result.created_at %>
<% name = result.name %>
<div class="report-element report-result-element report-result-text-element" data-ts="<%= timestamp.to_i %>" data-type="result_text" data-id='{ "result_id": <%= result.id %> }' data-scroll-id="<%= result.id %>" data-modal-title="<%=t "projects.reports.elements.modals.result_contents.head_title", result: result.name %>" data-name="<%= name %>" data-icon-class="glyphicon-asterisk">
<div class="report-element report-result-element report-result-text-element" data-ts="<%= timestamp.to_i %>" data-type="result_text" data-id='{ "result_id": <%= result.id %> }' data-scroll-id="<%= result.id %>" data-modal-title="<%=t "projects.reports.elements.modals.result_contents.head_title", result: result.name %>" data-name="<%= name %>" data-icon-class="glyphicon glyphicon-asterisk">
<div class="report-element-header">
<div class="row">
<div class="pull-left result-icon">

View file

@ -2,7 +2,7 @@
<% if order.blank? and @order.present? then order = @order end %>
<% timestamp = Time.current + 1.year - 1.days %>
<% samples_json = my_module.samples_json_hot(order) %>
<div class="report-element report-module-samples-element" data-sort-hot="3" data-ts="<%= timestamp.to_i %>" data-type="my_module_samples" data-id='{ "my_module_id": <%= my_module.id %> }' data-scroll-id="<%= my_module.id %>" data-order="<%= order == :asc ? "asc" : "desc" %>" data-name="<%=t "projects.reports.elements.module_samples.sidebar_name" %>" data-icon-class="glyphicon-tint">
<div class="report-element report-module-samples-element" data-sort-hot="3" data-ts="<%= timestamp.to_i %>" data-type="my_module_samples" data-id='{ "my_module_id": <%= my_module.id %> }' data-scroll-id="<%= my_module.id %>" data-order="<%= order == :asc ? "asc" : "desc" %>" data-name="<%=t "projects.reports.elements.module_samples.sidebar_name" %>" data-icon-class="glyphicon glyphicon-tint">
<div class="report-element-header">
<div class="row">
<div class="pull-left samples-icon">

View file

@ -6,7 +6,7 @@
<% assets = step.assets %>
<% checklists = step.checklists %>
<% comments = step.step_comments %>
<div class="report-element report-step-element" data-ts="<%= timestamp.to_i %>" data-type="step" data-id='{ "step_id": <%= step.id %> }' data-scroll-id="<%= step.id %>" data-modal-title="<%=t "projects.reports.elements.modals.step_contents.head_title", step: step.name %>" data-name="<%=t "projects.reports.elements.step.sidebar_name", pos: (step.position + 1), name: step.name %>" data-icon-class="glyphicon-circle-arrow-right">
<div class="report-element report-step-element" data-ts="<%= timestamp.to_i %>" data-type="step" data-id='{ "step_id": <%= step.id %> }' data-scroll-id="<%= step.id %>" data-modal-title="<%=t "projects.reports.elements.modals.step_contents.head_title", step: step.name %>" data-name="<%=t "projects.reports.elements.step.sidebar_name", pos: (step.position + 1), name: step.name %>" data-icon-class="glyphicon glyphicon-circle-arrow-right">
<div class="report-element-header">
<div class="row">
<div class="pull-left user-time">

View file

@ -1,6 +1,6 @@
<% if project.blank? and @project.present? then project = @project end %>
<% name = t("projects.reports.elements.project_header.title", project: project.name) %>
<div class="report-element report-project-header-element" data-ts="ignore" data-type="project_header" data-id='{ "project_id": <%= project.id %> }' data-scroll-id="<%= project.id %>" data-name="<%= name %>" data-icon-class="glyphicon-header">
<div class="report-element report-project-header-element" data-ts="ignore" data-type="project_header" data-id='{ "project_id": <%= project.id %> }' data-scroll-id="<%= project.id %>" data-name="<%= name %>" data-icon-class="glyphicon glyphicon-header">
<div class="report-element-header">
<div class="row">
<div class="pull-left user-time">

View file

@ -2,7 +2,7 @@
<% if order.blank? and @order.present? then order = @order end %>
<% comments = result.result_comments.order(created_at: order) %>
<% timestamp = Time.current + 1.year %>
<div class="report-element report-comments-element report-result-comments-element" data-ts="<%= timestamp.to_i %>" data-order="<%= order == :asc ? "asc" : "desc" %>" data-type="result_comments" data-id='{ "result_id": <%= result.id %> }' data-scroll-id="<%= result.id %>" data-name="<%=t "projects.reports.elements.result_comments.sidebar_name" %>" data-icon-class="glyphicon-comment">
<div class="report-element report-comments-element report-result-comments-element" data-ts="<%= timestamp.to_i %>" data-order="<%= order == :asc ? "asc" : "desc" %>" data-type="result_comments" data-id='{ "result_id": <%= result.id %> }' data-scroll-id="<%= result.id %>" data-name="<%=t "projects.reports.elements.result_comments.sidebar_name" %>" data-icon-class="glyphicon glyphicon-comment">
<div class="report-element-header">
<div class="row">
<div class="pull-left comments-icon">

View file

@ -1,7 +1,7 @@
<% if asset.blank? and @asset.present? then asset = @asset end %>
<% is_image = asset.is_image? %>
<% timestamp = asset.created_at %>
<% icon_class = is_image ? 'glyphicon-picture' : 'glyphicon-file' %>
<% icon_class = 'glyphicon ' + (is_image ? 'glyphicon-picture' : 'glyphicon-file') %>
<div class="report-element report-step-attachment-element report-step-asset-element" data-ts="<%= timestamp.to_i %>" data-type="step_asset" data-id='{ "asset_id": <%= asset.id %> }' data-scroll-id="<%= asset.id %>" data-name="<%=t "projects.reports.elements.step_asset.sidebar_name", file: asset.file_file_name %>" data-icon-class="<%= icon_class %>">
<div class="report-element-header">
<div class="row">

View file

@ -1,7 +1,7 @@
<% if checklist.blank? and @checklist.present? then checklist = @checklist end %>
<% items = checklist.checklist_items %>
<% timestamp = checklist.created_at %>
<div class="report-element report-step-attachment-element report-step-checklist-element" data-ts="<%= timestamp.to_i %>" data-type="step_checklist" data-id='{ "checklist_id": <%= checklist.id %> }' data-scroll-id="<%= checklist.id %>" data-name="<%= checklist.name %>" data-icon-class="glyphicon-list">
<div class="report-element report-step-attachment-element report-step-checklist-element" data-ts="<%= timestamp.to_i %>" data-type="step_checklist" data-id='{ "checklist_id": <%= checklist.id %> }' data-scroll-id="<%= checklist.id %>" data-name="<%= checklist.name %>" data-icon-class="glyphicon glyphicon-list">
<div class="report-element-header">
<div class="row">
<div class="pull-left attachment-icon">

View file

@ -2,7 +2,7 @@
<% if order.blank? and @order.present? then order = @order end %>
<% comments = step.step_comments.order(created_at: order) %>
<% timestamp = Time.current + 1.year %>
<div class="report-element report-comments-element report-step-comments-element" data-ts="<%= timestamp.to_i %>" data-order="asc" data-type="step_comments" data-id='{ "step_id": <%= step.id %> }' data-scroll-id="<%= step.id %>" data-name="<%=t "projects.reports.elements.step_comments.sidebar_name" %>" data-icon-class="glyphicon-comment">
<div class="report-element report-comments-element report-step-comments-element" data-ts="<%= timestamp.to_i %>" data-order="asc" data-type="step_comments" data-id='{ "step_id": <%= step.id %> }' data-scroll-id="<%= step.id %>" data-name="<%=t "projects.reports.elements.step_comments.sidebar_name" %>" data-icon-class="glyphicon glyphicon-comment">
<div class="report-element-header">
<div class="row">
<div class="pull-left comments-icon">

View file

@ -1,6 +1,6 @@
<% if table.blank? and @table.present? then table = @table end %>
<% timestamp = table.created_at %>
<div class="report-element report-step-attachment-element report-step-table-element" data-ts="<%= timestamp.to_i %>" data-type="step_table" data-id='{ "table_id": <%= table.id %> }' data-scroll-id="<%= table.id %>" data-name="<%= table.name %>" data-icon-class="glyphicon-th">
<div class="report-element report-step-attachment-element report-step-table-element" data-ts="<%= timestamp.to_i %>" data-type="step_table" data-id='{ "table_id": <%= table.id %> }' data-scroll-id="<%= table.id %>" data-name="<%= table.name %>" data-icon-class="glyphicon glyphicon-th">
<div class="report-element-header">
<div class="row">
<div class="pull-left attachment-icon">

View file

@ -0,0 +1,24 @@
<div class="modal fade"
id="exportRepositoryModal"
tabindex="-1"
role="dialog"
aria-labelledby="modal-export-repository-label">
<%= bootstrap_form_tag(url: export_repository_team_path(repository),
html: { id: 'form-export' }) do |f| %>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title"><%=t 'zip_export.modal_label' %></h4>
</div>
<div class="modal-body">
<%=t('zip_export.repository_html', repository: repository.name) %>
</div>
<div class="modal-footer">
<button type='button' class='btn btn-primary' data-dismiss='modal' id='export-repositories'> <%=t 'my_modules.repository.export' %> </button>
<button type='button' class='btn btn-default' data-dismiss='modal' id='close-modal-export-repositories'><%= t('general.close')%></button>
</div>
</div>
</div>
<% end %>
</div>

View file

@ -1,5 +1,7 @@
<%= render partial: "repositories/delete_record_modal.html.erb" %>
<%= render partial: "repositories/delete_column_modal.html.erb" %>
<%= render partial: 'repositories/export_repository_modal.html.erb',
locals: { repository: repository } %>
<div id="alert-container"></div>
@ -18,6 +20,59 @@
</button>
<% end %>
<% if can_view_repository(repository) %>
<a href="#" class="btn btn-default" id="exportRepositoriesButton">
<span class="glyphicon glyphicon-cloud-download"></span>
<span class="hidden-xs"><%= t("my_modules.repository.export") %></span>
</a>
<% end %>
<div class="repository-cog">
<div class="dropdown">
<div class="btn btn-default"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="true"
<%= "disabled='disabled'" if !can_edit_and_destroy_repository repository and !can_copy_repository repository %>>
<span class="glyphicon glyphicon-cog"></span>
<span class="caret"></span>
</div>
<% if can_edit_and_destroy_repository repository or can_copy_repository repository %>
<ul class="dropdown-menu pull-right">
<li class="dropdown-header">
<%= t("repositories.index.options_dropdown.header") %>
</li>
<% if can_edit_and_destroy_repository repository %>
<li>
<%= link_to t('repositories.index.options_dropdown.rename'),
team_repository_rename_modal_path(repository_id: repository),
class: "rename-repo-option",
remote: true %>
</li>
<% end %>
<% if can_copy_repository(repository) %>
<li>
<%= link_to t('repositories.index.options_dropdown.copy'),
team_repository_copy_modal_path(repository_id: repository),
class: "copy-repo-option",
remote: true %>
</li>
<% end %>
<% if can_edit_and_destroy_repository repository %>
<li role="separator" class="divider"></li>
<li>
<%= link_to t('repositories.index.modal_delete.delete'),
team_repository_destroy_modal_path(repository_id: repository),
class: "delete-repo-option",
remote: true %>
</li>
<% end %>
</ul>
<% end %>
</div>
</div>
<div id="datatables-buttons" style="display: inline;">
<div id="repository-columns-dropdown" class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
@ -81,3 +136,4 @@
%>
<%= render partial: 'repositories/import_repository_records.html.erb',
locals: { repository: repository } %>
<%= javascript_include_tag 'repositories/edit', 'data-turbolinks-track' => true %>

View file

@ -15,18 +15,14 @@
<thead>
<tr>
<th id="checkbox"><input name="select_all" value="1" type="checkbox"></th>
<% if @my_module %>
<th id="assigned"><%= t("repositories.table.assigned") %></th>
<% else %>
<th id="assigned"></th>
<% end %>
<th id="assigned"><%= t("repositories.table.assigned") %></th>
<th id="row-name"><%= t("repositories.table.row_name") %></th>
<th id="added-on"><%= t("repositories.table.added_on") %></th>
<th id="added-by"><%= t("repositories.table.added_by") %></th>
<% repository.repository_columns.each do |column| %>
<th class="repository-column" id="<%= column.id %>"
<%= 'data-editable' if can_edit_columns_in_repository(repository) %>
<%= 'data-deletable' if can_delete_columns_in_repository(repository) %>
<%= 'data-editable' if can_edit_column_in_repository(column) %>
<%= 'data-deletable' if can_delete_column_in_repository(column) %>
<%= "data-edit-url='#{edit_repository_repository_column_path(repository, column)}'" %>
<%= "data-update-url='#{repository_repository_column_path(repository, column)}'" %>
<%= "data-destroy-html-url='#{repository_columns_destroy_html_path(repository, column)}'" %>

View file

@ -25,6 +25,7 @@
<% if can_create_repository(current_team) %>
href="<%= create_modal_team_repositories_path %>"
class='create-repository'
data-remote='true'
<% end %>>
<span class="glyphicon glyphicon-plus"></span>
<span class="hidden-xs">&nbsp;<%= t('repositories.index.add_new_repository_tab') %></span>
@ -37,50 +38,9 @@
<% @repositories.each do |repo| %>
<div class="tab-pane tab-pane-settings" id="custom_repo_<%= repo.id %>">
<!-- Tab Content -->
<div id="repository-toolbar">
<div class="dropdown text-right">
<div class="btn btn-default btn-xs"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="true"
<%= "disabled='disabled'" if !can_edit_and_destroy_repository repo %>>
<span class="glyphicon glyphicon-cog"></span>
<span class="caret"></span>
</div>
<ul class="dropdown-menu pull-right">
<li class="dropdown-header">
<%= t("repositories.index.options_dropdown.header") %>
</li>
<% if can_edit_and_destroy_repository repo %>
<li>
<%= link_to t('repositories.index.options_dropdown.rename'),
team_repository_rename_modal_path(repository_id: repo),
class: "rename-repo-option",
remote: true %>
</li>
<% end %>
<% if can_copy_repository(repo) %>
<li>
<%= link_to t('repositories.index.options_dropdown.copy'),
team_repository_copy_modal_path(repository_id: repo),
class: "copy-repo-option",
remote: true %>
</li>
<% end %>
<% if can_edit_and_destroy_repository repo %>
<li role="separator" class="divider"></li>
<li>
<%= link_to t('repositories.index.modal_delete.delete'),
team_repository_destroy_modal_path(repository_id: repo),
class: "delete-repo-option",
remote: true %>
</li>
<% end %>
</ul>
</div>
<div class="container-fluid">
<div class="tab-content-body"></div>
</div>
<div class="tab-content-body"></div>
</div>
<% end %>
</div>

View file

@ -1,14 +1,22 @@
<div class="well">
<%= bootstrap_form_for(@result, url: my_module_result_assets_path(format: :json), remote: true, multipart: true, data: { type: :json }) do |f| %>
<%= f.text_field :name, style: "margin-top: 10px;" %><br />
<%= f.fields_for :asset do |ff| %>
<%= ff.file_field :file %>
<% end %>
<%= f.submit t("result_assets.new.create"),
class: 'btn btn-primary save-result',
onclick: "Results.processResult(event, Results.ResultTypeEnum.FILE, false);" %>
<button type="button" class="btn btn-default cancel-new">
<div id="new-result-assets-select"
class="text-center new-asset-box">
<%=t 'assets.drag_n_drop.label_html' %> <label><span class="btn btn-primary"><%=t 'assets.drag_n_drop.browse_label' %></span>
<input type="file"
onchange="DragNDropResults.init(this.files, 'select')"
id="drag-n-drop-assets"
style="display: none" multiple>
</label>
</div>
<br />
<button type="button"
class="btn btn-primary save-result"
data-href="<%= my_module_result_assets_path(format: :json) %>"
onClick="DragNDropResults.processResult(this)"><%=t 'result_assets.new.create' %></button>
<button type="button"
class="btn btn-default cancel-new"
onClick="DragNDropResults.destroyAll()">
<%= t("general.cancel")%>
</button>
<% end %>
<%# end %>
</div>

View file

@ -0,0 +1,20 @@
<div class="modal fade"
id="modal-export-samples-success"
tabindex="-1"
role="dialog"
aria-labelledby="modal-export-samples-successs-label">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title"><%=t 'zip_export.modal_label' %></h4>
</div>
<div class="modal-body">
<%=t('zip_export.modal_html', email: current_user.email) %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><%= t('general.cancel')%></button>
</div>
</div>
</div>
</div>

View file

@ -40,7 +40,7 @@
<br>
<span>
<%= t "samples.modal_info.custom_field", cf: sample_custom_field.custom_field.name %>
<%= sample_custom_field.value %>
<%= custom_auto_link(sample_custom_field.value, simple_format: false, team: @team) %>
</span>
<% end %>
</p>

View file

@ -0,0 +1,3 @@
<div class="is-dragover">
<span><%=t 'assets.drag_n_drop.drop_label' %></span>
</div>

View file

@ -46,7 +46,7 @@
</li>
<li>
<a id="repositories-link" href="<%= team_repositories_path(current_team) %>">
<span class="glyphicon glyphicon-oil"></span>
<i class="fa fa-cubes" aria-hidden="true"></i>
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.repositories') %></span>
</a>
</li>
@ -59,7 +59,7 @@
</li>
<li class="disabled">
<a id="repositories-link" href="#">
<span class="glyphicon glyphicon-oil"></span>
<span class="fa fa-cubes"></span>
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.repositories') %></span>
</a>
</li>

View file

@ -1,6 +1,7 @@
<%= render partial: "samples/import_samples_modal" %>
<%= render partial: "samples/delete_samples_modal" %>
<%= render partial: "samples/delete_custom_field_modal" %>
<%= render partial: 'samples/export_samples_modal' %>
<!-- Modal for parsing sample sheets should be empty at first -->
<div class="modal fade" id="modal-parse-samples" tabindex="-1" role="dialog" aria-labelledby=="modal-parse-samples-label"></div>
@ -8,8 +9,8 @@
<div id="alert-container"></div>
<% if can_view_samples(@team) %>
<%= bootstrap_form_tag(url: export_samples_team_path(@team, format: :csv),
html: { id: 'form-export', class: 'hidden' }) do |f| %>
<%= bootstrap_form_tag(url: export_samples_team_path(@team),
html: { id: 'form-export', class: 'hidden' }) do |f| %>
<% end %>
<% end %>
@ -36,6 +37,11 @@
<span class="glyphicon glyphicon-cloud-download"></span>
<span class="hidden-xs"><%= t("samples.export") %></span>
</a>
<div id="show-assigned-buttons" class="btn-group sample-assign-group" data-toggle="buttons">
<button type="button" class="active btn btn-primary" id="assignedSamples"><%= t("samples.view_assigned_samples") %></button>
<button type="button" class="btn btn-default" id="allSamples"><%= t("samples.view_all_samples") %></button>
</div>
<% end %>
<div id="datatables-buttons" style="display: inline;">

View file

@ -188,7 +188,7 @@
</a>
</li>
<% end %>
<% if can_view_repositories(@my_module.experiment.project.team) &&
<% if can_view_team_repositories(@my_module.experiment.project.team) &&
@my_module.experiment.project.team.repositories.exists? %>
<li id="repositories-nav-tab" class="<%= "active" if module_repository_page? %>">
<a href="#" id="repositoriesDropdownMenuLink" title="<%=t "nav2.modules.repositories" %>" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@ -197,10 +197,10 @@
<span class="caret"></span>
</a>
<ul class="dropdown-menu repositories-dropdown-menu" aria-labelledby="repositoriesDropdownMenuLink">
<% @my_module.experiment.project.team.repositories.each do |repository| %>
<% @my_module.experiment.project.team.repositories.order(created_at: :asc).each do |repository| %>
<li>
<a class="dropdown-item" href="<%= repository_my_module_url(repository_id: repository) %>" title="<%= repository.name %>">
<%= repository.name %>
<%= truncate(repository.name) %>
</a>
</li>
<% unless @my_module.experiment.project.team.repositories.last == repository %>

View file

@ -10,7 +10,9 @@
<%= t("protocols.steps.new.tab_checklists") %>
</a>
</li>
<li role="presentation" id="new-step-assets-tab">
<li role="presentation"
id="new-step-assets-tab"
onClick="dragNdropAssetsInit('steps')">
<a href="#new-step-assets" data-toggle="tab">
<span class="glyphicon glyphicon-file"></span>
<%= t("protocols.steps.new.tab_assets") %>
@ -40,13 +42,17 @@
<% end %>
</div>
<div class="tab-pane" role="tabpanel" id="new-step-assets">
<div class="text-center new-asset-box">
<%=t 'assets.drag_n_drop.label_html' %> <label><span class="btn btn-primary"><%=t 'assets.drag_n_drop.browse_label' %></span>
<input type="file"
onchange="DragNDropSteps.init(this.files)"
id="drag-n-drop-assets"
style="display: none" multiple>
</label>
</div>
<%= f.nested_fields_for :assets do |ff| %>
<%= render "form_assets.html.erb", ff: ff, step: step %>
<% end %>
<%= f.add_nested_fields_link :assets do %>
<span class="glyphicon glyphicon-plus"></span>
<%= t("protocols.steps.new.add_asset") %>
<% end %>
</div>
<div class="tab-pane" role="tabpanel" id="new-step-tables">
<%= f.nested_fields_for :tables do |ff| %>

View file

@ -1,5 +1,5 @@
<div class="well">
<%= bootstrap_form_for(@step, url: protocol_steps_path(@protocol, format: :json), remote: true, authenticity_token: true, multipart: true, data: { role: "new-step-form", type: :json }) do |f| %>
<%= bootstrap_form_for(@step, url: protocol_steps_path(@protocol, format: :json), authenticity_token: true, multipart: true, data: { role: "new-step-form", type: :json }) do |f| %>
<h4><%= t("protocols.steps.new.add_step_title") %></h4>
<hr>
<%= render partial: "empty_step.html.erb", locals: {step: @step, f: f} %>

View file

@ -73,6 +73,7 @@ Rails.application.config.assets.precompile += %w(samples/sample_types_groups.js)
Rails.application.config.assets.precompile += %w(highlightjs-github-theme.css)
Rails.application.config.assets.precompile += %w(search.js)
Rails.application.config.assets.precompile += %w(repositories/index.js)
Rails.application.config.assets.precompile += %w(repositories/edit.js)
Rails.application.config.assets.precompile +=
%w(repositories/repository_datatable.js)

View file

@ -612,6 +612,7 @@ en:
more_activities: "Load older activities"
repository:
head_title: "%{project} | %{module} | Custom repository %{repository}"
export: 'Export'
experiments:
new:
@ -820,6 +821,7 @@ en:
success_flash: "File result successfully deleted."
wopi_open_file: "Open in %{app}"
wopi_edit_file: "Edit in %{app}"
error_flash: 'Something went wrong! Please try again later.'
result_tables:
new:
@ -843,7 +845,7 @@ en:
index:
head_title: "Repositories"
title: "Repositories"
add_new_repository_tab: "Create new repository"
add_new_repository_tab: "Add repository"
delete_flash: "\"%{name}\" repository was successfully deleted!"
rename_flash: "\"%{old_name}\" repository was successfully renamed to \"%{new_name}\"!"
copy_flash: "\"%{new}\" repository was successfully copied from \"%{old}\"!"
@ -886,7 +888,7 @@ en:
row_name: "Name"
added_on: "Added on"
added_by: "Added by"
add_new_record: "Add record"
add_new_record: "Add new item"
import_records:
import: 'Import'
success_flash: "%{number_of_rows} new record(s) successfully imported."
@ -947,7 +949,7 @@ en:
no_records_assigned_flash: "No records were assigned to task"
no_records_unassigned_flash: "No records were unassigned from task"
default_column: 'Name'
samples:
columns: "Columns"
columns_visibility: "Toggle visibility"
@ -969,6 +971,8 @@ en:
add_new_sample_type: "Add sample type"
add_new_sample_group: "Add sample group"
add_new_column: "Add column"
view_all_samples: "View all samples"
view_assigned_samples: "View assigned samples"
modal_info:
added_on: "Added on"
added_by: "Added by"
@ -1135,8 +1139,8 @@ en:
uncomplete_module: "<i>%{user}</i> uncompleted task <strong>%{module}</strong>."
assign_sample: "<i>%{user}</i> assigned sample(s) <strong>%{samples}</strong> to task(s) <strong>%{tasks}</strong>"
unassign_sample: "<i>%{user}</i> unassigned sample(s) <strong>%{samples}</strong> from task(s) <strong>%{tasks}</strong>"
assign_repository_records: "<i>%{user}</i> assigned <strong>%{repository}</strong> repository records(s) <strong>%{records}</strong> to task <strong>%{task}</strong>"
unassign_repository_records: "<i>%{user}</i> unassigned <strong>%{repository}</strong> repository records(s) <strong>%{records}</strong> from task <strong>%{task}</strong>"
assign_repository_records: "<i>%{user}</i> assigned record(s) <strong>%{records}</strong> from <strong>%{repository}</strong> repository to task <strong>%{task}</strong>"
unassign_repository_records: "<i>%{user}</i> unassigned record(s) <strong>%{records}</strong> from <strong>%{repository}</strong> repository from task <strong>%{task}</strong>"
create_step: "<i>%{user}</i> created Step %{step} <strong>%{step_name}</strong>."
destroy_step: "<i>%{user}</i> deleted Step %{step} <strong>%{step_name}</strong>."
add_comment_to_step: "<i>%{user}</i> commented on Step %{step} <strong>%{step_name}</strong>."
@ -1684,7 +1688,11 @@ en:
head_title:
edit: "sciNote | %{file_name} | Edit"
view: "sciNote | %{file_name} | View"
drag_n_drop:
label_html: 'Drag &amp; drop files here or '
browse_label: 'Browse to add'
drop_label: 'Drop to add to Step'
file_label: 'File'
atwho:
no_results: "No results found"
users:
@ -1702,9 +1710,15 @@ en:
deleted: "(deleted)"
zip_export:
modal_label: 'Export repository'
notification_title: 'Your package is ready to be exported!'
expired_title: 'The required file was expired!'
expired_description: 'The downloadable file expires in 7 days after its creation.'
modal_label: 'Export request received'
modal_html: "<p>Your export request is being processed.</p><p>When completed we will <strong>send an email to %{email}</strong> inbox with a link to your exported samples. Note that the link will expire in 7 days.</p>"
repository_html: '<p>You are about to export selected items in repository %{repository}</p> <br> Repository will be exported in a .csv file format. You will receive <strong>email with a link</strong> where you can download it.'
export_error: "Error when creating zip export."
# This section contains general words that can be used in any parts of
# application.
tiny_mce:

View file

@ -155,6 +155,7 @@ Rails.application.routes.draw do
post 'parse_sheet'
post 'import_samples'
post 'export_samples'
post 'export_repository', to: 'repositories#export_repository'
# Used for atwho (smart annotations)
get 'atwho_users', to: 'at_who#users'
get 'atwho_samples', to: 'at_who#samples'