mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-25 17:36:14 +08:00
Merge pull request #2614 from biosistemika/develop
Merge develop to master
This commit is contained in:
commit
0007de2a95
2
Gemfile
2
Gemfile
|
@ -97,7 +97,7 @@ gem 'rufus-scheduler', '~> 3.5'
|
|||
gem 'discard', '~> 1.0'
|
||||
|
||||
gem 'ruby-graphviz', '~> 1.2' # Graphviz for rails
|
||||
gem 'tinymce-rails', '~> 4.9.3' # Rich text editor - SEE BELOW
|
||||
gem 'tinymce-rails', '~> 4.9.10' # Rich text editor - SEE BELOW
|
||||
# Any time you update tinymce-rails Gem, also update the cache_suffix parameter
|
||||
# in sitewide/tiny_mce.js - to prevent browsers from loading old, cached .js
|
||||
# TinyMCE files which might cause errors
|
||||
|
|
140
Gemfile.lock
140
Gemfile.lock
|
@ -1,6 +1,6 @@
|
|||
GIT
|
||||
remote: https://github.com/biosistemika/canaid
|
||||
revision: 2ac3004d728adbf1be7f4271689b83464f612b23
|
||||
revision: f595a096f402900e184bf51298dca38fbb7e0820
|
||||
branch: rails_6
|
||||
specs:
|
||||
canaid (1.0.4)
|
||||
|
@ -42,38 +42,38 @@ GIT
|
|||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
actioncable (6.0.0)
|
||||
actionpack (= 6.0.0)
|
||||
actioncable (6.0.3)
|
||||
actionpack (= 6.0.3)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.0.0)
|
||||
actionpack (= 6.0.0)
|
||||
activejob (= 6.0.0)
|
||||
activerecord (= 6.0.0)
|
||||
activestorage (= 6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
actionmailbox (6.0.3)
|
||||
actionpack (= 6.0.3)
|
||||
activejob (= 6.0.3)
|
||||
activerecord (= 6.0.3)
|
||||
activestorage (= 6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.0.0)
|
||||
actionpack (= 6.0.0)
|
||||
actionview (= 6.0.0)
|
||||
activejob (= 6.0.0)
|
||||
actionmailer (6.0.3)
|
||||
actionpack (= 6.0.3)
|
||||
actionview (= 6.0.3)
|
||||
activejob (= 6.0.3)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.0.0)
|
||||
actionview (= 6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
rack (~> 2.0)
|
||||
actionpack (6.0.3)
|
||||
actionview (= 6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.0.0)
|
||||
actionpack (= 6.0.0)
|
||||
activerecord (= 6.0.0)
|
||||
activestorage (= 6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
actiontext (6.0.3)
|
||||
actionpack (= 6.0.3)
|
||||
activerecord (= 6.0.3)
|
||||
activestorage (= 6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
actionview (6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
|
@ -83,27 +83,27 @@ GEM
|
|||
activemodel (>= 4.1, < 6.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
activejob (6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
activerecord (6.0.0)
|
||||
activemodel (= 6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
activemodel (6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
activerecord (6.0.3)
|
||||
activemodel (= 6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
activerecord-import (1.0.4)
|
||||
activerecord (>= 3.2)
|
||||
activestorage (6.0.0)
|
||||
actionpack (= 6.0.0)
|
||||
activejob (= 6.0.0)
|
||||
activerecord (= 6.0.0)
|
||||
activestorage (6.0.3)
|
||||
actionpack (= 6.0.3)
|
||||
activejob (= 6.0.3)
|
||||
activerecord (= 6.0.3)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (6.0.0)
|
||||
activesupport (6.0.3)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
zeitwerk (~> 2.1, >= 2.1.8)
|
||||
zeitwerk (~> 2.2, >= 2.2.2)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
aes_key_wrap (1.0.1)
|
||||
|
@ -159,7 +159,7 @@ GEM
|
|||
bootstrap3-datetimepicker-rails (4.17.47)
|
||||
momentjs-rails (>= 2.8.1)
|
||||
bootstrap_form (2.7.0)
|
||||
builder (3.2.3)
|
||||
builder (3.2.4)
|
||||
bullet (6.0.2)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
|
@ -195,10 +195,10 @@ GEM
|
|||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
commit_param_routing (0.0.1)
|
||||
concurrent-ruby (1.1.5)
|
||||
concurrent-ruby (1.1.6)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.5)
|
||||
crass (1.0.6)
|
||||
cucumber (3.1.2)
|
||||
builder (>= 2.1.2)
|
||||
cucumber-core (~> 3.2.0)
|
||||
|
@ -246,11 +246,11 @@ GEM
|
|||
discard (1.1.0)
|
||||
activerecord (>= 4.2, < 7)
|
||||
docile (1.3.2)
|
||||
doorkeeper (5.1.0)
|
||||
doorkeeper (5.1.1)
|
||||
railties (>= 5)
|
||||
down (5.0.0)
|
||||
addressable (~> 2.5)
|
||||
erubi (1.8.0)
|
||||
erubi (1.9.0)
|
||||
et-orbi (1.2.2)
|
||||
tzinfo
|
||||
execjs (2.7.0)
|
||||
|
@ -330,7 +330,7 @@ GEM
|
|||
logging (2.0.0)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
loofah (2.3.1)
|
||||
loofah (2.5.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
|
@ -341,7 +341,7 @@ GEM
|
|||
mime-types (3.3)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2019.0904)
|
||||
mimemagic (0.3.3)
|
||||
mimemagic (0.3.5)
|
||||
mini_magick (4.9.5)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
|
@ -359,7 +359,7 @@ GEM
|
|||
rails (>= 3.2.0)
|
||||
newrelic_rpm (6.6.0.358)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.8)
|
||||
nokogiri (1.10.9)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
nokogumbo (2.0.1)
|
||||
nokogiri (~> 1.8, >= 1.8.4)
|
||||
|
@ -404,30 +404,30 @@ GEM
|
|||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (4.0.1)
|
||||
puma (4.3.3)
|
||||
puma (4.3.5)
|
||||
nio4r (~> 2.0)
|
||||
raabro (1.1.6)
|
||||
rack (2.0.8)
|
||||
rack (2.2.2)
|
||||
rack-attack (6.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-proxy (0.6.5)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (6.0.0)
|
||||
actioncable (= 6.0.0)
|
||||
actionmailbox (= 6.0.0)
|
||||
actionmailer (= 6.0.0)
|
||||
actionpack (= 6.0.0)
|
||||
actiontext (= 6.0.0)
|
||||
actionview (= 6.0.0)
|
||||
activejob (= 6.0.0)
|
||||
activemodel (= 6.0.0)
|
||||
activerecord (= 6.0.0)
|
||||
activestorage (= 6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
rails (6.0.3)
|
||||
actioncable (= 6.0.3)
|
||||
actionmailbox (= 6.0.3)
|
||||
actionmailer (= 6.0.3)
|
||||
actionpack (= 6.0.3)
|
||||
actiontext (= 6.0.3)
|
||||
actionview (= 6.0.3)
|
||||
activejob (= 6.0.3)
|
||||
activemodel (= 6.0.3)
|
||||
activerecord (= 6.0.3)
|
||||
activestorage (= 6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 6.0.0)
|
||||
railties (= 6.0.3)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
|
@ -436,8 +436,8 @@ GEM
|
|||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.2.0)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
rails-html-sanitizer (1.3.0)
|
||||
loofah (~> 2.3)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
|
@ -445,9 +445,9 @@ GEM
|
|||
rails (> 3.1)
|
||||
rails_serve_static_assets (0.0.5)
|
||||
rails_stdout_logging (0.0.5)
|
||||
railties (6.0.0)
|
||||
actionpack (= 6.0.0)
|
||||
activesupport (= 6.0.0)
|
||||
railties (6.0.3)
|
||||
actionpack (= 6.0.3)
|
||||
activesupport (= 6.0.3)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.20.3, < 2.0)
|
||||
|
@ -541,7 +541,7 @@ GEM
|
|||
simplecov-html (0.10.2)
|
||||
spinjs-rails (1.4)
|
||||
rails (>= 3.1)
|
||||
sprockets (3.7.2)
|
||||
sprockets (4.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.1)
|
||||
|
@ -553,12 +553,12 @@ GEM
|
|||
thread_safe (0.3.6)
|
||||
tilt (2.0.9)
|
||||
timecop (0.9.1)
|
||||
tinymce-rails (4.9.4)
|
||||
tinymce-rails (4.9.10)
|
||||
railties (>= 3.1.1)
|
||||
turbolinks (5.1.1)
|
||||
turbolinks-source (~> 5.1)
|
||||
turbolinks-source (5.2.0)
|
||||
tzinfo (1.2.6)
|
||||
tzinfo (1.2.7)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (4.1.20)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
|
@ -584,7 +584,7 @@ GEM
|
|||
wkhtmltopdf-heroku (2.12.5.0)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.2.2)
|
||||
zeitwerk (2.3.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -687,7 +687,7 @@ DEPENDENCIES
|
|||
sneaky-save!
|
||||
spinjs-rails
|
||||
timecop
|
||||
tinymce-rails (~> 4.9.3)
|
||||
tinymce-rails (~> 4.9.10)
|
||||
turbolinks (~> 5.1.1)
|
||||
tzinfo-data
|
||||
uglifier (>= 1.3.0)
|
||||
|
|
3
app/assets/config/manifest.js
Normal file
3
app/assets/config/manifest.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
//= link_tree ../images
|
||||
//= link application.js
|
||||
//= link application.css
|
|
@ -26,6 +26,7 @@
|
|||
//= require nested_form_fields
|
||||
//= require highlight.pack
|
||||
//= require tinymce-jquery
|
||||
//= require_tree ./tinymce/plugins
|
||||
//= require jsPlumb-2.0.4-min
|
||||
//= require jsnetworkx
|
||||
//= require bootstrap-select
|
||||
|
|
|
@ -47,6 +47,7 @@ var DasboardCurrentTasksWidget = (function() {
|
|||
function filtersEnabled() {
|
||||
return dropdownSelector.getValues(experimentFilter)
|
||||
|| dropdownSelector.getValues(projectFilter)
|
||||
|| $('.current-tasks-widget .task-search-field').val().length > 0
|
||||
|| dropdownSelector.getValues(viewFilter) !== 'uncompleted';
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,78 @@
|
|||
/* global I18n dropdownSelector */
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
||||
function initTaskCollapseState() {
|
||||
let taskView = '.my-modules-protocols-index';
|
||||
let taskSection = '.task-section-caret';
|
||||
let taskId = $(taskView).data('task-id');
|
||||
|
||||
function collapseStateSave() {
|
||||
$(taskView).on('click', taskSection, function() {
|
||||
let collapsed = $(this).attr('aria-expanded');
|
||||
let taskSectionType = $(this).attr('aria-controls');
|
||||
|
||||
if (collapsed === 'true') {
|
||||
localStorage.setItem('task_section_collapsed/' + taskId + '/' + taskSectionType, collapsed);
|
||||
} else {
|
||||
localStorage.removeItem('task_section_collapsed/' + taskId + '/' + taskSectionType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function collapseStateLoad() {
|
||||
$(taskSection).each(function() {
|
||||
let taskSectionType = $(this).attr('aria-controls');
|
||||
var collapsed = localStorage.getItem('task_section_collapsed/' + taskId + '/' + taskSectionType);
|
||||
|
||||
if (JSON.parse(collapsed)) {
|
||||
$('#' + taskSectionType).collapse('hide');
|
||||
}
|
||||
$(this).closest('.task-section').removeClass('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
collapseStateSave();
|
||||
collapseStateLoad();
|
||||
}
|
||||
|
||||
function updateStartDate() {
|
||||
let updateUrl = $('#startDateContainer').data('update-url');
|
||||
let val = $('#calendarStartDate').val();
|
||||
$.ajax({
|
||||
url: updateUrl,
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
data: { my_module: { started_on: val } },
|
||||
success: function(result) {
|
||||
$('#startDateLabelContainer').html(result.start_date_label);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bind ajax for editing due dates
|
||||
function initStartDatePicker() {
|
||||
$('#calendarStartDate').on('dp.change', function() {
|
||||
updateStartDate();
|
||||
});
|
||||
}
|
||||
|
||||
function updateDueDate() {
|
||||
let updateUrl = $('.due-date-container').data('update-url');
|
||||
let val = $('#calendar-due-date').val();
|
||||
let updateUrl = $('#dueDateContainer').data('update-url');
|
||||
let val = $('#calendarDueDate').val();
|
||||
$.ajax({
|
||||
url: updateUrl,
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
data: { my_module: { due_date: val } },
|
||||
success: function(result) {
|
||||
$('#due-date-label-container').html(result.due_date_label);
|
||||
$('#dueDateLabelContainer').html(result.due_date_label);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bind ajax for editing due dates
|
||||
function initDueDatePicker() {
|
||||
$('#calendar-due-date').on('dp.change', function() {
|
||||
$('#calendarDueDate').on('dp.change', function() {
|
||||
updateDueDate();
|
||||
});
|
||||
}
|
||||
|
@ -190,7 +245,7 @@ function applyTaskCompletedCallBack() {
|
|||
button.find('.btn')
|
||||
.removeClass('btn-default').addClass('btn-primary');
|
||||
}
|
||||
$('.due-date-container').html(data.module_header_due_date);
|
||||
$('#dueDateContainer').html(data.module_header_due_date);
|
||||
initDueDatePicker();
|
||||
$('.task-state-label').html(data.module_state_label);
|
||||
button.find('button').replaceWith(data.new_btn);
|
||||
|
@ -220,7 +275,7 @@ function initTagsSelector() {
|
|||
}
|
||||
return `<span class="my-module-tags-color"></span>
|
||||
${data.label + ' '}
|
||||
<span class="my-module-tags-create-new"> (${I18n.t('my_modules.module_header.create_new_tag')})</span>`;
|
||||
<span class="my-module-tags-create-new"> (${I18n.t('my_modules.details.create_new_tag')})</span>`;
|
||||
},
|
||||
onOpen: function() {
|
||||
$('.select-container .edit-button-container').removeClass('hidden');
|
||||
|
@ -269,7 +324,65 @@ function initTagsSelector() {
|
|||
}).getContainer(myModuleTagsSelector).addClass('my-module-tags-container');
|
||||
}
|
||||
|
||||
function initAssignedUsersSelector() {
|
||||
var manageUsersModal = $('#manage-module-users-modal');
|
||||
var manageUsersModalBody = manageUsersModal.find('.modal-body');
|
||||
|
||||
// Initialize users editing modal remote loading
|
||||
function initUsersEditLink() {
|
||||
$('.task-details').on('ajax:success', '.manage-users-link', function(e, data) {
|
||||
manageUsersModal.modal('show');
|
||||
manageUsersModal.find('#manage-module-users-modal-module').text(data.my_module.name);
|
||||
initUsersModalBody(data);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize ajax listeners and elements style on modal body.
|
||||
// This function must be called when modal body is changed.
|
||||
function initUsersModalBody(data) {
|
||||
manageUsersModalBody.html(data.html);
|
||||
manageUsersModalBody.find('.selectpicker').selectpicker();
|
||||
}
|
||||
|
||||
// Initialize reloading manage user modal content after posting new user
|
||||
manageUsersModalBody.on('ajax:success', '.add-user-form', function(e, data) {
|
||||
initUsersModalBody(data);
|
||||
});
|
||||
|
||||
// Initialize remove user from my_module links
|
||||
manageUsersModalBody.on('ajax:success', '.remove-user-link', function(e, data) {
|
||||
initUsersModalBody(data);
|
||||
});
|
||||
|
||||
// Reload users HTML element when modal is closed
|
||||
manageUsersModal.on('hide.bs.modal', function() {
|
||||
var usersEl = $('.task-assigned-users');
|
||||
// Load HTML to refresh users
|
||||
$.ajax({
|
||||
url: usersEl.attr('data-module-users-url'),
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
$('.task-assigned-users').replaceWith(data.html);
|
||||
},
|
||||
error: function() {
|
||||
// TODO
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Remove users modal content when modal window is closed.
|
||||
manageUsersModal.on('hidden.bs.modal', function() {
|
||||
manageUsersModalBody.html('');
|
||||
});
|
||||
|
||||
initUsersEditLink();
|
||||
}
|
||||
|
||||
initTaskCollapseState();
|
||||
applyTaskCompletedCallBack();
|
||||
initTagsSelector();
|
||||
bindEditTagsAjax();
|
||||
initStartDatePicker();
|
||||
initDueDatePicker();
|
||||
initAssignedUsersSelector();
|
||||
|
|
|
@ -68,11 +68,9 @@ function initCopyToRepository() {
|
|||
var modal = $('#copy-to-repository-modal');
|
||||
var modalBody = modal.find('.modal-body');
|
||||
var submitBtn = modal.find(".modal-footer [data-action='submit']");
|
||||
|
||||
link
|
||||
.on('ajax:success', function(e, data) {
|
||||
modalBody.html(data.html);
|
||||
|
||||
modalBody.find("[data-role='copy-to-repository']")
|
||||
.on('ajax:success', function(e2, data2) {
|
||||
if (data2.refresh !== null) {
|
||||
|
@ -180,8 +178,8 @@ function initLoadFromRepository() {
|
|||
|
||||
modal.modal('show');
|
||||
|
||||
// Init Datatable on public tab
|
||||
initLoadFromRepositoryTable(modalBody.find('#public-tab'));
|
||||
// Init Datatable on recent tab
|
||||
initLoadFromRepositoryTable(modalBody.find('#recent-tab'));
|
||||
|
||||
modalBody.find("a[data-toggle='tab']")
|
||||
.on('hide.bs.tab', function(el) {
|
||||
|
@ -214,9 +212,7 @@ function initLoadFromRepository() {
|
|||
|
||||
function initLoadFromRepositoryTable(content) {
|
||||
var tableEl = content.find("[data-role='datatable']");
|
||||
|
||||
var datatable = tableEl.DataTable({
|
||||
order: [[1, 'asc']],
|
||||
dom: "RBfl<'row'<'col-sm-12't>><'row'<'col-sm-7'i><'col-sm-5'p>>",
|
||||
sScrollX: '100%',
|
||||
sScrollXInner: '100%',
|
||||
|
@ -224,6 +220,7 @@ function initLoadFromRepositoryTable(content) {
|
|||
processing: true,
|
||||
serverSide: true,
|
||||
responsive: true,
|
||||
order: tableEl.data('default-order') || [[1, 'asc']],
|
||||
ajax: {
|
||||
url: tableEl.data('source'),
|
||||
type: 'POST'
|
||||
|
@ -385,8 +382,9 @@ function refreshProtocolStatusBar() {
|
|||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
$("[data-role='protocol-status-bar']").html(data.html);
|
||||
$('.my-module-protocol-status').replaceWith(data.html);
|
||||
initLinkUpdate();
|
||||
initCopyToRepository();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -433,43 +431,16 @@ function initImport() {
|
|||
});
|
||||
}
|
||||
|
||||
function initRecentProtocols() {
|
||||
var recentProtocolContainer = $('.my-module-recent-protocols');
|
||||
var dropDownList = recentProtocolContainer.find('.dropdown-menu');
|
||||
recentProtocolContainer.find('.dropdown-button').click(function() {
|
||||
dropDownList.find('.protocol').remove();
|
||||
$.get('/protocols/recent_protocols', result => {
|
||||
$.each(result, (i, protocol) => {
|
||||
$('<div class="protocol"><i class="fas fa-file-alt"></i>'
|
||||
+ truncateLongString(protocol.name, GLOBAL_CONSTANTS.NAME_TRUNCATION_LENGTH)
|
||||
+ '</div>').appendTo(dropDownList)
|
||||
.click(() => {
|
||||
$.post(recentProtocolContainer.data('updateUrl'), { source_id: protocol.id })
|
||||
.success(() => {
|
||||
location.reload();
|
||||
})
|
||||
.error(ev => {
|
||||
HelperModule.flashAlertMsg(ev.responseJSON.message, 'warning');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('.protocol-description-content').on('ajax:success', () => {
|
||||
updateRecentProtocolsStatus();
|
||||
});
|
||||
}
|
||||
|
||||
function updateRecentProtocolsStatus() {
|
||||
var recentProtocolContainer = $('.my-module-recent-protocols');
|
||||
var steps = $('.step');
|
||||
var protocolDescription = $('#protocol_description_view').html();
|
||||
if (steps.length === 0 && protocolDescription.length === 0) {
|
||||
recentProtocolContainer.css('display', '');
|
||||
} else {
|
||||
recentProtocolContainer.css('display', 'none');
|
||||
}
|
||||
function initProtocolSectionOpenEvent() {
|
||||
$('#protocol-container').on('shown.bs.collapse', function() {
|
||||
$(this).find("[data-role='hot-table']").each(function() {
|
||||
var $container = $(this).find("[data-role='step-hot-table']");
|
||||
var hot = $container.handsontable('getInstance');
|
||||
hot.render();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -484,7 +455,7 @@ function init() {
|
|||
initLoadFromRepository();
|
||||
refreshProtocolStatusBar();
|
||||
initImport();
|
||||
initRecentProtocols();
|
||||
initProtocolSectionOpenEvent();
|
||||
}
|
||||
|
||||
init();
|
||||
|
|
643
app/assets/javascripts/my_modules/repositories.js
Normal file
643
app/assets/javascripts/my_modules/repositories.js
Normal file
|
@ -0,0 +1,643 @@
|
|||
/* eslint-disable no-param-reassign, no-use-before-define */
|
||||
/* global DataTableHelpers PerfectScrollbar FilePreviewModal animateSpinner HelperModule
|
||||
initAssignedTasksDropdown I18n */
|
||||
|
||||
var MyModuleRepositories = (function() {
|
||||
const FULL_VIEW_MODAL = $('#myModuleRepositoryFullViewModal');
|
||||
const UPDATE_REPOSITORY_MODAL = $('#updateRepositoryRecordModal');
|
||||
const STATUS_POLLING_INTERVAL = 10000;
|
||||
var SIMPLE_TABLE;
|
||||
var FULL_VIEW_TABLE;
|
||||
var FULL_VIEW_TABLE_SCROLLBAR;
|
||||
var SELECTED_ROWS = {};
|
||||
|
||||
function reloadRepositoriesList(repositoryId) {
|
||||
var repositoriesContainer = $('#assigned-items-container');
|
||||
$.get(repositoriesContainer.data('repositories-list-url'), function(result) {
|
||||
repositoriesContainer.html(result.html);
|
||||
$('.assigned-items-title').attr('data-assigned-items-count', result.assigned_rows_count);
|
||||
// expand recently updated repository
|
||||
$('#assigned-items-container').collapse('show');
|
||||
$('#assigned-repository-items-container-' + repositoryId).collapse('show');
|
||||
});
|
||||
}
|
||||
|
||||
function tableColumns(tableContainer, skipCheckbox = false) {
|
||||
var columns = $(tableContainer).data('default-table-columns');
|
||||
var customColumns = $(tableContainer).find('thead th[data-type]');
|
||||
for (let i = 0; i < columns.length; i += 1) {
|
||||
columns[i].data = String(i);
|
||||
columns[i].defaultContent = '';
|
||||
if (skipCheckbox && i === 0) columns[i].visible = false;
|
||||
}
|
||||
customColumns.each((i, column) => {
|
||||
columns.push({
|
||||
visible: true,
|
||||
searchable: true,
|
||||
data: String(columns.length),
|
||||
defaultContent: $.fn.dataTable.render['default' + column.dataset.type](column.id)
|
||||
});
|
||||
});
|
||||
return columns;
|
||||
}
|
||||
|
||||
function fullViewColumnDefs() {
|
||||
let columnDefs = [{
|
||||
targets: 0,
|
||||
visible: true,
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
className: 'dt-body-center',
|
||||
sWidth: '1%',
|
||||
render: function(data) {
|
||||
var checked = data ? 'checked' : '';
|
||||
return `<div class="sci-checkbox-container">
|
||||
<input class='repository-row-selector sci-checkbox' type='checkbox' ${checked}>
|
||||
<span class='sci-checkbox-label'></span>
|
||||
</div>`;
|
||||
}
|
||||
}];
|
||||
|
||||
if (FULL_VIEW_MODAL.find('.table').data('type') === 'live') {
|
||||
columnDefs.push({
|
||||
targets: 1,
|
||||
searchable: false,
|
||||
className: 'assigned-column',
|
||||
sWidth: '1%',
|
||||
render: function(data) {
|
||||
return $.fn.dataTable.render.AssignedTasksValue(data);
|
||||
}
|
||||
}, {
|
||||
targets: 3,
|
||||
render: function(data, type, row) {
|
||||
return "<a href='" + row.recordInfoUrl + "' class='record-info-link'>" + data + '</a>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
columnDefs.push(
|
||||
{
|
||||
targets: '_all',
|
||||
render: function(data) {
|
||||
if (typeof data === 'object' && $.fn.dataTable.render[data.value_type]) {
|
||||
return $.fn.dataTable.render[data.value_type](data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return columnDefs;
|
||||
}
|
||||
|
||||
function renderSimpleTable(tableContainer) {
|
||||
if (SIMPLE_TABLE) SIMPLE_TABLE.destroy();
|
||||
SIMPLE_TABLE = $(tableContainer).DataTable({
|
||||
dom: "Rt<'pagination-row'<'version-label'><'pagination-actions'p>>",
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
responsive: true,
|
||||
pageLength: 5,
|
||||
order: [[0, 'asc']],
|
||||
sScrollY: '100%',
|
||||
sScrollX: '100%',
|
||||
sScrollXInner: '100%',
|
||||
destroy: true,
|
||||
ajax: {
|
||||
url: $(tableContainer).data('source'),
|
||||
data: function(d) {
|
||||
d.order[0].column = tableContainer.data('name-column-id');
|
||||
d.assigned = 'assigned';
|
||||
d.view_mode = true;
|
||||
d.simple_view = true;
|
||||
},
|
||||
global: false,
|
||||
type: 'POST'
|
||||
},
|
||||
columnDefs: [{
|
||||
targets: 0,
|
||||
render: function(data, type, row) {
|
||||
return "<a href='" + row.recordInfoUrl + "'"
|
||||
+ "class='record-info-link'>" + data + '</a>';
|
||||
}
|
||||
}],
|
||||
drawCallback: function() {
|
||||
var repositoryContainer = $(this).closest('.assigned-repository-container');
|
||||
repositoryContainer.find('.table.dataTable').removeClass('hidden');
|
||||
repositoryContainer.find('.version-label').html(tableContainer.data('version-label'));
|
||||
SIMPLE_TABLE.columns.adjust();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderFullViewTable(tableContainer, options = {}) {
|
||||
if (FULL_VIEW_TABLE) FULL_VIEW_TABLE.destroy();
|
||||
SELECTED_ROWS = {};
|
||||
FULL_VIEW_TABLE_SCROLLBAR = false;
|
||||
FULL_VIEW_TABLE = $(tableContainer).DataTable({
|
||||
dom: "R<'main-actions hidden'<'toolbar'><'filter-container'f>>t<'pagination-row hidden'<'pagination-info'li><'pagination-actions'p>>",
|
||||
processing: true,
|
||||
stateSave: true,
|
||||
serverSide: true,
|
||||
order: $(tableContainer).data('default-order'),
|
||||
pageLength: 25,
|
||||
sScrollX: '100%',
|
||||
sScrollXInner: '100%',
|
||||
destroy: true,
|
||||
ajax: {
|
||||
url: $(tableContainer).data('source'),
|
||||
data: function(d) {
|
||||
if (options.assigned) d.assigned = 'assigned';
|
||||
d.view_mode = true;
|
||||
},
|
||||
global: false,
|
||||
type: 'POST'
|
||||
},
|
||||
columns: tableColumns(tableContainer, options.skipCheckbox),
|
||||
columnDefs: fullViewColumnDefs(),
|
||||
|
||||
fnInitComplete: function() {
|
||||
var dataTableWrapper = $(tableContainer).closest('.dataTables_wrapper');
|
||||
DataTableHelpers.initLengthApearance(dataTableWrapper);
|
||||
DataTableHelpers.initSearchField(dataTableWrapper);
|
||||
dataTableWrapper.find('.main-actions, .pagination-row').removeClass('hidden');
|
||||
if (options.assign_mode) {
|
||||
renderFullViewAssignButtons();
|
||||
} else {
|
||||
$('.table-container .toolbar').html($('#repositoryToolbarButtonsTemplate').html());
|
||||
if (FULL_VIEW_MODAL.find('.modal-content').hasClass('show-sidebar')) {
|
||||
FULL_VIEW_MODAL.find('#showVersionsSidebar').addClass('active');
|
||||
}
|
||||
}
|
||||
initAssignedTasksDropdown(tableContainer);
|
||||
},
|
||||
|
||||
drawCallback: function() {
|
||||
FULL_VIEW_TABLE.columns.adjust();
|
||||
FilePreviewModal.init();
|
||||
renderFullViewRepositoryName(
|
||||
tableContainer.attr('data-repository-name'),
|
||||
tableContainer.attr('data-repository-snapshot-created'),
|
||||
options.assign_mode
|
||||
);
|
||||
updateFullViewRowsCount(tableContainer.attr('data-assigned-items-count'));
|
||||
if (FULL_VIEW_TABLE_SCROLLBAR) {
|
||||
FULL_VIEW_TABLE_SCROLLBAR.update();
|
||||
} else {
|
||||
FULL_VIEW_TABLE_SCROLLBAR = new PerfectScrollbar(
|
||||
$(tableContainer).closest('.dataTables_scrollBody')[0],
|
||||
{
|
||||
wheelSpeed: 0.5,
|
||||
minScrollbarLength: 20
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
stateLoadCallback: function(settings, callback) {
|
||||
var loadStateUrl = $(tableContainer).data('load-state-url');
|
||||
$.post(loadStateUrl, function(json) {
|
||||
if (!options.assign_mode) {
|
||||
json.state.columns[0].visible = false;
|
||||
}
|
||||
json.state.search.search = null;
|
||||
callback(json.state);
|
||||
});
|
||||
},
|
||||
|
||||
rowCallback: function(row) {
|
||||
var checkbox = $(row).find('.repository-row-selector');
|
||||
if (SELECTED_ROWS[row.id]) {
|
||||
$(row).addClass('selected');
|
||||
checkbox.attr('checked', !checkbox.attr('checked'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setSelectedItem() {
|
||||
let versionsSidebar = FULL_VIEW_MODAL.find('.repository-versions-sidebar');
|
||||
let currentId = FULL_VIEW_MODAL.find('.table').data('id');
|
||||
versionsSidebar.find('.list-group-item').removeClass('active');
|
||||
versionsSidebar.find(`[data-id="${currentId}"]`).addClass('active');
|
||||
|
||||
if (!versionsSidebar.find(`[data-id="${currentId}"]`).data('selected')) {
|
||||
$('#setDefaultVersionButton').parent().removeClass('hidden');
|
||||
} else {
|
||||
$('#setDefaultVersionButton').parent().addClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function reloadTable(tableUrl) {
|
||||
animateSpinner(null, true);
|
||||
$.getJSON(tableUrl, (data) => {
|
||||
FULL_VIEW_MODAL.find('.table-container').html(data.html);
|
||||
renderFullViewTable(FULL_VIEW_MODAL.find('.table'), { assigned: true, skipCheckbox: true });
|
||||
setSelectedItem();
|
||||
animateSpinner(null, false);
|
||||
});
|
||||
}
|
||||
|
||||
function initSelectAllCheckbox() {
|
||||
FULL_VIEW_MODAL.on('click', 'input.select-all', function() {
|
||||
var selectAllCheckbox = $(this);
|
||||
var rows = FULL_VIEW_MODAL.find('.dataTables_scrollBody tbody tr');
|
||||
$.each(rows, function(i, row) {
|
||||
var checkbox = $(row).find('.repository-row-selector');
|
||||
if (checkbox.prop('checked') === selectAllCheckbox.prop('checked')) return;
|
||||
|
||||
checkbox.prop('checked', !checkbox.prop('checked'));
|
||||
selectFullViewRow(row);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function refreshSelectAllCheckbox() {
|
||||
var checkboxes = FULL_VIEW_MODAL.find('.dataTables_scrollBody .repository-row-selector');
|
||||
var selectedCheckboxes = FULL_VIEW_MODAL.find('.dataTables_scrollBody .repository-row-selector:checked');
|
||||
var selectAllCheckbox = FULL_VIEW_MODAL.find('input.select-all');
|
||||
selectAllCheckbox.prop('indeterminate', false);
|
||||
if (selectedCheckboxes.length === 0) {
|
||||
selectAllCheckbox.prop('checked', false);
|
||||
} else if (selectedCheckboxes.length === checkboxes.length) {
|
||||
selectAllCheckbox.prop('checked', true);
|
||||
} else {
|
||||
selectAllCheckbox.prop('indeterminate', true);
|
||||
}
|
||||
}
|
||||
|
||||
function checkSnapshotStatus(snapshotItem) {
|
||||
$.getJSON(snapshotItem.data('status-url'), (statusData) => {
|
||||
if (statusData.status === 'ready') {
|
||||
$.getJSON(snapshotItem.data('version-item-url'), (itemData) => {
|
||||
snapshotItem.replaceWith(itemData.html);
|
||||
});
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
checkSnapshotStatus(snapshotItem);
|
||||
}, STATUS_POLLING_INTERVAL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initSimpleTable() {
|
||||
$('#assigned-items-container').on('shown.bs.collapse', '.assigned-repository-container', function() {
|
||||
var repositoryContainer = $(this);
|
||||
var repositoryTemplate = $($('#myModuleRepositorySimpleTemplate').html());
|
||||
repositoryTemplate.attr('data-source', $(this).data('repository-url'));
|
||||
repositoryTemplate.attr('data-version-label', $(this).data('footer-label'));
|
||||
repositoryTemplate.attr('data-name-column-id', $(this).data('name-column-id'));
|
||||
repositoryContainer.html(repositoryTemplate);
|
||||
renderSimpleTable(repositoryTemplate);
|
||||
});
|
||||
|
||||
$('.navbar-secondary').on('sideBar::shown sideBar::hidden', function() {
|
||||
if (SIMPLE_TABLE) {
|
||||
SIMPLE_TABLE.columns.adjust();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initVersionsStatusCheck() {
|
||||
let sidebar = FULL_VIEW_MODAL.find('.repository-versions-sidebar');
|
||||
sidebar.find('.repository-snapshot-item.provisioning').each(function() {
|
||||
var snapshotItem = $(this);
|
||||
setTimeout(function() {
|
||||
checkSnapshotStatus(snapshotItem);
|
||||
}, STATUS_POLLING_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshCreationSpanshotInfoText() {
|
||||
var snapshotsCount = FULL_VIEW_MODAL.find('.repository-snapshot-item').length;
|
||||
var createSnapshotInfo = FULL_VIEW_MODAL.find('.create-snapshot-item .info');
|
||||
if (snapshotsCount) {
|
||||
createSnapshotInfo.addClass('hidden');
|
||||
} else {
|
||||
createSnapshotInfo.removeClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function initVersionsSidebarActions() {
|
||||
FULL_VIEW_MODAL.on('click', '#showVersionsSidebar', function(e) {
|
||||
$(this).toggleClass('active');
|
||||
if ($(this).hasClass('active')) {
|
||||
$.getJSON(FULL_VIEW_MODAL.find('.table').data('versions-sidebar-url'), (data) => {
|
||||
var snapshotsItemsScrollBar;
|
||||
FULL_VIEW_MODAL.find('.repository-versions-sidebar').html(data.html);
|
||||
snapshotsItemsScrollBar = new PerfectScrollbar(
|
||||
FULL_VIEW_MODAL.find('.repository-snapshots-container')[0]
|
||||
);
|
||||
setSelectedItem();
|
||||
FULL_VIEW_MODAL.find('.modal-content').addClass('show-sidebar');
|
||||
initVersionsStatusCheck();
|
||||
snapshotsItemsScrollBar.update();
|
||||
FULL_VIEW_TABLE.columns.adjust();
|
||||
});
|
||||
} else {
|
||||
FULL_VIEW_MODAL.find('#collapseVersionsSidebar').click();
|
||||
}
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('click', '#createRepositorySnapshotButton', function(e) {
|
||||
animateSpinner(null, true);
|
||||
$.ajax({
|
||||
url: $(this).data('action-path'),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
let snapshotItem = $(data.html);
|
||||
FULL_VIEW_MODAL.find('.snapshots-container-scrollbody').prepend(snapshotItem);
|
||||
setTimeout(function() {
|
||||
checkSnapshotStatus(snapshotItem);
|
||||
}, STATUS_POLLING_INTERVAL);
|
||||
animateSpinner(null, false);
|
||||
refreshCreationSpanshotInfoText();
|
||||
}
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('click', '.delete-snapshot-button', function(e) {
|
||||
let snapshotItem = $(this).closest('.repository-snapshot-item');
|
||||
animateSpinner(null, true);
|
||||
$.ajax({
|
||||
url: $(this).data('action-path'),
|
||||
type: 'DELETE',
|
||||
dataType: 'json',
|
||||
success: function() {
|
||||
if (snapshotItem.data('id') === FULL_VIEW_MODAL.find('.table').data('id')) {
|
||||
reloadTable(FULL_VIEW_MODAL.find('#selectLiveVersionButton').data('table-url'));
|
||||
}
|
||||
snapshotItem.remove();
|
||||
animateSpinner(null, false);
|
||||
refreshCreationSpanshotInfoText();
|
||||
}
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('click', '.select-snapshot-button', function(e) {
|
||||
reloadTable($(this).data('table-url'));
|
||||
e.stopPropagation();
|
||||
});
|
||||
FULL_VIEW_MODAL.on('click', '.repository-snapshot-item', function(e) {
|
||||
var snapshotButton = $(this).find('.select-snapshot-button');
|
||||
if (!snapshotButton.hasClass('disabled')) {
|
||||
snapshotButton.click();
|
||||
}
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('click', '#selectLiveVersionButton', function(e) {
|
||||
reloadTable(FULL_VIEW_MODAL.find('#selectLiveVersionButton').data('table-url'));
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('click', '#collapseVersionsSidebar', function(e) {
|
||||
FULL_VIEW_MODAL.find('.modal-content').removeClass('show-sidebar');
|
||||
FULL_VIEW_MODAL.find('#showVersionsSidebar').removeClass('active');
|
||||
FULL_VIEW_TABLE.columns.adjust();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('click', '#setDefaultVersionButton', function(e) {
|
||||
let data;
|
||||
animateSpinner(null, true);
|
||||
|
||||
if (FULL_VIEW_MODAL.find('.table').data('type') === 'live') {
|
||||
data = { repository_id: FULL_VIEW_MODAL.find('.table').data('id') };
|
||||
} else {
|
||||
data = { repository_snapshot_id: FULL_VIEW_MODAL.find('.table').data('id') };
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: $(this).data('select-path'),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: data,
|
||||
success: function() {
|
||||
let versionsList = FULL_VIEW_MODAL.find('.repository-versions-list');
|
||||
versionsList.find('.list-group-item').data('selected', false);
|
||||
versionsList.find('.list-group-item.active').data('selected', true);
|
||||
$('#setDefaultVersionButton').parent().addClass('hidden');
|
||||
animateSpinner(null, false);
|
||||
}
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('hidden.bs.modal', function() {
|
||||
FULL_VIEW_MODAL.find('.repository-versions-sidebar').empty();
|
||||
FULL_VIEW_MODAL.find('.modal-content').removeClass('show-sidebar');
|
||||
FULL_VIEW_MODAL.find('#showVersionsSidebar').removeClass('active');
|
||||
FULL_VIEW_TABLE.destroy();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('show.bs.modal', function() {
|
||||
FULL_VIEW_MODAL.find('.table-container').empty();
|
||||
FULL_VIEW_MODAL.find('.repository-name').empty();
|
||||
updateFullViewRowsCount('');
|
||||
});
|
||||
}
|
||||
|
||||
function initRepositoryFullView() {
|
||||
$('#assigned-items-container').on('click', '.action-buttons .full-screen', function(e) {
|
||||
var repositoryNameObject = $(this).closest('.assigned-repository-caret')
|
||||
.find('.assigned-repository-title');
|
||||
|
||||
|
||||
renderFullViewRepositoryName(repositoryNameObject.text());
|
||||
FULL_VIEW_MODAL.modal('show');
|
||||
$.getJSON($(this).data('table-url'), (data) => {
|
||||
FULL_VIEW_MODAL.find('.table-container').html(data.html);
|
||||
renderFullViewTable(FULL_VIEW_MODAL.find('.table'), { assigned: true, skipCheckbox: true });
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
function initRepositoriesDropdown() {
|
||||
$('.repositories-assign-container').on('show.bs.dropdown', function() {
|
||||
var dropdownContainer = $(this);
|
||||
$.getJSON(dropdownContainer.data('repositories-url'), function(result) {
|
||||
dropdownContainer.find('.repositories-dropdown-menu').html(result.html);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function selectFullViewRow(row) {
|
||||
var id = row.id;
|
||||
|
||||
if (!SELECTED_ROWS[id]) {
|
||||
SELECTED_ROWS[id] = {
|
||||
row_name: $(row).find('.record-info-link').text(),
|
||||
assigned: $(row).find('.repository-row-selector').prop('checked')
|
||||
};
|
||||
} else {
|
||||
delete SELECTED_ROWS[id];
|
||||
}
|
||||
|
||||
$(row).toggleClass('selected');
|
||||
|
||||
if (Object.keys(SELECTED_ROWS).length) {
|
||||
$('#assignRepositoryRecords, #updateRepositoryRecords').attr('disabled', false);
|
||||
} else {
|
||||
$('#assignRepositoryRecords, #updateRepositoryRecords').attr('disabled', true);
|
||||
}
|
||||
|
||||
refreshSelectAllCheckbox();
|
||||
}
|
||||
|
||||
function renderFullViewAssignButtons() {
|
||||
var toolbar = FULL_VIEW_MODAL.find('.dataTables_wrapper .toolbar');
|
||||
toolbar.empty();
|
||||
if (parseInt(FULL_VIEW_MODAL.data('rows-count'), 10) === 0) {
|
||||
toolbar.append($('#my-module-repository-full-view-assign-button').html());
|
||||
} else {
|
||||
toolbar.append($('#my-module-repository-full-view-update-button').html());
|
||||
}
|
||||
}
|
||||
|
||||
function updateFullViewRowsCount(value) {
|
||||
FULL_VIEW_MODAL.data('rows-count', value);
|
||||
FULL_VIEW_MODAL.find('.repository-name').attr('data-rows-count', value);
|
||||
}
|
||||
|
||||
function renderFullViewRepositoryName(name, snapshotDate, assignMode) {
|
||||
var title;
|
||||
var repositoryName = name || FULL_VIEW_MODAL.find('.repository-name').data('repository-name');
|
||||
|
||||
if (assignMode) {
|
||||
title = I18n.t('my_modules.repository.full_view.assign_modal_header', {
|
||||
repository_name: repositoryName
|
||||
});
|
||||
} else if (snapshotDate) {
|
||||
title = I18n.t('my_modules.repository.full_view.modal_snapshot_header', {
|
||||
repository_name: repositoryName,
|
||||
snaphot_date: snapshotDate
|
||||
});
|
||||
} else {
|
||||
title = I18n.t('my_modules.repository.full_view.modal_live_header', {
|
||||
repository_name: repositoryName
|
||||
});
|
||||
}
|
||||
FULL_VIEW_MODAL.find('.repository-name').data('repository-name', repositoryName);
|
||||
FULL_VIEW_MODAL.find('.repository-name').html(title);
|
||||
}
|
||||
|
||||
function initRepoistoryAssignView() {
|
||||
$('.repositories-dropdown-menu').on('click', '.repository', function(e) {
|
||||
var assignUrlModal = $(this).data('assign-url-modal');
|
||||
var updateUrlModal = $(this).data('update-url-modal');
|
||||
FULL_VIEW_MODAL.modal('show');
|
||||
$.get($(this).data('table-url'), (data) => {
|
||||
FULL_VIEW_MODAL.find('.table-container').html(data.html);
|
||||
FULL_VIEW_MODAL.data('assign-url-modal', assignUrlModal);
|
||||
FULL_VIEW_MODAL.data('update-url-modal', updateUrlModal);
|
||||
renderFullViewTable(FULL_VIEW_MODAL.find('.table'), { assign_mode: true });
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
FULL_VIEW_MODAL.on('click', '.table tbody tr', function() {
|
||||
var checkbox = $(this).find('.repository-row-selector');
|
||||
checkbox.prop('checked', !checkbox.prop('checked'));
|
||||
selectFullViewRow(this);
|
||||
}).on('click', '.table tbody tr .repository-row-selector', function(e) {
|
||||
selectFullViewRow($(this).closest('tr')[0]);
|
||||
e.stopPropagation();
|
||||
}).on('click', '#assignRepositoryRecords', function() {
|
||||
openAssignRecordsModal();
|
||||
}).on('click', '#updateRepositoryRecords', function() {
|
||||
openUpdateRecordsModal();
|
||||
});
|
||||
|
||||
UPDATE_REPOSITORY_MODAL.on('click', '.downstream-action', function() {
|
||||
submitUpdateRepositoryRecord({ downstream: true });
|
||||
}).on('click', '.task-action', function() {
|
||||
submitUpdateRepositoryRecord({ downstream: false });
|
||||
});
|
||||
}
|
||||
|
||||
function openUpdateRecordsModal() {
|
||||
var updateUrl = FULL_VIEW_MODAL.data('update-url-modal');
|
||||
$.get(updateUrl, { selected_rows: SELECTED_ROWS }, function(data) {
|
||||
var assignList;
|
||||
var assignListScrollbar;
|
||||
var unassignList;
|
||||
var unassignListScrollbar;
|
||||
UPDATE_REPOSITORY_MODAL.find('.modal-content').html(data.html);
|
||||
UPDATE_REPOSITORY_MODAL.data('update-url', data.update_url);
|
||||
assignList = UPDATE_REPOSITORY_MODAL.find('.rows-to-assign .rows-list')[0];
|
||||
unassignList = UPDATE_REPOSITORY_MODAL.find('.rows-to-unassign .rows-list')[0];
|
||||
if (assignList) assignListScrollbar = new PerfectScrollbar(assignList);
|
||||
if (unassignList) unassignListScrollbar = new PerfectScrollbar(unassignList);
|
||||
UPDATE_REPOSITORY_MODAL.modal('show');
|
||||
if (assignList) assignListScrollbar.update();
|
||||
if (unassignList) unassignListScrollbar.update();
|
||||
});
|
||||
}
|
||||
|
||||
function openAssignRecordsModal() {
|
||||
var assignUrl = FULL_VIEW_MODAL.data('assign-url-modal');
|
||||
$.get(assignUrl, { selected_rows: SELECTED_ROWS }, function(data) {
|
||||
UPDATE_REPOSITORY_MODAL.find('.modal-content').html(data.html);
|
||||
UPDATE_REPOSITORY_MODAL.data('update-url', data.update_url);
|
||||
UPDATE_REPOSITORY_MODAL.modal('show');
|
||||
});
|
||||
}
|
||||
|
||||
function submitUpdateRepositoryRecord(options = {}) {
|
||||
var rowsToAssign = [];
|
||||
var rowsToUnassign = [];
|
||||
$.each(Object.keys(SELECTED_ROWS), function(i, rowId) {
|
||||
if (SELECTED_ROWS[rowId].assigned) {
|
||||
rowsToAssign.push(rowId);
|
||||
} else {
|
||||
rowsToUnassign.push(rowId);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: UPDATE_REPOSITORY_MODAL.data('update-url'),
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
rows_to_assign: rowsToAssign,
|
||||
rows_to_unassign: rowsToUnassign,
|
||||
downstream: options.downstream
|
||||
},
|
||||
success: function(data) {
|
||||
UPDATE_REPOSITORY_MODAL.modal('hide');
|
||||
HelperModule.flashAlertMsg(data.flash, 'success');
|
||||
SELECTED_ROWS = {};
|
||||
$(FULL_VIEW_TABLE.table().container()).find('.dataTable')
|
||||
.attr('data-assigned-items-count', data.rows_count);
|
||||
FULL_VIEW_TABLE.ajax.reload(null, false);
|
||||
reloadRepositoriesList(data.repository_id);
|
||||
updateFullViewRowsCount(data.rows_count);
|
||||
renderFullViewAssignButtons();
|
||||
},
|
||||
error: function(data) {
|
||||
UPDATE_REPOSITORY_MODAL.modal('hide');
|
||||
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
|
||||
SELECTED_ROWS = {};
|
||||
FULL_VIEW_TABLE.ajax.reload(null, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: () => {
|
||||
initSimpleTable();
|
||||
initRepositoryFullView();
|
||||
initRepositoriesDropdown();
|
||||
initVersionsSidebarActions();
|
||||
initRepoistoryAssignView();
|
||||
initSelectAllCheckbox();
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
MyModuleRepositories.init();
|
|
@ -244,7 +244,6 @@ function initializeEdit() {
|
|||
{ color: 'white', shadow: true }
|
||||
);
|
||||
});
|
||||
$.initTooltips();
|
||||
}
|
||||
|
||||
function destroyEdit() {
|
||||
|
@ -900,8 +899,6 @@ function bindEditTagsAjax(elements) {
|
|||
.find(".edit-tags-link")
|
||||
.html(my_module.tags_html);
|
||||
});
|
||||
// initialize tooltips again
|
||||
$.initTooltips();
|
||||
},
|
||||
error: function(data){
|
||||
// TODO
|
||||
|
|
|
@ -100,7 +100,6 @@ function initProtocolsTable() {
|
|||
fnDrawCallback: function(settings, json) {
|
||||
animateSpinner(this, false);
|
||||
initRowSelection();
|
||||
$.initTooltips();
|
||||
},
|
||||
preDrawCallback: function(settings) {
|
||||
animateSpinner(this);
|
||||
|
|
|
@ -115,7 +115,6 @@
|
|||
toggleButtons(true);
|
||||
|
||||
setTimeout(function() {
|
||||
$.initTooltips();
|
||||
initStepsComments();
|
||||
FilePreviewModal.init();
|
||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||
|
@ -489,6 +488,7 @@
|
|||
var $btn = $(this);
|
||||
$btn.off();
|
||||
animateSpinner(null, true);
|
||||
$('#protocol-container').collapse('show');
|
||||
|
||||
$.ajax({
|
||||
url: $btn.data('href'),
|
||||
|
@ -617,9 +617,7 @@
|
|||
animateSpinner(null, false);
|
||||
DragNDropSteps.clearFiles();
|
||||
FilePreviewModal.init();
|
||||
$.initTooltips();
|
||||
if (typeof refreshProtocolStatusBar === 'function') refreshProtocolStatusBar();
|
||||
if (typeof updateRecentProtocolsStatus === 'function') updateRecentProtocolsStatus();
|
||||
},
|
||||
error: function(xhr) {
|
||||
if (xhr.responseJSON['assets.file']) {
|
||||
|
|
|
@ -127,7 +127,7 @@
|
|||
.selectpicker({liveSearch: true})
|
||||
.ajaxSelectPicker({
|
||||
ajax: {
|
||||
url: '<%= Rails.application.routes.url_helpers.available_rows_path %>',
|
||||
url: '<%= Rails.application.routes.url_helpers.available_rows_repositories_path %>',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: function () {
|
||||
|
|
|
@ -154,3 +154,27 @@ $.fn.dataTable.render.RepositoryNumberValue = function(data) {
|
|||
${data.value}
|
||||
</span>`;
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.AssignedTasksValue = function(data) {
|
||||
if (data.tasks > 0) {
|
||||
let tooltip = I18n.t('repositories.table.assigned_tooltip', {
|
||||
tasks: data.tasks,
|
||||
experiments: data.experiments,
|
||||
projects: data.projects
|
||||
});
|
||||
return `<div class="assign-counter-container dropdown" title="${tooltip}"
|
||||
data-task-list-url="${data.task_list_url}">
|
||||
<a href="#" class="assign-counter has-assigned"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">${data.tasks}</a>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="sci-input-container right-icon">
|
||||
<input type="text" class="sci-input-field search-tasks"
|
||||
placeholder="${I18n.t('repositories.table.assigned_search')}"></input>
|
||||
<i class="fas fa-times-circle clear-search"></i>
|
||||
</div>
|
||||
<div class="tasks"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
return "<div class='assign-counter-container'><span class='assign-counter'>0</span></div>";
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
globals I18n _ SmartAnnotation FilePreviewModal animateSpinner Promise dropdownSelector
|
||||
HelperModule animateLoading hideAssignUnasignModal RepositoryDatatableRowEditor
|
||||
globals I18n _ SmartAnnotation FilePreviewModal animateSpinner Promise DataTableHelpers
|
||||
HelperModule animateLoading RepositoryDatatableRowEditor
|
||||
initAssignedTasksDropdown
|
||||
*/
|
||||
|
||||
//= require jquery-ui/widgets/sortable
|
||||
|
@ -14,7 +15,8 @@ var RepositoryDatatable = (function(global) {
|
|||
var TABLE_WRAPPER_ID = '.repository-table';
|
||||
var TABLE = null;
|
||||
var EDITABLE = false;
|
||||
var SELECT_ALL_SELECTOR = "#checkbox > input[name=select_all]"
|
||||
var SELECT_ALL_SELECTOR = '#checkbox > input[name=select_all]';
|
||||
const STATUS_POLLING_INTERVAL = 10000;
|
||||
|
||||
var rowsSelected = [];
|
||||
var rowsLocked = [];
|
||||
|
@ -265,6 +267,12 @@ var RepositoryDatatable = (function(global) {
|
|||
if ($('#assigned').text().length === 0) {
|
||||
TABLE.column(1).visible(false);
|
||||
}
|
||||
|
||||
$.getJSON($(TABLE_ID).data('toolbar-url'), (data) => {
|
||||
$('#toolbarButtonsDatatable').remove();
|
||||
$(data.html).appendTo('div.toolbar');
|
||||
});
|
||||
|
||||
TABLE.ajax.reload(null, false);
|
||||
changeToViewMode();
|
||||
SmartAnnotation.closePopup();
|
||||
|
@ -398,6 +406,18 @@ var RepositoryDatatable = (function(global) {
|
|||
});
|
||||
}
|
||||
|
||||
function checkSnapshottingStatus() {
|
||||
$.getJSON($(TABLE_ID).data('status-url'), (statusData) => {
|
||||
if (statusData.snapshot_provisioning) {
|
||||
setTimeout(() => { checkSnapshottingStatus(); }, STATUS_POLLING_INTERVAL);
|
||||
} else {
|
||||
EDITABLE = statusData.editable;
|
||||
$('.repository-provisioning-notice').remove();
|
||||
resetTableView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function dataTableInit() {
|
||||
viewAssigned = 'assigned';
|
||||
TABLE = $(TABLE_ID).DataTable({
|
||||
|
@ -442,7 +462,7 @@ var RepositoryDatatable = (function(global) {
|
|||
className: 'assigned-column',
|
||||
sWidth: '1%',
|
||||
render: function(data, type, row) {
|
||||
let content = data;
|
||||
let content = $.fn.dataTable.render.AssignedTasksValue(data);
|
||||
let icon;
|
||||
if (!row.recordEditable) {
|
||||
icon = `<i class="repository-row-lock-icon fas fa-lock" title="${I18n.t('repositories.table.locked_item')}"></i>`;
|
||||
|
@ -511,8 +531,10 @@ var RepositoryDatatable = (function(global) {
|
|||
changeToViewMode();
|
||||
updateDataTableSelectAllCtrl();
|
||||
FilePreviewModal.init();
|
||||
|
||||
// Prevent row toggling when selecting user smart annotation link
|
||||
SmartAnnotation.preventPropagation('.atwho-user-popover');
|
||||
|
||||
// Show number of selected rows near pages info
|
||||
$('#repository-table_info').append('<span id="selected_info"></span>');
|
||||
$('#selected_info').html(' (' + rowsSelected.length + ' entries selected)');
|
||||
|
@ -546,17 +568,15 @@ var RepositoryDatatable = (function(global) {
|
|||
});
|
||||
},
|
||||
fnInitComplete: function() {
|
||||
var tableLengthSelect = $('.dataTables_length select');
|
||||
var tableFilterInput = $('.dataTables_filter input');
|
||||
|
||||
disableCheckboxToggleOnAssetDownload();
|
||||
FilePreviewModal.init();
|
||||
initHeaderTooltip();
|
||||
disableCheckboxToggleOnCheckboxPreview();
|
||||
|
||||
// Append button to inner toolbar in table
|
||||
$('div.toolbarButtonsDatatable').appendTo('div.toolbar');
|
||||
$('div.toolbarButtonsDatatable').show();
|
||||
// Append buttons to inner toolbar in the table
|
||||
$.getJSON($(TABLE_ID).data('toolbar-url'), (data) => {
|
||||
$(data.html).appendTo('div.toolbar');
|
||||
});
|
||||
|
||||
$('div.toolbar-filter-buttons').prependTo('div.filter-container');
|
||||
$('div.toolbar-filter-buttons').show();
|
||||
|
@ -565,45 +585,25 @@ var RepositoryDatatable = (function(global) {
|
|||
$('div.toolbarButtons').appendTo('div.toolbar');
|
||||
$('div.toolbarButtons').show();
|
||||
|
||||
if (EDITABLE) {
|
||||
RepositoryDatatableRowEditor.initFormSubmitAction(TABLE);
|
||||
initItemEditIcon();
|
||||
initSaveButton();
|
||||
initCancelButton();
|
||||
}
|
||||
|
||||
DataTableHelpers.initLengthApearance($(TABLE_ID).closest('.dataTables_wrapper'));
|
||||
DataTableHelpers.initSearchField($(TABLE_ID).closest('.dataTables_wrapper'));
|
||||
|
||||
if ($('.repository-show').length) {
|
||||
$('.dataTables_scrollBody, .dataTables_scrollHead').css('overflow', '');
|
||||
}
|
||||
|
||||
if (tableLengthSelect.val() == null) {
|
||||
tableLengthSelect.val(10).change();
|
||||
}
|
||||
$.each(tableLengthSelect.find('option'), (i, option) => {
|
||||
option.innerHTML = I18n.t('repositories.index.show_per_page', { number: option.value });
|
||||
});
|
||||
$('.dataTables_length').append(tableLengthSelect).find('label').remove();
|
||||
dropdownSelector.init(tableLengthSelect, {
|
||||
noEmptyOption: true,
|
||||
singleSelect: true,
|
||||
closeOnSelect: true,
|
||||
selectAppearance: 'simple'
|
||||
});
|
||||
|
||||
tableFilterInput.attr('placeholder', I18n.t('repositories.index.filter_inventory'))
|
||||
.addClass('sci-input-field')
|
||||
.css('margin', 0);
|
||||
$('.dataTables_filter').append(`
|
||||
<div class="sci-input-container left-icon">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>`).find('.sci-input-container').prepend(tableFilterInput);
|
||||
$('.dataTables_filter').find('label').remove();
|
||||
|
||||
$('.main-actions, .pagination-row').removeClass('hidden');
|
||||
|
||||
$(TABLE_ID).find('tr[data-editable=false]').each(function(_, e) {
|
||||
rowsLocked.push(parseInt($(e).attr('id'), 10));
|
||||
});
|
||||
|
||||
initAssignedTasksDropdown(TABLE_ID);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -662,19 +662,6 @@ var RepositoryDatatable = (function(global) {
|
|||
});
|
||||
};
|
||||
|
||||
global.openAssignRecordsModal = function() {
|
||||
$.post(
|
||||
$('#assignRepositoryRecords').data('assign-url-modal'),
|
||||
{ selected_rows: rowsSelected }
|
||||
).done(
|
||||
function(data) {
|
||||
$(data.html).appendTo('body').promise().done(function() {
|
||||
$('#assignRepositoryRecordModal').modal('show');
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
global.hideAssignUnasignModal = function(id) {
|
||||
$(id).modal('hide').promise().done(
|
||||
function() {
|
||||
|
@ -683,28 +670,6 @@ var RepositoryDatatable = (function(global) {
|
|||
);
|
||||
};
|
||||
|
||||
global.submitAssignRepositoryRecord = function(option) {
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('#assignRepositoryRecordModal').data('assign-url'),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: { selected_rows: rowsSelected, downstream: (option === 'downstream') },
|
||||
success: function(data) {
|
||||
hideAssignUnasignModal('#assignRepositoryRecordModal');
|
||||
HelperModule.flashAlertMsg(data.flash, 'success');
|
||||
resetTableView();
|
||||
clearRowSelection();
|
||||
},
|
||||
error: function(data) {
|
||||
hideAssignUnasignModal('#assignRepositoryRecordModal');
|
||||
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
|
||||
resetTableView();
|
||||
clearRowSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
global.openUnassignRecordsModal = function() {
|
||||
$.post(
|
||||
$('#unassignRepositoryRecords').data('unassign-url'),
|
||||
|
@ -718,28 +683,6 @@ var RepositoryDatatable = (function(global) {
|
|||
);
|
||||
};
|
||||
|
||||
global.submitUnassignRepositoryRecord = function(option) {
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('#unassignRepositoryRecordModal').data('unassign-url'),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: { selected_rows: rowsSelected, downstream: (option === 'downstream') },
|
||||
success: function(data) {
|
||||
hideAssignUnasignModal('#unassignRepositoryRecordModal');
|
||||
HelperModule.flashAlertMsg(data.flash, 'success');
|
||||
resetTableView();
|
||||
clearRowSelection();
|
||||
},
|
||||
error: function(data) {
|
||||
hideAssignUnasignModal('#unassignRepositoryRecordModal');
|
||||
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
|
||||
resetTableView();
|
||||
clearRowSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
global.onClickDeleteRecord = function() {
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
|
@ -834,6 +777,9 @@ var RepositoryDatatable = (function(global) {
|
|||
TABLE_ID = id;
|
||||
EDITABLE = $(TABLE_ID).data('editable');
|
||||
TABLE = dataTableInit();
|
||||
if ($(TABLE_ID).data('snapshot-provisioning')) {
|
||||
setTimeout(() => { checkSnapshottingStatus(); }, STATUS_POLLING_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
|
|
|
@ -35,7 +35,8 @@ var RepositoryChecklistColumnType = (function() {
|
|||
previewContainer,
|
||||
itemsTextarea,
|
||||
delimiterDropdown,
|
||||
dropdownOptions
|
||||
dropdownOptions,
|
||||
GLOBAL_CONSTANTS.REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN
|
||||
);
|
||||
$('.changing-existing-list-items-warning').removeClass('hidden');
|
||||
initChecklistDropdown();
|
||||
|
@ -45,7 +46,8 @@ var RepositoryChecklistColumnType = (function() {
|
|||
previewContainer,
|
||||
itemsTextarea,
|
||||
delimiterDropdown,
|
||||
dropdownOptions
|
||||
dropdownOptions,
|
||||
GLOBAL_CONSTANTS.REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN
|
||||
);
|
||||
initChecklistDropdown();
|
||||
initUpdatePlaceholder(this);
|
||||
|
@ -55,7 +57,8 @@ var RepositoryChecklistColumnType = (function() {
|
|||
previewContainer,
|
||||
itemsTextarea,
|
||||
delimiterDropdown,
|
||||
dropdownOptions
|
||||
dropdownOptions,
|
||||
GLOBAL_CONSTANTS.REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN
|
||||
);
|
||||
initChecklistDropdown();
|
||||
})
|
||||
|
@ -71,7 +74,7 @@ var RepositoryChecklistColumnType = (function() {
|
|||
checkValidation: () => {
|
||||
var $manageModal = $(manageModal);
|
||||
var count = $manageModal.find('.items-count').attr('data-count');
|
||||
return count < GLOBAL_CONSTANTS.REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN;
|
||||
return count <= GLOBAL_CONSTANTS.REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN;
|
||||
},
|
||||
loadParams: () => {
|
||||
var repositoryColumnParams = {};
|
||||
|
|
|
@ -83,7 +83,7 @@ var RepositoryListColumnType = (function() {
|
|||
});
|
||||
}
|
||||
|
||||
function refreshCounter(number) {
|
||||
function refreshCounter(number, limit) {
|
||||
var $manageModal = $(manageModal);
|
||||
var $counterContainer = $manageModal.find('.limit-counter-container');
|
||||
var $btn = $manageModal.find('.column-save-btn');
|
||||
|
@ -91,7 +91,7 @@ var RepositoryListColumnType = (function() {
|
|||
|
||||
$counterContainer.find('.items-count').html(number).attr('data-count', number);
|
||||
|
||||
if (number >= GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN) {
|
||||
if (number > limit) {
|
||||
$counterContainer.addClass('error-to-many-items');
|
||||
$textarea.addClass('too-many-items');
|
||||
$btn.addClass('disabled');
|
||||
|
@ -102,11 +102,11 @@ var RepositoryListColumnType = (function() {
|
|||
}
|
||||
}
|
||||
|
||||
function refreshPreviewDropdownList(preview, textarea, delimiterContainer, dropdown) {
|
||||
function refreshPreviewDropdownList(preview, textarea, delimiterContainer, dropdown, limit) {
|
||||
var items = textToItems($(textarea).val(), delimiterContainer);
|
||||
var hashItems = [];
|
||||
drawDropdownPreview(items, preview);
|
||||
refreshCounter(items.length);
|
||||
refreshCounter(items.length, limit);
|
||||
|
||||
$.each(items, (index, option) => {
|
||||
hashItems.push({ data: option });
|
||||
|
@ -126,7 +126,8 @@ var RepositoryListColumnType = (function() {
|
|||
previewContainer,
|
||||
itemsTextarea,
|
||||
delimiterDropdown,
|
||||
dropdownOptions
|
||||
dropdownOptions,
|
||||
GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN
|
||||
);
|
||||
initListDropdown();
|
||||
$('.changing-existing-list-items-warning').removeClass('hidden');
|
||||
|
@ -137,7 +138,8 @@ var RepositoryListColumnType = (function() {
|
|||
previewContainer,
|
||||
itemsTextarea,
|
||||
delimiterDropdown,
|
||||
dropdownOptions
|
||||
dropdownOptions,
|
||||
GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN
|
||||
);
|
||||
initListDropdown();
|
||||
})
|
||||
|
@ -146,7 +148,8 @@ var RepositoryListColumnType = (function() {
|
|||
previewContainer,
|
||||
itemsTextarea,
|
||||
delimiterDropdown,
|
||||
dropdownOptions
|
||||
dropdownOptions,
|
||||
GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN
|
||||
);
|
||||
initListDropdown();
|
||||
})
|
||||
|
@ -162,7 +165,7 @@ var RepositoryListColumnType = (function() {
|
|||
checkValidation: () => {
|
||||
var $manageModal = $(manageModal);
|
||||
var count = $manageModal.find('.items-count').attr('data-count');
|
||||
return count < GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN;
|
||||
return count <= GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN;
|
||||
},
|
||||
loadParams: () => {
|
||||
var repositoryColumnParams = {};
|
||||
|
@ -172,8 +175,8 @@ var RepositoryListColumnType = (function() {
|
|||
return repositoryColumnParams;
|
||||
},
|
||||
|
||||
refreshPreviewDropdownList: (preview, textarea, delimiter, dropdown) => {
|
||||
refreshPreviewDropdownList(preview, textarea, delimiter, dropdown);
|
||||
refreshPreviewDropdownList: (preview, textarea, delimiter, dropdown, limit) => {
|
||||
refreshPreviewDropdownList(preview, textarea, delimiter, dropdown, limit);
|
||||
},
|
||||
|
||||
initListDropdown: () => {
|
||||
|
|
|
@ -21,9 +21,6 @@ var RepositoryColumns = (function() {
|
|||
|
||||
function reloadDataTablePartial() {
|
||||
// Append buttons for inventory datatable
|
||||
$('div.toolbarButtonsDatatable').appendTo('.repository-show');
|
||||
$('div.toolbarButtonsDatatable').hide();
|
||||
|
||||
$('div.toolbar-filter-buttons').appendTo('.repository-show');
|
||||
$('div.toolbar-filter-buttons').hide();
|
||||
|
||||
|
|
|
@ -26,11 +26,18 @@
|
|||
|
||||
function toggle() {
|
||||
var btn = $('#sidebar-arrow');
|
||||
var sideBarEvent;
|
||||
if (btn.is('[data-shown]')) {
|
||||
hide();
|
||||
sideBarEvent = 'sideBar::hidden'
|
||||
} else {
|
||||
show();
|
||||
sideBarEvent = 'sideBar::shown'
|
||||
}
|
||||
|
||||
$('.navbar-secondary').one("transitionend",function(event) {
|
||||
$('.navbar-secondary').trigger(sideBarEvent);
|
||||
})
|
||||
}
|
||||
|
||||
function isShown() {
|
||||
|
|
|
@ -381,8 +381,15 @@ var SmartAnnotation = (function() {
|
|||
function init() {
|
||||
$(field)
|
||||
.on("reposition.atwho", function(event, flag, query) {
|
||||
if (query.$inputor.offset().left > $(window).width()) {
|
||||
query.$el.find('.atwho-view').css('left', (flag.left + $(window).scrollLeft()) + 'px');
|
||||
let inputFieldLeft = query.$inputor.offset().left;
|
||||
if (inputFieldLeft > $(window).width()) {
|
||||
let leftPosition;
|
||||
if (inputFieldLeft < flag.left + $(window).scrollLeft()) {
|
||||
leftPosition = inputFieldLeft;
|
||||
} else {
|
||||
leftPosition = flag.left + $(window).scrollLeft();
|
||||
}
|
||||
query.$el.find('.atwho-view').css('left', leftPosition + 'px');
|
||||
}
|
||||
if ($('.repository-show').length) {
|
||||
query.$el.find('.atwho-view').css('top', flag.top + 'px');
|
||||
|
|
|
@ -19,16 +19,16 @@ var Comments = (function() {
|
|||
function newCommentValidation(textarea, submitBtn) {
|
||||
textarea.off().on('focus', function() {
|
||||
$(this).addClass('border');
|
||||
if (this.value.length > 0) {
|
||||
if (this.value.trim().length > 0) {
|
||||
submitBtn.addClass('show');
|
||||
}
|
||||
}).on('blur', function() {
|
||||
if (this.value.length === 0) {
|
||||
if (this.value.trim().length === 0) {
|
||||
$(this).removeClass('border');
|
||||
submitBtn.removeClass('show');
|
||||
}
|
||||
}).on('keyup', function() {
|
||||
if (this.value.length > 0) {
|
||||
if (this.value.trim().length > 0) {
|
||||
submitBtn.addClass('show');
|
||||
} else {
|
||||
submitBtn.removeClass('show');
|
||||
|
|
37
app/assets/javascripts/sitewide/datatable_helpers.js
Normal file
37
app/assets/javascripts/sitewide/datatable_helpers.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* global dropdownSelector I18n */
|
||||
|
||||
var DataTableHelpers = (function() {
|
||||
return {
|
||||
initLengthApearance: function(dataTableWraper) {
|
||||
var tableLengthSelect = $(dataTableWraper).find('.dataTables_length select');
|
||||
if (tableLengthSelect.val() == null) {
|
||||
tableLengthSelect.val(10).change();
|
||||
}
|
||||
$.each(tableLengthSelect.find('option'), (i, option) => {
|
||||
option.innerHTML = I18n.t('repositories.index.show_per_page', { number: option.value });
|
||||
});
|
||||
$(dataTableWraper).find('.dataTables_length')
|
||||
.append(tableLengthSelect).find('label')
|
||||
.remove();
|
||||
dropdownSelector.init(tableLengthSelect, {
|
||||
noEmptyOption: true,
|
||||
singleSelect: true,
|
||||
closeOnSelect: true,
|
||||
disableSearch: true,
|
||||
selectAppearance: 'simple'
|
||||
});
|
||||
},
|
||||
|
||||
initSearchField: function(dataTableWraper) {
|
||||
var tableFilterInput = $(dataTableWraper).find('.dataTables_filter input');
|
||||
tableFilterInput.attr('placeholder', I18n.t('repositories.index.filter_inventory'))
|
||||
.addClass('sci-input-field')
|
||||
.css('margin', 0);
|
||||
$('.dataTables_filter').append(`
|
||||
<div class="sci-input-container left-icon">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>`).find('.sci-input-container').prepend(tableFilterInput);
|
||||
$('.dataTables_filter').find('label').remove();
|
||||
}
|
||||
};
|
||||
}());
|
|
@ -158,7 +158,6 @@
|
|||
function init(location) {
|
||||
LOCATION = location;
|
||||
global.addEventListener('paste', listener, false);
|
||||
$.initTooltips();
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
|
@ -274,11 +273,9 @@
|
|||
if (totalSize > fileMaxSize) {
|
||||
filesValid = false;
|
||||
disableSubmitButton();
|
||||
$.each($('.panel-step-attachment-new'), function() {
|
||||
if (!$(this).find('p').hasClass('dnd-total-error')) {
|
||||
$(this)
|
||||
.find('.panel-body')
|
||||
.append("<p class='dnd-total-error'>" + I18n.t('general.file.total_size', { size: fileMaxSizeMb }) + '</p>');
|
||||
$.each($('.attachment-placeholder.new'), function() {
|
||||
if (!$(this).find('p').hasClass('dnd-error')) {
|
||||
$(this).append("<p class='dnd-total-error'>" + I18n.t('general.file.total_size', { size: fileMaxSizeMb }) + '</p>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -62,17 +62,18 @@ var dropdownSelector = (function() {
|
|||
var modalContainer = container.closest('.modal-dialog');
|
||||
var modalContainerBottom = 0;
|
||||
var maxHeight = 0;
|
||||
const bottomTreshold = 280;
|
||||
|
||||
if (modalContainer.length) {
|
||||
windowHeight = modalContainer.height() + modalContainer[0].getBoundingClientRect().top;
|
||||
containerPositionLeft -= modalContainer[0].getBoundingClientRect().left;
|
||||
modalContainerBottom = modalContainer[0].getBoundingClientRect().bottom;
|
||||
if (modalContainer.length && windowHeight - modalContainer.height() > bottomTreshold) {
|
||||
let modalClientRect = modalContainer[0].getBoundingClientRect();
|
||||
windowHeight = modalContainer.height() + modalClientRect.top;
|
||||
containerPositionLeft -= modalClientRect.left;
|
||||
modalContainerBottom = windowHeight + modalClientRect.bottom;
|
||||
maxHeight += modalContainerBottom;
|
||||
}
|
||||
|
||||
bottomSpace = windowHeight - containerPosition - containerHeight;
|
||||
|
||||
if ((modalContainerBottom + bottomSpace) < 280) {
|
||||
if ((modalContainerBottom + bottomSpace) < bottomTreshold) {
|
||||
container.addClass('inverse');
|
||||
container.find('.dropdown-container').css('max-height', `${(containerPosition - 122 + maxHeight)}px`)
|
||||
.css('margin-bottom', `${(containerPosition * -1)}px`)
|
||||
|
|
|
@ -18,3 +18,34 @@
|
|||
|
||||
initUnsavedWorkDialog();
|
||||
}());
|
||||
|
||||
function initAssignedTasksDropdown(table) {
|
||||
function loadTasks(counterContainer) {
|
||||
var tasksContainer = counterContainer.find('.tasks');
|
||||
var tasksUrl = counterContainer.data('task-list-url');
|
||||
var searchQuery = counterContainer.find('.search-tasks').val();
|
||||
$.get(tasksUrl, { query: searchQuery }, function(result) {
|
||||
tasksContainer.html(result.html);
|
||||
});
|
||||
}
|
||||
|
||||
$(table).on('show.bs.dropdown', '.assign-counter-container', function() {
|
||||
var cell = $(this);
|
||||
loadTasks(cell);
|
||||
});
|
||||
|
||||
$(table).on('click', '.assign-counter-container .dropdown-menu', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$(table).on('click', '.assign-counter-container .clear-search', function() {
|
||||
var cell = $(this).closest('.assign-counter-container');
|
||||
$(this).prev('.search-tasks').val('');
|
||||
loadTasks(cell);
|
||||
});
|
||||
|
||||
$(table).on('keyup', '.assign-counter-container .search-tasks', function() {
|
||||
var cell = $(this).closest('.assign-counter-container');
|
||||
loadTasks(cell);
|
||||
});
|
||||
}
|
||||
|
|
2
app/assets/javascripts/sitewide/tiny_mce.js
vendored
2
app/assets/javascripts/sitewide/tiny_mce.js
vendored
|
@ -126,7 +126,7 @@ var TinyMCE = (function() {
|
|||
}
|
||||
|
||||
tinyMCE.init({
|
||||
cache_suffix: '?v=4.9.3', // This suffix should be changed any time library is updated
|
||||
cache_suffix: '?v=4.9.10', // This suffix should be changed any time library is updated
|
||||
selector: selector,
|
||||
convert_urls: false,
|
||||
menubar: 'file edit view insert format',
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
$.initTooltips = function() {
|
||||
var popoversArray = [];
|
||||
var leaveTimeout;
|
||||
var enterTimeout;
|
||||
|
||||
if ($(document.body).data('tooltips-enabled') === true || $(document.body).data('tooltips-enabled') == null) {
|
||||
$('.tooltip_open').remove(); // Destroy all (if any) old open popovers
|
||||
$('.help_tooltips').each(function(i, obj) {
|
||||
var popoverObject = obj;
|
||||
popoversArray.push(popoverObject);
|
||||
});
|
||||
$('.help_tooltips').each(function(i, obj) {
|
||||
var link = $(obj).data('tooltiplink');
|
||||
var textData = $(obj).data('tooltipcontent');
|
||||
var customStyle = $(obj).data('tooltipstyle');
|
||||
obj.dataset.tooltipId = i;
|
||||
|
||||
$(obj)
|
||||
.popover({
|
||||
html: true,
|
||||
container: 'body',
|
||||
placement: 'auto right',
|
||||
trigger: 'manual',
|
||||
content: 'popovers will not display if empty',
|
||||
template:
|
||||
'<div class="popover tooltip_' + i + '_window tooltip-open" '
|
||||
+ 'role="tooltip" style="' + customStyle + '" data-popover-id="' + i + '">'
|
||||
+ '<div class="popover-body" >' + textData + '</div>'
|
||||
+ '<br><br><br>'
|
||||
+ '<div class="popover-footer">'
|
||||
+ '<a class="btn btn-link text-nowrap" href="' + link + '" target="_blank" rel="noopener noreferrer" >'
|
||||
+ 'Read more <i class="fas fa-external-link-alt"></i>'
|
||||
+ '</a>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
})
|
||||
.off('shown.bs.popover')
|
||||
.on('shown.bs.popover', function() {
|
||||
// hide popup if object element hidden
|
||||
if (!$(obj).is(':visible') || $(obj).is(':disabled')) {
|
||||
$(obj).popover('hide');
|
||||
}
|
||||
// hide all other popovers
|
||||
popoversArray.forEach(function(arrayItem) {
|
||||
if (obj !== arrayItem) {
|
||||
$(arrayItem).popover('hide');
|
||||
}
|
||||
});
|
||||
})
|
||||
.off('mouseleave')
|
||||
.on('mouseleave', function() {
|
||||
clearTimeout(enterTimeout);
|
||||
leaveTimeout = setTimeout(function() {
|
||||
if (!$('.tooltip_' + i + '_window:hover').length > 0) {
|
||||
$(obj).popover('hide');
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
.off('mouseenter')
|
||||
.on('mouseenter', function() {
|
||||
clearTimeout(leaveTimeout);
|
||||
enterTimeout = setTimeout(function() {
|
||||
var top;
|
||||
if ($(obj).hover().length > 0) {
|
||||
$(obj).popover('show');
|
||||
$('.tooltip_' + i + '_window').removeClass('tooltip-enter');
|
||||
top = $(obj).offset().top;
|
||||
$('.tooltip_' + i + '_window').css({
|
||||
top: (top) + 'px'
|
||||
});
|
||||
$('.tooltip_' + i + '_window').off('mouseleave').on('mouseleave', function() {
|
||||
$('.tooltip_' + i + '_window').removeClass('tooltip-enter');
|
||||
$(obj).popover('hide');
|
||||
});
|
||||
$('.tooltip_' + i + '_window').off('mouseenter').on('mouseenter', function() {
|
||||
$('.tooltip_' + i + '_window').addClass('tooltip-enter');
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(document.body).on('click', function() {
|
||||
$('.help_tooltips').each(function(i, obj) {
|
||||
$(obj).popover('hide');
|
||||
});
|
||||
$('.popover.tooltip-open').each(function(i, obj) {
|
||||
if ($('*[data-tooltip-id="' + obj.dataset.popoverId + '"]').length === 0) {
|
||||
$(obj).remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
$(document.body).on('mousemove', function(e) {
|
||||
var mouse = { x: e.clientX, y: e.clientY };
|
||||
$('.popover.tooltip-open').each(function(i, obj) {
|
||||
var tooltipObj = '*[data-tooltip-id="' + obj.dataset.popoverId + '"]';
|
||||
var objHeight;
|
||||
var objWidth;
|
||||
var objLeft;
|
||||
var objTop;
|
||||
var objCorners;
|
||||
if ($(tooltipObj).length === 0) return;
|
||||
objHeight = $(tooltipObj)[0].clientHeight;
|
||||
objWidth = $(tooltipObj)[0].clientWidth;
|
||||
objLeft = $(tooltipObj)[0].offsetLeft;
|
||||
objTop = $(tooltipObj)[0].offsetTop;
|
||||
objCorners = {
|
||||
tl: { x: objLeft, y: objTop },
|
||||
tr: { x: (objLeft + objWidth), y: objTop },
|
||||
bl: { x: objLeft, y: (objTop + objHeight) },
|
||||
br: { x: (objLeft + objWidth), y: (objTop + objHeight) }
|
||||
};
|
||||
if (
|
||||
!(mouse.x > objCorners.tl.x && mouse.x < objCorners.br.x)
|
||||
|| !(mouse.y > objCorners.tl.y && mouse.y < objCorners.br.y)
|
||||
) {
|
||||
$(tooltipObj).popover('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$(document).on('turbolinks:load', function() {
|
||||
$.initTooltips();
|
||||
});
|
||||
}());
|
|
@ -76,19 +76,6 @@
|
|||
);
|
||||
}
|
||||
|
||||
// Initialize tooltips settings form
|
||||
function tooltipSettings() {
|
||||
var toggleInput = $('[name="tooltips_enabled"]');
|
||||
toggleInput
|
||||
.checkboxpicker({ onActiveCls: 'btn-toggle', offActiveCls: 'btn-toggle' });
|
||||
|
||||
if (toggleInput.attr('value') === 'true') {
|
||||
toggleInput.prop('checked', true);
|
||||
} else {
|
||||
toggleInput.prop('checked', false);
|
||||
}
|
||||
}
|
||||
|
||||
// triggers submit action when the user clicks
|
||||
function initTogglableSettingsForm() {
|
||||
$('#togglable-settings-panel')
|
||||
|
@ -148,6 +135,5 @@
|
|||
initTimeZoneSelector();
|
||||
initDateFormatSelector();
|
||||
notificationsSettings();
|
||||
tooltipSettings();
|
||||
initTogglableSettingsForm();
|
||||
})();
|
||||
|
|
|
@ -120,8 +120,8 @@
|
|||
|
||||
.tag-label {
|
||||
display: inline-block;
|
||||
margin-bottom: 1px;
|
||||
margin-right: 5px;
|
||||
margin-top: 1px;
|
||||
max-width: 500px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Place all the styles related to the MyModules controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
// scss-lint:disable SelectorDepth SelectorFormat
|
||||
// scss-lint:disable NestingDepth QualifyingElement
|
||||
|
||||
@import "constants";
|
||||
|
||||
|
@ -55,9 +57,6 @@
|
|||
|
||||
// Create wopi file
|
||||
.create-wopi-file-btn {
|
||||
border: 0;
|
||||
display: contents;
|
||||
|
||||
img {
|
||||
margin-right: 5px;
|
||||
height: 20px;
|
||||
|
@ -116,3 +115,80 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile view
|
||||
@media (max-width: 700px) {
|
||||
.task-section {
|
||||
border-left: 0;
|
||||
padding-left: 0;
|
||||
|
||||
.task-section-header {
|
||||
.actions-block {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 5px;
|
||||
width: 100%;
|
||||
|
||||
.dropdown {
|
||||
margin-bottom: 5px;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-details {
|
||||
.module-tags {
|
||||
.dropdown-selector-container {
|
||||
.input-field {
|
||||
padding-right: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datetime-container {
|
||||
.date-text {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
left: -50px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#steps {
|
||||
.panel-heading {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.panel-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
max-width: 100%;
|
||||
|
||||
.complete-step-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attachments {
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) !important;
|
||||
|
||||
.attachment-placeholder {
|
||||
margin: 4px 0 16px;
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
#filePreviewModal {
|
||||
.modal-body {
|
||||
width: 100%;
|
||||
|
||||
.file-preview-container {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,61 +6,162 @@
|
|||
@import "constants";
|
||||
@import "mixins";
|
||||
|
||||
#manage-module-tags-modal {
|
||||
|
||||
.well {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.content-pane.my-modules-protocols-index {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.module-header {
|
||||
.task-section {
|
||||
border-left: 3px solid $color-concrete;
|
||||
margin: 16px 0;
|
||||
padding-left: 16px;
|
||||
|
||||
.task-section-caret {
|
||||
color: $color-volcano;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
|
||||
+ .protocol-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin: 10px 0 15px;
|
||||
}
|
||||
|
||||
.fas.block-icon {
|
||||
color: $color-silver;
|
||||
font-size: 18px;
|
||||
.fas {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
&:not(.collapsed) .fas {
|
||||
@include rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.task-section-title {
|
||||
display: inline-block;
|
||||
|
||||
h2 {
|
||||
margin: 10px 0;
|
||||
|
||||
&.assigned-items-title {
|
||||
&::after {
|
||||
@include font-h3;
|
||||
color: $color-alto;
|
||||
content: '[' attr(data-assigned-items-count) ']';
|
||||
display: inline;
|
||||
line-height: 22px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-section-header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
float: left;
|
||||
margin-bottom: 10px;
|
||||
width: calc(100% - 280px);
|
||||
|
||||
.actions-block {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
|
||||
.caret {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.repositories-assign-container {
|
||||
flex-grow: 1;
|
||||
max-width: 200px;
|
||||
|
||||
.btn {
|
||||
text-align: left;
|
||||
|
||||
.caret {
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.repositories-dropdown-menu {
|
||||
.repository {
|
||||
@include font-button;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: 8px 16px;
|
||||
|
||||
.assigned-items,
|
||||
.shared-icon {
|
||||
flex-shrink: 0;
|
||||
|
||||
.fas {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.assigned-items {
|
||||
color: $color-alto;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
@include font-button;
|
||||
|
||||
a {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
|
||||
.fas {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.complete-button-container {
|
||||
display: inline;
|
||||
float: right;
|
||||
width: 260px;
|
||||
|
||||
.my_module-state-buttons {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.task-details {
|
||||
.fas.block-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.flex-block {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
line-height: 34px;
|
||||
|
||||
.flex-block-label {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-right: 3px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.due-date-container {
|
||||
.empty-label {
|
||||
color: $color-silver-chalice;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.datetime-container {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
|
||||
.date-text {
|
||||
border: 1px solid transparent;
|
||||
font-weight: bold;
|
||||
line-height: 34px;
|
||||
line-height: 32px;
|
||||
margin-right: 25px;
|
||||
padding: 0 3px;
|
||||
padding: 0 4px;
|
||||
position: relative;
|
||||
|
||||
.alert-green {
|
||||
|
@ -95,7 +196,11 @@
|
|||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
#calendar-due-date {
|
||||
#calendarDueDate {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#calendarStartDate {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
@ -106,8 +211,8 @@
|
|||
|
||||
&:hover {
|
||||
.date-text[data-editable=true] {
|
||||
border-color: $color-silver;
|
||||
border-radius: 3px;
|
||||
background-color: $color-concrete;
|
||||
border-radius: 4px;
|
||||
|
||||
.clear-date {
|
||||
display: inline;
|
||||
|
@ -115,40 +220,8 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.complete-button-container {
|
||||
float: right;
|
||||
width: 260px;
|
||||
|
||||
.my_module-state-buttons {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.module-description {
|
||||
float: left;
|
||||
width: 100%;
|
||||
|
||||
.no-description {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
padding: 20px 0 5px;
|
||||
}
|
||||
|
||||
.my-module-description-content {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.module-tags {
|
||||
float: left;
|
||||
width: 100%;
|
||||
|
||||
#module-tags {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
@ -185,7 +258,7 @@
|
|||
|
||||
&:not(.view-mode):hover {
|
||||
.input-field {
|
||||
border: 1px solid $color-gainsboro;
|
||||
border: 1px solid $color-alto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +274,7 @@
|
|||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
width: 37px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.select-container {
|
||||
|
@ -209,7 +282,7 @@
|
|||
flex-basis: 100px;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
max-width: calc(100% - 65px);
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
z-index: 110;
|
||||
|
||||
|
@ -250,56 +323,195 @@
|
|||
float: left;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.my-module-recent-protocols {
|
||||
flex-grow: 1;
|
||||
height: 36px;
|
||||
margin-bottom: 5px;
|
||||
position: relative;
|
||||
#manage-module-tags-modal {
|
||||
.well {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.task-assigned-users {
|
||||
align-items: center;
|
||||
border-radius: 17px;
|
||||
display: flex;
|
||||
float: right;
|
||||
height: 33px;
|
||||
|
||||
&.empty {
|
||||
border-radius: 4px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown-button {
|
||||
cursor: pointer;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
left: auto;
|
||||
padding: 0;
|
||||
right: 0;
|
||||
width: 402px;
|
||||
|
||||
.protocol {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
padding: 5px 10px;
|
||||
transition: $md-transaction;
|
||||
width: 200px;
|
||||
|
||||
&:hover {
|
||||
background: $color-gainsboro;
|
||||
background-color: $color-concrete;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fas {
|
||||
.assign-new-user {
|
||||
background-color: $color-alto;
|
||||
color: $color-volcano;
|
||||
text-align: center;
|
||||
|
||||
.fa-plus {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-notes {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.no-description {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.task-notes-content {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.my-module-protocol-status {
|
||||
position: relative;
|
||||
|
||||
.status-label {
|
||||
@include font-h3;
|
||||
color: $color-alto;
|
||||
float: left;
|
||||
margin: 0 3px;
|
||||
|
||||
&.linked {
|
||||
color: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.status-info {
|
||||
@include font-h2;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.protocol-newer {
|
||||
color: $brand-focus;
|
||||
}
|
||||
|
||||
&.parent-newer {
|
||||
color: $brand-warning;
|
||||
}
|
||||
}
|
||||
|
||||
.status-info-dropdown {
|
||||
left: -125px;
|
||||
max-width: 100vw;
|
||||
width: 650px;
|
||||
|
||||
.dropdown-body {
|
||||
border-bottom: $border-tertiary;
|
||||
padding: 10px 32px;
|
||||
|
||||
.info-line {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 9px 0;
|
||||
|
||||
.description {
|
||||
@include font-button;
|
||||
flex-grow: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.value {
|
||||
@include font-h3;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.new-parent-version {
|
||||
.value {
|
||||
color: $brand-warning;
|
||||
|
||||
&::before {
|
||||
@include font-awesome;
|
||||
content: "\f2f1";
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notification-line {
|
||||
@include font-button;
|
||||
color: $color-silver-chalice;
|
||||
display: flex;
|
||||
margin: 8px 0;
|
||||
|
||||
.fas {
|
||||
line-height: 21px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
&.new-parent-version {
|
||||
color: $brand-warning;
|
||||
}
|
||||
|
||||
&.new-protocol-version {
|
||||
color: $brand-focus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-footer {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-details-dropdown-container {
|
||||
.task-details-button {
|
||||
@include font-h2;
|
||||
cursor: pointer;
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
@include font-button;
|
||||
min-width: 500px;
|
||||
padding: 1em 2em;
|
||||
|
||||
.task-details-value {
|
||||
@include font-h3;
|
||||
}
|
||||
|
||||
.row-v-margin {
|
||||
margin-bottom: .5em;
|
||||
margin-top: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.my-module-protocol-status {
|
||||
.status-info-dropdown {
|
||||
left: -75px;
|
||||
width: 300px;
|
||||
|
||||
.dropdown-footer {
|
||||
.btn {
|
||||
float: left !important;
|
||||
margin: 5px 0;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,27 @@
|
|||
// scss-lint:disable SelectorDepth SelectorFormat QualifyingElement
|
||||
// scss-lint:disable NestingDepth ImportantRule
|
||||
|
||||
@mixin my-module-repository-title {
|
||||
@include font-h3;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
padding-right: 55px;
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&::after {
|
||||
color: $color-alto;
|
||||
content: '[' attr(data-rows-count) ']';
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
padding-left: 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 55px;
|
||||
}
|
||||
}
|
||||
|
||||
.my-module-inventories {
|
||||
|
||||
.main-actions {
|
||||
|
@ -74,3 +95,434 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#assigned-items-container {
|
||||
padding-top: 10px;
|
||||
|
||||
.assigned-repository {
|
||||
border: $border-default;
|
||||
border-radius: $border-radius-modal;
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
.assigned-repository-caret {
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
height: 52px;
|
||||
padding: 0 18px;
|
||||
text-decoration: none;
|
||||
|
||||
&.collapsed:hover,
|
||||
&.collapsed:active {
|
||||
background: $color-concrete;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:not(.collapsed) .fa-caret-right {
|
||||
@include rotate(90deg);
|
||||
}
|
||||
|
||||
.fa-caret-right {
|
||||
flex-shrink: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.assigned-repository-title {
|
||||
@include my-module-repository-title;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
|
||||
.full-screen:hover {
|
||||
background: $color-alto;
|
||||
}
|
||||
}
|
||||
|
||||
.snapshot-tag {
|
||||
background-color: $color-concrete;
|
||||
color: $color-silver-chalice;
|
||||
padding: .3em;
|
||||
}
|
||||
}
|
||||
|
||||
.assigned-repository-container {
|
||||
.table.dataTable {
|
||||
margin-top: 0 !important;
|
||||
|
||||
.row-name {
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-row {
|
||||
border-top: $border-default;
|
||||
padding: 5px 10px;
|
||||
|
||||
.pagination {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.dataTables_paginate {
|
||||
height: 38px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#myModuleRepositoryFullViewModal {
|
||||
padding-left: 0 !important;
|
||||
z-index: 1045;
|
||||
|
||||
.modal-dialog {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
width: 100vw;
|
||||
|
||||
.modal-content {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
display: grid;
|
||||
grid-template-areas: 'header sidebar'
|
||||
'table sidebar';
|
||||
grid-template-columns: minmax(50%, 100%) 0;
|
||||
grid-template-rows: 55px calc(100% - 55px);
|
||||
height: inherit;
|
||||
transition: all $timing-function-sharp;
|
||||
|
||||
&.show-sidebar {
|
||||
grid-template-columns: minmax(50%, 100%) minmax(250px, 400px);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
grid-area: header;
|
||||
height: 55px;
|
||||
padding: 10px 24px;
|
||||
|
||||
.close {
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
flex-grow: 1;
|
||||
max-width: calc(100% - 20px);
|
||||
|
||||
.repository-name {
|
||||
@include my-module-repository-title;
|
||||
@include font-h2;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
align-items: center;
|
||||
color: $color-silver-chalice;
|
||||
display: flex;
|
||||
font-size: 10px;
|
||||
height: 20px;
|
||||
width: 90%;
|
||||
|
||||
.my-module,
|
||||
.project,
|
||||
.experiment {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.slash {
|
||||
flex-basis: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
grid-area: table;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dataTables_scrollBody {
|
||||
flex-grow: 1;
|
||||
|
||||
tbody tr.selected {
|
||||
background: $brand-warning-light;
|
||||
}
|
||||
|
||||
.assigned-column {
|
||||
position: relative;
|
||||
|
||||
.assign-counter-container {
|
||||
border-radius: $border-radius-tag;
|
||||
cursor: pointer;
|
||||
line-height: 35px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 1px;
|
||||
width: calc(100% - 16px);
|
||||
|
||||
.assign-counter {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.has-assigned {
|
||||
color: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $color-alto;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
min-width: 320px;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dataTables_scrollHead {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
height: 100%;
|
||||
padding: 1em 1.5em 0;
|
||||
width: 100%;
|
||||
|
||||
.dataTables_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: inherit;
|
||||
|
||||
.dataTables_scroll {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
// Checklists
|
||||
.checklist-dropdown {
|
||||
.dropdown-menu {
|
||||
min-width: 220px;
|
||||
|
||||
.checklist-item {
|
||||
line-height: 18px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
color: $brand-primary;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-actions {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pagination-row {
|
||||
border-top: $border-default;
|
||||
flex-shrink: 0;
|
||||
margin-left: -1.5em;
|
||||
padding: 1em 1.5em;
|
||||
width: calc(100% + 3em);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.repository-versions-sidebar {
|
||||
background-color: $color-concrete;
|
||||
grid-area: sidebar;
|
||||
overflow: hidden;
|
||||
|
||||
.sidebar-collapse-button {
|
||||
color: $color-volcano;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.repository-versions-header {
|
||||
border-bottom: 1px solid $color-alto;
|
||||
height: 55px;
|
||||
padding: 0 1em;
|
||||
|
||||
h4 {
|
||||
line-height: 55px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.repository-versions-list {
|
||||
display: grid;
|
||||
grid-template-rows: min-content min-content minmax(0, 100%);
|
||||
grid-auto-rows: min-content;
|
||||
height: calc(100% - 55px);
|
||||
margin-bottom: 0;
|
||||
|
||||
.repository-snapshots-container {
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
align-items: center;
|
||||
background-color: $color-concrete;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
|
||||
|
||||
.list-group-item-text {
|
||||
@include font-small;
|
||||
color: $color-silver-chalice;
|
||||
}
|
||||
|
||||
.version-button {
|
||||
color: $color-volcano;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.delete-snapshot-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: $color-alto;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-concrete;
|
||||
}
|
||||
}
|
||||
|
||||
&.repository-snapshot-item:hover,
|
||||
&.live-version-item:hover {
|
||||
background-color: $color-alto;
|
||||
|
||||
.delete-snapshot-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $brand-primary;
|
||||
|
||||
&:hover {
|
||||
background-color: $brand-primary;
|
||||
}
|
||||
|
||||
.list-group-item-heading,
|
||||
.list-group-item-text,
|
||||
.delete-snapshot-button {
|
||||
background: transparent;
|
||||
color: $color-white;
|
||||
|
||||
&:hover {
|
||||
border-color: $color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.repository-snapshot-item:not(.provisioning){
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.create-snapshot-item {
|
||||
border-bottom: 1px solid $color-alto;
|
||||
padding: 12px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.update-repository-record-modal {
|
||||
.rows-list-container {
|
||||
display: flex;
|
||||
margin: 0 -10px;
|
||||
|
||||
.header {
|
||||
font-weight: bold;
|
||||
|
||||
.fas {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.rows-list {
|
||||
background: $color-concrete;
|
||||
height: 170px;
|
||||
list-style: none;
|
||||
overflow: hidden;
|
||||
padding-left: 16px;
|
||||
position: relative;
|
||||
|
||||
li {
|
||||
margin: 6px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.rows-to-assign,
|
||||
.rows-to-unassign {
|
||||
flex-grow: 1;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.rows-to-assign .header .fas {
|
||||
color: $brand-success;
|
||||
}
|
||||
|
||||
.rows-to-unassign .header .fas {
|
||||
color: $brand-danger;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 700px) {
|
||||
#myModuleRepositoryFullViewModal {
|
||||
.modal-dialog {
|
||||
.modal-content {
|
||||
grid-template-areas: 'header'
|
||||
'table'
|
||||
'sidebar';
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: 55px calc(100% - 55px) 0;
|
||||
|
||||
&.show-sidebar {
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: 55px 0 calc(100% - 55px);
|
||||
|
||||
.modal-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-collapse-button {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,11 @@
|
|||
.add-result-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.add-result-text {
|
||||
display: inline-block;
|
||||
line-height: 36px;
|
||||
margin-bottom: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,8 +123,8 @@
|
|||
|
||||
.fas-custom {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
top: 2px;
|
||||
margin-right: 15px;
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
.active {
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
.key-words-container {
|
||||
display: inline-block;
|
||||
flex-grow: 1;
|
||||
margin-left: 5px;
|
||||
margin: 0 40px 0 5px;
|
||||
|
||||
.dropdown-selector-container {
|
||||
.input-field {
|
||||
|
|
|
@ -211,6 +211,7 @@ label {
|
|||
.user-time {
|
||||
color: $color-emperor;
|
||||
margin-left: 15px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.controls {
|
||||
margin-right: 15px;
|
||||
|
@ -276,6 +277,12 @@ label {
|
|||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.module-start-date,
|
||||
.module-due-date {
|
||||
margin-left: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.module-tags {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
|
@ -328,6 +335,7 @@ label {
|
|||
|
||||
.user-time {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,10 @@
|
|||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.repository-provisioning-notice {
|
||||
color: $brand-info;
|
||||
}
|
||||
}
|
||||
|
||||
.dataTables_scroll {
|
||||
|
@ -187,29 +191,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.repositories-dropdown-menu {
|
||||
border: 1px solid $color-gainsboro;
|
||||
border-top: 0;
|
||||
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, .05);
|
||||
height: auto;
|
||||
max-height: 400px;
|
||||
overflow-x: hidden;
|
||||
text-transform: initial;
|
||||
width: 300px;
|
||||
|
||||
li:not(:first-child) {
|
||||
border-top: 1px solid $color-gainsboro;
|
||||
}
|
||||
|
||||
.fas-custom {
|
||||
float: right;
|
||||
}
|
||||
|
||||
a.muted {
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
|
||||
.repository-share-status {
|
||||
display: contents !important;
|
||||
|
||||
|
@ -373,7 +354,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.toolbarButtonsDatatable {
|
||||
#toolbarButtonsDatatable {
|
||||
.view-only-label {
|
||||
opacity: .6;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
|
||||
.assign-counter-container {
|
||||
border-radius: $border-radius-tag;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
line-height: 35px;
|
||||
position: absolute;
|
||||
|
@ -49,7 +48,16 @@
|
|||
width: calc(100% - 40px);
|
||||
|
||||
.assign-counter {
|
||||
margin-left: 5px;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
padding-left: 5px;
|
||||
width: 100%;
|
||||
|
||||
&:hover,
|
||||
&:visited,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.has-assigned {
|
||||
color: $brand-primary;
|
||||
|
@ -59,6 +67,19 @@
|
|||
&:hover {
|
||||
background-color: $color-alto;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
padding: 8px;
|
||||
width: 320px;
|
||||
|
||||
.search-tasks:placeholder-shown + .fa-times-circle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fa-times-circle {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.circle-icon {
|
||||
|
|
|
@ -20,6 +20,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.task-assigned-users .global-avatar-container {
|
||||
margin: 2px;
|
||||
|
||||
img {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.new-avatar-preview-container {
|
||||
height: 200px;
|
||||
margin-bottom: 45px;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
}
|
||||
|
||||
.comment-message {
|
||||
@include font-main;
|
||||
float: left;
|
||||
width: 100%;
|
||||
|
||||
|
@ -218,6 +219,7 @@
|
|||
|
||||
textarea {
|
||||
border: 1px solid transparent;
|
||||
border-radius: $border-radius-default;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
|
|
62
app/assets/stylesheets/shared/datatable.scss
Normal file
62
app/assets/stylesheets/shared/datatable.scss
Normal file
|
@ -0,0 +1,62 @@
|
|||
// scss-lint:disable SelectorDepth SelectorFormat
|
||||
// scss-lint:disable NestingDepth QualifyingElement
|
||||
|
||||
.dataTables_wrapper {
|
||||
|
||||
.main-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.toolbar {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
min-height: 68px;
|
||||
width: 100%;
|
||||
|
||||
.pagination-info,
|
||||
.pagination-actions {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.dataTables_info {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.dataTables_info {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dataTables_length {
|
||||
margin-right: 24px;
|
||||
width: 170px;
|
||||
|
||||
.dropdown-selector-container {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.pagination-info {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -89,8 +89,8 @@
|
|||
|
||||
.tag-label {
|
||||
display: inline-block;
|
||||
margin-bottom: 1px;
|
||||
margin-right: 5px;
|
||||
margin-top: 1px;
|
||||
max-width: 240px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
@ -56,4 +56,40 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.private-tasks-counter {
|
||||
@include font-button;
|
||||
border-top: $border-tertiary;
|
||||
color: $color-silver-chalice;
|
||||
padding-top: .5em;
|
||||
}
|
||||
|
||||
.no-results-placeholder {
|
||||
color: $color-silver-chalice;
|
||||
padding: 2em 0 4em;
|
||||
text-align: center;
|
||||
|
||||
.fa-stack {
|
||||
@include font-h1;
|
||||
|
||||
.fas {
|
||||
line-height: inherit;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.archived {
|
||||
@include font-small;
|
||||
background: $brand-warning;
|
||||
border-radius: $border-radius-tag;
|
||||
color: $color-white;
|
||||
line-height: 14px;
|
||||
margin-right: 3px;
|
||||
padding: 2px 3px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ $color-alabaster: $color-concrete;
|
|||
$color-gainsboro: $color-concrete;
|
||||
$color-silver: $color-alto;
|
||||
$color-dove-gray: $color-volcano;
|
||||
$color-emperor: $color-volcano;
|
||||
$color-emperor: $color-black;
|
||||
$brand-default: $color-white;
|
||||
$brand-info: $brand-focus;
|
||||
$brand-other: $brand-success;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
border-radius: $border-radius-default;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 36px;
|
||||
line-height: 20px;
|
||||
outline: 0;
|
||||
padding: 7px 16px;
|
||||
|
@ -24,7 +25,9 @@
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
&:active,
|
||||
&.active {
|
||||
box-shadow: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -43,7 +46,8 @@
|
|||
color: $color-white;
|
||||
}
|
||||
|
||||
&:active {
|
||||
&:active,
|
||||
&.active {
|
||||
background: $brand-primary-press;
|
||||
color: $color-white;
|
||||
}
|
||||
|
@ -65,7 +69,8 @@
|
|||
color: $color-volcano;
|
||||
}
|
||||
|
||||
&:active {
|
||||
&:active,
|
||||
&.active {
|
||||
background: $color-alto;
|
||||
border: $border-secondary;
|
||||
color: $color-volcano;
|
||||
|
@ -88,7 +93,8 @@
|
|||
color: $color-volcano;
|
||||
}
|
||||
|
||||
&:active {
|
||||
&:active,
|
||||
&.active {
|
||||
background: $color-alto;
|
||||
border: $border-transparent;
|
||||
color: $color-volcano;
|
||||
|
@ -110,7 +116,8 @@
|
|||
color: $color-white;
|
||||
}
|
||||
|
||||
&:active {
|
||||
&:active,
|
||||
&.active {
|
||||
background: $brand-danger-press;
|
||||
color: $color-white;
|
||||
}
|
||||
|
|
32
app/assets/stylesheets/shared_styles/elements/dropdown.scss
Normal file
32
app/assets/stylesheets/shared_styles/elements/dropdown.scss
Normal file
|
@ -0,0 +1,32 @@
|
|||
.sci-dropdown {
|
||||
[data-toggle="dropdown"] {
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.open {
|
||||
[data-toggle="dropdown"] {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-color: $brand-focus;
|
||||
|
||||
.caret {
|
||||
transform: rotateX(180deg)
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
box-shadow: $flyout-shadow;
|
||||
margin-top: -1px;
|
||||
width: 100%;
|
||||
|
||||
li:hover {
|
||||
background: $color-concrete;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -44,15 +44,20 @@
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.panel-heading {
|
||||
.step-heading {
|
||||
align-items: center;
|
||||
border: 0;
|
||||
height: 46px;
|
||||
display: flex;
|
||||
min-height: 46px;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
|
||||
.panel-options {
|
||||
bottom: 0;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
line-height: 46px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
span.step-number {
|
||||
|
@ -65,7 +70,9 @@
|
|||
.left-floats {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
min-height: inherit;
|
||||
overflow: hidden;
|
||||
padding-right: 15px;
|
||||
|
||||
.step-name-link {
|
||||
|
@ -299,9 +306,11 @@
|
|||
.attachments-actions {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.attachments-order {
|
||||
|
@ -327,3 +336,7 @@
|
|||
.comments-title {
|
||||
color: $color-emperor;
|
||||
}
|
||||
|
||||
.expand-all-steps {
|
||||
margin: 0 0 15px 15px;
|
||||
}
|
||||
|
|
|
@ -750,7 +750,7 @@ ul.double-line > li {
|
|||
|
||||
.panel-options {
|
||||
position: relative;
|
||||
bottom: 6px;
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
.panel-footer {
|
||||
|
@ -921,13 +921,6 @@ ul.content-activities {
|
|||
flex-wrap: wrap;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.protocol-button {
|
||||
|
||||
.sci-btn-group {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.protocol-status-bar {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
|
@ -1899,10 +1892,6 @@ th.custom-field .modal-tooltiptext {
|
|||
background-color: $color-alto;
|
||||
}
|
||||
|
||||
.my_module-state-buttons {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.parse-records-table {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
|
|
@ -67,8 +67,7 @@ module Api
|
|||
params.require(:data).require(:attributes)
|
||||
params.permit(data: { attributes: %i(data) })[:data].merge(
|
||||
created_by: @current_user,
|
||||
last_modified_by: @current_user,
|
||||
repository: @inventory
|
||||
last_modified_by: @current_user
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -71,8 +71,7 @@ module Api
|
|||
params.require(:data).require(:attributes)
|
||||
params.permit(data: { attributes: %i(data) })[:data].merge(
|
||||
created_by: @current_user,
|
||||
last_modified_by: @current_user,
|
||||
repository: @inventory
|
||||
last_modified_by: @current_user
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -61,8 +61,7 @@ module Api
|
|||
params.require(:data).require(:attributes)
|
||||
params.permit(data: { attributes: %i(status icon) })[:data].merge(
|
||||
created_by: @current_user,
|
||||
last_modified_by: @current_user,
|
||||
repository: @inventory
|
||||
last_modified_by: @current_user
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class AssetsController < ApplicationController
|
|||
can_manage_protocol_in_module?(@protocol) || can_manage_protocol_in_repository?(@protocol)
|
||||
elsif @assoc.class == Result
|
||||
can_manage_module?(@my_module)
|
||||
elsif @assoc.class == RepositoryCell
|
||||
elsif @assoc.class == RepositoryCell && !@repository.is_a?(RepositorySnapshot)
|
||||
can_manage_repository_rows?(@repository)
|
||||
end
|
||||
if response_json['type'] == 'previewable'
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
module Dashboard
|
||||
class CalendarsController < ApplicationController
|
||||
include IconsHelper
|
||||
include MyModulesHelper
|
||||
|
||||
def show
|
||||
date = DateTime.parse(params[:date])
|
||||
|
@ -26,9 +27,10 @@ module Dashboard
|
|||
.where(projects: { archived: false })
|
||||
.where('DATE(my_modules.due_date) = DATE(?)', date)
|
||||
.where(projects: { team_id: current_team.id })
|
||||
.my_modules_list_partial
|
||||
render json: {
|
||||
html: render_to_string(partial: 'shared/my_modules_list_partial.html.erb', locals: { task_groups: my_modules })
|
||||
html: render_to_string(partial: 'shared/my_modules_list_partial.html.erb', locals: {
|
||||
my_modules: my_modules
|
||||
})
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
162
app/controllers/my_module_repositories_controller.rb
Normal file
162
app/controllers/my_module_repositories_controller.rb
Normal file
|
@ -0,0 +1,162 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MyModuleRepositoriesController < ApplicationController
|
||||
include ApplicationHelper
|
||||
|
||||
before_action :load_my_module
|
||||
before_action :load_repository, except: %i(repositories_dropdown_list repositories_list_html)
|
||||
before_action :check_my_module_view_permissions
|
||||
before_action :check_repository_view_permissions, except: %i(repositories_dropdown_list repositories_list_html)
|
||||
before_action :check_assign_repository_records_permissions, only: :update
|
||||
|
||||
def index_dt
|
||||
@draw = params[:draw].to_i
|
||||
per_page = params[:length] == '-1' ? Constants::REPOSITORY_DEFAULT_PAGE_SIZE : params[:length].to_i
|
||||
page = (params[:start].to_i / per_page) + 1
|
||||
datatable_service = RepositoryDatatableService.new(@repository, params, current_user, @my_module)
|
||||
|
||||
@datatable_params = {
|
||||
view_mode: params[:view_mode],
|
||||
my_module: @my_module
|
||||
}
|
||||
@all_rows_count = datatable_service.all_count
|
||||
@columns_mappings = datatable_service.mappings
|
||||
if params[:simple_view]
|
||||
repository_rows = datatable_service.repository_rows
|
||||
rows_view = 'repository_rows/simple_view_index.json'
|
||||
else
|
||||
repository_rows = datatable_service.repository_rows.preload(:repository_columns,
|
||||
:created_by,
|
||||
repository_cells: @repository.cell_preload_includes)
|
||||
rows_view = 'repository_rows/index.json'
|
||||
end
|
||||
@repository_rows = repository_rows.page(page).per(per_page)
|
||||
|
||||
render rows_view
|
||||
end
|
||||
|
||||
def update
|
||||
service = RepositoryRows::MyModuleAssignUnassignService.call(my_module: @my_module,
|
||||
repository: @repository,
|
||||
user: current_user,
|
||||
params: params)
|
||||
if service.succeed? &&
|
||||
(service.assigned_rows_count.positive? ||
|
||||
service.unassigned_rows_count.positive?)
|
||||
flash = update_flash_message(service)
|
||||
status = :ok
|
||||
else
|
||||
flash = t('my_modules.repository.flash.update_error')
|
||||
status = :bad_request
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
flash: flash,
|
||||
rows_count: @my_module.repository_rows_count(@repository),
|
||||
repository_id: @repository.repository_snapshots.find_by(selected: true)&.id || @repository.id
|
||||
}, status: status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_repository_records_modal
|
||||
modal = render_to_string(
|
||||
partial: 'my_modules/modals/update_repository_records_modal_content.html.erb',
|
||||
locals: { my_module: @my_module,
|
||||
repository: @repository,
|
||||
selected_rows: params[:selected_rows] }
|
||||
)
|
||||
render json: {
|
||||
html: modal,
|
||||
update_url: my_module_repository_path(@my_module, @repository)
|
||||
}, status: :ok
|
||||
end
|
||||
|
||||
def assign_repository_records_modal
|
||||
modal = render_to_string(
|
||||
partial: 'my_modules/modals/assign_repository_records_modal_content.html.erb',
|
||||
locals: { my_module: @my_module,
|
||||
repository: @repository,
|
||||
selected_rows: params[:selected_rows] }
|
||||
)
|
||||
render json: {
|
||||
html: modal,
|
||||
update_url: my_module_repository_path(@my_module, @repository)
|
||||
}, status: :ok
|
||||
end
|
||||
|
||||
def repositories_list_html
|
||||
@assigned_repositories = @my_module.live_and_snapshot_repositories_list
|
||||
render json: {
|
||||
html: render_to_string(partial: 'my_modules/repositories/repositories_list'),
|
||||
assigned_rows_count: @assigned_repositories.map{|i| i.assigned_rows_count}.sum
|
||||
}
|
||||
end
|
||||
|
||||
def full_view_table
|
||||
render json: {
|
||||
html: render_to_string(partial: 'my_modules/repositories/full_view_table')
|
||||
}
|
||||
end
|
||||
|
||||
def repositories_dropdown_list
|
||||
@repositories = Repository.accessible_by_teams(current_team).order(:name)
|
||||
|
||||
render json: { html: render_to_string(partial: 'my_modules/repositories/repositories_dropdown_list') }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_my_module
|
||||
@my_module = MyModule.find_by(id: params[:my_module_id])
|
||||
render_404 unless @my_module
|
||||
end
|
||||
|
||||
def load_repository
|
||||
@repository = Repository.find_by(id: params[:id])
|
||||
render_404 unless @repository
|
||||
end
|
||||
|
||||
def check_my_module_view_permissions
|
||||
render_403 unless can_read_experiment?(@my_module.experiment)
|
||||
end
|
||||
|
||||
def check_repository_view_permissions
|
||||
render_403 unless can_read_repository?(@repository)
|
||||
end
|
||||
|
||||
def check_assign_repository_records_permissions
|
||||
render_403 unless can_assign_repository_rows_to_module?(@my_module)
|
||||
end
|
||||
|
||||
def update_flash_message(service)
|
||||
assigned_count = service.assigned_rows_count
|
||||
unassigned_count = service.unassigned_rows_count
|
||||
|
||||
if params[:downstream] == 'true'
|
||||
if assigned_count && unassigned_count
|
||||
t('my_modules.repository.flash.assign_and_unassign_from_task_and_downstream_html',
|
||||
assigned_items: assigned_count,
|
||||
unassigned_items: unassigned_count)
|
||||
elsif assigned_count
|
||||
t('my_modules.repository.flash.assign_to_task_and_downstream_html',
|
||||
assigned_items: assigned_count)
|
||||
elsif unassigned_count
|
||||
t('my_modules.repository.flash.unassign_from_task_and_downstream_html',
|
||||
unassigned_items: unassigned_count)
|
||||
end
|
||||
elsif assigned_count && unassigned_count
|
||||
t('my_modules.repository.flash.assign_and_unassign_from_task_html',
|
||||
assigned_items: assigned_count,
|
||||
unassigned_items: unassigned_count)
|
||||
elsif assigned_count
|
||||
t('my_modules.repository.flash.assign_to_task_html',
|
||||
assigned_items: assigned_count)
|
||||
elsif unassigned_count
|
||||
t('my_modules.repository.flash.unassign_from_task_html',
|
||||
unassigned_items: unassigned_count)
|
||||
end
|
||||
end
|
||||
end
|
129
app/controllers/my_module_repository_snapshots_controller.rb
Normal file
129
app/controllers/my_module_repository_snapshots_controller.rb
Normal file
|
@ -0,0 +1,129 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MyModuleRepositorySnapshotsController < ApplicationController
|
||||
before_action :load_my_module
|
||||
before_action :load_repository, only: :create
|
||||
before_action :load_repository_snapshot, except: %i(create full_view_sidebar select)
|
||||
before_action :check_view_permissions, except: %i(create destroy select)
|
||||
before_action :check_manage_permissions, only: %i(create destroy select)
|
||||
|
||||
def index_dt
|
||||
@draw = params[:draw].to_i
|
||||
per_page = params[:length] == '-1' ? Constants::REPOSITORY_DEFAULT_PAGE_SIZE : params[:length].to_i
|
||||
page = (params[:start].to_i / per_page) + 1
|
||||
datatable_service = RepositorySnapshotDatatableService.new(@repository_snapshot, params, current_user, @my_module)
|
||||
|
||||
@all_rows_count = datatable_service.all_count
|
||||
@columns_mappings = datatable_service.mappings
|
||||
if params[:simple_view]
|
||||
repository_rows = datatable_service.repository_rows
|
||||
rows_view = 'repository_rows/simple_view_index.json'
|
||||
else
|
||||
repository_rows = datatable_service.repository_rows
|
||||
.preload(:repository_columns,
|
||||
:created_by,
|
||||
repository_cells: @repository_snapshot.cell_preload_includes)
|
||||
rows_view = 'repository_rows/snapshot_index.json'
|
||||
end
|
||||
@repository_rows = repository_rows.page(page).per(per_page)
|
||||
|
||||
render rows_view
|
||||
end
|
||||
|
||||
def create
|
||||
repository_snapshot = @repository.provision_snapshot(@my_module, current_user)
|
||||
|
||||
render json: {
|
||||
html: render_to_string(partial: 'my_modules/repositories/full_view_version',
|
||||
locals: { repository_snapshot: repository_snapshot,
|
||||
can_delete_snapshot: can_manage_my_module_repository_snapshots?(@my_module) })
|
||||
}
|
||||
end
|
||||
|
||||
def status
|
||||
render json: {
|
||||
status: @repository_snapshot.status
|
||||
}
|
||||
end
|
||||
|
||||
def show
|
||||
render json: {
|
||||
html: render_to_string(partial: 'my_modules/repositories/full_view_version',
|
||||
locals: { repository_snapshot: @repository_snapshot,
|
||||
can_delete_snapshot: can_manage_my_module_repository_snapshots?(@my_module) })
|
||||
}
|
||||
end
|
||||
|
||||
def destroy
|
||||
@repository_snapshot.destroy!
|
||||
render json: {}
|
||||
end
|
||||
|
||||
def full_view_table
|
||||
render json: {
|
||||
html: render_to_string(partial: 'my_modules/repositories/full_view_snapshot_table')
|
||||
}
|
||||
end
|
||||
|
||||
def full_view_sidebar
|
||||
@repository = Repository.find_by(id: params[:repository_id])
|
||||
|
||||
if @repository
|
||||
return render_403 unless can_read_repository?(@repository)
|
||||
end
|
||||
|
||||
@repository_snapshots = @my_module.repository_snapshots
|
||||
.where(parent_id: params[:repository_id])
|
||||
.order(created_at: :desc)
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'my_modules/repositories/full_view_sidebar',
|
||||
locals: {
|
||||
live_items_present: @repository ? @my_module.repository_rows_count(@repository).positive? : false
|
||||
}
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def select
|
||||
if params[:repository_id]
|
||||
@my_module.repository_snapshots.where(parent_id: params[:repository_id]).update(selected: nil)
|
||||
else
|
||||
repository_snapshot = @my_module.repository_snapshots.find_by(id: params[:repository_snapshot_id])
|
||||
return render_404 unless repository_snapshot
|
||||
|
||||
@my_module.repository_snapshots
|
||||
.where(original_repository: repository_snapshot.original_repository)
|
||||
.update(selected: nil)
|
||||
repository_snapshot.update!(selected: true)
|
||||
end
|
||||
|
||||
render json: {}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_my_module
|
||||
@my_module = MyModule.find_by(id: params[:my_module_id])
|
||||
render_404 unless @my_module
|
||||
end
|
||||
|
||||
def load_repository
|
||||
@repository = Repository.find_by(id: params[:repository_id])
|
||||
render_404 unless @repository
|
||||
render_403 unless can_read_repository?(@repository)
|
||||
end
|
||||
|
||||
def load_repository_snapshot
|
||||
@repository_snapshot = @my_module.repository_snapshots.find_by(id: params[:id])
|
||||
render_404 unless @repository_snapshot
|
||||
end
|
||||
|
||||
def check_view_permissions
|
||||
render_403 unless can_read_experiment?(@my_module.experiment)
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
render_403 unless can_manage_my_module_repository_snapshots?(@my_module)
|
||||
end
|
||||
end
|
|
@ -143,21 +143,15 @@ class MyModulesController < ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
update_params = my_module_params
|
||||
if update_params[:due_date].present?
|
||||
update_params[:due_date] =
|
||||
Time.zone.strptime(update_params[:due_date], I18n.backend.date_format.dup.gsub(/%-/, '%') + ' %H:%M')
|
||||
end
|
||||
@my_module.assign_attributes(update_params)
|
||||
@my_module.assign_attributes(my_module_params)
|
||||
@my_module.last_modified_by = current_user
|
||||
description_changed = @my_module.description_changed?
|
||||
start_date_changes = @my_module.changes[:started_on]
|
||||
due_date_changes = @my_module.changes[:due_date]
|
||||
|
||||
if @my_module.archived_changed?(from: false, to: true)
|
||||
|
||||
saved = @my_module.archive(current_user)
|
||||
elsif @my_module.archived_changed?(from: true, to: false)
|
||||
|
||||
saved = @my_module.restore(current_user)
|
||||
if saved
|
||||
restored = true
|
||||
|
@ -165,28 +159,14 @@ class MyModulesController < ApplicationController
|
|||
end
|
||||
else
|
||||
saved = @my_module.save
|
||||
|
||||
if saved
|
||||
if description_changed
|
||||
log_activity(:change_module_description)
|
||||
TinyMceAsset.update_images(@my_module, params[:tiny_mce_images], current_user)
|
||||
end
|
||||
|
||||
if due_date_changes
|
||||
# rubocop:disable Metrics/BlockNesting # temporary solution
|
||||
type_of = if due_date_changes[0].nil? # set due_date
|
||||
message_items = { my_module_duedate: @my_module.due_date }
|
||||
:set_task_due_date
|
||||
elsif due_date_changes[1].nil? # remove due_date
|
||||
message_items = { my_module_duedate: due_date_changes[0] }
|
||||
:remove_task_due_date
|
||||
else # change due_date
|
||||
message_items = { my_module_duedate: @my_module.due_date }
|
||||
:change_task_due_date
|
||||
end
|
||||
# rubocop:enable Metrics/BlockNesting
|
||||
log_activity(type_of, @my_module, message_items)
|
||||
end
|
||||
log_start_date_change_activity(start_date_changes) if start_date_changes.present?
|
||||
log_due_date_change_activity(due_date_changes) if due_date_changes.present?
|
||||
end
|
||||
end
|
||||
respond_to do |format|
|
||||
|
@ -199,7 +179,7 @@ class MyModulesController < ApplicationController
|
|||
redirect_to module_archive_experiment_path(@my_module.experiment)
|
||||
end
|
||||
elsif saved
|
||||
format.json {
|
||||
format.json do
|
||||
alerts = []
|
||||
alerts << 'alert-green' if @my_module.completed?
|
||||
unless @my_module.completed?
|
||||
|
@ -208,6 +188,10 @@ class MyModulesController < ApplicationController
|
|||
end
|
||||
render json: {
|
||||
status: :ok,
|
||||
start_date_label: render_to_string(
|
||||
partial: 'my_modules/start_date_label.html.erb',
|
||||
locals: { my_module: @my_module }
|
||||
),
|
||||
due_date_label: render_to_string(
|
||||
partial: 'my_modules/due_date_label.html.erb',
|
||||
locals: { my_module: @my_module }
|
||||
|
@ -226,12 +210,12 @@ class MyModulesController < ApplicationController
|
|||
),
|
||||
alerts: alerts
|
||||
}
|
||||
}
|
||||
end
|
||||
else
|
||||
format.json {
|
||||
format.json do
|
||||
render json: @my_module.errors,
|
||||
status: :unprocessable_entity
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -282,11 +266,7 @@ class MyModulesController < ApplicationController
|
|||
|
||||
def protocols
|
||||
@protocol = @my_module.protocol
|
||||
@recent_protcols_positive = Protocol.recent_protocols(
|
||||
current_user,
|
||||
current_team,
|
||||
Constants::RECENT_PROTOCOL_LIMIT
|
||||
).any?
|
||||
@assigned_repositories = @my_module.live_and_snapshot_repositories_list
|
||||
current_team_switch(@protocol.team)
|
||||
end
|
||||
|
||||
|
@ -416,123 +396,45 @@ class MyModulesController < ApplicationController
|
|||
|
||||
# Submit actions
|
||||
def assign_repository_records
|
||||
if params[:selected_rows].present? && params[:repository_id].present?
|
||||
records_names = []
|
||||
downstream = ActiveModel::Type::Boolean.new.cast(params[:downstream])
|
||||
downstream_my_modules = []
|
||||
dowmstream_records = {}
|
||||
RepositoryRow
|
||||
.where(id: params[:selected_rows],
|
||||
repository_id: params[:repository_id])
|
||||
.find_each do |record|
|
||||
unless @my_module.repository_rows.include?(record)
|
||||
record.last_modified_by = current_user
|
||||
record.save
|
||||
service = RepositoryRows::MyModuleAssigningService.call(my_module: @my_module,
|
||||
repository: @repository,
|
||||
user: current_user,
|
||||
params: params)
|
||||
|
||||
MyModuleRepositoryRow.create!(
|
||||
my_module: @my_module,
|
||||
repository_row: record,
|
||||
assigned_by: current_user
|
||||
)
|
||||
records_names << record.name
|
||||
end
|
||||
|
||||
next unless downstream
|
||||
@my_module.downstream_modules.each do |my_module|
|
||||
next if my_module.repository_rows.include?(record)
|
||||
dowmstream_records[my_module.id] = [] unless dowmstream_records[my_module.id]
|
||||
MyModuleRepositoryRow.create!(
|
||||
my_module: my_module,
|
||||
repository_row: record,
|
||||
assigned_by: current_user
|
||||
)
|
||||
dowmstream_records[my_module.id] << record.name
|
||||
downstream_my_modules.push(my_module)
|
||||
end
|
||||
end
|
||||
|
||||
if records_names.any?
|
||||
records_names.uniq!
|
||||
log_activity(:assign_repository_record,
|
||||
@my_module,
|
||||
repository: @repository.id,
|
||||
record_names: records_names.join(', '))
|
||||
downstream_my_modules.uniq.each do |my_module|
|
||||
log_activity(:assign_repository_record,
|
||||
my_module,
|
||||
repository: @repository.id,
|
||||
record_names: dowmstream_records[my_module.id].join(', '))
|
||||
end
|
||||
records_names.map! { |n| escape_input(n) }
|
||||
flash = I18n.t('repositories.assigned_records_flash',
|
||||
records: records_names.join(', '))
|
||||
flash = I18n.t('repositories.assigned_records_downstream_flash',
|
||||
records: records_names.join(', ')) if downstream
|
||||
respond_to do |format|
|
||||
format.json { render json: { flash: flash }, status: :ok }
|
||||
end
|
||||
if service.succeed? && service.assigned_rows_names.any?
|
||||
names = service.assigned_rows_names.map { |name| escape_input(name) }
|
||||
message = params[:downstream].blank? ? 'assigned_records_flash' : 'assigned_records_downstream_flash'
|
||||
flash = I18n.t("repositories.#{message}", records: names.join(', '))
|
||||
status = :ok
|
||||
else
|
||||
flash = t('repositories.no_records_assigned_flash')
|
||||
status = :bad_request
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
flash: t('repositories.no_records_assigned_flash')
|
||||
}, status: :bad_request
|
||||
end
|
||||
end
|
||||
render json: { flash: flash }, status: status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unassign_repository_records
|
||||
if params[:selected_rows].present? && params[:repository_id].present?
|
||||
downstream = ActiveModel::Type::Boolean.new.cast(params[:downstream])
|
||||
|
||||
records = RepositoryRow.assigned_on_my_module(params[:selected_rows],
|
||||
@my_module)
|
||||
|
||||
@my_module.repository_rows.destroy(records & @my_module.repository_rows)
|
||||
|
||||
if downstream
|
||||
@my_module.downstream_modules.each do |my_module|
|
||||
assigned_records = RepositoryRow.assigned_on_my_module(
|
||||
params[:selected_rows],
|
||||
my_module
|
||||
)
|
||||
my_module.repository_rows.destroy(
|
||||
assigned_records & my_module.repository_rows
|
||||
)
|
||||
assigned_records.update_all(last_modified_by_id: current_user.id)
|
||||
next unless assigned_records.any?
|
||||
|
||||
log_activity(:unassign_repository_record,
|
||||
my_module,
|
||||
repository: @repository.id,
|
||||
record_names: assigned_records.map(&:name).join(', '))
|
||||
end
|
||||
end
|
||||
|
||||
# update last last_modified_by
|
||||
records.update_all(last_modified_by_id: current_user.id)
|
||||
|
||||
if records.any?
|
||||
log_activity(:unassign_repository_record,
|
||||
@my_module,
|
||||
repository: @repository.id,
|
||||
record_names: records.map(&:name).join(', '))
|
||||
|
||||
service = RepositoryRows::MyModuleUnassigningService.call(my_module: @my_module,
|
||||
repository: @repository,
|
||||
user: current_user,
|
||||
params: params)
|
||||
if service.succeed? && service.unassigned_rows_names.any?
|
||||
flash = I18n.t('repositories.unassigned_records_flash',
|
||||
records: records.map { |r| escape_input(r.name) }.join(', '))
|
||||
respond_to do |format|
|
||||
format.json { render json: { flash: flash }, status: :ok }
|
||||
end
|
||||
records: service.unassigned_rows_names.map { |name| escape_input(name) }.join(', '))
|
||||
status = :ok
|
||||
else
|
||||
flash = t('repositories.no_records_unassigned_flash')
|
||||
status = :bad_request
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
flash: t('repositories.no_records_unassigned_flash')
|
||||
}, status: :bad_request
|
||||
end
|
||||
end
|
||||
render json: { flash: flash }, status: status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -672,7 +574,7 @@ class MyModulesController < ApplicationController
|
|||
end
|
||||
|
||||
def load_repository
|
||||
@repository = Repository.find_by_id(params[:repository_id])
|
||||
@repository = Repository.find_by(id: params[:repository_id])
|
||||
render_404 unless @repository
|
||||
render_403 unless can_read_repository?(@repository)
|
||||
end
|
||||
|
@ -725,8 +627,46 @@ class MyModulesController < ApplicationController
|
|||
end
|
||||
|
||||
def my_module_params
|
||||
params.require(:my_module).permit(:name, :description, :due_date,
|
||||
:archived)
|
||||
update_params = params.require(:my_module).permit(:name, :description, :started_on, :due_date, :archived)
|
||||
|
||||
if update_params[:started_on].present?
|
||||
update_params[:started_on] =
|
||||
Time.zone.strptime(update_params[:started_on], I18n.backend.date_format.dup.gsub(/%-/, '%') + ' %H:%M')
|
||||
end
|
||||
if update_params[:due_date].present?
|
||||
update_params[:due_date] =
|
||||
Time.zone.strptime(update_params[:due_date], I18n.backend.date_format.dup.gsub(/%-/, '%') + ' %H:%M')
|
||||
end
|
||||
|
||||
update_params
|
||||
end
|
||||
|
||||
def log_start_date_change_activity(start_date_changes)
|
||||
type_of = if start_date_changes[0].nil? # set started_on
|
||||
message_items = { my_module_started_on: @my_module.started_on }
|
||||
:set_task_start_date
|
||||
elsif start_date_changes[1].nil? # remove started_on
|
||||
message_items = { my_module_started_on: start_date_changes[0] }
|
||||
:remove_task_start_date
|
||||
else # change started_on
|
||||
message_items = { my_module_started_on: @my_module.started_on }
|
||||
:change_task_start_date
|
||||
end
|
||||
log_activity(type_of, @my_module, message_items)
|
||||
end
|
||||
|
||||
def log_due_date_change_activity(due_date_changes)
|
||||
type_of = if due_date_changes[0].nil? # set due_date
|
||||
message_items = { my_module_duedate: @my_module.due_date }
|
||||
:set_task_due_date
|
||||
elsif due_date_changes[1].nil? # remove due_date
|
||||
message_items = { my_module_duedate: due_date_changes[0] }
|
||||
:remove_task_due_date
|
||||
else # change due_date
|
||||
message_items = { my_module_duedate: @my_module.due_date }
|
||||
:change_task_due_date
|
||||
end
|
||||
log_activity(type_of, @my_module, message_items)
|
||||
end
|
||||
|
||||
def log_activity(type_of, my_module = nil, message_items = {})
|
||||
|
@ -747,5 +687,4 @@ class MyModulesController < ApplicationController
|
|||
:page, :starting_timestamp, :from_date, :to_date, types: [], users: [], subjects: {}
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -23,6 +23,7 @@ class ProtocolsController < ApplicationController
|
|||
linked_children
|
||||
linked_children_datatable
|
||||
)
|
||||
before_action :switch_team_with_param, only: :index
|
||||
before_action :check_view_all_permissions, only: %i(
|
||||
index
|
||||
datatable
|
||||
|
@ -107,14 +108,6 @@ class ProtocolsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def recent_protocols
|
||||
render json: Protocol.recent_protocols(
|
||||
current_user,
|
||||
current_team,
|
||||
Constants::RECENT_PROTOCOL_LIMIT
|
||||
).select(:id, :name)
|
||||
end
|
||||
|
||||
def linked_children
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
|
|
|
@ -31,6 +31,7 @@ class ReportsController < ApplicationController
|
|||
only: %i(new edit available_repositories)
|
||||
|
||||
before_action :check_manage_permissions, only: BEFORE_ACTION_METHODS
|
||||
before_action :switch_team_with_param, only: :index
|
||||
|
||||
# Index showing all reports of a single project
|
||||
def index; end
|
||||
|
|
|
@ -5,20 +5,17 @@ class RepositoriesController < ApplicationController
|
|||
include ActionView::Helpers::TagHelper
|
||||
include ActionView::Context
|
||||
include IconsHelper
|
||||
include TeamsHelper
|
||||
|
||||
before_action :load_vars,
|
||||
except: %i(index create create_modal parse_sheet)
|
||||
before_action :load_parent_vars, except:
|
||||
%i(repository_table_index parse_sheet)
|
||||
before_action :switch_team_with_param, only: :show
|
||||
before_action :load_repository, except: %i(index create create_modal)
|
||||
before_action :load_repositories, only: %i(index show)
|
||||
before_action :check_view_all_permissions, only: :index
|
||||
before_action :check_view_permissions, only: %i(load_table export_repository show)
|
||||
before_action :check_manage_permissions, only:
|
||||
%i(destroy destroy_modal rename_modal update)
|
||||
before_action :check_view_permissions, except: %i(index create_modal create update destroy parse_sheet import_records)
|
||||
before_action :check_manage_permissions, only: %i(destroy destroy_modal rename_modal update)
|
||||
before_action :check_share_permissions, only: :share_modal
|
||||
before_action :check_create_permissions, only:
|
||||
%i(create_modal create)
|
||||
before_action :check_copy_permissions, only:
|
||||
%i(copy_modal copy)
|
||||
before_action :check_create_permissions, only: %i(create_modal create)
|
||||
before_action :check_copy_permissions, only: %i(copy_modal copy)
|
||||
before_action :set_inline_name_editing, only: %i(show)
|
||||
|
||||
layout 'fluid'
|
||||
|
@ -32,6 +29,20 @@ class RepositoriesController < ApplicationController
|
|||
@display_edit_button = can_create_repository_rows?(@repository)
|
||||
@display_delete_button = can_delete_repository_rows?(@repository)
|
||||
@display_duplicate_button = can_create_repository_rows?(@repository)
|
||||
@snapshot_provisioning = @repository.repository_snapshots.provisioning.any?
|
||||
end
|
||||
|
||||
def table_toolbar
|
||||
render json: {
|
||||
html: render_to_string(partial: 'repositories/toolbar_buttons.html.erb')
|
||||
}
|
||||
end
|
||||
|
||||
def status
|
||||
render json: {
|
||||
editable: can_manage_repository_rows?(@repository),
|
||||
snapshot_provisioning: @repository.repository_snapshots.provisioning.any?
|
||||
}
|
||||
end
|
||||
|
||||
def load_table
|
||||
|
@ -67,7 +78,7 @@ class RepositoriesController < ApplicationController
|
|||
|
||||
def create
|
||||
@repository = Repository.new(
|
||||
team: @team,
|
||||
team: current_team,
|
||||
created_by: current_user
|
||||
)
|
||||
@repository.assign_attributes(repository_params)
|
||||
|
@ -144,7 +155,7 @@ class RepositoriesController < ApplicationController
|
|||
|
||||
def copy_modal
|
||||
@tmp_repository = Repository.new(
|
||||
team: @team,
|
||||
team: current_team,
|
||||
created_by: current_user,
|
||||
name: @repository.name
|
||||
)
|
||||
|
@ -161,7 +172,7 @@ class RepositoriesController < ApplicationController
|
|||
|
||||
def copy
|
||||
@tmp_repository = Repository.new(
|
||||
team: @team,
|
||||
team: current_team,
|
||||
created_by: current_user
|
||||
)
|
||||
@tmp_repository.assign_attributes(repository_params)
|
||||
|
@ -194,25 +205,15 @@ class RepositoriesController < ApplicationController
|
|||
|
||||
# AJAX actions
|
||||
def repository_table_index
|
||||
if @repository.nil? || !can_read_repository?(@repository)
|
||||
render_403
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render json: ::RepositoryDatatable.new(view_context,
|
||||
@repository,
|
||||
nil,
|
||||
current_user)
|
||||
end
|
||||
render json: ::RepositoryDatatable.new(view_context, @repository, nil, current_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_sheet
|
||||
repository = Repository.accessible_by_teams(current_team).find_by_id(import_params[:id])
|
||||
|
||||
render_403 unless can_create_repository_rows?(repository)
|
||||
render_403 unless can_create_repository_rows?(@repository)
|
||||
|
||||
unless import_params[:file]
|
||||
repository_response(t('teams.parse_sheet.errors.no_file_selected'))
|
||||
|
@ -221,7 +222,7 @@ class RepositoriesController < ApplicationController
|
|||
begin
|
||||
parsed_file = ImportRepository::ParseRepository.new(
|
||||
file: import_params[:file],
|
||||
repository: repository,
|
||||
repository: @repository,
|
||||
session: session
|
||||
)
|
||||
if parsed_file.too_large?
|
||||
|
@ -328,16 +329,14 @@ class RepositoriesController < ApplicationController
|
|||
)
|
||||
end
|
||||
|
||||
def load_vars
|
||||
def load_repository
|
||||
repository_id = params[:id] || params[:repository_id]
|
||||
@repository = Repository.accessible_by_teams(current_team).find_by_id(repository_id)
|
||||
@repository = Repository.accessible_by_teams(current_team).find_by(id: repository_id)
|
||||
render_404 unless @repository
|
||||
end
|
||||
|
||||
def load_parent_vars
|
||||
@team = current_team
|
||||
render_404 unless @team
|
||||
@repositories = Repository.accessible_by_teams(@team).order('repositories.created_at ASC')
|
||||
def load_repositories
|
||||
@repositories = Repository.accessible_by_teams(current_team).order('repositories.created_at ASC')
|
||||
end
|
||||
|
||||
def set_inline_name_editing
|
||||
|
@ -354,7 +353,7 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def check_view_all_permissions
|
||||
render_403 unless can_read_team?(@team)
|
||||
render_403 unless can_read_team?(current_team)
|
||||
end
|
||||
|
||||
def check_view_permissions
|
||||
|
@ -362,11 +361,11 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def check_create_permissions
|
||||
render_403 unless can_create_repositories?(@team)
|
||||
render_403 unless can_create_repositories?(current_team)
|
||||
end
|
||||
|
||||
def check_copy_permissions
|
||||
render_403 if !can_create_repositories?(@team) || @repository.shared_with?(current_team)
|
||||
render_403 if !can_create_repositories?(current_team) || @repository.shared_with?(current_team)
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
|
@ -405,7 +404,7 @@ class RepositoriesController < ApplicationController
|
|||
.call(activity_type: type_of,
|
||||
owner: current_user,
|
||||
subject: @repository,
|
||||
team: @team,
|
||||
team: current_team,
|
||||
message_items: message_items)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -164,7 +164,7 @@ class RepositoryColumnsController < ApplicationController
|
|||
end
|
||||
|
||||
def available_columns
|
||||
render json: { columns: @repository.available_columns_ids }, status: :ok
|
||||
render json: { columns: @repository.repository_columns.pluck(:id) }, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -2,19 +2,15 @@ class RepositoryRowsController < ApplicationController
|
|||
include InputSanitizeHelper
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ApplicationHelper
|
||||
include MyModulesHelper
|
||||
|
||||
before_action :load_info_modal_vars, only: :show
|
||||
before_action :load_vars, only: %i(edit update)
|
||||
before_action :load_repository,
|
||||
only: %i(create
|
||||
delete_records
|
||||
index
|
||||
copy_records
|
||||
available_rows)
|
||||
before_action :load_repository
|
||||
before_action :load_repository_row, only: %i(update show assigned_task_list)
|
||||
before_action :check_read_permissions, except: %i(create update delete_records copy_records)
|
||||
before_action :check_snapshotting_status, only: %i(create update delete_records copy_records)
|
||||
before_action :check_create_permissions, only: :create
|
||||
before_action :check_delete_permissions, only: :delete_records
|
||||
before_action :check_manage_permissions,
|
||||
only: %i(edit update copy_records)
|
||||
before_action :check_manage_permissions, only: %i(update copy_records)
|
||||
|
||||
def index
|
||||
@draw = params[:draw].to_i
|
||||
|
@ -53,6 +49,10 @@ class RepositoryRowsController < ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
@assigned_modules = @repository_row.my_modules.joins(experiment: :project)
|
||||
@viewable_modules = @assigned_modules.viewable_by_user(current_user, current_user.teams)
|
||||
@private_modules = @assigned_modules - @viewable_modules
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
|
@ -64,54 +64,26 @@ class RepositoryRowsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
json = {
|
||||
repository_row: {
|
||||
name: escape_input(@record.name),
|
||||
repository_cells: {},
|
||||
repository_column_items: fetch_columns_list_items
|
||||
}
|
||||
}
|
||||
|
||||
# Add custom cells ids as key (easier lookup on js side)
|
||||
@record.repository_cells.each do |cell|
|
||||
if cell.value_type == 'RepositoryAssetValue'
|
||||
cell_value = cell.value.asset
|
||||
else
|
||||
cell_value = escape_input(cell.value.data)
|
||||
end
|
||||
|
||||
json[:repository_row][:repository_cells][cell.repository_column_id] = {
|
||||
repository_cell_id: cell.id,
|
||||
cell_column_id: cell.repository_column.id, # needed for mappings
|
||||
value: cell_value,
|
||||
type: cell.value_type,
|
||||
list_items: fetch_list_items(cell)
|
||||
}
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render json: json }
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
row_update = RepositoryRows::UpdateRepositoryRowService
|
||||
.call(repository_row: @record, user: current_user, params: update_params)
|
||||
.call(repository_row: @repository_row, user: current_user, params: update_params)
|
||||
|
||||
if row_update.succeed?
|
||||
if row_update.record_updated
|
||||
log_activity(:edit_item_inventory, @record)
|
||||
@record.repository_cells.where(value_type: 'RepositoryTextValue').each do |repository_cell|
|
||||
record_annotation_notification(@record, repository_cell)
|
||||
log_activity(:edit_item_inventory, @repository_row)
|
||||
@repository_row.repository_cells.where(value_type: 'RepositoryTextValue').each do |repository_cell|
|
||||
record_annotation_notification(@repository_row, repository_cell)
|
||||
end
|
||||
end
|
||||
|
||||
render json: { id: @record.id, flash: t('repositories.update.success_flash',
|
||||
record: escape_input(@record.name),
|
||||
repository: escape_input(@repository.name)) },
|
||||
status: :ok
|
||||
render json: {
|
||||
id: @repository_row.id,
|
||||
flash: t(
|
||||
'repositories.update.success_flash',
|
||||
record: escape_input(@repository_row.name),
|
||||
repository: escape_input(@repository.name)
|
||||
)
|
||||
}, status: :ok
|
||||
else
|
||||
render json: row_update.errors, status: :bad_request
|
||||
end
|
||||
|
@ -121,7 +93,7 @@ class RepositoryRowsController < ApplicationController
|
|||
deleted_count = 0
|
||||
if selected_params
|
||||
selected_params.each do |row_id|
|
||||
row = @repository.repository_rows.find_by_id(row_id)
|
||||
row = @repository.repository_rows.find_by(id: row_id)
|
||||
next unless row && can_manage_repository_rows?(@repository)
|
||||
|
||||
log_activity(:delete_item_inventory, row)
|
||||
|
@ -180,38 +152,57 @@ class RepositoryRowsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def assigned_task_list
|
||||
assigned_modules = @repository_row.my_modules.joins(experiment: :project)
|
||||
viewable_modules = assigned_modules.viewable_by_user(current_user, current_user.teams)
|
||||
private_modules = assigned_modules - viewable_modules
|
||||
|
||||
viewable_modules = viewable_modules.where_attributes_like(
|
||||
['my_modules.name', 'experiments.name', 'projects.name'],
|
||||
params[:query],
|
||||
whole_phrase: true
|
||||
)
|
||||
render json: {
|
||||
html: render_to_string(partial: 'shared/my_modules_list_partial.html.erb', locals: {
|
||||
my_modules: viewable_modules,
|
||||
private_modules: private_modules
|
||||
})
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
include StringUtility
|
||||
AvailableRepositoryRow = Struct.new(:id, :name, :has_file_attached)
|
||||
|
||||
def load_info_modal_vars
|
||||
@repository_row = RepositoryRow.eager_load(:created_by, repository: [:team])
|
||||
.find_by_id(params[:id])
|
||||
@assigned_modules = MyModuleRepositoryRow.eager_load(
|
||||
my_module: [{ experiment: :project }]
|
||||
).where(repository_row: @repository_row)
|
||||
render_404 and return unless @repository_row
|
||||
render_403 unless can_read_repository?(@repository_row.repository)
|
||||
end
|
||||
|
||||
def load_vars
|
||||
def load_repository
|
||||
@repository = Repository.accessible_by_teams(current_team)
|
||||
.eager_load(:repository_columns)
|
||||
.find_by_id(params[:repository_id])
|
||||
|
||||
@record = @repository.repository_rows
|
||||
.eager_load(:repository_columns)
|
||||
.find_by_id(params[:id])
|
||||
render_404 unless @repository && @record
|
||||
.find_by(id: params[:repository_id])
|
||||
render_404 unless @repository
|
||||
end
|
||||
|
||||
def load_repository
|
||||
@repository = Repository.accessible_by_teams(current_team).find_by_id(params[:repository_id])
|
||||
render_404 unless @repository
|
||||
def load_repository_row
|
||||
@repository_row = @repository.repository_rows.eager_load(:repository_columns).find_by(id: params[:id])
|
||||
render_404 unless @repository_row
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 unless can_read_repository?(@repository)
|
||||
end
|
||||
|
||||
def check_snapshotting_status
|
||||
return if @repository.repository_snapshots.provisioning.none?
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
flash: t('repositories.index.snapshot_provisioning_in_progress')
|
||||
}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_create_permissions
|
||||
render_403 unless can_create_repository_rows?(@repository)
|
||||
end
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
class UserMyModulesController < ApplicationController
|
||||
before_action :load_vars
|
||||
before_action :check_view_permissions, only: :index
|
||||
before_action :check_view_permissions, only: %i(index index_old)
|
||||
before_action :check_manage_permissions, only: %i(create index_edit destroy)
|
||||
|
||||
def index
|
||||
def index_old
|
||||
@user_my_modules = @my_module.user_my_modules
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'index.html.erb'
|
||||
partial: 'index_old.html.erb'
|
||||
),
|
||||
my_module_id: @my_module.id,
|
||||
counter: @my_module.users.count # Used for counter badge
|
||||
|
@ -19,6 +19,18 @@ class UserMyModulesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'index.html.erb'
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def index_edit
|
||||
@user_my_modules = @my_module.user_my_modules
|
||||
@unassigned_users = @my_module.unassigned_users
|
||||
|
|
|
@ -30,7 +30,7 @@ class UserRepositoriesController < ApplicationController
|
|||
private
|
||||
|
||||
def load_vars
|
||||
@repository = Repository.find_by_id(params[:repository_id])
|
||||
@repository = RepositoryBase.find_by(id: params[:repository_id])
|
||||
render_403 if @repository.nil? || !can_read_repository?(@repository)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -122,10 +122,10 @@ module Users
|
|||
Activities::CreateActivityService
|
||||
.call(activity_type: :invite_user_to_team,
|
||||
owner: current_user,
|
||||
subject: current_team,
|
||||
team: current_team,
|
||||
subject: @team,
|
||||
team: @team,
|
||||
message_items: {
|
||||
team: current_team.id,
|
||||
team: @team.id,
|
||||
user_invited: user.id,
|
||||
role: user_team.role_str
|
||||
})
|
||||
|
|
|
@ -43,9 +43,6 @@ module Users
|
|||
read_from_params(:system_message_notification_email) do |val|
|
||||
@user.system_message_email_notification = val
|
||||
end
|
||||
read_from_params(:tooltips_enabled) do |val|
|
||||
@user.settings[:tooltips_enabled] = val
|
||||
end
|
||||
if @user.save
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
|
|
|
@ -36,9 +36,9 @@ class WopiController < ActionController::Base
|
|||
when 'REFRESH_LOCK'
|
||||
refresh_lock
|
||||
when 'GET_SHARE_URL'
|
||||
render body: nil, status: 501 and return
|
||||
render body: nil, status: :not_implemented
|
||||
else
|
||||
render body: nil, status: 404 and return
|
||||
render body: nil, status: :not_found
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -69,12 +69,9 @@ class WopiController < ActionController::Base
|
|||
UserCanWrite: @can_write,
|
||||
UserCanNotWriteRelative: true,
|
||||
CloseUrl: @close_url,
|
||||
DownloadUrl: url_for(controller: 'assets', action: 'file_url',
|
||||
id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
HostEditUrl: url_for(controller: 'assets', action: 'edit',
|
||||
id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
HostViewUrl: url_for(controller: 'assets', action: 'view',
|
||||
id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
DownloadUrl: url_for(controller: 'assets', action: 'file_url', id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
HostEditUrl: url_for(controller: 'assets', action: 'edit', id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
HostViewUrl: url_for(controller: 'assets', action: 'view', id: @asset.id, host: ENV['WOPI_USER_HOST']),
|
||||
BreadcrumbBrandName: @breadcrumb_brand_name,
|
||||
BreadcrumbBrandUrl: @breadcrumb_brand_url,
|
||||
BreadcrumbFolderName: @breadcrumb_folder_name,
|
||||
|
@ -83,31 +80,33 @@ 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'] = Scinote::Application::VERSION
|
||||
render json: msg and return
|
||||
|
||||
render json: msg
|
||||
end
|
||||
|
||||
def put_relative
|
||||
render body: nil, status: 501 and return
|
||||
render body: nil, status: :not_implemented
|
||||
end
|
||||
|
||||
def lock
|
||||
lock = request.headers['X-WOPI-Lock']
|
||||
logger.warn 'WOPI: lock; ' + lock.to_s
|
||||
render body: nil, status: 404 and return if lock.nil? || lock.blank?
|
||||
return render body: nil, status: :not_found if lock.blank?
|
||||
|
||||
@asset.with_lock do
|
||||
if @asset.locked?
|
||||
if @asset.lock == lock
|
||||
@asset.refresh_lock
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
render body: nil, status: 200 and return
|
||||
return render body: nil, status: :ok
|
||||
else
|
||||
response.headers['X-WOPI-Lock'] = @asset.lock
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
else
|
||||
@asset.lock_asset(lock)
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
render body: nil, status: 200 and return
|
||||
return render body: nil, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -116,82 +115,79 @@ class WopiController < ActionController::Base
|
|||
logger.warn 'lock and relock'
|
||||
lock = request.headers['X-WOPI-Lock']
|
||||
old_lock = request.headers['X-WOPI-OldLock']
|
||||
if lock.nil? || lock.blank? || old_lock.blank?
|
||||
render body: nil, status: 400 and return
|
||||
end
|
||||
|
||||
return render body: nil, status: :bad_request if lock.blank? || old_lock.blank?
|
||||
|
||||
@asset.with_lock do
|
||||
if @asset.locked?
|
||||
if @asset.lock == old_lock
|
||||
@asset.unlock
|
||||
@asset.lock_asset(lock)
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
render body: nil, status: 200 and return
|
||||
return render body: nil, status: :ok
|
||||
else
|
||||
response.headers['X-WOPI-Lock'] = @asset.lock
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
else
|
||||
response.headers['X-WOPI-Lock'] = ' '
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unlock
|
||||
lock = request.headers['X-WOPI-Lock']
|
||||
render body: nil, status: 400 and return if lock.nil? || lock.blank?
|
||||
return render body: nil, status: :bad_request if lock.blank?
|
||||
|
||||
@asset.with_lock do
|
||||
if @asset.locked?
|
||||
logger.warn "WOPI: current asset lock: #{@asset.lock},
|
||||
unlocking lock #{lock}"
|
||||
logger.warn "WOPI: current asset lock: #{@asset.lock}, unlocking lock #{lock}"
|
||||
if @asset.lock == lock
|
||||
@asset.unlock
|
||||
@asset.post_process_file # Space is already taken in put_file
|
||||
create_wopi_file_activity(@user, false)
|
||||
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
render body: nil, status: 200 and return
|
||||
return render body: nil, status: :ok
|
||||
else
|
||||
response.headers['X-WOPI-Lock'] = @asset.lock
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
else
|
||||
logger.warn 'WOPI: tried to unlock non-locked file'
|
||||
response.headers['X-WOPI-Lock'] = ' '
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_lock
|
||||
lock = request.headers['X-WOPI-Lock']
|
||||
render body: nil, status: 400 and return if lock.nil? || lock.blank?
|
||||
return render body: nil, status: :bad_request if lock.nil? || lock.blank?
|
||||
|
||||
@asset.with_lock do
|
||||
if @asset.locked?
|
||||
if @asset.lock == lock
|
||||
@asset.refresh_lock
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
render body: nil, status: 200 and return
|
||||
return render body: nil, status: :ok
|
||||
else
|
||||
response.headers['X-WOPI-Lock'] = @asset.lock
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
else
|
||||
response.headers['X-WOPI-Lock'] = ' '
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_lock
|
||||
@asset.with_lock do
|
||||
if @asset.locked?
|
||||
response.headers['X-WOPI-Lock'] = @asset.lock
|
||||
else
|
||||
response.headers['X-WOPI-Lock'] = ' '
|
||||
end
|
||||
render body: nil, status: 200 and return
|
||||
response.headers['X-WOPI-Lock'] = @asset.locked? ? @asset.lock : ' '
|
||||
return render body: nil, status: :ok
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -210,14 +206,14 @@ class WopiController < ActionController::Base
|
|||
@team.take_space(@asset.estimated_size)
|
||||
@team.save
|
||||
|
||||
@protocol.update(updated_at: Time.now) if @protocol
|
||||
@protocol&.update(updated_at: Time.now.utc)
|
||||
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
render body: nil, status: 200 and return
|
||||
return render body: nil, status: :ok
|
||||
else
|
||||
logger.warn 'WOPI: wrong lock used to try and modify file'
|
||||
response.headers['X-WOPI-Lock'] = @asset.lock
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
elsif !@asset.file_size.nil? && @asset.file_size.zero?
|
||||
logger.warn 'WOPI: initializing empty file'
|
||||
|
@ -229,21 +225,21 @@ class WopiController < ActionController::Base
|
|||
@team.save
|
||||
|
||||
response.headers['X-WOPI-ItemVersion'] = @asset.version
|
||||
render body: nil, status: 200 and return
|
||||
return render body: nil, status: :ok
|
||||
else
|
||||
logger.warn 'WOPI: trying to modify unlocked file'
|
||||
response.headers['X-WOPI-Lock'] = ' '
|
||||
render body: nil, status: 409 and return
|
||||
return render body: nil, status: :conflict
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load_vars
|
||||
@asset = Asset.find_by_id(params[:id])
|
||||
@asset = Asset.find_by(id: params[:id])
|
||||
if @asset.nil?
|
||||
render body: nil, status: 404 and return
|
||||
render body: nil, status: :not_found
|
||||
else
|
||||
logger.warn 'Found asset: ' + @asset.id.to_s
|
||||
logger.warn "Found asset: #{@asset.id}"
|
||||
step_assoc = @asset.step
|
||||
result_assoc = @asset.result
|
||||
repository_cell_assoc = @asset.repository_cell
|
||||
|
@ -270,16 +266,15 @@ class WopiController < ActionController::Base
|
|||
wopi_token = params[:access_token]
|
||||
if wopi_token.nil?
|
||||
logger.warn 'WOPI: nil wopi token'
|
||||
render body: nil, status: 401 and return
|
||||
return render body: nil, status: :unauthorized
|
||||
end
|
||||
|
||||
@user = User.find_by_valid_wopi_token(wopi_token)
|
||||
if @user.nil?
|
||||
logger.warn 'WOPI: no user with this token found'
|
||||
render body: nil, status: 401 and return
|
||||
return render body: nil, status: :unauthorized
|
||||
end
|
||||
logger.warn 'WOPI: user found by token ' + wopi_token +
|
||||
' ID: ' + @user.id.to_s
|
||||
logger.warn "WOPI: user found by token #{wopi_token} ID: #{@user.id}"
|
||||
|
||||
# This is what we get for settings permission methods with
|
||||
# current_user
|
||||
|
@ -288,25 +283,19 @@ class WopiController < ActionController::Base
|
|||
if @protocol.in_module?
|
||||
@can_read = can_read_protocol_in_module?(@protocol)
|
||||
@can_write = can_manage_protocol_in_module?(@protocol)
|
||||
@close_url = protocols_my_module_url(@protocol.my_module,
|
||||
only_path: false,
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@close_url = protocols_my_module_url(@protocol.my_module, only_path: false, 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_USER_HOST'])
|
||||
@breadcrumb_brand_url = project_url(project, only_path: false, host: ENV['WOPI_USER_HOST'])
|
||||
@breadcrumb_folder_name = @protocol.my_module.name
|
||||
else
|
||||
@can_read = can_read_protocol_in_repository?(@protocol)
|
||||
@can_write = can_manage_protocol_in_repository?(@protocol)
|
||||
@close_url = protocols_url(only_path: false,
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@close_url = protocols_url(only_path: false, host: ENV['WOPI_USER_HOST'])
|
||||
|
||||
@breadcrump_brand_name = 'Projects'
|
||||
@breadcrumb_brand_url = root_url(only_path: false,
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@breadcrumb_brand_url = root_url(only_path: false, host: ENV['WOPI_USER_HOST'])
|
||||
@breadcrumb_folder_name = 'Protocol managament'
|
||||
end
|
||||
@breadcrumb_folder_url = @close_url
|
||||
|
@ -314,9 +303,7 @@ class WopiController < ActionController::Base
|
|||
@can_read = can_read_experiment?(@my_module.experiment)
|
||||
@can_write = can_manage_module?(@my_module)
|
||||
|
||||
@close_url = results_my_module_url(@my_module,
|
||||
only_path: false,
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@close_url = results_my_module_url(@my_module, only_path: false, host: ENV['WOPI_USER_HOST'])
|
||||
|
||||
@breadcrumb_brand_name = @my_module.experiment.project.name
|
||||
@breadcrumb_brand_url = project_url(@my_module.experiment.project,
|
||||
|
@ -326,11 +313,9 @@ class WopiController < ActionController::Base
|
|||
@breadcrumb_folder_url = @close_url
|
||||
elsif @assoc.class == RepositoryCell
|
||||
@can_read = can_read_repository?(@repository)
|
||||
@can_write = can_edit_wopi_file_in_repository_rows?
|
||||
@can_write = !@repository.is_a?(RepositorySnapshot) && can_edit_wopi_file_in_repository_rows?
|
||||
|
||||
@close_url = repository_url(@repository,
|
||||
only_path: false,
|
||||
host: ENV['WOPI_USER_HOST'])
|
||||
@close_url = repository_url(@repository, only_path: false, host: ENV['WOPI_USER_HOST'])
|
||||
|
||||
@breadcrumb_brand_name = @team.name
|
||||
@breadcrumb_brand_url = @close_url
|
||||
|
@ -338,7 +323,7 @@ class WopiController < ActionController::Base
|
|||
@breadcrumb_folder_url = @close_url
|
||||
end
|
||||
|
||||
render body: nil, status: 404 and return unless @can_read
|
||||
return render body: nil, status: :not_found unless @can_read
|
||||
end
|
||||
|
||||
def verify_proof!
|
||||
|
@ -349,24 +334,22 @@ class WopiController < ActionController::Base
|
|||
url = request.original_url.upcase.encode('utf-8')
|
||||
|
||||
if convert_to_unix_timestamp(timestamp) + 20.minutes >= Time.now
|
||||
if current_wopi_discovery.verify_proof(token, timestamp, signed_proof,
|
||||
signed_proof_old, url)
|
||||
if current_wopi_discovery.verify_proof(token, timestamp, signed_proof, signed_proof_old, url)
|
||||
logger.warn 'WOPI: proof verification: successful'
|
||||
else
|
||||
logger.warn 'WOPI: proof verification: not verified'
|
||||
render body: nil, status: 500 and return
|
||||
render body: nil, status: :internal_server_error
|
||||
end
|
||||
else
|
||||
logger.warn 'WOPI: proof verification: timestamp too old; ' +
|
||||
timestamp.to_s
|
||||
render body: nil, status: 500 and return
|
||||
render body: nil, status: :internal_server_error
|
||||
end
|
||||
rescue => e
|
||||
rescue StandardError => e
|
||||
logger.warn 'WOPI: proof verification: failed; ' + e.message
|
||||
render body: nil, status: 500 and return
|
||||
render body: nil, status: :internal_server_error
|
||||
end
|
||||
|
||||
# Overwrriten in electronic signature for locked inventory items
|
||||
def can_edit_wopi_file_in_repository_rows?
|
||||
can_manage_repository_rows?(@repository)
|
||||
end
|
||||
|
|
|
@ -93,13 +93,21 @@ class LoadFromRepositoryProtocolsDatatable < CustomDatatable
|
|||
.joins('LEFT OUTER JOIN users ON users.id = protocols.added_by_id')
|
||||
.where('protocols.protocol_type = ?',
|
||||
Protocol.protocol_types[:in_repository_public])
|
||||
else
|
||||
elsif @type == :private
|
||||
records =
|
||||
records
|
||||
.joins('LEFT OUTER JOIN users ON users.id = protocols.added_by_id')
|
||||
.where('protocols.protocol_type = ?',
|
||||
Protocol.protocol_types[:in_repository_private])
|
||||
.where(added_by: @user)
|
||||
else
|
||||
records =
|
||||
records
|
||||
.joins('LEFT OUTER JOIN users ON users.id = protocols.added_by_id')
|
||||
.where('(protocols.protocol_type = ? OR (protocols.protocol_type = ? AND added_by_id = ?))',
|
||||
Protocol.protocol_types[:in_repository_public],
|
||||
Protocol.protocol_types[:in_repository_private],
|
||||
@user.id)
|
||||
end
|
||||
|
||||
records.group('"protocols"."id"')
|
||||
|
|
|
@ -198,12 +198,8 @@ class ProtocolsDatatable < CustomDatatable
|
|||
end
|
||||
|
||||
def modules_html(record)
|
||||
"<a href='#' data-action='load-linked-children' class='help_tooltips' " \
|
||||
"data-tooltiplink='" +
|
||||
I18n.t('tooltips.link.protocol.num_linked') +
|
||||
"' data-tooltipcontent='" +
|
||||
I18n.t('tooltips.text.protocol.num_linked') +
|
||||
"' data-url='#{linked_children_protocol_path(record)}'>" \
|
||||
"<a href='#' data-action='load-linked-children'" \
|
||||
"data-url='#{linked_children_protocol_path(record)}'>" \
|
||||
"#{record.nr_of_linked_children}" \
|
||||
"</a>"
|
||||
end
|
||||
|
|
|
@ -6,7 +6,8 @@ module ApplicationHelper
|
|||
include InputSanitizeHelper
|
||||
|
||||
def module_page?
|
||||
controller_name == 'my_modules'
|
||||
controller_name == 'my_modules' ||
|
||||
controller_name == 'my_module_repositories'
|
||||
end
|
||||
|
||||
def experiment_page?
|
||||
|
|
|
@ -2,6 +2,7 @@ module BootstrapFormHelper
|
|||
|
||||
# Extend Bootstrap form builder
|
||||
class BootstrapForm::FormBuilder
|
||||
include BootstrapFormHelper
|
||||
|
||||
# Returns Bootstrap date-time picker of the "datetime" type tailored for accessing a specified datetime attribute (identified by +name+) on an object
|
||||
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
|
||||
|
@ -22,18 +23,9 @@ module BootstrapFormHelper
|
|||
def datetime_picker(name, options = {})
|
||||
id = "#{@object_name}_#{name.to_s}"
|
||||
input_name = "#{@object_name}[#{name.to_s}]"
|
||||
date_format = I18n.backend.date_format.dup
|
||||
value = options[:value] ? options[:value].strftime(date_format + ' %H:%M') : ''
|
||||
value = options[:value] ? options[:value].strftime("#{I18n.backend.date_format} %H:%M") : ''
|
||||
js_locale = I18n.locale.to_s
|
||||
js_format = date_format
|
||||
js_format.gsub!(/%-d/, 'D')
|
||||
js_format.gsub!(/%d/, 'DD')
|
||||
js_format.gsub!(/%-m/, 'M')
|
||||
js_format.gsub!(/%m/, 'MM')
|
||||
js_format.gsub!(/%b/, 'MMM')
|
||||
js_format.gsub!(/%B/, 'MMMM')
|
||||
js_format.gsub!('%Y', 'YYYY')
|
||||
js_format << ' HH:mm' if options[:time] == true
|
||||
js_format = options[:time] ? datetime_picker_format_full : datetime_picker_format_date_only
|
||||
|
||||
label = options[:label] || name.to_s.humanize
|
||||
|
||||
|
@ -266,4 +258,24 @@ module BootstrapFormHelper
|
|||
text_area(name, options)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns date only format string for Bootstrap DateTimePicker
|
||||
def datetime_picker_format_date_only
|
||||
js_format = I18n.backend.date_format.dup
|
||||
js_format.gsub!(/%-d/, 'D')
|
||||
js_format.gsub!(/%d/, 'DD')
|
||||
js_format.gsub!(/%-m/, 'M')
|
||||
js_format.gsub!(/%m/, 'MM')
|
||||
js_format.gsub!(/%b/, 'MMM')
|
||||
js_format.gsub!(/%B/, 'MMMM')
|
||||
js_format.gsub!('%Y', 'YYYY')
|
||||
js_format
|
||||
end
|
||||
|
||||
# Returns date and time format string for Bootstrap DateTimePicker
|
||||
def datetime_picker_format_full
|
||||
js_format = datetime_picker_format_date_only
|
||||
js_format << ' HH:mm'
|
||||
js_format
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,17 +42,17 @@ module GlobalActivitiesHelper
|
|||
# Not link for now
|
||||
return current_value
|
||||
when Team
|
||||
path = projects_path
|
||||
path = projects_path(team: obj.id)
|
||||
when Repository
|
||||
path = repository_path(obj)
|
||||
path = repository_path(obj, team: obj.team.id)
|
||||
when RepositoryRow
|
||||
return current_value unless obj.repository
|
||||
|
||||
path = repository_path(obj.repository)
|
||||
path = repository_path(obj.repository, team: obj.repository.team.id)
|
||||
when RepositoryColumn
|
||||
return current_value unless obj.repository
|
||||
|
||||
path = repository_path(obj.repository)
|
||||
path = repository_path(obj.repository, team: obj.repository.team.id)
|
||||
when Project
|
||||
path = obj.archived? ? projects_path : project_path(obj)
|
||||
when Experiment
|
||||
|
@ -64,20 +64,16 @@ module GlobalActivitiesHelper
|
|||
|
||||
path = if obj.archived?
|
||||
module_archive_experiment_path(obj.experiment)
|
||||
else
|
||||
path = if %w(assign_repository_record unassign_repository_record).include? activity.type_of
|
||||
repository_my_module_path(obj, activity.values['message_items']['repository']['id'])
|
||||
else
|
||||
protocols_my_module_path(obj)
|
||||
end
|
||||
end
|
||||
when Protocol
|
||||
if obj.in_repository_public?
|
||||
path = protocols_path(type: :public)
|
||||
path = protocols_path(type: :public, team: obj.team.id)
|
||||
elsif obj.in_repository_private?
|
||||
path = protocols_path(type: :private)
|
||||
path = protocols_path(type: :private, team: obj.team.id)
|
||||
elsif obj.in_repository_archived?
|
||||
path = protocols_path(type: :archive)
|
||||
path = protocols_path(type: :archive, team: obj.team.id)
|
||||
elsif obj.my_module.navigable?
|
||||
path = protocols_my_module_path(obj.my_module)
|
||||
else
|
||||
|
@ -90,7 +86,7 @@ module GlobalActivitiesHelper
|
|||
when Step
|
||||
return current_value
|
||||
when Report
|
||||
path = reports_path
|
||||
path = reports_path(team: obj.team.id)
|
||||
else
|
||||
return current_value
|
||||
end
|
||||
|
|
|
@ -55,4 +55,49 @@ module MyModulesHelper
|
|||
def is_results_page?
|
||||
action_name == 'results'
|
||||
end
|
||||
|
||||
def grouped_by_prj_exp(my_modules)
|
||||
ungrouped_tasks = my_modules.joins(experiment: :project)
|
||||
.select('experiments.name as experiment_name,
|
||||
experiments.archived as experiment_archived,
|
||||
projects.name as project_name,
|
||||
projects.archived as project_archived,
|
||||
my_modules.*')
|
||||
ungrouped_tasks.group_by { |i| [i[:project_name], i[:experiment_name]] }.map do |group, tasks|
|
||||
{
|
||||
project_name: group[0],
|
||||
project_archived: tasks[0]&.project_archived,
|
||||
experiment_name: group[1],
|
||||
experiment_archived: tasks[0]&.experiment_archived,
|
||||
tasks: tasks
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def assigned_repository_full_view_table_path(my_module, repository)
|
||||
if repository.is_a?(RepositorySnapshot)
|
||||
return full_view_table_my_module_repository_snapshot_path(my_module, repository)
|
||||
end
|
||||
|
||||
full_view_table_my_module_repository_path(my_module, repository)
|
||||
end
|
||||
|
||||
def assigned_repository_simple_view_index_path(my_module, repository)
|
||||
return index_dt_my_module_repository_snapshot_path(my_module, repository) if repository.is_a?(RepositorySnapshot)
|
||||
|
||||
index_dt_my_module_repository_path(my_module, repository)
|
||||
end
|
||||
|
||||
def assigned_repository_simple_view_footer_label(repository)
|
||||
if repository.is_a?(RepositorySnapshot)
|
||||
return t('my_modules.repository.snapshots.simple_view.snapshot_bottom_label',
|
||||
date_time: l(repository.created_at, format: :full))
|
||||
end
|
||||
|
||||
t('my_modules.repository.snapshots.simple_view.live_bottom_label')
|
||||
end
|
||||
|
||||
def assigned_repository_simple_view_name_column_id(repository)
|
||||
repository.is_a?(RepositorySnapshot) ? 2 : 3
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,22 +1,5 @@
|
|||
module ProtocolStatusHelper
|
||||
|
||||
def protocol_status_href(protocol)
|
||||
parent = protocol.parent
|
||||
res = ''
|
||||
res << '<a href="#" data-toggle="popover" data-html="true" class="preview-protocol"'
|
||||
res << 'data-trigger="focus" data-placement="bottom" title="'
|
||||
res << protocol_status_popover_title(parent) +
|
||||
'" data-content="' + protocol_status_popover_content(parent) +
|
||||
'">' + protocol_name(parent).truncate(Constants::NAME_TRUNCATION_LENGTH) + '</a>'
|
||||
res.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def protocol_private_for_current_user?(protocol)
|
||||
protocol.in_repository_private? && protocol.added_by != current_user
|
||||
end
|
||||
|
||||
def protocol_name(protocol)
|
||||
if protocol_private_for_current_user?(protocol)
|
||||
I18n.t('my_modules.protocols.protocol_status_bar.private_parent')
|
||||
|
@ -25,43 +8,9 @@ module ProtocolStatusHelper
|
|||
end
|
||||
end
|
||||
|
||||
def protocol_status_popover_title(protocol)
|
||||
res = ""
|
||||
if protocol.in_repository_public?
|
||||
res << "<span class='fas fa-eye' title='" + I18n.t("my_modules.protocols.protocol_status_bar.public_desc") + "'></span>"
|
||||
elsif protocol.in_repository_private?
|
||||
res << "<span class='fas fa-eye-slash' title='" + I18n.t("my_modules.protocols.protocol_status_bar.private_desc") + "'></span>"
|
||||
end
|
||||
res << " "
|
||||
if can_read_protocol_in_repository?(protocol)
|
||||
res << "<a href='" + edit_protocol_path(protocol) + "' target='_blank'>" + protocol_name(protocol) + "</a>"
|
||||
else
|
||||
res << "<span style='font-weight: bold;'>" + protocol_name(protocol) + "</span>"
|
||||
end
|
||||
res << " - "
|
||||
res << "<span style='font-style: italic;'>" + I18n.t("my_modules.protocols.protocol_status_bar.added_by") + " "
|
||||
res << "<a href='#' data-toggle='tooltip' data-placement='right' title='" +
|
||||
I18n.t('my_modules.protocols.protocol_status_bar.added_by_tooltip',
|
||||
ts: I18n.l(protocol.created_at, format: :full)) + "'>" +
|
||||
escape_input(protocol.added_by.full_name) + '</a></span>'
|
||||
res
|
||||
end
|
||||
private
|
||||
|
||||
def protocol_status_popover_content(protocol)
|
||||
if protocol_private_for_current_user?(protocol)
|
||||
res = '<p><em>' + I18n.t('my_modules.protocols.protocol_status_bar.private_protocol_desc') + '</em></p>'
|
||||
else
|
||||
res = '<p><b>' + I18n.t('my_modules.protocols.protocol_status_bar.keywords') + ':</b> '
|
||||
if protocol.protocol_keywords.size.positive?
|
||||
protocol.protocol_keywords.each do |kw|
|
||||
res << kw.name + ', '
|
||||
end
|
||||
res = res[0..-3]
|
||||
else
|
||||
res << '<em>' + I18n.t('my_modules.protocols.protocol_status_bar.no_keywords') + '</em>'
|
||||
end
|
||||
res << '</p>'
|
||||
end
|
||||
escape_input(res)
|
||||
def protocol_private_for_current_user?(protocol)
|
||||
protocol.in_repository_private? && protocol.added_by != current_user
|
||||
end
|
||||
end
|
||||
|
|
|
@ -122,6 +122,12 @@ module ReportsHelper
|
|||
)
|
||||
end
|
||||
|
||||
def assign_repository_or_snapshot(my_module, element_id, snapshot, repository)
|
||||
original_repository = Repository.find_by(id: element_id) if element_id
|
||||
repository ||= snapshot
|
||||
repository || my_module.active_snapshot_or_live(original_repository) || original_repository
|
||||
end
|
||||
|
||||
def step_status_label(step)
|
||||
if step.completed
|
||||
style = 'success'
|
||||
|
|
|
@ -3,13 +3,8 @@
|
|||
module RepositoryDatatableHelper
|
||||
include InputSanitizeHelper
|
||||
|
||||
def prepare_row_columns(repository_rows,
|
||||
repository,
|
||||
columns_mappings,
|
||||
team)
|
||||
parsed_records = []
|
||||
|
||||
repository_rows.each do |record|
|
||||
def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {})
|
||||
repository_rows.map do |record|
|
||||
row = {
|
||||
'DT_RowId': record.id,
|
||||
'1': assigned_row(record),
|
||||
|
@ -17,49 +12,64 @@ module RepositoryDatatableHelper
|
|||
'3': escape_input(record.name),
|
||||
'4': I18n.l(record.created_at, format: :full),
|
||||
'5': escape_input(record.created_by.full_name),
|
||||
'recordEditUrl': Rails.application.routes.url_helpers
|
||||
.edit_repository_repository_row_path(
|
||||
repository,
|
||||
record.id
|
||||
),
|
||||
'recordUpdateUrl': Rails.application.routes.url_helpers
|
||||
.repository_repository_row_path(
|
||||
repository,
|
||||
record.id
|
||||
),
|
||||
'recordInfoUrl': Rails.application.routes.url_helpers
|
||||
.repository_row_path(record.id),
|
||||
'recordEditable': record.editable?
|
||||
'recordInfoUrl': Rails.application.routes.url_helpers.repository_repository_row_path(repository, record)
|
||||
}
|
||||
|
||||
unless options[:view_mode]
|
||||
row['recordUpdateUrl'] =
|
||||
Rails.application.routes.url_helpers.repository_repository_row_path(repository, record)
|
||||
row['recordEditable'] = record.editable?
|
||||
end
|
||||
|
||||
row['0'] = record[:row_assigned] if options[:my_module]
|
||||
|
||||
# Add custom columns
|
||||
record.repository_cells.each do |cell|
|
||||
row[columns_mappings[cell.repository_column.id]] =
|
||||
display_cell_value(cell, team)
|
||||
end
|
||||
parsed_records << row
|
||||
|
||||
row
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_simple_view_row_columns(repository_rows)
|
||||
repository_rows.map do |record|
|
||||
{
|
||||
'DT_RowId': record.id,
|
||||
'0': escape_input(record.name),
|
||||
'recordInfoUrl': Rails.application.routes.url_helpers.repository_repository_row_path(record.repository, record)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_snapshot_row_columns(repository_rows, columns_mappings, team)
|
||||
repository_rows.map do |record|
|
||||
row = {
|
||||
'DT_RowId': record.id,
|
||||
'1': record.parent_id,
|
||||
'2': escape_input(record.name),
|
||||
'3': I18n.l(record.created_at, format: :full),
|
||||
'4': escape_input(record.created_by.full_name),
|
||||
'recordInfoUrl': Rails.application.routes.url_helpers.repository_repository_row_path(record.repository, record)
|
||||
}
|
||||
|
||||
# Add custom columns
|
||||
record.repository_cells.each do |cell|
|
||||
row[columns_mappings[cell.repository_column.id]] = display_cell_value(cell, team)
|
||||
end
|
||||
|
||||
row
|
||||
end
|
||||
parsed_records
|
||||
end
|
||||
|
||||
def assigned_row(record)
|
||||
if @my_module
|
||||
if record.assigned_my_modules_count.positive?
|
||||
"<span class='circle-icon'> </span>"
|
||||
else
|
||||
"<span class='circle-icon disabled'> </span>"
|
||||
end
|
||||
elsif record.assigned_my_modules_count.positive?
|
||||
tooltip = t('repositories.table.assigned_tooltip',
|
||||
{
|
||||
tasks: record.assigned_my_modules_count,
|
||||
experiments: record.assigned_experiments_count,
|
||||
projects: record.assigned_projects_count)
|
||||
|
||||
"<div class='assign-counter-container' title='#{tooltip}'>"\
|
||||
"<span class='assign-counter has-assigned'>#{record.assigned_my_modules_count}</span></div>"
|
||||
else
|
||||
"<div class='assign-counter-container'><span class='assign-counter'>0</span></div>"
|
||||
end
|
||||
projects: record.assigned_projects_count,
|
||||
task_list_url: assigned_task_list_repository_repository_row_path(record.repository, record)
|
||||
}
|
||||
end
|
||||
|
||||
def can_perform_repository_actions(repository)
|
||||
|
@ -77,6 +87,14 @@ module RepositoryDatatableHelper
|
|||
Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].to_json
|
||||
end
|
||||
|
||||
def default_snapshot_table_order_as_js_array
|
||||
Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['order'].to_json
|
||||
end
|
||||
|
||||
def default_snapshot_table_columns
|
||||
Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['columns'].to_json
|
||||
end
|
||||
|
||||
def display_cell_value(cell, team)
|
||||
value_name = cell.repository_column.data_type.demodulize.underscore
|
||||
serializer_class = "RepositoryDatatable::#{cell.repository_column.data_type}Serializer".constantize
|
||||
|
|
|
@ -30,11 +30,6 @@ module SidebarHelper
|
|||
samples_my_module_url(my_module)
|
||||
elsif action_name.in?(%w(archive module_archive experiment_archive))
|
||||
archive_my_module_url(my_module)
|
||||
elsif action_name == 'repository' && @repository
|
||||
repository_my_module_url(
|
||||
id: my_module.id,
|
||||
repository_id: @repository.id
|
||||
)
|
||||
else
|
||||
protocols_my_module_url(my_module)
|
||||
end
|
||||
|
|
|
@ -20,4 +20,8 @@ module TeamsHelper
|
|||
def team_created_by(team)
|
||||
User.find_by_id(team.created_by_id)
|
||||
end
|
||||
|
||||
def switch_team_with_param
|
||||
current_team_switch(Team.find_by(id: params[:team])) if params[:team]
|
||||
end
|
||||
end
|
||||
|
|
9
app/jobs/application_job.rb
Normal file
9
app/jobs/application_job.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationJob < ActiveJob::Base
|
||||
# Automatically retry jobs that encountered a deadlock
|
||||
retry_on ActiveRecord::Deadlocked
|
||||
|
||||
# Most jobs are safe to ignore if the underlying records are no longer available
|
||||
discard_on ActiveJob::DeserializationError
|
||||
end
|
11
app/jobs/repository_snapshot_provisioning_job.rb
Normal file
11
app/jobs/repository_snapshot_provisioning_job.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositorySnapshotProvisioningJob < ApplicationJob
|
||||
queue_as :high_priority
|
||||
|
||||
def perform(repository_snapshot)
|
||||
service = Repositories::SnapshotProvisioningService.call(repository_snapshot: repository_snapshot)
|
||||
|
||||
repository_snapshot.failed! unless service.succeed?
|
||||
end
|
||||
end
|
|
@ -48,7 +48,7 @@ class Activity < ApplicationRecord
|
|||
}
|
||||
|
||||
scope :repositories_joins, lambda {
|
||||
joins("LEFT JOIN repositories ON subject_type = 'Repository' AND subject_id = repositories.id")
|
||||
joins("LEFT JOIN repositories ON subject_type = 'RepositoryBase' AND subject_id = repositories.id")
|
||||
}
|
||||
|
||||
scope :reports_joins, lambda {
|
||||
|
|
|
@ -9,6 +9,7 @@ module TeamBySubjectModel
|
|||
valid_subjects = Extends::ACTIVITY_SUBJECT_CHILDREN
|
||||
# Check all activity subject
|
||||
valid_subjects.each do |subject, _children|
|
||||
subject = subject.to_s.camelize.to_sym
|
||||
next unless subjects[subject]
|
||||
|
||||
parent_array = [subject.to_s.underscore]
|
||||
|
|
|
@ -40,21 +40,12 @@ class MyModule < ApplicationRecord
|
|||
has_many :my_module_tags, inverse_of: :my_module, dependent: :destroy
|
||||
has_many :tags, through: :my_module_tags
|
||||
has_many :task_comments, foreign_key: :associated_id, dependent: :destroy
|
||||
has_many :inputs,
|
||||
class_name: 'Connection',
|
||||
foreign_key: 'input_id',
|
||||
inverse_of: :to,
|
||||
dependent: :destroy
|
||||
has_many :outputs,
|
||||
class_name: 'Connection',
|
||||
foreign_key: 'output_id',
|
||||
inverse_of: :from,
|
||||
dependent: :destroy
|
||||
has_many :my_modules, through: :outputs, source: :to
|
||||
has_many :my_module_antecessors,
|
||||
through: :inputs,
|
||||
source: :from,
|
||||
class_name: 'MyModule'
|
||||
|
||||
has_many :inputs, class_name: 'Connection', foreign_key: 'input_id', inverse_of: :to, dependent: :destroy
|
||||
has_many :outputs, class_name: 'Connection', foreign_key: 'output_id', inverse_of: :from, dependent: :destroy
|
||||
has_many :my_modules, through: :outputs, source: :to, class_name: 'MyModule'
|
||||
has_many :my_module_antecessors, through: :inputs, source: :from, class_name: 'MyModule'
|
||||
|
||||
has_many :sample_my_modules,
|
||||
inverse_of: :my_module,
|
||||
dependent: :destroy
|
||||
|
@ -62,6 +53,9 @@ class MyModule < ApplicationRecord
|
|||
has_many :my_module_repository_rows,
|
||||
inverse_of: :my_module, dependent: :destroy
|
||||
has_many :repository_rows, through: :my_module_repository_rows
|
||||
has_many :repository_snapshots,
|
||||
dependent: :destroy,
|
||||
inverse_of: :my_module
|
||||
has_many :user_my_modules, inverse_of: :my_module, dependent: :destroy
|
||||
has_many :users, through: :user_my_modules
|
||||
has_many :report_elements, inverse_of: :my_module, dependent: :destroy
|
||||
|
@ -205,6 +199,61 @@ class MyModule < ApplicationRecord
|
|||
.count
|
||||
end
|
||||
|
||||
def assigned_repositories
|
||||
team = experiment.project.team
|
||||
team.repositories
|
||||
.joins(repository_rows: :my_module_repository_rows)
|
||||
.where(my_module_repository_rows: { my_module_id: id })
|
||||
.group(:id)
|
||||
end
|
||||
|
||||
def live_and_snapshot_repositories_list
|
||||
snapshots = repository_snapshots.left_outer_joins(:original_repository)
|
||||
|
||||
selected_snapshots = snapshots.where(selected: true)
|
||||
.or(snapshots.where(original_repositories_repositories: { id: nil }))
|
||||
.or(snapshots.where.not(parent_id: assigned_repositories.select(:id)))
|
||||
.select('DISTINCT ON ("repositories"."parent_id") "repositories".*')
|
||||
.select('COUNT(repository_rows.id) AS assigned_rows_count')
|
||||
.joins(:repository_rows)
|
||||
.group(:parent_id, :id)
|
||||
.order(:parent_id, updated_at: :desc)
|
||||
|
||||
live_repositories = assigned_repositories
|
||||
.select('repositories.*, COUNT(repository_rows.id) AS assigned_rows_count')
|
||||
.where.not(id: repository_snapshots.where(selected: true).select(:parent_id))
|
||||
|
||||
(live_repositories + selected_snapshots).sort_by { |r| r.name.downcase }
|
||||
end
|
||||
|
||||
def active_snapshot_or_live(rep_or_snap, exclude_snpashot_ids: [])
|
||||
return unless rep_or_snap
|
||||
|
||||
parent_id = rep_or_snap.is_a?(Repository) ? rep_or_snap.id : rep_or_snap.parent_id
|
||||
|
||||
selected_snapshot_for_repo(parent_id, exclude_snpashot_ids: exclude_snpashot_ids) ||
|
||||
assigned_repositories&.where(id: parent_id)&.first ||
|
||||
repository_snapshots
|
||||
.where(parent_id: parent_id)
|
||||
.where.not(id: exclude_snpashot_ids)
|
||||
.order(updated_at: :desc).first
|
||||
end
|
||||
|
||||
def update_report_repository_references(rep_or_snap)
|
||||
ids = if rep_or_snap.is_a?(Repository)
|
||||
RepositorySnapshot.where(parent_id: rep_or_snap.id).pluck(:id)
|
||||
else
|
||||
Repository.where(id: rep_or_snap.parent_id).pluck(:id) +
|
||||
RepositorySnapshot.where(parent_id: rep_or_snap.parent_id).pluck(:id)
|
||||
end
|
||||
|
||||
report_elements.where(repository_id: ids).update(repository_id: rep_or_snap.id)
|
||||
end
|
||||
|
||||
def selected_snapshot_for_repo(repository_id, exclude_snpashot_ids: [])
|
||||
repository_snapshots.where(parent_id: repository_id).where.not(id: exclude_snpashot_ids).where(selected: true).first
|
||||
end
|
||||
|
||||
def unassigned_users
|
||||
User.find_by_sql(
|
||||
"SELECT DISTINCT users.id, users.full_name FROM users " +
|
||||
|
@ -377,14 +426,12 @@ class MyModule < ApplicationRecord
|
|||
|
||||
# Generate the repository rows belonging to this module
|
||||
# in JSON form, suitable for display in handsontable.js
|
||||
def repository_json_hot(repository_id, order)
|
||||
def repository_json_hot(repository, order)
|
||||
data = []
|
||||
repository_rows
|
||||
.includes(:created_by)
|
||||
.where(repository_id: repository_id)
|
||||
.order(created_at: order).find_each do |row|
|
||||
rows = repository.assigned_rows(self).includes(:created_by).order(created_at: order)
|
||||
rows.find_each do |row|
|
||||
row_json = []
|
||||
row_json << row.id
|
||||
row_json << (row.repository.is_a?(RepositorySnapshot) ? row.parent_id : row.id)
|
||||
row_json << row.name
|
||||
row_json << I18n.l(row.created_at, format: :full)
|
||||
row_json << row.created_by.full_name
|
||||
|
@ -401,29 +448,7 @@ class MyModule < ApplicationRecord
|
|||
{ data: data, headers: headers }
|
||||
end
|
||||
|
||||
def repository_json(repository_id, order, user)
|
||||
headers = [
|
||||
I18n.t('repositories.table.id'),
|
||||
I18n.t('repositories.table.row_name'),
|
||||
I18n.t('repositories.table.added_on'),
|
||||
I18n.t('repositories.table.added_by')
|
||||
]
|
||||
repository = Repository.find_by_id(repository_id)
|
||||
return false unless repository
|
||||
|
||||
repository.repository_columns.order(:id).each do |column|
|
||||
headers.push(column.name)
|
||||
end
|
||||
|
||||
params = { assigned: 'assigned', search: {}, order: { values: { column: '1', dir: order } } }
|
||||
records = RepositoryDatatableService.new(repository,
|
||||
params,
|
||||
user,
|
||||
self)
|
||||
{ headers: headers, data: records }
|
||||
end
|
||||
|
||||
def repository_docx_json(repository_id)
|
||||
def repository_docx_json(repository)
|
||||
headers = [
|
||||
I18n.t('repositories.table.id'),
|
||||
I18n.t('repositories.table.row_name'),
|
||||
|
@ -431,7 +456,6 @@ class MyModule < ApplicationRecord
|
|||
I18n.t('repositories.table.added_by')
|
||||
]
|
||||
custom_columns = []
|
||||
repository = Repository.find_by(id: repository_id)
|
||||
return false unless repository
|
||||
|
||||
repository.repository_columns.order(:id).each do |column|
|
||||
|
@ -439,7 +463,7 @@ class MyModule < ApplicationRecord
|
|||
custom_columns.push(column.id)
|
||||
end
|
||||
|
||||
records = repository_rows.where(repository_id: repository_id).select(:id, :name, :created_at, :created_by_id)
|
||||
records = repository.assigned_rows(self).select(:id, :name, :created_at, :created_by_id)
|
||||
{ headers: headers, rows: records, custom_columns: custom_columns }
|
||||
end
|
||||
|
||||
|
@ -524,21 +548,6 @@ class MyModule < ApplicationRecord
|
|||
self.completed_on = nil
|
||||
end
|
||||
|
||||
def self.my_modules_list_partial
|
||||
ungrouped_tasks = joins(experiment: :project)
|
||||
.select('experiments.name as experiment_name,
|
||||
projects.name as project_name,
|
||||
my_modules.name as task_name,
|
||||
my_modules.id')
|
||||
ungrouped_tasks.group_by { |i| [i[:project_name], i[:experiment_name]] }.map do |group, tasks|
|
||||
{
|
||||
project_name: group[0],
|
||||
experiment_name: group[1],
|
||||
tasks: tasks.map { |task| { id: task.id, task_name: task.task_name } }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def assign_user(user, assigned_by = nil)
|
||||
user_my_modules.create(
|
||||
assigned_by: assigned_by || user,
|
||||
|
|
|
@ -9,6 +9,5 @@ class MyModuleRepositoryRow < ApplicationRecord
|
|||
touch: true,
|
||||
inverse_of: :my_module_repository_rows
|
||||
|
||||
validates :repository_row, :my_module, presence: true
|
||||
validates :repository_row, uniqueness: { scope: :my_module }
|
||||
end
|
||||
|
|
|
@ -17,12 +17,6 @@ class Protocol < ApplicationRecord
|
|||
in_repository_archived: 4
|
||||
}
|
||||
|
||||
scope :recent_protocols, lambda { |user, team, amount|
|
||||
where(team: team, protocol_type: :in_repository_public)
|
||||
.or(where(team: team, protocol_type: :in_repository_private, added_by: user))
|
||||
.order(updated_at: :desc).limit(amount)
|
||||
}
|
||||
|
||||
auto_strip_attributes :name, :description, nullify: false
|
||||
# Name is required when its actually specified (i.e. :in_repository? is true)
|
||||
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
|
|
|
@ -33,7 +33,8 @@ class ReportElement < ApplicationRecord
|
|||
belongs_to :checklist, inverse_of: :report_elements, optional: true
|
||||
belongs_to :asset, inverse_of: :report_elements, optional: true
|
||||
belongs_to :table, inverse_of: :report_elements, optional: true
|
||||
belongs_to :repository, inverse_of: :report_elements, optional: true
|
||||
belongs_to :repository, inverse_of: :report_elements, optional: true,
|
||||
foreign_key: :repository_id, class_name: 'RepositoryBase'
|
||||
|
||||
def has_children?
|
||||
children.length > 0
|
||||
|
|
|
@ -1,36 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Repository < ApplicationRecord
|
||||
class Repository < RepositoryBase
|
||||
include SearchableModel
|
||||
include SearchableByNameModel
|
||||
include RepositoryImportParser
|
||||
include Discard::Model
|
||||
|
||||
enum permission_level: Extends::SHARED_INVENTORIES_PERMISSION_LEVELS
|
||||
|
||||
attribute :discarded_by_id, :integer
|
||||
|
||||
belongs_to :team
|
||||
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
|
||||
has_many :repository_columns, dependent: :destroy
|
||||
has_many :repository_rows, dependent: :destroy
|
||||
has_many :repository_table_states,
|
||||
inverse_of: :repository, dependent: :destroy
|
||||
has_many :report_elements, inverse_of: :repository, dependent: :destroy
|
||||
has_many :repository_list_items, inverse_of: :repository, dependent: :destroy
|
||||
has_many :repository_checklist_items, inverse_of: :repository, dependent: :destroy
|
||||
has_many :team_repositories, inverse_of: :repository, dependent: :destroy
|
||||
has_many :teams_shared_with, through: :team_repositories, source: :team
|
||||
has_many :repository_snapshots,
|
||||
class_name: 'RepositorySnapshot',
|
||||
foreign_key: :parent_id,
|
||||
inverse_of: :original_repository
|
||||
|
||||
before_save :sync_name_with_snapshots, if: :name_changed?
|
||||
|
||||
auto_strip_attributes :name, nullify: false
|
||||
validates :name,
|
||||
presence: true,
|
||||
uniqueness: { scope: :team_id, case_sensitive: false },
|
||||
length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
validates :team, presence: true
|
||||
validates :created_by, presence: true
|
||||
|
||||
default_scope -> { kept }
|
||||
scope :accessible_by_teams, lambda { |teams|
|
||||
left_outer_joins(:team_repositories)
|
||||
.where('repositories.team_id IN (?) '\
|
||||
|
@ -97,6 +87,10 @@ class Repository < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def default_columns_count
|
||||
Constants::REPOSITORY_TABLE_DEFAULT_STATE['length']
|
||||
end
|
||||
|
||||
def i_shared?(team)
|
||||
shared_with_anybody? && self.team == team
|
||||
end
|
||||
|
@ -139,10 +133,6 @@ class Repository < ApplicationRecord
|
|||
where('repositories.name ILIKE ?', "%#{query}%")
|
||||
end
|
||||
|
||||
def available_columns_ids
|
||||
repository_columns.pluck(:id)
|
||||
end
|
||||
|
||||
def importable_repository_fields
|
||||
fields = {}
|
||||
# First and foremost add record name
|
||||
|
@ -203,11 +193,26 @@ class Repository < ApplicationRecord
|
|||
importer.run
|
||||
end
|
||||
|
||||
def destroy_discarded(discarded_by_id = nil)
|
||||
self.discarded_by_id = discarded_by_id
|
||||
destroy
|
||||
def provision_snapshot(my_module, created_by = nil)
|
||||
created_by ||= self.created_by
|
||||
repository_snapshot = dup.becomes(RepositorySnapshot)
|
||||
repository_snapshot.assign_attributes(type: RepositorySnapshot.name,
|
||||
original_repository: self,
|
||||
my_module: my_module,
|
||||
created_by: created_by)
|
||||
repository_snapshot.provisioning!
|
||||
repository_snapshot.reload
|
||||
RepositorySnapshotProvisioningJob.perform_later(repository_snapshot)
|
||||
repository_snapshot
|
||||
end
|
||||
|
||||
def assigned_rows(my_module)
|
||||
repository_rows.joins(:my_module_repository_rows).where(my_module_repository_rows: { my_module_id: my_module.id })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sync_name_with_snapshots
|
||||
repository_snapshots.update(name: name)
|
||||
end
|
||||
handle_asynchronously :destroy_discarded,
|
||||
queue: :clear_discarded_repository,
|
||||
priority: 20
|
||||
end
|
||||
|
|
|
@ -45,6 +45,24 @@ class RepositoryAssetValue < ApplicationRecord
|
|||
asset.save! && save!
|
||||
end
|
||||
|
||||
def snapshot!(cell_snapshot)
|
||||
value_snapshot = dup
|
||||
asset_snapshot = asset.dup
|
||||
|
||||
asset_snapshot.save!
|
||||
|
||||
# ActiveStorage::Blob is immutable, so we can just attach it to the new snapshot
|
||||
asset_snapshot.file.attach(asset.blob)
|
||||
|
||||
value_snapshot.assign_attributes(
|
||||
repository_cell: cell_snapshot,
|
||||
asset: asset_snapshot,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
value_snapshot.save!
|
||||
end
|
||||
|
||||
def self.new_with_payload(payload, attributes)
|
||||
value = new(attributes)
|
||||
team = value.repository_cell.repository_column.repository.team
|
||||
|
|
37
app/models/repository_base.rb
Normal file
37
app/models/repository_base.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositoryBase < ApplicationRecord
|
||||
include Discard::Model
|
||||
|
||||
self.table_name = 'repositories'
|
||||
|
||||
attribute :discarded_by_id, :integer
|
||||
|
||||
belongs_to :team
|
||||
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
|
||||
has_many :repository_columns, foreign_key: :repository_id, inverse_of: :repository, dependent: :destroy
|
||||
has_many :repository_rows, foreign_key: :repository_id, inverse_of: :repository, dependent: :destroy
|
||||
has_many :repository_table_states, foreign_key: :repository_id, inverse_of: :repository, dependent: :destroy
|
||||
has_many :report_elements, inverse_of: :repository, dependent: :destroy, foreign_key: :repository_id
|
||||
|
||||
auto_strip_attributes :name, nullify: false
|
||||
validates :team, presence: true
|
||||
validates :created_by, presence: true
|
||||
|
||||
# Not discarded
|
||||
default_scope -> { kept }
|
||||
|
||||
def cell_preload_includes
|
||||
cell_includes = []
|
||||
repository_columns.pluck(:data_type).each do |data_type|
|
||||
cell_includes << data_type.constantize::PRELOAD_INCLUDE
|
||||
end
|
||||
cell_includes
|
||||
end
|
||||
|
||||
def destroy_discarded(discarded_by_id = nil)
|
||||
self.discarded_by_id = discarded_by_id
|
||||
destroy
|
||||
end
|
||||
handle_asynchronously :destroy_discarded, queue: :clear_discarded_repository, priority: 20
|
||||
end
|
|
@ -126,6 +126,20 @@ class RepositoryCell < ApplicationRecord
|
|||
cell
|
||||
end
|
||||
|
||||
def snapshot!(row_snapshot)
|
||||
cell_snapshot = dup
|
||||
column_snapshot = row_snapshot.repository
|
||||
.repository_columns
|
||||
.find { |c| c.parent_id == repository_column.id }
|
||||
cell_snapshot.assign_attributes(
|
||||
repository_row: row_snapshot,
|
||||
repository_column: column_snapshot,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
value.snapshot!(cell_snapshot)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repository_column_data_type
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositoryChecklistItem < ApplicationRecord
|
||||
belongs_to :repository, inverse_of: :repository_checklist_items
|
||||
belongs_to :repository_column
|
||||
belongs_to :created_by, foreign_key: 'created_by_id', class_name: 'User',
|
||||
inverse_of: :created_repository_checklist_types
|
||||
|
|
|
@ -48,6 +48,22 @@ class RepositoryChecklistValue < ApplicationRecord
|
|||
save!
|
||||
end
|
||||
|
||||
def snapshot!(cell_snapshot)
|
||||
value_snapshot = dup
|
||||
item_values = repository_checklist_items.pluck(:data)
|
||||
checklist_items_snapshot = cell_snapshot.repository_column
|
||||
.repository_checklist_items
|
||||
.select { |snapshot_item| item_values.include?(snapshot_item.data) }
|
||||
|
||||
value_snapshot.assign_attributes(
|
||||
repository_cell: cell_snapshot,
|
||||
repository_checklist_items: checklist_items_snapshot,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
value_snapshot.save!
|
||||
end
|
||||
|
||||
def self.new_with_payload(payload, attributes)
|
||||
item_ids = payload.is_a?(String) ? JSON.parse(payload) : payload
|
||||
value = new(attributes)
|
||||
|
@ -67,8 +83,7 @@ class RepositoryChecklistValue < ApplicationRecord
|
|||
if checklist_item.blank?
|
||||
checklist_item = column.repository_checklist_items.new(data: text,
|
||||
created_by: value.created_by,
|
||||
last_modified_by: value.last_modified_by,
|
||||
repository: column.repository)
|
||||
last_modified_by: value.last_modified_by)
|
||||
|
||||
return nil unless checklist_item.save
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositoryColumn < ApplicationRecord
|
||||
belongs_to :repository
|
||||
belongs_to :repository, class_name: 'RepositoryBase'
|
||||
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
|
||||
has_many :repository_cells, dependent: :destroy
|
||||
has_many :repository_rows, through: :repository_cells
|
||||
|
@ -75,4 +75,44 @@ class RepositoryColumn < ApplicationRecord
|
|||
def importable?
|
||||
Extends::REPOSITORY_IMPORTABLE_TYPES.include?(data_type.to_sym)
|
||||
end
|
||||
|
||||
def deep_dup
|
||||
new_column = super
|
||||
|
||||
extra_method_name = "#{data_type.underscore}_deep_dup"
|
||||
__send__(extra_method_name, new_column) if respond_to?(extra_method_name, true)
|
||||
|
||||
new_column
|
||||
end
|
||||
|
||||
def snapshot!(repository_snapshot)
|
||||
column_snapshot = deep_dup
|
||||
column_snapshot.assign_attributes(
|
||||
repository: repository_snapshot,
|
||||
parent_id: id,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
column_snapshot.save!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repository_list_value_deep_dup(new_column)
|
||||
repository_list_items.each do |item|
|
||||
new_column.repository_list_items << item.deep_dup
|
||||
end
|
||||
end
|
||||
|
||||
def repository_checklist_value_deep_dup(new_column)
|
||||
repository_checklist_items.each do |item|
|
||||
new_column.repository_checklist_items << item.deep_dup
|
||||
end
|
||||
end
|
||||
|
||||
def repository_status_value_deep_dup(new_column)
|
||||
repository_status_items.each do |item|
|
||||
new_column.repository_status_items << item.deep_dup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ class RepositoryDateTimeRangeValueBase < ApplicationRecord
|
|||
inverse_of: :created_repository_date_time_values
|
||||
belongs_to :last_modified_by, foreign_key: :last_modified_by_id, class_name: 'User', optional: true,
|
||||
inverse_of: :modified_repository_date_time_values
|
||||
has_one :repository_cell, as: :value, dependent: :destroy, inverse_of: :repository_date_time_value
|
||||
has_one :repository_cell, as: :value, dependent: :destroy, inverse_of: :value
|
||||
accepts_nested_attributes_for :repository_cell
|
||||
|
||||
validates :repository_cell, :start_time, :end_time, :type, presence: true
|
||||
|
@ -33,4 +33,14 @@ class RepositoryDateTimeRangeValueBase < ApplicationRecord
|
|||
self.last_modified_by = user
|
||||
save!
|
||||
end
|
||||
|
||||
def snapshot!(cell_snapshot)
|
||||
value_snapshot = dup
|
||||
value_snapshot.assign_attributes(
|
||||
repository_cell: cell_snapshot,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
value_snapshot.save!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,4 +24,14 @@ class RepositoryDateTimeValueBase < ApplicationRecord
|
|||
self.last_modified_by = user
|
||||
save!
|
||||
end
|
||||
|
||||
def snapshot!(cell_snapshot)
|
||||
value_snapshot = dup
|
||||
value_snapshot.assign_attributes(
|
||||
repository_cell: cell_snapshot,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
value_snapshot.save!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
class RepositoryListItem < ApplicationRecord
|
||||
has_many :repository_list_values, inverse_of: :repository_list_item, dependent: :destroy
|
||||
belongs_to :repository, inverse_of: :repository_list_items
|
||||
belongs_to :repository_column, inverse_of: :repository_list_items
|
||||
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
|
||||
belongs_to :last_modified_by, foreign_key: :last_modified_by_id, class_name: 'User'
|
||||
|
|
|
@ -42,6 +42,20 @@ class RepositoryListValue < ApplicationRecord
|
|||
save!
|
||||
end
|
||||
|
||||
def snapshot!(cell_snapshot)
|
||||
value_snapshot = dup
|
||||
list_item = cell_snapshot.repository_column
|
||||
.repository_list_items
|
||||
.find { |item| item.data == repository_list_item.data }
|
||||
value_snapshot.assign_attributes(
|
||||
repository_cell: cell_snapshot,
|
||||
repository_list_item: list_item,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
value_snapshot.save!
|
||||
end
|
||||
|
||||
def self.new_with_payload(payload, attributes)
|
||||
value = new(attributes)
|
||||
value.repository_list_item = value.repository_cell
|
||||
|
@ -59,8 +73,7 @@ class RepositoryListValue < ApplicationRecord
|
|||
if list_item.blank?
|
||||
list_item = column.repository_list_items.new(data: text,
|
||||
created_by: value.created_by,
|
||||
last_modified_by: value.last_modified_by,
|
||||
repository: column.repository)
|
||||
last_modified_by: value.last_modified_by)
|
||||
|
||||
return nil unless list_item.save
|
||||
end
|
||||
|
|
|
@ -28,6 +28,16 @@ class RepositoryNumberValue < ApplicationRecord
|
|||
save!
|
||||
end
|
||||
|
||||
def snapshot!(cell_snapshot)
|
||||
value_snapshot = dup
|
||||
value_snapshot.assign_attributes(
|
||||
repository_cell: cell_snapshot,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
value_snapshot.save!
|
||||
end
|
||||
|
||||
def self.new_with_payload(payload, attributes)
|
||||
value = new(attributes)
|
||||
value.data = BigDecimal(payload)
|
||||
|
|
|
@ -4,7 +4,7 @@ class RepositoryRow < ApplicationRecord
|
|||
include SearchableModel
|
||||
include SearchableByNameModel
|
||||
|
||||
belongs_to :repository, optional: true
|
||||
belongs_to :repository, class_name: 'RepositoryBase'
|
||||
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
|
||||
belongs_to :last_modified_by, foreign_key: :last_modified_by_id, class_name: 'User'
|
||||
has_many :repository_cells, -> { order(:id) }, dependent: :destroy
|
||||
|
@ -23,11 +23,6 @@ class RepositoryRow < ApplicationRecord
|
|||
where(repository: Repository.viewable_by_user(user, teams))
|
||||
end
|
||||
|
||||
def self.assigned_on_my_module(ids, my_module)
|
||||
where(id: ids).joins(:my_module_repository_rows)
|
||||
.where('my_module_repository_rows.my_module' => my_module)
|
||||
end
|
||||
|
||||
def self.name_like(query)
|
||||
where('repository_rows.name ILIKE ?', "%#{query}%")
|
||||
end
|
||||
|
@ -41,4 +36,17 @@ class RepositoryRow < ApplicationRecord
|
|||
def editable?
|
||||
true
|
||||
end
|
||||
|
||||
def snapshot!(repository_snapshot)
|
||||
row_snapshot = dup
|
||||
row_snapshot.assign_attributes(
|
||||
repository: repository_snapshot,
|
||||
parent_id: id,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at
|
||||
)
|
||||
row_snapshot.save!
|
||||
|
||||
repository_cells.each { |cell| cell.snapshot!(row_snapshot) }
|
||||
end
|
||||
end
|
||||
|
|
62
app/models/repository_snapshot.rb
Normal file
62
app/models/repository_snapshot.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositorySnapshot < RepositoryBase
|
||||
enum status: { provisioning: 0, ready: 1, failed: 2 }
|
||||
after_save :refresh_report_references, if: :saved_change_to_selected
|
||||
before_destroy :refresh_report_references_for_destroy, prepend: true
|
||||
|
||||
belongs_to :original_repository, foreign_key: :parent_id,
|
||||
class_name: 'Repository',
|
||||
inverse_of: :repository_snapshots,
|
||||
optional: true
|
||||
belongs_to :my_module, optional: true
|
||||
|
||||
validates :name, presence: true, length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
validates :status, presence: true
|
||||
validate :only_one_selected_for_my_module, if: ->(obj) { obj.changed.include? :selected }
|
||||
|
||||
scope :with_deleted_parent_by_team, lambda { |team|
|
||||
joins(my_module: { experiment: :project })
|
||||
.where('projects.team_id = ?', team.id)
|
||||
.left_outer_joins(:original_repository)
|
||||
.where(original_repositories_repositories: { id: nil })
|
||||
.select('DISTINCT ON ("repositories"."parent_id") "repositories".*')
|
||||
.order(:parent_id, updated_at: :desc)
|
||||
}
|
||||
|
||||
def default_columns_count
|
||||
Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['length']
|
||||
end
|
||||
|
||||
def assigned_rows(_my_module)
|
||||
repository_rows
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def only_one_selected_for_my_module
|
||||
return unless selected
|
||||
|
||||
if my_module.repository_snapshots.where(original_repository: original_repository, selected: true).any?
|
||||
errors.add(:selected, I18n.t('activerecord.errors.models.repository_snapshot.attributes.selected.already_taken'))
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_report_references
|
||||
if selected
|
||||
ids = Repository.where(id: parent_id).pluck(:id) +
|
||||
RepositorySnapshot.where(parent_id: parent_id).pluck(:id)
|
||||
|
||||
ReportElement.where(my_module: my_module).where(repository_id: ids).update(repository_id: id)
|
||||
elsif original_repository && !my_module.selected_snapshot_for_repo(original_repository.id)
|
||||
my_module.update_report_repository_references(original_repository)
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_report_references_for_destroy
|
||||
repository_or_snap = original_repository || self
|
||||
default_view_candidate =
|
||||
my_module.active_snapshot_or_live(repository_or_snap, exclude_snpashot_ids: [id])
|
||||
my_module.update_report_repository_references(default_view_candidate) if default_view_candidate
|
||||
end
|
||||
end
|
|
@ -1,14 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositoryStatusItem < ApplicationRecord
|
||||
validates :repository, :repository_column, :icon, presence: true
|
||||
validates :repository_column, :icon, presence: true
|
||||
validates :status, presence: true, length: { minimum: Constants::NAME_MIN_LENGTH,
|
||||
maximum: Constants::NAME_MAX_LENGTH }
|
||||
belongs_to :repository
|
||||
belongs_to :repository_column
|
||||
belongs_to :created_by, foreign_key: 'created_by_id', class_name: 'User', optional: true,
|
||||
inverse_of: :created_repository_status_types
|
||||
belongs_to :last_modified_by, foreign_key: 'last_modified_by_id', class_name: 'User', optional: true,
|
||||
inverse_of: :modified_repository_status_types
|
||||
has_many :repository_status_values, inverse_of: :repository_status_item, dependent: :destroy
|
||||
|
||||
def data
|
||||
"#{icon} #{status}"
|
||||
end
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue