mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-11-11 08:51:32 +08:00
Merge branch 'master' into rails-5.1
Conflicts: Gemfile.lock app/datatables/repository_datatable.rb app/models/repository_cell.rb
This commit is contained in:
commit
ae5bccf709
36 changed files with 1332 additions and 1115 deletions
3
Gemfile
3
Gemfile
|
|
@ -51,7 +51,7 @@ gem 'ajax-datatables-rails', '~> 0.3.1'
|
|||
gem 'commit_param_routing' # Enables different submit actions in the same form to route to different actions in controller
|
||||
gem 'kaminari'
|
||||
gem "i18n-js", ">= 3.0.0.rc11" # Localization in javascript files
|
||||
gem 'roo', '~> 2.1.0' # Spreadsheet parser
|
||||
gem 'roo', '~> 2.7.1' # Spreadsheet parser
|
||||
gem 'wicked_pdf'
|
||||
gem 'silencer' # Silence certain Rails logs
|
||||
gem 'wkhtmltopdf-heroku'
|
||||
|
|
@ -64,6 +64,7 @@ gem 'sneaky-save', git: 'https://github.com/einzige/sneaky-save'
|
|||
gem 'rails_autolink', '~> 1.1', '>= 1.1.6'
|
||||
gem 'delayed_paperclip'
|
||||
gem 'rubyzip'
|
||||
gem 'activerecord-import'
|
||||
|
||||
gem 'paperclip', '~> 5.1' # File attachment, image attachment library
|
||||
gem 'aws-sdk', '~> 2'
|
||||
|
|
|
|||
61
Gemfile.lock
61
Gemfile.lock
|
|
@ -7,7 +7,7 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: https://github.com/einzige/sneaky-save
|
||||
revision: e7c77674abe74d598dfd58db7c680dd85936f207
|
||||
revision: 7e7596720e76a3c243042be2f5f916525b143a54
|
||||
specs:
|
||||
sneaky-save (0.1.2)
|
||||
activerecord (>= 3.2.0)
|
||||
|
|
@ -56,6 +56,8 @@ GEM
|
|||
activemodel (= 5.1.1)
|
||||
activesupport (= 5.1.1)
|
||||
arel (~> 8.0)
|
||||
activerecord-import (0.19.1)
|
||||
activerecord (>= 3.2)
|
||||
activesupport (5.1.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (~> 0.7)
|
||||
|
|
@ -70,26 +72,28 @@ GEM
|
|||
ast (2.3.0)
|
||||
auto_strip_attributes (2.1.0)
|
||||
activerecord (>= 3.0)
|
||||
autoprefixer-rails (7.1.1.2)
|
||||
autoprefixer-rails (7.1.2.4)
|
||||
execjs
|
||||
autosize-rails (1.18.17)
|
||||
rails (>= 3.1)
|
||||
awesome_print (1.8.0)
|
||||
aws-sdk (2.2.37)
|
||||
aws-sdk-resources (= 2.2.37)
|
||||
aws-sdk-core (2.2.37)
|
||||
aws-sdk (2.10.21)
|
||||
aws-sdk-resources (= 2.10.21)
|
||||
aws-sdk-core (2.10.21)
|
||||
aws-sigv4 (~> 1.0)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-resources (2.2.37)
|
||||
aws-sdk-core (= 2.2.37)
|
||||
aws-sdk-resources (2.10.21)
|
||||
aws-sdk-core (= 2.10.21)
|
||||
aws-sigv4 (1.0.1)
|
||||
babel-source (5.8.35)
|
||||
babel-transpiler (0.7.0)
|
||||
babel-source (>= 4.0, < 6)
|
||||
execjs (~> 2.0)
|
||||
base62 (1.0.0)
|
||||
bcrypt (3.1.11)
|
||||
better_errors (2.1.1)
|
||||
better_errors (2.3.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
|
|
@ -102,9 +106,9 @@ GEM
|
|||
bootstrap_form (2.7.0)
|
||||
builder (3.2.3)
|
||||
byebug (9.0.6)
|
||||
capybara (2.14.4)
|
||||
capybara (2.15.1)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
|
|
@ -169,15 +173,14 @@ GEM
|
|||
devise (>= 4.0.0)
|
||||
diff-lcs (1.3)
|
||||
docile (1.1.5)
|
||||
erubi (1.6.0)
|
||||
erubis (2.7.0)
|
||||
erubi (1.6.1)
|
||||
execjs (2.7.0)
|
||||
factory_girl (4.8.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.8.0)
|
||||
factory_girl (~> 4.8.0)
|
||||
railties (>= 3.0.0)
|
||||
faker (1.7.3)
|
||||
faker (1.8.4)
|
||||
i18n (~> 0.5)
|
||||
ffi (1.9.18)
|
||||
figaro (1.1.1)
|
||||
|
|
@ -188,8 +191,8 @@ GEM
|
|||
globalid (0.4.0)
|
||||
activesupport (>= 4.2.0)
|
||||
hammerjs-rails (2.0.4)
|
||||
i18n (0.8.4)
|
||||
i18n-js (3.0.0)
|
||||
i18n (0.8.6)
|
||||
i18n-js (3.0.1)
|
||||
i18n (~> 0.6, >= 0.6.6)
|
||||
introjs-rails (1.0.0)
|
||||
sass-rails (>= 3.2)
|
||||
|
|
@ -236,17 +239,18 @@ GEM
|
|||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mimemagic (0.3.2)
|
||||
mini_mime (0.1.3)
|
||||
mini_portile2 (2.2.0)
|
||||
minitest (5.10.2)
|
||||
minitest (5.10.3)
|
||||
momentjs-rails (2.17.1)
|
||||
railties (>= 3.1)
|
||||
multi_json (1.12.1)
|
||||
multi_test (0.1.2)
|
||||
nested_form_fields (0.8)
|
||||
nested_form_fields (0.8.1)
|
||||
coffee-rails (>= 3.2.1)
|
||||
jquery-rails
|
||||
rails (>= 3.2.0)
|
||||
newrelic_rpm (4.2.0.334)
|
||||
newrelic_rpm (4.3.0.335)
|
||||
nio4r (2.1.0)
|
||||
nokogiri (1.8.0)
|
||||
mini_portile2 (~> 2.2.0)
|
||||
|
|
@ -260,7 +264,7 @@ GEM
|
|||
cocaine (~> 0.5.5)
|
||||
mime-types
|
||||
mimemagic (~> 0.3.0)
|
||||
parallel (1.11.2)
|
||||
parallel (1.12.0)
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
pg (0.21.0)
|
||||
|
|
@ -318,7 +322,7 @@ GEM
|
|||
rainbow (2.2.2)
|
||||
rake
|
||||
rake (12.0.0)
|
||||
rb-fsevent (0.9.8)
|
||||
rb-fsevent (0.10.2)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rdoc (4.3.0)
|
||||
|
|
@ -332,7 +336,7 @@ GEM
|
|||
lazy_priority_queue (~> 0.1.0)
|
||||
stream (~> 0.5.0)
|
||||
rkelly-remix (0.0.7)
|
||||
roo (2.1.1)
|
||||
roo (2.7.1)
|
||||
nokogiri (~> 1)
|
||||
rubyzip (~> 1.1, < 2.0.0)
|
||||
rspec-core (3.6.0)
|
||||
|
|
@ -367,7 +371,7 @@ GEM
|
|||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4.1)
|
||||
sass (3.4.24)
|
||||
sass (3.4.25)
|
||||
sass-rails (5.0.6)
|
||||
railties (>= 4.0.0, < 6)
|
||||
sass (~> 3.1)
|
||||
|
|
@ -380,7 +384,7 @@ GEM
|
|||
sdoc (0.4.2)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
rdoc (~> 4.0)
|
||||
shoulda-matchers (3.1.1)
|
||||
shoulda-matchers (3.1.2)
|
||||
activesupport (>= 4.0.0)
|
||||
silencer (1.0.1)
|
||||
simple_token_authentication (1.15.1)
|
||||
|
|
@ -413,8 +417,8 @@ GEM
|
|||
stream (0.5)
|
||||
thor (0.19.4)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.7)
|
||||
tinymce-rails (4.6.4)
|
||||
tilt (2.0.8)
|
||||
tinymce-rails (4.6.5)
|
||||
railties (>= 3.1.1)
|
||||
turbolinks (5.0.1)
|
||||
turbolinks-source (~> 5)
|
||||
|
|
@ -446,6 +450,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
activerecord-import
|
||||
ajax-datatables-rails (~> 0.3.1)
|
||||
aspector
|
||||
auto_strip_attributes (~> 2.1)
|
||||
|
|
@ -506,7 +511,7 @@ DEPENDENCIES
|
|||
recaptcha
|
||||
remotipart (~> 1.2)
|
||||
rgl
|
||||
roo (~> 2.1.0)
|
||||
roo (~> 2.7.1)
|
||||
rspec-rails
|
||||
rubocop
|
||||
ruby-graphviz (~> 1.2)
|
||||
|
|
@ -536,4 +541,4 @@ RUBY VERSION
|
|||
ruby 2.4.1p111
|
||||
|
||||
BUNDLED WITH
|
||||
1.15.1
|
||||
1.15.3
|
||||
|
|
|
|||
1
VERSION
Normal file
1
VERSION
Normal file
|
|
@ -0,0 +1 @@
|
|||
1.12.3
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
$('#form-records-file').on('ajax:success', function(ev, data) {
|
||||
$('#modal-import-records').modal('hide');
|
||||
$(data.html).appendTo('body').promise().done(function() {
|
||||
$('#parse-records_modal')
|
||||
$('#parse-records-modal')
|
||||
.modal('show')
|
||||
.on('hidden.bs.modal', function() {
|
||||
animateSpinner();
|
||||
|
|
@ -26,6 +26,11 @@
|
|||
});
|
||||
repositoryRecordsImporter();
|
||||
});
|
||||
}).on('ajax:error', function(ev, data) {
|
||||
$(this).find('.form-group').addClass('has-error');
|
||||
$(this).find('.form-group').find('.help-block').remove();
|
||||
$(this).find('.form-group').append("<span class='help-block'>" +
|
||||
data.responseJSON.message + '</span>');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -41,8 +46,10 @@
|
|||
success: function (data) {
|
||||
var tabBody = $(pane.context.hash).find(".tab-content-body");
|
||||
tabBody.html(data.html);
|
||||
pane.tab('show').promise().done(function() {
|
||||
pane.tab('show').promise().done(function(el) {
|
||||
initImportRecordsModal();
|
||||
RepositoryDatatable.destroy()
|
||||
RepositoryDatatable.init(el.attr('data-repo-table'));
|
||||
});
|
||||
},
|
||||
error: function (error) {
|
||||
|
|
|
|||
10
app/assets/javascripts/repositories/my_module_repository.js
Normal file
10
app/assets/javascripts/repositories/my_module_repository.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
// initialze repository datatable
|
||||
$(document).ready(function() {
|
||||
RepositoryDatatable.destroy()
|
||||
RepositoryDatatable.init($('#content').attr('data-repo-id'));
|
||||
onClickToggleAssignedRecords();
|
||||
});
|
||||
})();
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
//= require jquery-ui/widgets/sortable
|
||||
|
||||
var RepositoryDatatable = (function(global) {
|
||||
'use strict';
|
||||
|
||||
var TABLE_ID = '';
|
||||
var TABLE = {};
|
||||
|
||||
// Extend datatables API with searchable options
|
||||
// (http://stackoverflow.com/questions/39912395/datatables-dynamically-set-columns-searchable)
|
||||
$.fn.dataTable.Api.register('isColumnSearchable()', function(colSelector) {
|
||||
|
|
@ -31,7 +37,6 @@ var selectedRecord;
|
|||
var myData;
|
||||
var loadFirstTime = true;
|
||||
|
||||
var table;
|
||||
var originalHeader;
|
||||
|
||||
// Tells whether to filter only assigned repository records
|
||||
|
|
@ -39,9 +44,9 @@ var viewAssigned;
|
|||
|
||||
function dataTableInit() {
|
||||
// Make a copy of original repository table header
|
||||
originalHeader = $('#repository-table thead').children().clone();
|
||||
originalHeader = $(TABLE_ID + ' thead').children().clone();
|
||||
viewAssigned = 'assigned';
|
||||
table = $('#repository-table').DataTable({
|
||||
TABLE = $(TABLE_ID).DataTable({
|
||||
order: [[2, 'desc']],
|
||||
dom: "R<'row'<'col-sm-9-custom toolbar'l><'col-sm-3-custom'f>>tpi",
|
||||
stateSave: true,
|
||||
|
|
@ -57,7 +62,7 @@ function dataTableInit() {
|
|||
},
|
||||
destroy: true,
|
||||
ajax: {
|
||||
url: $('#repository-table').data('source'),
|
||||
url: $(TABLE_ID).data('source'),
|
||||
data: function(d) {
|
||||
d.assigned = viewAssigned;
|
||||
},
|
||||
|
|
@ -82,7 +87,6 @@ function dataTableInit() {
|
|||
rowCallback: function(row, data) {
|
||||
// Get row ID
|
||||
var rowId = data.DT_RowId;
|
||||
|
||||
// If row ID is in the list of selected row IDs
|
||||
if ($.inArray(rowId, rowsSelected) !== -1) {
|
||||
$(row).find('input[type="checkbox"]').prop('checked', true);
|
||||
|
|
@ -90,7 +94,7 @@ function dataTableInit() {
|
|||
}
|
||||
},
|
||||
columns: (function() {
|
||||
var numOfColumns = $('#repository-table').data('num-columns');
|
||||
var numOfColumns = $(TABLE_ID).data('num-columns');
|
||||
var columns = [];
|
||||
for (var i = 0; i < numOfColumns; i++) {
|
||||
var visible = (i <= 4);
|
||||
|
|
@ -108,7 +112,7 @@ function dataTableInit() {
|
|||
animateSpinner(this, false);
|
||||
changeToViewMode();
|
||||
updateButtons();
|
||||
updateDataTableSelectAllCtrl(table);
|
||||
updateDataTableSelectAllCtrl();
|
||||
// Prevent row toggling when selecting user smart annotation link
|
||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||
|
||||
|
|
@ -127,7 +131,7 @@ function dataTableInit() {
|
|||
// Send an Ajax request to the server to get the data. Note that
|
||||
// this is a synchronous request since the data is expected back from the
|
||||
// function
|
||||
var repositoryId = $('#repository-table').data('repository-id');
|
||||
var repositoryId = $(TABLE_ID).data('repository-id');
|
||||
$.ajax({
|
||||
url: '/repositories/' + repositoryId + '/state_load',
|
||||
data: {},
|
||||
|
|
@ -142,13 +146,12 @@ function dataTableInit() {
|
|||
},
|
||||
stateSaveCallback: function(settings, data) {
|
||||
// Send an Ajax request to the server with the state object
|
||||
var repositoryId = $('#repository-table').data('repository-id');
|
||||
var repositoryId = $(TABLE_ID).data('repository-id');
|
||||
// Save correct data
|
||||
if (loadFirstTime === true) {
|
||||
data = myData;
|
||||
}
|
||||
$.ajax({
|
||||
async: false,
|
||||
url: '/repositories/' + repositoryId + '/state_save',
|
||||
data: { state: data },
|
||||
dataType: 'json',
|
||||
|
|
@ -160,9 +163,9 @@ function dataTableInit() {
|
|||
fnInitComplete: function(oSettings) {
|
||||
// Reload correct column order and visibility (if you refresh page)
|
||||
// First two columns are fixed
|
||||
table.column(0).visible(true);
|
||||
table.column(1).visible(true);
|
||||
for (var i = 2; i < table.columns()[0].length; i++) {
|
||||
TABLE.column(0).visible(true);
|
||||
TABLE.column(1).visible(true);
|
||||
for (var i = 2; i < TABLE.columns()[0].length; i++) {
|
||||
var visibility = false;
|
||||
if (myData.columns[i]) {
|
||||
visibility = myData.columns[i].visible;
|
||||
|
|
@ -170,15 +173,16 @@ function dataTableInit() {
|
|||
if (typeof (visibility) === 'string') {
|
||||
visibility = (visibility === 'true');
|
||||
}
|
||||
table.column(i).visible(visibility);
|
||||
table.setColumnSearchable(i, visibility);
|
||||
TABLE.column(i).visible(visibility);
|
||||
TABLE.setColumnSearchable(i, visibility);
|
||||
}
|
||||
oSettings._colReorder.fnOrder(myData.ColReorder);
|
||||
table.on('mousedown', function() {
|
||||
TABLE.on('mousedown', function() {
|
||||
$('#repository-columns-dropdown').removeClass('open');
|
||||
});
|
||||
initHeaderTooltip();
|
||||
initRowSelection();
|
||||
bindExportActions();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -187,7 +191,7 @@ function dataTableInit() {
|
|||
$('div.toolbarButtons').show();
|
||||
|
||||
// Handle click on table cells with checkboxes
|
||||
$('#repository-table').on('click', 'tbody td', function(e) {
|
||||
$(TABLE_ID).on('click', 'tbody td', function(e) {
|
||||
if ($(e.target).is('.repository-row-selector')) {
|
||||
// Skip if clicking on selector checkbox
|
||||
return;
|
||||
|
|
@ -195,7 +199,7 @@ function dataTableInit() {
|
|||
$(this).parent().find('.repository-row-selector').trigger('click');
|
||||
});
|
||||
|
||||
table.on('column-reorder', function() {
|
||||
TABLE.on('column-reorder', function() {
|
||||
initRowSelection();
|
||||
});
|
||||
|
||||
|
|
@ -203,19 +207,18 @@ function dataTableInit() {
|
|||
animateLoading();
|
||||
});
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
table = dataTableInit();
|
||||
|
||||
// Timeout for table header scrolling
|
||||
setTimeout(function() {
|
||||
table.columns.adjust();
|
||||
TABLE.columns.adjust();
|
||||
}, 10);
|
||||
|
||||
return TABLE;
|
||||
}
|
||||
|
||||
// Enables noSearchHidden plugin
|
||||
$.fn.dataTable.defaults.noSearchHidden = true;
|
||||
|
||||
function bindExportActions() {
|
||||
$('form#form-export').submit(function() {
|
||||
var form = this;
|
||||
|
||||
|
|
@ -225,7 +228,7 @@ $('form#form-export').submit(function() {
|
|||
$(form).find('input[name=header_ids\\[\\]]').remove();
|
||||
|
||||
// Append visible column information
|
||||
$('.active table#repository-table thead tr th').each(function() {
|
||||
$('.active table' + TABLE_ID + ' thead tr th').each(function() {
|
||||
var th = $(this);
|
||||
var val;
|
||||
switch ($(th).attr('id')) {
|
||||
|
|
@ -259,6 +262,8 @@ $('form#form-export').submit(function() {
|
|||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function appendInput(form, val, name) {
|
||||
$(form).append(
|
||||
|
|
@ -277,7 +282,7 @@ function initRowSelection() {
|
|||
}
|
||||
// Get row ID
|
||||
var $row = $(this).closest('tr');
|
||||
var data = table.row($row).data();
|
||||
var data = TABLE.row($row).data();
|
||||
var rowId = data.DT_RowId;
|
||||
|
||||
// Determine whether row ID is in the list of selected row IDs
|
||||
|
|
@ -297,7 +302,7 @@ function initRowSelection() {
|
|||
$row.removeClass('selected');
|
||||
}
|
||||
|
||||
updateDataTableSelectAllCtrl(table);
|
||||
updateDataTableSelectAllCtrl();
|
||||
|
||||
e.stopPropagation();
|
||||
updateButtons();
|
||||
|
|
@ -318,9 +323,9 @@ function initRowSelection() {
|
|||
}
|
||||
|
||||
// Updates "Select all" control in a data table
|
||||
function updateDataTableSelectAllCtrl(table) {
|
||||
var $table = table.table().node();
|
||||
var $header = table.table().header();
|
||||
function updateDataTableSelectAllCtrl() {
|
||||
var $table = TABLE.table().node();
|
||||
var $header = TABLE.table().header();
|
||||
var $chkboxAll = $('.repository-row-selector', $table);
|
||||
var $chkboxChecked = $('.repository-row-selector:checked', $table);
|
||||
var chkboxSelectAll = $('input[name="select_all"]', $header).get(0);
|
||||
|
|
@ -361,9 +366,9 @@ function initHeaderTooltip() {
|
|||
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;
|
||||
if ($(this).parents(TABLE_ID).length) {
|
||||
offsetLeft = $(TABLE_ID).offset().left + 100;
|
||||
width = $(TABLE_ID).width() - 200;
|
||||
}
|
||||
$('body').append($tooltip);
|
||||
$tooltip.css('background-color', '#d2d2d2');
|
||||
|
|
@ -386,16 +391,16 @@ function initHeaderTooltip() {
|
|||
});
|
||||
}
|
||||
|
||||
function onClickAddRecord() {
|
||||
global.onClickAddRecord = function() {
|
||||
changeToEditMode();
|
||||
updateButtons();
|
||||
|
||||
saveAction = 'create';
|
||||
var tr = document.createElement('tr');
|
||||
if (table.column(1).visible() === false) {
|
||||
table.column(1).visible(true);
|
||||
if (TABLE.column(1).visible() === false) {
|
||||
TABLE.column(1).visible(true);
|
||||
}
|
||||
$('table#repository-table thead tr').children('th').each(function() {
|
||||
$('table' + TABLE_ID + ' thead tr').children('th').each(function() {
|
||||
var th = $(this);
|
||||
var td;
|
||||
var input;
|
||||
|
|
@ -418,7 +423,7 @@ function onClickAddRecord() {
|
|||
tr.appendChild(createTdElement(''));
|
||||
}
|
||||
});
|
||||
$('table#repository-table').prepend(tr);
|
||||
$('table' + TABLE_ID).prepend(tr);
|
||||
selectedRecord = tr;
|
||||
|
||||
// initialize smart annotation
|
||||
|
|
@ -431,7 +436,7 @@ function onClickAddRecord() {
|
|||
adjustTableHeader();
|
||||
}
|
||||
|
||||
(function onClickToggleAssignedRecords() {
|
||||
global.onClickToggleAssignedRecords = function() {
|
||||
$('.repository-assign-group > .btn').click(function() {
|
||||
$('.btn-group > .btn').removeClass('active btn-primary');
|
||||
$('.btn-group > .btn').addClass('btn-default');
|
||||
|
|
@ -440,19 +445,19 @@ function onClickAddRecord() {
|
|||
|
||||
$('#assigned-repo-records').on('click', function() {
|
||||
viewAssigned = 'assigned';
|
||||
table.ajax.reload(function() {
|
||||
TABLE.ajax.reload(function() {
|
||||
initRowSelection();
|
||||
}, false);
|
||||
});
|
||||
$('#all-repo-records').on('click', function() {
|
||||
viewAssigned = 'all';
|
||||
table.ajax.reload(function() {
|
||||
TABLE.ajax.reload(function() {
|
||||
initRowSelection();
|
||||
}, false);
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
function onClickAssignRecords() {
|
||||
global.onClickAssignRecords = function() {
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('#assignRepositoryRecords').data('assign-url'),
|
||||
|
|
@ -472,7 +477,7 @@ function onClickAssignRecords() {
|
|||
});
|
||||
}
|
||||
|
||||
function onClickUnassignRecords() {
|
||||
global.onClickUnassignRecords = function() {
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('#unassignRepositoryRecords').data('unassign-url'),
|
||||
|
|
@ -492,10 +497,10 @@ function onClickUnassignRecords() {
|
|||
});
|
||||
}
|
||||
|
||||
function onClickDeleteRecord() {
|
||||
global.onClickDeleteRecord = function() {
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('table#repository-table').data('delete-record'),
|
||||
url: $('table' + TABLE_ID).data('delete-record'),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {selected_rows: rowsSelected},
|
||||
|
|
@ -515,12 +520,12 @@ function onClickDeleteRecord() {
|
|||
}
|
||||
|
||||
// Edit record
|
||||
function onClickEdit() {
|
||||
global.onClickEdit = function() {
|
||||
if (rowsSelected.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var row = table.row('#' + rowsSelected[0]);
|
||||
var row = TABLE.row('#' + rowsSelected[0]);
|
||||
var node = row.node();
|
||||
var rowData = row.data();
|
||||
|
||||
|
|
@ -537,8 +542,8 @@ function onClickEdit() {
|
|||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (table.column(1).visible() === false) {
|
||||
table.column(1).visible(true);
|
||||
if (TABLE.column(1).visible() === false) {
|
||||
TABLE.column(1).visible(true);
|
||||
}
|
||||
// Show save and cancel buttons in first two columns
|
||||
$(node).children('td').eq(0).html($('#saveRecord').clone());
|
||||
|
|
@ -556,8 +561,8 @@ function onClickEdit() {
|
|||
var cells = data.repository_row.repository_cells;
|
||||
$(node).children('td').each(function(i) {
|
||||
var td = $(this);
|
||||
var rawIndex = table.column.index('fromVisible', i);
|
||||
var colHeader = table.column(rawIndex).header();
|
||||
var rawIndex = TABLE.column.index('fromVisible', i);
|
||||
var colHeader = TABLE.column(rawIndex).header();
|
||||
if ($(colHeader).hasClass('repository-column')) {
|
||||
// Check if cell on this record exists
|
||||
var cell = cells[$(colHeader).attr('id')];
|
||||
|
|
@ -573,7 +578,6 @@ function onClickEdit() {
|
|||
});
|
||||
|
||||
// initialize smart annotation
|
||||
SmartAnnotation.init($('[data-object="repository_cell"]'));
|
||||
_.each($('[data-object="repository_cell"]'), function(el) {
|
||||
if (_.isUndefined($(el).data('atwho'))) {
|
||||
SmartAnnotation.init(el);
|
||||
|
|
@ -596,11 +600,11 @@ function onClickEdit() {
|
|||
}
|
||||
|
||||
// Save record
|
||||
function onClickSave() {
|
||||
global.onClickSave = function() {
|
||||
var node;
|
||||
var rowData;
|
||||
if (saveAction === 'update') {
|
||||
var row = table.row(selectedRecord);
|
||||
var row = TABLE.row(selectedRecord);
|
||||
node = row.node();
|
||||
rowData = row.data();
|
||||
} else if (saveAction === 'create') {
|
||||
|
|
@ -608,7 +612,7 @@ function onClickSave() {
|
|||
}
|
||||
// First fetch all the data in input fields
|
||||
var data = {
|
||||
request_url: $('#repository-table').data('current-uri'),
|
||||
request_url: $(TABLE_ID).data('current-uri'),
|
||||
repository_row_id: $(selectedRecord).attr('id'),
|
||||
repository_row: {},
|
||||
repository_cells: {}
|
||||
|
|
@ -633,7 +637,7 @@ function onClickSave() {
|
|||
type = 'PUT';
|
||||
} else {
|
||||
type = 'POST';
|
||||
url = $('table#repository-table').data('create-record');
|
||||
url = $('table' + TABLE_ID).data('create-record');
|
||||
}
|
||||
$.ajax({
|
||||
url: url,
|
||||
|
|
@ -642,9 +646,11 @@ function onClickSave() {
|
|||
data: data,
|
||||
success: function(data) {
|
||||
HelperModule.flashAlertMsg(data.flash, 'success');
|
||||
SmartAnnotation.closePopup();
|
||||
onClickCancel();
|
||||
},
|
||||
error: function(e) {
|
||||
SmartAnnotation.closePopup();
|
||||
var data = e.responseJSON;
|
||||
clearAllErrors();
|
||||
|
||||
|
|
@ -785,15 +791,16 @@ function clearRowSelection() {
|
|||
}
|
||||
|
||||
// Restore previous table
|
||||
function onClickCancel() {
|
||||
global.onClickCancel = function() {
|
||||
if ($('#assigned').text().length === 0) {
|
||||
table.column(1).visible(false);
|
||||
TABLE.column(1).visible(false);
|
||||
}
|
||||
table.ajax.reload(function() {
|
||||
TABLE.ajax.reload(function() {
|
||||
initRowSelection();
|
||||
}, false);
|
||||
changeToViewMode();
|
||||
updateButtons();
|
||||
SmartAnnotation.closePopup();
|
||||
animateSpinner(null, false);
|
||||
}
|
||||
|
||||
|
|
@ -811,7 +818,7 @@ function getColumnIndex(id) {
|
|||
if (id < 0) {
|
||||
return false;
|
||||
}
|
||||
return table.column(id).index('visible');
|
||||
return TABLE.column(id).index('visible');
|
||||
}
|
||||
|
||||
// Takes object and surrounds it with input
|
||||
|
|
@ -829,34 +836,31 @@ function createTdElement(content) {
|
|||
|
||||
// Adjust columns width in table header
|
||||
function adjustTableHeader() {
|
||||
table.columns.adjust();
|
||||
TABLE.columns.adjust();
|
||||
$('.dropdown-menu').parent()
|
||||
.on('shown.bs.dropdown hidden.bs.dropdown', function() {
|
||||
table.columns.adjust();
|
||||
TABLE.columns.adjust();
|
||||
});
|
||||
}
|
||||
|
||||
function changeToViewMode() {
|
||||
currentMode = 'viewMode';
|
||||
// Table specific stuff
|
||||
table.button(0).enable(true);
|
||||
TABLE.button(0).enable(true);
|
||||
}
|
||||
|
||||
function changeToEditMode() {
|
||||
currentMode = 'editMode';
|
||||
// Table specific stuff
|
||||
table.button(0).enable(false);
|
||||
TABLE.button(0).enable(false);
|
||||
initHeaderTooltip();
|
||||
}
|
||||
|
||||
/*
|
||||
* Repository columns dropdown
|
||||
*/
|
||||
(function(table) {
|
||||
'use strict';
|
||||
|
||||
var dropdown = $('#repository-columns-dropdown');
|
||||
var dropdownList = $('#repository-columns-list');
|
||||
var dropdown, dropdownList;
|
||||
|
||||
function createNewColumn() {
|
||||
// Make an Ajax request to repository_columns_controller
|
||||
|
|
@ -892,11 +896,11 @@ function changeToEditMode() {
|
|||
$('div.toolbarButtons').hide();
|
||||
|
||||
// Destroy datatable
|
||||
table.destroy();
|
||||
TABLE.destroy();
|
||||
|
||||
// Add number of columns
|
||||
$('#repository-table').data('num-columns',
|
||||
$('#repository-table').data('num-columns') + 1);
|
||||
$(TABLE_ID).data('num-columns',
|
||||
$(TABLE_ID).data('num-columns') + 1);
|
||||
// Add column to table (=table header)
|
||||
originalHeader.append(
|
||||
'<th class="repository-column" id="' + data.id + '" ' +
|
||||
|
|
@ -908,13 +912,13 @@ function changeToEditMode() {
|
|||
|
||||
// Remove all event handlers as we re-initialize them later with
|
||||
// new table
|
||||
$('#repository-table').off();
|
||||
$('#repository-table thead').empty();
|
||||
$('#repository-table thead').append(originalHeader);
|
||||
$(TABLE_ID).off();
|
||||
$(TABLE_ID +' thead').empty();
|
||||
$(TABLE_ID + ' thead').append(originalHeader);
|
||||
|
||||
// Re-initialize datatable
|
||||
table = dataTableInit();
|
||||
table.on('init.dt', function() {
|
||||
TABLE = dataTableInit();
|
||||
TABLE.on('init.dt', function() {
|
||||
loadColumnsNames();
|
||||
dropdownOverflow();
|
||||
});
|
||||
|
|
@ -960,10 +964,10 @@ function changeToEditMode() {
|
|||
var scrollPosition = dropdownList.scrollTop();
|
||||
// Clear the list
|
||||
dropdownList.find('li[data-position]').remove();
|
||||
_.each(table.columns().header(), function(el, index) {
|
||||
_.each(TABLE.columns().header(), function(el, index) {
|
||||
if (index > 1) {
|
||||
var colIndex = $(el).attr('data-column-index');
|
||||
var visible = table.column(colIndex).visible();
|
||||
var visible = TABLE.column(colIndex).visible();
|
||||
var editable = $(el).is('[data-editable]');
|
||||
var deletable = $(el).is('[data-deletable]');
|
||||
|
||||
|
|
@ -993,19 +997,19 @@ function changeToEditMode() {
|
|||
'form-control" style="display: none;" />' +
|
||||
'<span class="pull-right controls">' +
|
||||
'<span class="ok glyphicon glyphicon-ok" style="display: none;" ' +
|
||||
'title="' + $('#repository-table').data('save-text') + '"></span>' +
|
||||
'title="' + $(TABLE_ID).data('save-text') + '"></span>' +
|
||||
'<span class="cancel glyphicon glyphicon-remove" ' +
|
||||
'style="display: none;" ' +
|
||||
'title="' + $('#repository-table').data('cancel-text') +
|
||||
'title="' + $(TABLE_ID).data('cancel-text') +
|
||||
'"></span>' +
|
||||
'<span class="vis glyphicon ' + visClass + '" title="' +
|
||||
$('#repository-table').data('columns-visibility-text') + '">' +
|
||||
$(TABLE_ID).data('columns-visibility-text') + '">' +
|
||||
'</span> ' +
|
||||
'<span class="edit glyphicon glyphicon-pencil ' + editClass +
|
||||
'" title="' + $('#repository-table').data('edit-text') +
|
||||
'" title="' + $(TABLE_ID).data('edit-text') +
|
||||
'"></span>' +
|
||||
'<span class="del glyphicon glyphicon-trash ' + delClass +
|
||||
'" title="' + $('#repository-table').data('columns-delete-text') +
|
||||
'" title="' + $(TABLE_ID).data('columns-delete-text') +
|
||||
'"></span>' +
|
||||
'</span><br></span></li>';
|
||||
dropdownList.append(html);
|
||||
|
|
@ -1037,27 +1041,27 @@ function changeToEditMode() {
|
|||
event.stopPropagation();
|
||||
var self = $(this);
|
||||
var li = self.closest('li');
|
||||
var column = table.column(li.attr('data-position'));
|
||||
var column = TABLE.column(li.attr('data-position'));
|
||||
|
||||
if (column.visible()) {
|
||||
self.addClass('glyphicon-eye-close');
|
||||
self.removeClass('glyphicon-eye-open');
|
||||
li.addClass('col-invisible');
|
||||
column.visible(false);
|
||||
table.setColumnSearchable(column.index(), false);
|
||||
TABLE.setColumnSearchable(column.index(), false);
|
||||
} else {
|
||||
self.addClass('glyphicon-eye-open');
|
||||
self.removeClass('glyphicon-eye-close');
|
||||
li.removeClass('col-invisible');
|
||||
column.visible(true);
|
||||
table.setColumnSearchable(column.index(), true);
|
||||
TABLE.setColumnSearchable(column.index(), true);
|
||||
initHeaderTooltip();
|
||||
}
|
||||
|
||||
// Re-filter/search if neccesary
|
||||
var searchText = $('div.dataTables_filter input').val();
|
||||
if (!_.isEmpty(searchText)) {
|
||||
table.search(searchText).draw();
|
||||
TABLE.search(searchText).draw();
|
||||
}
|
||||
initRowSelection();
|
||||
});
|
||||
|
|
@ -1069,7 +1073,7 @@ function changeToEditMode() {
|
|||
cancel: '.new-repository-column',
|
||||
axis: 'y',
|
||||
update: function() {
|
||||
var reorderer = table.colReorder;
|
||||
var reorderer = TABLE.colReorder;
|
||||
var listIds = [];
|
||||
// We skip first two columns
|
||||
listIds.push(0, 1);
|
||||
|
|
@ -1106,7 +1110,7 @@ function changeToEditMode() {
|
|||
dropdownList.sortable('enable');
|
||||
$(li).clearFormErrors();
|
||||
text.html(generateColumnNameTooltip(newName));
|
||||
$(table.columns().header()).filter('#' + id)
|
||||
$(TABLE.columns().header()).filter('#' + id)
|
||||
.html(generateColumnNameTooltip(newName));
|
||||
originalHeader.find('#' + id).html(newName);
|
||||
cancelEditMode();
|
||||
|
|
@ -1290,28 +1294,28 @@ function changeToEditMode() {
|
|||
$('div.toolbarButtons').hide();
|
||||
|
||||
// Destroy datatable
|
||||
table.destroy();
|
||||
TABLE.destroy();
|
||||
|
||||
// Subtract number of columns
|
||||
$('#repository-table').data(
|
||||
$(TABLE_ID).data(
|
||||
'num-columns',
|
||||
$('#repository-table').data('num-columns') - 1
|
||||
$(TABLE_ID).data('num-columns') - 1
|
||||
);
|
||||
|
||||
// Remove column from table (=table header) & rows
|
||||
var th = originalHeader.find('#' + id);
|
||||
var index = th.index();
|
||||
th.remove();
|
||||
$('#repository-table tbody td:nth-child(' + (index + 1) + ')').remove();
|
||||
$(TABLE_ID + ' tbody td:nth-child(' + (index + 1) + ')').remove();
|
||||
|
||||
// Remove all event handlers as we re-initialize them later with
|
||||
// new table
|
||||
$('#repository-table').off();
|
||||
$('#repository-table thead').empty();
|
||||
$('#repository-table thead').append(originalHeader);
|
||||
$(TABLE_ID).off();
|
||||
$(TABLE_ID + ' thead').empty();
|
||||
$(TABLE_ID + ' thead').append(originalHeader);
|
||||
|
||||
// Re-initialize datatable
|
||||
table = dataTableInit();
|
||||
TABLE = dataTableInit();
|
||||
loadColumnsNames();
|
||||
|
||||
// Hide modal
|
||||
|
|
@ -1343,7 +1347,7 @@ function changeToEditMode() {
|
|||
}
|
||||
|
||||
function generateColumnNameTooltip(name) {
|
||||
var maxLength = $('#repository-table').data('max-dropdown-length');
|
||||
var maxLength = $(TABLE_ID).data('max-dropdown-length');
|
||||
if ($.trim(name).length > maxLength) {
|
||||
return '<div class="modal-tooltip">' +
|
||||
truncateLongString(name, maxLength) +
|
||||
|
|
@ -1354,7 +1358,9 @@ function changeToEditMode() {
|
|||
|
||||
// initialze dropdown after the table is loaded
|
||||
function initDropdown() {
|
||||
table.on('init.dt', function() {
|
||||
TABLE.on('init.dt', function() {
|
||||
dropdown = $('#repository-columns-dropdown');
|
||||
dropdownList = $('#repository-columns-list');
|
||||
initNewColumnForm();
|
||||
initSorting();
|
||||
toggleColumnVisibility();
|
||||
|
|
@ -1371,5 +1377,19 @@ function changeToEditMode() {
|
|||
});
|
||||
}
|
||||
|
||||
function init(id) {
|
||||
TABLE_ID = id;
|
||||
TABLE = dataTableInit();
|
||||
initDropdown();
|
||||
})(table);
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
TABLE = {};
|
||||
TABLE_ID = '';
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
init: init,
|
||||
destroy: destroy
|
||||
});
|
||||
})(window);
|
||||
|
|
|
|||
9
app/assets/javascripts/sitewide/about_modal.js
Normal file
9
app/assets/javascripts/sitewide/about_modal.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
$(document).ready(function() {
|
||||
$("[data-trigger='about-modal']").on('click', function() {
|
||||
$('[data-role=about-modal]').modal('show');
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
|
@ -424,6 +424,11 @@ var SmartAnnotation = (function() {
|
|||
init: init
|
||||
};
|
||||
}
|
||||
// Closes the atwho popup * needed in repositories to close the popup
|
||||
// if nothing is selected and the user leaves the form *
|
||||
function closePopup() {
|
||||
$('.atwho-header-res').find('.glyphicon-remove').click();
|
||||
}
|
||||
|
||||
function initialize(field) {
|
||||
var atWho = new setAtWho(field);
|
||||
|
|
@ -432,7 +437,8 @@ var SmartAnnotation = (function() {
|
|||
|
||||
var publicApi = Object.freeze({
|
||||
init: initialize,
|
||||
preventPropagation: atwhoStopPropagation
|
||||
preventPropagation: atwhoStopPropagation,
|
||||
closePopup: closePopup
|
||||
});
|
||||
|
||||
return publicApi;
|
||||
|
|
|
|||
|
|
@ -184,18 +184,17 @@ class RepositoriesController < ApplicationController
|
|||
|
||||
def parse_sheet
|
||||
repository = current_team.repositories.find_by_id(params[:id])
|
||||
parsed_file = ImportRepository::ParseRepository.new(
|
||||
file: params[:file],
|
||||
repository: repository,
|
||||
session: session
|
||||
)
|
||||
|
||||
respond_to do |format|
|
||||
unless params[:file]
|
||||
repository_response(t('teams.parse_sheet.errors.no_file_selected'))
|
||||
return
|
||||
end
|
||||
begin
|
||||
parsed_file = ImportRepository::ParseRepository.new(
|
||||
file: params[:file],
|
||||
repository: repository,
|
||||
session: session
|
||||
)
|
||||
if parsed_file.too_large?
|
||||
repository_response(t('general.file.size_exceeded',
|
||||
file_size: Constants::FILE_MAX_SIZE_MB))
|
||||
|
|
@ -205,6 +204,7 @@ class RepositoriesController < ApplicationController
|
|||
else
|
||||
@import_data = parsed_file.data
|
||||
if parsed_file.generated_temp_file?
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
|
|
@ -212,6 +212,7 @@ class RepositoriesController < ApplicationController
|
|||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
else
|
||||
repository_response(t('teams.parse_sheet.errors.temp_file_failure'))
|
||||
end
|
||||
|
|
@ -223,7 +224,6 @@ class RepositoriesController < ApplicationController
|
|||
repository_response(t('teams.parse_sheet.errors.invalid_extension'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def import_records
|
||||
respond_to do |format|
|
||||
|
|
@ -238,8 +238,9 @@ class RepositoriesController < ApplicationController
|
|||
number_of_rows: status[:nr_of_added])
|
||||
render json: {}, status: :ok
|
||||
else
|
||||
flash[:alert] = t('repositories.import_records.error_flash',
|
||||
message: status[:errors])
|
||||
flash[:alert] =
|
||||
t('repositories.import_records.partial_success_flash',
|
||||
nr: status[:nr_of_added], total_nr: status[:total_nr])
|
||||
render json: {}, status: :unprocessable_entity
|
||||
end
|
||||
else
|
||||
|
|
@ -320,6 +321,7 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def repository_response(message)
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
flash[:alert] = message
|
||||
redirect_to :back
|
||||
|
|
@ -329,6 +331,7 @@ class RepositoriesController < ApplicationController
|
|||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def generate_zip
|
||||
# Fetch rows in the same order as in the currently viewed datatable
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ class RepositoryRowsController < ApplicationController
|
|||
|
||||
record.transaction do
|
||||
record.name = record_params[:name] unless record_params[:name].blank?
|
||||
unless record.save
|
||||
errors[:default_fields] = record.errors.messages
|
||||
end
|
||||
errors[:default_fields] = record.errors.messages unless record.save
|
||||
if params[:repository_cells]
|
||||
params[:repository_cells].each do |key, value|
|
||||
column = @repository.repository_columns.detect do |c|
|
||||
|
|
@ -94,9 +92,7 @@ class RepositoryRowsController < ApplicationController
|
|||
|
||||
@record.transaction do
|
||||
@record.name = record_params[:name].blank? ? nil : record_params[:name]
|
||||
unless @record.save
|
||||
errors[:default_fields] = @record.errors.messages
|
||||
end
|
||||
errors[:default_fields] = @record.errors.messages unless @record.save
|
||||
if params[:repository_cells]
|
||||
params[:repository_cells].each do |key, value|
|
||||
existing = @record.repository_cells.detect do |c|
|
||||
|
|
@ -119,7 +115,7 @@ class RepositoryRowsController < ApplicationController
|
|||
column = @repository.repository_columns.detect do |c|
|
||||
c.id == key.to_i
|
||||
end
|
||||
value = RepositoryTextValue.new(
|
||||
cell_value = RepositoryTextValue.new(
|
||||
data: value,
|
||||
created_by: current_user,
|
||||
last_modified_by: current_user,
|
||||
|
|
@ -128,15 +124,15 @@ class RepositoryRowsController < ApplicationController
|
|||
repository_column: column
|
||||
}
|
||||
)
|
||||
if value.save
|
||||
record_annotation_notification(@record, value.repository_cell)
|
||||
if cell_value.save
|
||||
record_annotation_notification(@record,
|
||||
cell_value.repository_cell)
|
||||
else
|
||||
errors[:repository_cells] << {
|
||||
"#{column.id}": value.errors.messages
|
||||
"#{column.id}": cell_value.errors.messages
|
||||
}
|
||||
end
|
||||
end
|
||||
raise ActiveRecord::Rollback if errors[:repository_cells].any?
|
||||
end
|
||||
# Clean up empty cells, not present in updated record
|
||||
@record.repository_cells.each do |cell|
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ class TeamsController < ApplicationController
|
|||
|
||||
# Get data (it will trigger any errors as well)
|
||||
@header = sheet.row(1)
|
||||
@rows = [];
|
||||
@rows << Hash[[@header, sheet.row(2)].transpose]
|
||||
@columns = sheet.row(2)
|
||||
|
||||
# Fill in fields for dropdown
|
||||
@available_fields = @team.get_available_sample_fields
|
||||
|
|
|
|||
|
|
@ -70,11 +70,11 @@ class WopiController < ActionController::Base
|
|||
UserCanNotWriteRelative: true,
|
||||
CloseUrl: @close_url,
|
||||
DownloadUrl: url_for(controller: 'assets', action: 'download',
|
||||
id: @asset.id),
|
||||
id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
HostEditUrl: url_for(controller: 'assets', action: 'edit',
|
||||
id: @asset.id),
|
||||
id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
HostViewUrl: url_for(controller: 'assets', action: 'view',
|
||||
id: @asset.id),
|
||||
id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
BreadcrumbBrandName: @breadcrumb_brand_name,
|
||||
BreadcrumbBrandUrl: @breadcrumb_brand_url,
|
||||
BreadcrumbFolderName: @breadcrumb_folder_name,
|
||||
|
|
@ -82,7 +82,7 @@ class WopiController < ActionController::Base
|
|||
}
|
||||
response.headers['X-WOPI-HostEndpoint'] = ENV['WOPI_ENDPOINT_URL']
|
||||
response.headers['X-WOPI-MachineName'] = ENV['WOPI_ENDPOINT_URL']
|
||||
response.headers['X-WOPI-ServerVersion'] = Constants::APP_VERSION
|
||||
response.headers['X-WOPI-ServerVersion'] = Scinote::Application::VERSION
|
||||
render json: msg and return
|
||||
end
|
||||
|
||||
|
|
@ -286,21 +286,21 @@ class WopiController < ActionController::Base
|
|||
if @protocol.in_module?
|
||||
@close_url = protocols_my_module_url(@protocol.my_module,
|
||||
only_path: false,
|
||||
host: ENV['WOPI_BREADCRUMBS_HOST'])
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
|
||||
project = @protocol.my_module.experiment.project
|
||||
@breadcrumb_brand_name = project.name
|
||||
@breadcrumb_brand_url = project_url(project,
|
||||
only_path: false,
|
||||
host: ENV['WOPI_BREADCRUMBS_HOST'])
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@breadcrumb_folder_name = @protocol.my_module.name
|
||||
else
|
||||
@close_url = protocols_url(only_path: false,
|
||||
host: ENV['WOPI_BREADCRUMBS_HOST'])
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
|
||||
@breadcrump_brand_name = 'Projects'
|
||||
@breadcrumb_brand_url = root_url(only_path: false,
|
||||
host: ENV['WOPI_BREADCRUMBS_HOST'])
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@breadcrumb_folder_name = 'Protocol managament'
|
||||
end
|
||||
@breadcrumb_folder_url = @close_url
|
||||
|
|
@ -310,12 +310,12 @@ class WopiController < ActionController::Base
|
|||
|
||||
@close_url = results_my_module_url(@my_module,
|
||||
only_path: false,
|
||||
host: ENV['WOPI_BREADCRUMBS_HOST'])
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
|
||||
@breadcrumb_brand_name = @my_module.experiment.project.name
|
||||
@breadcrumb_brand_url = project_url(@my_module.experiment.project,
|
||||
only_path: false,
|
||||
host: ENV['WOPI_BREADCRUMBS_HOST'])
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@breadcrumb_folder_name = @my_module.name
|
||||
@breadcrumb_folder_url = @close_url
|
||||
end
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class RepositoryDatatable < CustomDatatable
|
|||
# Make mappings of custom columns, so we have same id for every column
|
||||
i = 5
|
||||
@columns_mappings = {}
|
||||
@repository.repository_columns.each do |column|
|
||||
@repository.repository_columns.order(:id).each do |column|
|
||||
@columns_mappings[column.id] = i.to_s
|
||||
i += 1
|
||||
end
|
||||
|
|
@ -230,13 +230,68 @@ class RepositoryDatatable < CustomDatatable
|
|||
|
||||
# Override default sort method if needed
|
||||
def sort_records(records)
|
||||
if sort_column(order_params) == ASSIGNED_SORT_COL
|
||||
if params[:order].present? && params[:order].length == 1
|
||||
if sort_column(params[:order].values[0]) == ASSIGNED_SORT_COL
|
||||
# If "assigned" column is sorted when viewing assigned items
|
||||
return records if @my_module && params[:assigned] == 'assigned'
|
||||
# If "assigned" column is sorted
|
||||
direction = sort_null_direction(order_params)
|
||||
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
|
||||
return records if dt_params[:assigned] == 'assigned'
|
||||
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
|
||||
sort_assigned_records(records, params[:order].values[0]['dir'])
|
||||
end
|
||||
elsif sorting_by_custom_column
|
||||
# Check if have to filter records first
|
||||
# if params[:search].present? && params[:search][:value].present?
|
||||
# # Couldn't force ActiveRecord to yield the same query as below because
|
||||
# # Rails apparently forgets to join stuff in subqueries -
|
||||
# # #justrailsthings
|
||||
# conditions = build_conditions_for(params[:search][:value])
|
||||
#
|
||||
# filter_query = %(SELECT "samples"."id" FROM "samples"
|
||||
# LEFT OUTER JOIN "sample_custom_fields" ON
|
||||
# "sample_custom_fields"."sample_id" = "samples"."id"
|
||||
# LEFT OUTER JOIN "users" ON "users"."id" = "repository_row"."user_id"
|
||||
# WHERE "samples"."team_id" = #{@team.id} AND #{conditions.to_sql})
|
||||
#
|
||||
# records = records.where("samples.id IN (#{filter_query})")
|
||||
# end
|
||||
|
||||
ci = sortable_displayed_columns[
|
||||
params[:order].values[0][:column].to_i - 1
|
||||
]
|
||||
column_id = @columns_mappings.key((ci.to_i + 1).to_s)
|
||||
dir = sort_direction(params[:order].values[0])
|
||||
|
||||
# Because repository records can have multiple custom cells,
|
||||
# we first group them by samples.id and inside that group we sort them by column_id. Because
|
||||
# we sort them ASC, sorted columns will be on top. Distinct then only
|
||||
# takes the first row and cuts the rest of every group and voila we have
|
||||
# 1 row for every sample, which are not sorted yet ...
|
||||
# records = records.select('DISTINCT ON (repository_rows.id) *')
|
||||
# .order("repository_rows.id, CASE WHEN repository_cells.repository_column_id = #{column_id} THEN 1 ELSE 2 END ASC")
|
||||
|
||||
# ... this little gem (pun intended) then takes the records query, sorts it again
|
||||
# and paginates it. sq.t0_* are determined empirically and are crucial -
|
||||
# imagine A -> B -> C transitive relation but where A and C are the
|
||||
# same. Useless right? But not when you acknowledge that find_by_sql
|
||||
# method does some funky stuff when your query spans multiple queries -
|
||||
# Sample object might have id from SampleType, name from
|
||||
# User ... chaos ensues basically. If something changes in db this might
|
||||
# change.
|
||||
# formated_date = (I18n.t 'time.formats.datatables_date').gsub!(/^\"|\"?$/, '')
|
||||
# Sample.find_by_sql("SELECT sq.t0_r0 as id, sq.t0_r1 as name, to_char( sq.t0_r4, '#{ formated_date }' ) as created_at, sq.t0_r5, s, sq.t0_r2 as user_id, sq.custom_field_id FROM (#{records.to_sql})
|
||||
# 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}")
|
||||
|
||||
records.joins(
|
||||
"LEFT OUTER JOIN my_module_repository_rows ON
|
||||
(repository_rows.id = my_module_repository_rows.repository_row_id
|
||||
|
|
@ -307,4 +362,21 @@ class RepositoryDatatable < CustomDatatable
|
|||
|
||||
@sortable_displayed_columns = sort_order
|
||||
end
|
||||
|
||||
def sort_assigned_records(records, direction)
|
||||
assigned = records.joins(:my_module_repository_rows).distinct.pluck(:id)
|
||||
unassigned = records.where.not(id: assigned).pluck(:id)
|
||||
if direction == 'asc'
|
||||
ids = assigned + unassigned
|
||||
elsif direction == 'desc'
|
||||
ids = unassigned + assigned
|
||||
end
|
||||
|
||||
order_by_index = ActiveRecord::Base.send(
|
||||
:sanitize_sql_array,
|
||||
["position((',' || repository_rows.id || ',') in ?)",
|
||||
ids.join(',') + ',']
|
||||
)
|
||||
records.order(order_by_index)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
8
app/helpers/addons_helper.rb
Normal file
8
app/helpers/addons_helper.rb
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
module AddonsHelper
|
||||
def list_all_addons
|
||||
Rails::Engine
|
||||
.subclasses
|
||||
.select { |c| c.name.start_with?('Scinote') }
|
||||
.map(&:parent)
|
||||
end
|
||||
end
|
||||
|
|
@ -1075,11 +1075,13 @@ module PermissionHelper
|
|||
end
|
||||
|
||||
def can_delete_column_in_repository(column)
|
||||
is_normal_user_or_admin_of_team(column.repository.team)
|
||||
column.created_by == current_user ||
|
||||
is_admin_of_team(column.repository.team)
|
||||
end
|
||||
|
||||
def can_edit_column_in_repository(column)
|
||||
is_normal_user_or_admin_of_team(column.repository.team)
|
||||
column.created_by == current_user ||
|
||||
is_admin_of_team(column.repository.team)
|
||||
end
|
||||
|
||||
def can_create_repository_records(repository)
|
||||
|
|
|
|||
|
|
@ -384,10 +384,17 @@ class Asset < ApplicationRecord
|
|||
action = get_action(file_ext, action)
|
||||
if !action.nil?
|
||||
action_url = action.urlsrc
|
||||
if ENV['WOPI_BUSINESS_USERS'] && ENV['WOPI_BUSINESS_USERS']=='true'
|
||||
action_url = action_url.gsub(/<IsLicensedUser=BUSINESS_USER&>/,
|
||||
'IsLicensedUser=1&')
|
||||
action_url = action_url.gsub(/<IsLicensedUser=BUSINESS_USER>/,
|
||||
'IsLicensedUser=1')
|
||||
else
|
||||
action_url = action_url.gsub(/<IsLicensedUser=BUSINESS_USER&>/,
|
||||
'IsLicensedUser=0&')
|
||||
action_url = action_url.gsub(/<IsLicensedUser=BUSINESS_USER>/,
|
||||
'IsLicensedUser=0')
|
||||
end
|
||||
action_url = action_url.gsub(/<.*?=.*?>/, '')
|
||||
|
||||
rest_url = Rails.application.routes.url_helpers.wopi_rest_endpoint_url(
|
||||
|
|
|
|||
|
|
@ -111,57 +111,82 @@ class Repository < ApplicationRecord
|
|||
|
||||
# Imports records
|
||||
def import_records(sheet, mappings, user)
|
||||
errors = []
|
||||
custom_fields = []
|
||||
errors = false
|
||||
columns = []
|
||||
name_index = -1
|
||||
total_nr = 0
|
||||
nr_of_added = 0
|
||||
|
||||
mappings.each.with_index do |(_k, value), index|
|
||||
if value == '-1'
|
||||
# Fill blank space, so our indices stay the same
|
||||
custom_fields << nil
|
||||
columns << nil
|
||||
name_index = index
|
||||
else
|
||||
cf = repository_columns.find_by_id(value)
|
||||
custom_fields << cf
|
||||
columns << repository_columns.find_by_id(value)
|
||||
end
|
||||
end
|
||||
|
||||
# Check for duplicate columns
|
||||
col_compact = columns.compact
|
||||
unless col_compact.map(&:id).uniq.length == col_compact.length
|
||||
return { status: :error, nr_of_added: nr_of_added, total_nr: total_nr }
|
||||
end
|
||||
|
||||
# Now we can iterate through record data and save stuff into db
|
||||
transaction do
|
||||
(2..sheet.last_row).each do |i|
|
||||
error = []
|
||||
total_nr += 1
|
||||
record_row = RepositoryRow.new(name: sheet.row(i)[name_index],
|
||||
repository: self,
|
||||
created_by: user,
|
||||
last_modified_by: user)
|
||||
record_row.transaction(requires_new: true) do
|
||||
unless record_row.save
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
row_cell_values = []
|
||||
|
||||
next unless record_row.valid?
|
||||
sheet.row(i).each.with_index do |value, index|
|
||||
if custom_fields[index] && value
|
||||
rep_column = RepositoryTextValue.new(
|
||||
if columns[index] && value
|
||||
cell_value = RepositoryTextValue.new(
|
||||
data: value,
|
||||
created_by: user,
|
||||
last_modified_by: user,
|
||||
repository_cell_attributes: {
|
||||
repository_row: record_row,
|
||||
repository_column: custom_fields[index]
|
||||
repository_column: columns[index]
|
||||
}
|
||||
)
|
||||
error << rep_column.errors.messages unless rep_column.save
|
||||
cell = RepositoryCell.new(repository_row: record_row,
|
||||
repository_column: columns[index],
|
||||
value: cell_value)
|
||||
cell.skip_on_import = true
|
||||
cell_value.repository_cell = cell
|
||||
unless cell.valid? && cell_value.valid?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
row_cell_values << cell_value
|
||||
end
|
||||
end
|
||||
if error.any?
|
||||
record_row.destroy
|
||||
else
|
||||
if RepositoryTextValue.import(row_cell_values,
|
||||
recursive: true,
|
||||
validate: false).failed_instances.any?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
nr_of_added += 1
|
||||
record_row.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if errors.count > 0
|
||||
return { status: :error, errors: errors, nr_of_added: nr_of_added }
|
||||
if errors
|
||||
return { status: :error, nr_of_added: nr_of_added, total_nr: total_nr }
|
||||
end
|
||||
{ status: :ok, nr_of_added: nr_of_added }
|
||||
{ status: :ok, nr_of_added: nr_of_added, total_nr: total_nr }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -170,13 +195,11 @@ class Repository < ApplicationRecord
|
|||
case File.extname(filename)
|
||||
when '.csv'
|
||||
Roo::CSV.new(file_path, extension: :csv)
|
||||
when '.tdv'
|
||||
Roo::CSV.new(file_path, nil, :ignore, csv_options: { col_sep: '\t' })
|
||||
when '.tsv'
|
||||
Roo::CSV.new(file_path, csv_options: { col_sep: "\t" })
|
||||
when '.txt'
|
||||
# This assumption is based purely on biologist's habits
|
||||
Roo::CSV.new(file_path, csv_options: { col_sep: '\t' })
|
||||
when '.xls'
|
||||
Roo::Excel.new(file_path)
|
||||
Roo::CSV.new(file_path, csv_options: { col_sep: "\t" })
|
||||
when '.xlsx'
|
||||
Roo::Excelx.new(file_path)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
class RepositoryCell < ApplicationRecord
|
||||
belongs_to :repository_row, optional: true
|
||||
belongs_to :repository_column, optional: true
|
||||
belongs_to :value, polymorphic: true, dependent: :destroy, optional: true
|
||||
class RepositoryCell < ActiveRecord::Base
|
||||
attr_accessor :skip_on_import
|
||||
|
||||
belongs_to :repository_row
|
||||
belongs_to :repository_column
|
||||
belongs_to :value, polymorphic: true, dependent: :destroy
|
||||
|
||||
validates :repository_column, presence: true
|
||||
validate :repository_column_data_type
|
||||
validates :repository_row, uniqueness: { scope: :repository_column }
|
||||
validates :repository_row,
|
||||
uniqueness: { scope: :repository_column },
|
||||
unless: :skip_on_import
|
||||
|
||||
private
|
||||
|
||||
|
|
|
|||
|
|
@ -43,16 +43,14 @@ class Team < ApplicationRecord
|
|||
end
|
||||
|
||||
case File.extname(filename)
|
||||
when ".csv" then
|
||||
when '.csv' then
|
||||
Roo::CSV.new(file_path, extension: :csv)
|
||||
when ".tdv" then
|
||||
Roo::CSV.new(file_path, nil, :ignore, csv_options: {col_sep: "\t"})
|
||||
when ".txt" then
|
||||
when '.tsv' then
|
||||
Roo::CSV.new(file_path, csv_options: { col_sep: "\t" })
|
||||
when '.txt' then
|
||||
# This assumption is based purely on biologist's habits
|
||||
Roo::CSV.new(file_path, csv_options: { col_sep: "\t" })
|
||||
when ".xls" then
|
||||
Roo::Excel.new(file_path)
|
||||
when ".xlsx" then
|
||||
when '.xlsx' then
|
||||
Roo::Excelx.new(file_path)
|
||||
else
|
||||
raise TypeError
|
||||
|
|
@ -71,7 +69,7 @@ class Team < ApplicationRecord
|
|||
# -3 == sample_group
|
||||
# TODO: use constants
|
||||
def import_samples(sheet, mappings, user)
|
||||
errors = []
|
||||
errors = false
|
||||
nr_of_added = 0
|
||||
total_nr = 0
|
||||
|
||||
|
|
@ -80,15 +78,15 @@ class Team < ApplicationRecord
|
|||
sname_index = -1
|
||||
stype_index = -1
|
||||
sgroup_index = -1
|
||||
mappings.each.with_index do |(k, v), i|
|
||||
if v == "-1"
|
||||
mappings.each.with_index do |(_, v), i|
|
||||
if v == '-1'
|
||||
# Fill blank space, so our indices stay the same
|
||||
custom_fields << nil
|
||||
sname_index = i
|
||||
elsif v == "-2"
|
||||
elsif v == '-2'
|
||||
custom_fields << nil
|
||||
stype_index = i
|
||||
elsif v == "-3"
|
||||
elsif v == '-3'
|
||||
custom_fields << nil
|
||||
sgroup_index = i
|
||||
else
|
||||
|
|
@ -99,87 +97,70 @@ class Team < ApplicationRecord
|
|||
custom_fields << cf
|
||||
end
|
||||
end
|
||||
|
||||
# Now we can iterate through sample data and save stuff into db
|
||||
(2..sheet.last_row).each do |i|
|
||||
error = []
|
||||
total_nr += 1
|
||||
sample = Sample.new(name: sheet.row(i)[sname_index],
|
||||
team: self,
|
||||
user: user)
|
||||
|
||||
sample = Sample.new(
|
||||
name: sheet.row(i)[sname_index],
|
||||
team_id: id,
|
||||
user: user
|
||||
)
|
||||
sample.transaction do
|
||||
unless sample.valid?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
if sample.save
|
||||
sheet.row(i).each.with_index do |value, index|
|
||||
# We need to have sample saved before messing with custom fields (they
|
||||
# need sample id)
|
||||
if index == stype_index
|
||||
stype = SampleType.where(name: value, team_id: id).take
|
||||
stype = SampleType.where(name: value, team: self).take
|
||||
|
||||
if stype
|
||||
unless stype
|
||||
stype = SampleType.new(name: value, team: self)
|
||||
unless stype.save
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
sample.sample_type = stype
|
||||
else
|
||||
sample.create_sample_type(
|
||||
name: value,
|
||||
team_id: id
|
||||
)
|
||||
end
|
||||
sample.save
|
||||
elsif index == sgroup_index
|
||||
sgroup = SampleGroup.where(name: value, team_id: id).take
|
||||
sgroup = SampleGroup.where(name: value, team: self).take
|
||||
|
||||
if sgroup
|
||||
sample.sample_group = sgroup
|
||||
else
|
||||
sample.create_sample_group(
|
||||
name: value,
|
||||
team_id: id
|
||||
)
|
||||
unless sgroup
|
||||
sgroup = SampleGroup.new(name: value, team: self)
|
||||
unless sgroup.save
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
sample.save
|
||||
elsif value and mappings[index.to_s].strip.present? and index != sname_index
|
||||
if custom_fields[index]
|
||||
end
|
||||
sample.sample_group = sgroup
|
||||
elsif value && custom_fields[index]
|
||||
# we're working with CustomField
|
||||
scf = SampleCustomField.new(
|
||||
sample_id: sample.id,
|
||||
custom_field_id: custom_fields[index].id,
|
||||
sample: sample,
|
||||
custom_field: custom_fields[index],
|
||||
value: value
|
||||
)
|
||||
|
||||
if !scf.save
|
||||
error << scf.errors.messages
|
||||
unless scf.valid?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
else
|
||||
# This custom_field does not exist
|
||||
error << {"#{mappings[index]}": "Does not exists"}
|
||||
sample.sample_custom_fields << scf
|
||||
end
|
||||
end
|
||||
if Sample.import([sample],
|
||||
recursive: true,
|
||||
validate: false).failed_instances.any?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
else
|
||||
error << sample.errors.messages
|
||||
end
|
||||
if error.present?
|
||||
errors << { "#{i}": error}
|
||||
else
|
||||
nr_of_added += 1
|
||||
end
|
||||
end
|
||||
|
||||
if errors.count > 0 then
|
||||
return {
|
||||
status: :error,
|
||||
errors: errors,
|
||||
nr_of_added: nr_of_added,
|
||||
total_nr: total_nr
|
||||
}
|
||||
if errors
|
||||
return { status: :error, nr_of_added: nr_of_added, total_nr: total_nr }
|
||||
else
|
||||
return {
|
||||
status: :ok,
|
||||
nr_of_added: nr_of_added,
|
||||
total_nr: total_nr
|
||||
}
|
||||
return { status: :ok, nr_of_added: nr_of_added, total_nr: total_nr }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,15 +11,14 @@ module ImportRepository
|
|||
def data
|
||||
# Get data (it will trigger any errors as well)
|
||||
header = @sheet.row(1)
|
||||
rows = []
|
||||
rows << Hash[[header, @sheet.row(2)].transpose]
|
||||
columns = @sheet.row(2)
|
||||
# Fill in fields for dropdown
|
||||
@repository.available_repository_fields.transform_values! do |name|
|
||||
truncate(name, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN)
|
||||
end
|
||||
@temp_file = TempFile.create(session_id: @session.id, file: @file)
|
||||
Data.new(header,
|
||||
rows,
|
||||
columns,
|
||||
@repository.available_repository_fields,
|
||||
@repository,
|
||||
@temp_file)
|
||||
|
|
@ -47,7 +46,7 @@ module ImportRepository
|
|||
end
|
||||
|
||||
Data = Struct.new(
|
||||
:header, :rows, :available_fields, :repository, :temp_file
|
||||
:header, :columns, :available_fields, :repository, :temp_file
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- About us modal -->
|
||||
<%= render "shared/about_modal" %>
|
||||
|
||||
<%= render "shared/navigation" %>
|
||||
|
||||
<div id="notifications">
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@
|
|||
<% end %>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<div id="content"
|
||||
data-repo-id="#repository-table-<%= @repository.id %>">
|
||||
<%= render partial: "repositories/repository_table",
|
||||
locals: {
|
||||
repository: @repository,
|
||||
|
|
@ -40,3 +41,7 @@
|
|||
}
|
||||
%>
|
||||
</div>
|
||||
|
||||
<%= stylesheet_link_tag 'datatables' %>
|
||||
<%= javascript_include_tag 'repositories/repository_datatable' %>
|
||||
<%= javascript_include_tag 'repositories/my_module_repository' %>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="modal fade"
|
||||
id="parse-records_modal"
|
||||
id="parse-records-modal"
|
||||
aria-labelledby="parse-modal-title"
|
||||
role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
|
|
@ -30,6 +30,9 @@
|
|||
include_blank: t('teams.parse_sheet.do_not_include_column'),
|
||||
hide_label: true) %>
|
||||
<br />
|
||||
<% if th.nil? %>
|
||||
<i><%= t('repositories.import_records.no_header_name') %></i>
|
||||
<% else %>
|
||||
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<div class="modal-tooltip">
|
||||
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
|
||||
|
|
@ -37,22 +40,21 @@
|
|||
<% else %>
|
||||
<%= th %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @import_data.rows.each do |row| %>
|
||||
<tr>
|
||||
<td>
|
||||
<p><%= t('teams.parse_sheet.example_value') %></p>
|
||||
</td>
|
||||
<% row.each do |td| %>
|
||||
<% @import_data.columns.each do |td| %>
|
||||
<td>
|
||||
<%= td[1] %>
|
||||
<%= td %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="repository-table">
|
||||
<table id="repository-table" class="table"
|
||||
<table id="repository-table-<%= repository.id %>" class="table"
|
||||
data-current-uri="<%= request.original_url %>"
|
||||
data-repository-id="<%= repository.id %>"
|
||||
data-source="<%= repository_index_link %>"
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<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| %>
|
||||
<% repository.repository_columns.order(:id).each do |column| %>
|
||||
<th class="repository-column" id="<%= column.id %>"
|
||||
<%= 'data-editable' if can_edit_column_in_repository(column) %>
|
||||
<%= 'data-deletable' if can_delete_column_in_repository(column) %>
|
||||
|
|
@ -35,6 +35,3 @@
|
|||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<%= stylesheet_link_tag 'datatables' %>
|
||||
<%= javascript_include_tag('repositories/repository_datatable') %>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
<li role="presentation">
|
||||
<a href="#custom_repo_<%= repo.id %>"
|
||||
data-toggle="tab"
|
||||
data-repo-table="#repository-table-<%= repo.id %>"
|
||||
aria-controls="custom_repo_<%= repo.id %>"
|
||||
data-url="<%=team_repository_show_tab_path(current_team, repo)%>"
|
||||
title="<%=repo.name%>"><%= truncate(repo.name, length: Constants::NAME_TRUNCATION_LENGTH) %></a>
|
||||
|
|
@ -53,4 +54,6 @@
|
|||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= stylesheet_link_tag 'datatables' %>
|
||||
<%= javascript_include_tag 'repositories/repository_datatable' %>
|
||||
<%= javascript_include_tag "repositories/index", "data-turbolinks-track" => true %>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
include_blank: t('teams.parse_sheet.do_not_include_column'),
|
||||
hide_label: true) %>
|
||||
<br />
|
||||
<% if th.nil? %>
|
||||
<i><%= t('samples.modal_import.no_header_name') %></i>
|
||||
<% else %>
|
||||
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<div class="modal-tooltip">
|
||||
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
|
||||
|
|
@ -32,22 +35,21 @@
|
|||
<% else %>
|
||||
<%= th %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @rows.each do |row| %>
|
||||
<tr>
|
||||
<td>
|
||||
<p><%= t('teams.parse_sheet.example_value') %></p>
|
||||
</td>
|
||||
<% row.each do |td| %>
|
||||
<% @columns.each do |td| %>
|
||||
<td>
|
||||
<%= td[1] %>
|
||||
<%= td %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
28
app/views/shared/_about_modal.html.erb
Normal file
28
app/views/shared/_about_modal.html.erb
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<div class="modal" id="aboutModal" tabindex="-1" role="dialog" aria-labelledby="aboutModal" data-role="about-modal">
|
||||
<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">×</span></button>
|
||||
<h4 class="modal-title"><%= t('about.modal_title') %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<strong><%= t('about.core_version') %></strong>
|
||||
<br />
|
||||
<%= Scinote::Application::VERSION %>
|
||||
<br />
|
||||
<br />
|
||||
<div data-hook="about-modal-addon-versions">
|
||||
<strong><%= t('about.addon_versions') %></strong>
|
||||
<br />
|
||||
<% list_all_addons.each do |addon| %>
|
||||
<%= "#{addon.name}:" %>
|
||||
<br />
|
||||
<%= addon::VERSION %>
|
||||
<br />
|
||||
<br />
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<% if show_version %>
|
||||
<%= image_tag('/images/logo.png', class: 'with-version', id: 'logo') %>
|
||||
<span class="version">
|
||||
<%= Constants::APP_VERSION %>
|
||||
<%= Scinote::Application::VERSION %>
|
||||
</span>
|
||||
<% else %>
|
||||
<%= image_tag('/images/logo.png', id: 'logo') %>
|
||||
|
|
@ -234,6 +234,12 @@
|
|||
<li><%= link_to t('nav.help.contact'),
|
||||
Constants::CONTACT_URL,
|
||||
target: "_blank" %></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li>
|
||||
<%= link_to '#', data: { trigger: 'about-modal' } do %>
|
||||
<%= t('nav.help.about') %>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,10 @@
|
|||
<ul class="dropdown-menu repositories-dropdown-menu" aria-labelledby="repositoriesDropdownMenuLink">
|
||||
<% @my_module.experiment.project.team.repositories.order(created_at: :asc).each do |repository| %>
|
||||
<li>
|
||||
<a class="dropdown-item" href="<%= repository_my_module_url(id: @my_module, repository_id: repository) %>" title="<%= repository.name %>">
|
||||
<a class="dropdown-item"
|
||||
href="<%= repository_my_module_url(id: @my_module, repository_id: repository) %>"
|
||||
title="<%= repository.name %>"
|
||||
data-no-turbolink="true">
|
||||
<%= truncate(repository.name) %>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -29,5 +29,8 @@ module Scinote
|
|||
csv: 'text/plain',
|
||||
wopitest: ['text/plain', 'inode/x-empty']
|
||||
}
|
||||
|
||||
# sciNote Core Application version
|
||||
VERSION = File.read(Rails.root.join('VERSION')).strip.freeze
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ 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)
|
||||
Rails.application.config.assets.precompile +=
|
||||
%w(repositories/my_module_repository.js)
|
||||
|
||||
# Libraries needed for Handsontable formulas
|
||||
Rails.application.config.assets.precompile += %w(lodash.js)
|
||||
|
|
|
|||
|
|
@ -196,9 +196,6 @@ class Constants
|
|||
# Other
|
||||
#=============================================================================
|
||||
|
||||
# Application version
|
||||
APP_VERSION = '1.12.1'.freeze
|
||||
|
||||
TEXT_EXTRACT_FILE_TYPES = [
|
||||
'application/pdf',
|
||||
'application/rtf',
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ Devise.setup do |config|
|
|||
# The period the generated invitation token is valid, after
|
||||
# this period, the invited resource won't be able to accept the invitation.
|
||||
# When invite_for is 0 (the default), the invitation won't expire.
|
||||
config.invite_for = 3.days
|
||||
config.invite_for = 7.days
|
||||
|
||||
# Number of invitations users can send.
|
||||
# - If invitation_limit is nil, there is no limit for invitations, users can
|
||||
|
|
@ -163,7 +163,7 @@ Devise.setup do |config|
|
|||
# their account can't be confirmed with the token any more.
|
||||
# Default is nil, meaning there is no restriction on how long a user can take
|
||||
# before confirming their account.
|
||||
config.confirm_within = 3.days
|
||||
config.confirm_within = 7.days
|
||||
|
||||
# If true, requires any email changes to be confirmed (exactly the same way as
|
||||
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ module Paperclip
|
|||
# Determine file content type from its name
|
||||
def content_types_from_name
|
||||
@content_types_from_name ||=
|
||||
Paperclip.run('mimetype', '-b :file_name', file_name: @name).chomp
|
||||
Paperclip.run('mimetype', '-b -- :file_name', file_name: @name).chomp
|
||||
end
|
||||
|
||||
# Determine file media type from its name
|
||||
|
|
@ -92,7 +92,7 @@ module Paperclip
|
|||
# Determine file content type from mimetype command
|
||||
def type_from_mimetype_command
|
||||
@type_from_mimetype_command ||=
|
||||
Paperclip.run('mimetype', '-b :file', file: @file.path).chomp
|
||||
Paperclip.run('mimetype', '-b -- :file', file: @file.path).chomp
|
||||
end
|
||||
|
||||
# Determine file media type from mimetype command
|
||||
|
|
@ -105,7 +105,7 @@ module Paperclip
|
|||
def type_from_file_command
|
||||
unless defined? @type_from_file_command
|
||||
@type_from_file_command =
|
||||
Paperclip.run('file', '-b --mime :file', file: @file.path)
|
||||
Paperclip.run('file', '-b --mime -- :file', file: @file.path)
|
||||
.split(/[:;]\s+/).first
|
||||
|
||||
if allowed_spoof_exception?(@type_from_file_command,
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ en:
|
|||
support: "Customer support"
|
||||
premium: "Premium"
|
||||
contact: "Contact us"
|
||||
about: "About sciNote"
|
||||
activities:
|
||||
none: "No activities!"
|
||||
label:
|
||||
|
|
@ -87,6 +88,11 @@ en:
|
|||
info: "Info"
|
||||
account: "Account"
|
||||
|
||||
about:
|
||||
modal_title: "About sciNote"
|
||||
core_version: "sciNote core version"
|
||||
addon_versions: "Addon versions"
|
||||
|
||||
sidebar:
|
||||
title: "Navigation"
|
||||
no_module_group: "No workflow"
|
||||
|
|
@ -905,8 +911,9 @@ en:
|
|||
add_new_record: "Add new item"
|
||||
import_records:
|
||||
import: 'Import'
|
||||
no_header_name: 'No column name'
|
||||
success_flash: "%{number_of_rows} new item(s) successfully imported."
|
||||
error_flash: "Something went wrong: %{message}"
|
||||
partial_success_flash: "%{nr} of %{total_nr} successfully imported. Other rows contained errors."
|
||||
error_message:
|
||||
temp_file_not_found: "This file could not be found. Your session might expire."
|
||||
session_expired: "Your session expired. Please try again."
|
||||
|
|
@ -944,7 +951,7 @@ en:
|
|||
title: 'Import items'
|
||||
modal_import:
|
||||
title: 'Import items'
|
||||
notice: 'You may upload .csv file (comma separated) or tab separated file (.txt or .tdv) or Excel file (.xls, .xlsx). First row should include header names, followed by rows with sample data.'
|
||||
notice: 'You may upload .csv file (comma separated) or tab separated file (.txt or .tsv) or Excel file (.xlsx). First row should include header names, followed by rows with sample data.'
|
||||
upload: 'Upload file'
|
||||
js:
|
||||
permission_error: "You don't have permission to edit this item."
|
||||
|
|
@ -1002,7 +1009,8 @@ en:
|
|||
sample_type: "Sample type:"
|
||||
modal_import:
|
||||
title: "Import samples"
|
||||
notice: "You may upload .csv file (comma separated) or tab separated file (.txt or .tdv) or Excel file (.xls, .xlsx). First row should include header names, followed by rows with sample data."
|
||||
notice: "You may upload .csv file (comma separated) or tab separated file (.txt or .tsv) or Excel file (.xlsx). First row should include header names, followed by rows with sample data."
|
||||
no_header_name: 'No column name'
|
||||
upload: "Upload file"
|
||||
modal_delete:
|
||||
title: "Delete samples"
|
||||
|
|
|
|||
|
|
@ -114,12 +114,16 @@ class AddonGenerator < Rails::Generators::NamedBase
|
|||
gsub_file(file_path, '${ADDON_NAME}', @addon_name)
|
||||
|
||||
# lib/.../version.rb
|
||||
dots = @modules.map { '/..' }.join
|
||||
create_file(
|
||||
"addons/#{@addon_name}/lib/" \
|
||||
"#{@folders_path}/version.rb"
|
||||
) do
|
||||
embed_into_modules do
|
||||
"VERSION = '0.0.1'.freeze\n"
|
||||
"VERSION =\n" \
|
||||
" File.read(\n" \
|
||||
" \"\#{File.dirname(__FILE__)}#{dots}/../VERSION\"\n" \
|
||||
" ).strip.freeze\n"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -182,6 +186,7 @@ class AddonGenerator < Rails::Generators::NamedBase
|
|||
gsub_file(file_path, '${FULL_UNDERSCORE_NAME}', @full_underscore_name)
|
||||
gsub_file(file_path, '${NAME}', name)
|
||||
gsub_file(file_path, '${FOLDERS_PATH}', @folders_path)
|
||||
create_file("addons/#{@addon_name}/VERSION") { '0.0.1' }
|
||||
|
||||
# Rakefile
|
||||
file_path = "addons/#{@addon_name}/Rakefile"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue