mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-11-09 16:01:30 +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();
|
||||
});
|
||||
})();
|
||||
File diff suppressed because it is too large
Load diff
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,27 +184,27 @@ 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
|
||||
if parsed_file.too_large?
|
||||
repository_response(t('general.file.size_exceeded',
|
||||
file_size: Constants::FILE_MAX_SIZE_MB))
|
||||
elsif parsed_file.empty?
|
||||
flash[:notice] = t('teams.parse_sheet.errors.empty_file')
|
||||
redirect_to back and return
|
||||
else
|
||||
@import_data = parsed_file.data
|
||||
if parsed_file.generated_temp_file?
|
||||
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))
|
||||
elsif parsed_file.empty?
|
||||
flash[:notice] = t('teams.parse_sheet.errors.empty_file')
|
||||
redirect_to back and return
|
||||
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,16 +212,16 @@ class RepositoriesController < ApplicationController
|
|||
)
|
||||
}
|
||||
end
|
||||
else
|
||||
repository_response(t('teams.parse_sheet.errors.temp_file_failure'))
|
||||
end
|
||||
else
|
||||
repository_response(t('teams.parse_sheet.errors.temp_file_failure'))
|
||||
end
|
||||
rescue ArgumentError, CSV::MalformedCSVError
|
||||
repository_response(t('teams.parse_sheet.errors.invalid_file',
|
||||
encoding: ''.encoding))
|
||||
rescue TypeError
|
||||
repository_response(t('teams.parse_sheet.errors.invalid_extension'))
|
||||
end
|
||||
rescue ArgumentError, CSV::MalformedCSVError
|
||||
repository_response(t('teams.parse_sheet.errors.invalid_file',
|
||||
encoding: ''.encoding))
|
||||
rescue TypeError
|
||||
repository_response(t('teams.parse_sheet.errors.invalid_extension'))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -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,13 +321,15 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def repository_response(message)
|
||||
format.html do
|
||||
flash[:alert] = message
|
||||
redirect_to :back
|
||||
end
|
||||
format.json do
|
||||
render json: { message: message },
|
||||
status: :unprocessable_entity
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
flash[:alert] = message
|
||||
redirect_to :back
|
||||
end
|
||||
format.json do
|
||||
render json: { message: message },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -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 "assigned" column is sorted
|
||||
direction = sort_null_direction(order_params)
|
||||
if @my_module
|
||||
# Depending on the sort, order nulls first or
|
||||
# nulls last on repository_cells association
|
||||
return records if dt_params[:assigned] == 'assigned'
|
||||
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(params[:order].values[0])
|
||||
if @my_module
|
||||
# Depending on the sort, order nulls first or
|
||||
# nulls last on repository_cells association
|
||||
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
|
||||
action_url = action_url.gsub(/<IsLicensedUser=BUSINESS_USER&>/,
|
||||
'IsLicensedUser=0&')
|
||||
action_url = action_url.gsub(/<IsLicensedUser=BUSINESS_USER>/,
|
||||
'IsLicensedUser=0')
|
||||
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
|
||||
(2..sheet.last_row).each do |i|
|
||||
error = []
|
||||
record_row = RepositoryRow.new(name: sheet.row(i)[name_index],
|
||||
repository: self,
|
||||
created_by: user,
|
||||
last_modified_by: user)
|
||||
transaction do
|
||||
(2..sheet.last_row).each do |i|
|
||||
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
|
||||
|
||||
next unless record_row.valid?
|
||||
sheet.row(i).each.with_index do |value, index|
|
||||
if custom_fields[index] && value
|
||||
rep_column = RepositoryTextValue.new(
|
||||
data: value,
|
||||
created_by: user,
|
||||
last_modified_by: user,
|
||||
repository_cell_attributes: {
|
||||
repository_row: record_row,
|
||||
repository_column: custom_fields[index]
|
||||
}
|
||||
)
|
||||
error << rep_column.errors.messages unless rep_column.save
|
||||
row_cell_values = []
|
||||
|
||||
sheet.row(i).each.with_index do |value, index|
|
||||
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: columns[index]
|
||||
}
|
||||
)
|
||||
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 RepositoryTextValue.import(row_cell_values,
|
||||
recursive: true,
|
||||
validate: false).failed_instances.any?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
nr_of_added += 1
|
||||
end
|
||||
end
|
||||
if error.any?
|
||||
record_row.destroy
|
||||
else
|
||||
nr_of_added += 1
|
||||
record_row.save
|
||||
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
|
||||
Roo::CSV.new(file_path, csv_options: { col_sep: "\t" })
|
||||
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,17 +78,17 @@ 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
|
||||
sgroup_index = i
|
||||
else
|
||||
cf = CustomField.find_by_id(v)
|
||||
|
||||
|
|
@ -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
|
||||
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
|
||||
|
||||
if sgroup
|
||||
sample.sample_group = sgroup
|
||||
else
|
||||
sample.create_sample_group(
|
||||
name: value,
|
||||
team_id: id
|
||||
)
|
||||
end
|
||||
sample.save
|
||||
elsif value and mappings[index.to_s].strip.present? and index != sname_index
|
||||
if custom_fields[index]
|
||||
# we're working with CustomField
|
||||
scf = SampleCustomField.new(
|
||||
sample_id: sample.id,
|
||||
custom_field_id: custom_fields[index].id,
|
||||
value: value
|
||||
)
|
||||
|
||||
if !scf.save
|
||||
error << scf.errors.messages
|
||||
unless stype
|
||||
stype = SampleType.new(name: value, team: self)
|
||||
unless stype.save
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
else
|
||||
# This custom_field does not exist
|
||||
error << {"#{mappings[index]}": "Does not exists"}
|
||||
end
|
||||
sample.sample_type = stype
|
||||
elsif index == sgroup_index
|
||||
sgroup = SampleGroup.where(name: value, team: self).take
|
||||
|
||||
unless sgroup
|
||||
sgroup = SampleGroup.new(name: value, team: self)
|
||||
unless sgroup.save
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
sample.sample_group = sgroup
|
||||
elsif value && custom_fields[index]
|
||||
# we're working with CustomField
|
||||
scf = SampleCustomField.new(
|
||||
sample: sample,
|
||||
custom_field: custom_fields[index],
|
||||
value: value
|
||||
)
|
||||
unless scf.valid?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
sample.sample_custom_fields << scf
|
||||
end
|
||||
end
|
||||
else
|
||||
error << sample.errors.messages
|
||||
end
|
||||
if error.present?
|
||||
errors << { "#{i}": error}
|
||||
else
|
||||
if Sample.import([sample],
|
||||
recursive: true,
|
||||
validate: false).failed_instances.any?
|
||||
errors = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
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,29 +30,31 @@
|
|||
include_blank: t('teams.parse_sheet.do_not_include_column'),
|
||||
hide_label: true) %>
|
||||
<br />
|
||||
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<div class="modal-tooltip">
|
||||
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
|
||||
</div>
|
||||
<% if th.nil? %>
|
||||
<i><%= t('repositories.import_records.no_header_name') %></i>
|
||||
<% else %>
|
||||
<%= th %>
|
||||
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<div class="modal-tooltip">
|
||||
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= th %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @import_data.rows.each do |row| %>
|
||||
<tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><%= t('teams.parse_sheet.example_value') %></p>
|
||||
</td>
|
||||
<% @import_data.columns.each do |td| %>
|
||||
<td>
|
||||
<p><%= t('teams.parse_sheet.example_value') %></p>
|
||||
<%= td %>
|
||||
</td>
|
||||
<% row.each do |td| %>
|
||||
<td>
|
||||
<%= td[1] %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tr>
|
||||
</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,29 +25,31 @@
|
|||
include_blank: t('teams.parse_sheet.do_not_include_column'),
|
||||
hide_label: true) %>
|
||||
<br />
|
||||
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<div class="modal-tooltip">
|
||||
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
|
||||
</div>
|
||||
<% if th.nil? %>
|
||||
<i><%= t('samples.modal_import.no_header_name') %></i>
|
||||
<% else %>
|
||||
<%= th %>
|
||||
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<div class="modal-tooltip">
|
||||
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= th %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @rows.each do |row| %>
|
||||
<tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><%= t('teams.parse_sheet.example_value') %></p>
|
||||
</td>
|
||||
<% @columns.each do |td| %>
|
||||
<td>
|
||||
<p><%= t('teams.parse_sheet.example_value') %></p>
|
||||
<%= td %>
|
||||
</td>
|
||||
<% row.each do |td| %>
|
||||
<td>
|
||||
<%= td[1] %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tr>
|
||||
</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