mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-13 00:24:42 +08:00
Merge branch 'features/protocol_versioning' into sb_SCI-8108
This commit is contained in:
commit
bc900cd26d
56 changed files with 485 additions and 220 deletions
|
@ -491,29 +491,37 @@ var ExperimnetTable = {
|
|||
}
|
||||
});
|
||||
},
|
||||
initFilters: function() {
|
||||
this.filterDropdown = filterDropdown.init();
|
||||
getFilterValues: function() {
|
||||
let $experimentFilter = $('#experimentTable .my-modules-filters');
|
||||
$.each(this.filters, (_i, filter) => {
|
||||
this.activeFilters[filter.name] = filter.apply($experimentFilter);
|
||||
});
|
||||
|
||||
// filters are active if they have any non-empty value
|
||||
let filtersEmpty = Object.values(this.activeFilters).every(value => /^\s+$/.test(value)
|
||||
|| value === null
|
||||
|| value === undefined
|
||||
|| (value && value.length === 0));
|
||||
this.filtersActive = !filtersEmpty;
|
||||
},
|
||||
filtersEnabled: function() {
|
||||
this.getFilterValues();
|
||||
|
||||
return this.filters.some((filter) => {
|
||||
return filter.active(this.activeFilters[filter.name]);
|
||||
});
|
||||
},
|
||||
initFilters: function() {
|
||||
let $experimentFilter = $('#experimentTable .my-modules-filters');
|
||||
|
||||
this.filterDropdown = filterDropdown.init(() => this.filtersEnabled());
|
||||
|
||||
$.each(this.filters, (_i, filter) => {
|
||||
filter.init($experimentFilter);
|
||||
});
|
||||
|
||||
this.filterDropdown.on('filter:apply', () => {
|
||||
$.each(this.filters, (_i, filter) => {
|
||||
this.activeFilters[filter.name] = filter.apply($experimentFilter);
|
||||
});
|
||||
|
||||
// filters are active if they have any non-empty value
|
||||
let filtersEmpty = Object.values(this.activeFilters).every(value => /^\s+$/.test(value) || value === null || value === undefined || value && value.length === 0);
|
||||
this.filtersActive = !filtersEmpty;
|
||||
|
||||
filterDropdown.toggleFilterMark(
|
||||
this.filterDropdown,
|
||||
this.filters.some((filter) => {
|
||||
return filter.active(this.activeFilters[filter.name]);
|
||||
})
|
||||
);
|
||||
filterDropdown.toggleFilterMark(this.filterDropdown, this.filtersEnabled());
|
||||
|
||||
this.loadTable();
|
||||
});
|
||||
|
|
|
@ -40,8 +40,9 @@ function initLinkUpdate() {
|
|||
var modalTitle = modal.find('.modal-title');
|
||||
var modalMessage = modal.find('.modal-body .message');
|
||||
var updateBtn = modal.find(".modal-footer [data-action='submit']");
|
||||
$("[data-action='unlink'], [data-action='revert'], [data-action='update-parent'], [data-action='update-self']")
|
||||
.on('ajax:success', function(e, data) {
|
||||
$('.protocol-options-dropdown')
|
||||
.on('ajax:success', "[data-action='unlink'], [data-action='revert'], [data-action='update-parent'],"
|
||||
+ "[data-action='update-self']", function(e, data) {
|
||||
modalTitle.html(data.title);
|
||||
modalMessage.html(data.message);
|
||||
updateBtn.text(data.btn_text);
|
||||
|
|
|
@ -559,7 +559,7 @@ var ProjectsIndex = (function() {
|
|||
}
|
||||
|
||||
function initProjectsFilters() {
|
||||
var $filterDropdown = filterDropdown.init();
|
||||
var $filterDropdown = filterDropdown.init(filtersEnabled);
|
||||
let $projectsFilter = $('.projects-index .projects-filters');
|
||||
let $membersFilter = $('.members-filter', $projectsFilter);
|
||||
let $foldersCB = $('#folder_search', $projectsFilter);
|
||||
|
@ -569,15 +569,30 @@ var ProjectsIndex = (function() {
|
|||
let $archivedOnToFilter = $('.archived-on-filter .to-date', $projectsFilter);
|
||||
let $textFilter = $('#textSearchFilterInput', $projectsFilter);
|
||||
|
||||
function getFilterValues() {
|
||||
createdOnFromFilter = selectDate($createdOnFromFilter);
|
||||
createdOnToFilter = selectDate($createdOnToFilter);
|
||||
membersFilter = dropdownSelector.getValues($('.members-filter'));
|
||||
lookInsideFolders = $foldersCB.prop('checked') ? 'true' : '';
|
||||
archivedOnFromFilter = selectDate($archivedOnFromFilter);
|
||||
archivedOnToFilter = selectDate($archivedOnToFilter);
|
||||
projectsViewSearch = $textFilter.val();
|
||||
}
|
||||
|
||||
function filtersEnabled() {
|
||||
getFilterValues();
|
||||
|
||||
return projectsViewSearch
|
||||
|| createdOnFromFilter
|
||||
|| createdOnToFilter
|
||||
|| (membersFilter && membersFilter.length !== 0)
|
||||
|| lookInsideFolders
|
||||
|| archivedOnFromFilter
|
||||
|| archivedOnToFilter;
|
||||
}
|
||||
|
||||
function appliedFiltersMark() {
|
||||
let filtersEnabled = projectsViewSearch
|
||||
|| createdOnFromFilter
|
||||
|| createdOnToFilter
|
||||
|| (membersFilter && membersFilter.length !== 0)
|
||||
|| lookInsideFolders
|
||||
|| archivedOnFromFilter
|
||||
|| archivedOnToFilter;
|
||||
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled);
|
||||
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled());
|
||||
}
|
||||
|
||||
dropdownSelector.init($membersFilter, {
|
||||
|
@ -602,14 +617,6 @@ var ProjectsIndex = (function() {
|
|||
});
|
||||
|
||||
$filterDropdown.on('filter:apply', function() {
|
||||
createdOnFromFilter = selectDate($createdOnFromFilter);
|
||||
createdOnToFilter = selectDate($createdOnToFilter);
|
||||
membersFilter = dropdownSelector.getValues($('.members-filter'));
|
||||
lookInsideFolders = $foldersCB.prop('checked') ? 'true' : '';
|
||||
archivedOnFromFilter = selectDate($archivedOnFromFilter);
|
||||
archivedOnToFilter = selectDate($archivedOnToFilter);
|
||||
projectsViewSearch = $textFilter.val();
|
||||
|
||||
appliedFiltersMark();
|
||||
refreshCurrentView();
|
||||
});
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
}
|
||||
|
||||
function initExperimentsFilters() {
|
||||
var $filterDropdown = filterDropdown.init();
|
||||
var $filterDropdown = filterDropdown.init(filtersEnabled);
|
||||
|
||||
let $experimentsFilter = $('.experiments-filters');
|
||||
let $startedOnFromFilter = $('.started-on-filter .from-date', $experimentsFilter);
|
||||
|
@ -158,18 +158,7 @@
|
|||
let $archivedOnToFilter = $('.archived-on-filter .to-date', $experimentsFilter);
|
||||
let $textFilter = $('#textSearchFilterInput', $experimentsFilter);
|
||||
|
||||
function appliedFiltersMark() {
|
||||
let filtersEnabled = experimentsViewSearch
|
||||
|| startedOnFromFilter
|
||||
|| startedOnToFilter
|
||||
|| modifiedOnFromFilter
|
||||
|| modifiedOnToFilter
|
||||
|| archivedOnFromFilter
|
||||
|| archivedOnToFilter;
|
||||
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled);
|
||||
}
|
||||
|
||||
$filterDropdown.on('filter:apply', function() {
|
||||
function getFilterValues() {
|
||||
startedOnFromFilter = selectDate($startedOnFromFilter);
|
||||
startedOnToFilter = selectDate($startedOnToFilter);
|
||||
modifiedOnFromFilter = selectDate($modifiedOnFromFilter);
|
||||
|
@ -177,6 +166,26 @@
|
|||
archivedOnFromFilter = selectDate($archivedOnFromFilter);
|
||||
archivedOnToFilter = selectDate($archivedOnToFilter);
|
||||
experimentsViewSearch = $textFilter.val();
|
||||
}
|
||||
|
||||
function filtersEnabled() {
|
||||
getFilterValues();
|
||||
|
||||
return experimentsViewSearch
|
||||
|| startedOnFromFilter
|
||||
|| startedOnToFilter
|
||||
|| modifiedOnFromFilter
|
||||
|| modifiedOnToFilter
|
||||
|| archivedOnFromFilter
|
||||
|| archivedOnToFilter;
|
||||
}
|
||||
|
||||
|
||||
function appliedFiltersMark() {
|
||||
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled());
|
||||
}
|
||||
|
||||
$filterDropdown.on('filter:apply', function() {
|
||||
appliedFiltersMark();
|
||||
refreshCurrentView();
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
// Global variables
|
||||
var ProtocolsIndex = (function() {
|
||||
const ALL_VERSIONS_VALUE = 'All';
|
||||
var PERMISSIONS = ['archivable', 'restorable', 'copyable'];
|
||||
var rowsSelected = [];
|
||||
var protocolsTableEl = null;
|
||||
|
@ -51,7 +52,7 @@ var ProtocolsIndex = (function() {
|
|||
}
|
||||
|
||||
function initProtocolsFilters() {
|
||||
var $filterDropdown = filterDropdown.init();
|
||||
var $filterDropdown = filterDropdown.init(filtersEnabled);
|
||||
let $protocolsFilter = $('.protocols-index .protocols-filters');
|
||||
let $publishedByFilter = $('.published-by-filter', $protocolsFilter);
|
||||
let $accessByFilter = $('.access-by-filter', $protocolsFilter);
|
||||
|
@ -64,18 +65,36 @@ var ProtocolsIndex = (function() {
|
|||
let $archivedOnToFilter = $('.archived-on-filter .to-date', $protocolsFilter);
|
||||
let $textFilter = $('#textSearchFilterInput', $protocolsFilter);
|
||||
|
||||
function getFilterValues() {
|
||||
publishedOnFromFilter = selectDate($publishedOnFromFilter);
|
||||
publishedOnToFilter = selectDate($publishedOnToFilter);
|
||||
modifiedOnFromFilter = selectDate($modifiedOnFromFilter);
|
||||
modifiedOnToFilter = selectDate($modifiedOnToFilter);
|
||||
publishedByFilter = dropdownSelector.getValues($('.published-by-filter'));
|
||||
accessByFilter = dropdownSelector.getValues($('.access-by-filter'));
|
||||
hasDraftFilter = $hasDraft.prop('checked') ? 'true' : '';
|
||||
archivedOnFromFilter = selectDate($archivedOnFromFilter);
|
||||
archivedOnToFilter = selectDate($archivedOnToFilter);
|
||||
protocolsViewSearch = $textFilter.val();
|
||||
}
|
||||
|
||||
function filtersEnabled() {
|
||||
getFilterValues();
|
||||
|
||||
return protocolsViewSearch
|
||||
|| publishedOnFromFilter
|
||||
|| publishedOnToFilter
|
||||
|| modifiedOnFromFilter
|
||||
|| modifiedOnToFilter
|
||||
|| (publishedByFilter && publishedByFilter.length !== 0)
|
||||
|| (accessByFilter && accessByFilter.length !== 0)
|
||||
|| hasDraftFilter
|
||||
|| archivedOnFromFilter
|
||||
|| archivedOnToFilter;
|
||||
}
|
||||
|
||||
function appliedFiltersMark() {
|
||||
let filtersEnabled = protocolsViewSearch
|
||||
|| publishedOnFromFilter
|
||||
|| publishedOnToFilter
|
||||
|| modifiedOnFromFilter
|
||||
|| modifiedOnToFilter
|
||||
|| (publishedByFilter && publishedByFilter.length !== 0)
|
||||
|| (accessByFilter && accessByFilter.length !== 0)
|
||||
|| hasDraftFilter
|
||||
|| archivedOnFromFilter
|
||||
|| archivedOnToFilter;
|
||||
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled);
|
||||
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled());
|
||||
}
|
||||
|
||||
$.each([$publishedByFilter, $accessByFilter], function(_i, selector) {
|
||||
|
@ -97,17 +116,6 @@ var ProtocolsIndex = (function() {
|
|||
});
|
||||
|
||||
$filterDropdown.on('filter:apply', function() {
|
||||
publishedOnFromFilter = selectDate($publishedOnFromFilter);
|
||||
publishedOnToFilter = selectDate($publishedOnToFilter);
|
||||
modifiedOnFromFilter = selectDate($modifiedOnFromFilter);
|
||||
modifiedOnToFilter = selectDate($modifiedOnToFilter);
|
||||
publishedByFilter = dropdownSelector.getValues($('.published-by-filter'));
|
||||
accessByFilter = dropdownSelector.getValues($('.access-by-filter'));
|
||||
hasDraftFilter = $hasDraft.prop('checked') ? 'true' : '';
|
||||
archivedOnFromFilter = selectDate($archivedOnFromFilter);
|
||||
archivedOnToFilter = selectDate($archivedOnToFilter);
|
||||
protocolsViewSearch = $textFilter.val();
|
||||
|
||||
appliedFiltersMark();
|
||||
protocolsDatatable.ajax.reload();
|
||||
});
|
||||
|
@ -137,28 +145,9 @@ var ProtocolsIndex = (function() {
|
|||
|
||||
function initManageAccess() {
|
||||
let protocolsContainer = '.protocols-container';
|
||||
let manageAccessModal = '.protocol-assignments-modal';
|
||||
|
||||
function loadManageAccessModal(href) {
|
||||
$.get(href, function(data) {
|
||||
$(protocolsContainer).append($.parseHTML(data.html));
|
||||
$(manageAccessModal).modal('show');
|
||||
|
||||
// Remove modal when it gets closed
|
||||
$(manageAccessModal).on('hidden.bs.modal', function() {
|
||||
$(manageAccessModal).remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protocolsTableEl.on('click', '.protocol-users-link', function(e) {
|
||||
loadManageAccessModal(this.href);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$(protocolsContainer).on('click', '#manageProtocolAccess', function() {
|
||||
loadManageAccessModal($(`tr[data-row-id=${rowsSelected[0]}] .protocol-users-link`).attr('href'));
|
||||
$(`tr[data-row-id=${rowsSelected[0]}] .protocol-users-link`).click();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -566,19 +555,43 @@ var ProtocolsIndex = (function() {
|
|||
modal.modal('show');
|
||||
|
||||
let childrenTableEl = modalBody.find('#linked-children-table');
|
||||
let versionFromDropdown = ALL_VERSIONS_VALUE;
|
||||
|
||||
const initVersionsDropdown = (childrenDatatable) => {
|
||||
const versionSelector = $('#version-selector');
|
||||
dropdownSelector.init(versionSelector, {
|
||||
noEmptyOption: true,
|
||||
singleSelect: true,
|
||||
selectAppearance: 'simple',
|
||||
closeOnSelect: true,
|
||||
onSelect: function() {
|
||||
versionFromDropdown = dropdownSelector.getValues(versionSelector);
|
||||
childrenDatatable.ajax.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let childrenDatatable;
|
||||
|
||||
if (childrenTableEl) {
|
||||
// Only initialize table if it's present
|
||||
childrenTableEl.DataTable({
|
||||
childrenDatatable = childrenTableEl.DataTable({
|
||||
autoWidth: false,
|
||||
dom: 'RBltpi',
|
||||
dom: 'RBtpl',
|
||||
stateSave: false,
|
||||
buttons: [],
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
ajax: {
|
||||
url: childrenTableEl.data('source'),
|
||||
type: 'POST'
|
||||
type: 'POST',
|
||||
data: function(d) {
|
||||
if (versionFromDropdown !== ALL_VERSIONS_VALUE) {
|
||||
d.version = versionFromDropdown;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
},
|
||||
colReorder: {
|
||||
fixedColumnsLeft: 1000000 // Disable reordering
|
||||
|
@ -591,6 +604,17 @@ var ProtocolsIndex = (function() {
|
|||
columns: [
|
||||
{ data: '1' }
|
||||
],
|
||||
lengthMenu: [
|
||||
[10, 25, 50],
|
||||
[
|
||||
I18n.t('protocols.index.linked_children.length_menu', { number: 10 }),
|
||||
I18n.t('protocols.index.linked_children.length_menu', { number: 25 }),
|
||||
I18n.t('protocols.index.linked_children.length_menu', { number: 50 })
|
||||
]
|
||||
],
|
||||
language: {
|
||||
lengthMenu: '_MENU_'
|
||||
},
|
||||
fnDrawCallback: function() {
|
||||
animateSpinner(this, false);
|
||||
},
|
||||
|
@ -599,6 +623,8 @@ var ProtocolsIndex = (function() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
initVersionsDropdown(childrenDatatable);
|
||||
},
|
||||
error: function() {
|
||||
// TODO
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
})
|
||||
.on('show.bs.modal', function() {
|
||||
$(`${protocolModal} #protocol_name`).parent().removeClass('error');
|
||||
$(`${protocolModal} form[data-action="new"] #protocol_name`).val('');
|
||||
$(`${protocolModal} #protocol_name`).val('');
|
||||
$(this).find('.sci-input-field').focus();
|
||||
});
|
||||
|
||||
let roleSelector = `${protocolModal} #protocol_role_selector`;
|
||||
|
@ -22,6 +23,9 @@
|
|||
});
|
||||
|
||||
$(protocolModal)
|
||||
.on('shown.bs.modal', function() {
|
||||
$(this).find('.sci-input-field').focus();
|
||||
})
|
||||
.on('ajax:error', 'form', function(e, error) {
|
||||
let msg = error.responseJSON.error;
|
||||
$(`${protocolModal} #protocol_name`).parent().addClass('error').attr('data-error-text', msg);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
let newContainer = $(html).find(targetID).length ? $(html).find(targetID) : $(html);
|
||||
targetElement.replaceWith(newContainer);
|
||||
newContainer.find('.selectpicker').selectpicker();
|
||||
newContainer.find('.new-assignment-user-search').focus();
|
||||
|
||||
if (flash) {
|
||||
HelperModule.flashAlertMsg(flash, 'success');
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var filterDropdown = (function() {
|
||||
var $filterContainer = '';
|
||||
var $filtersEnabled = false;
|
||||
|
||||
function initClearButton() {
|
||||
$('.clear-button', $filterContainer).click(function(e) {
|
||||
|
@ -21,7 +22,7 @@ var filterDropdown = (function() {
|
|||
});
|
||||
}
|
||||
|
||||
function initSearchField() {
|
||||
function initSearchField(filtersEnabledFunction) {
|
||||
var $textFilter = $('#textSearchFilterInput', $filterContainer);
|
||||
|
||||
$filterContainer.on('show.bs.dropdown', function() {
|
||||
|
@ -47,7 +48,9 @@ var filterDropdown = (function() {
|
|||
}).on('hide.bs.dropdown', function(e) {
|
||||
if (e.target === e.currentTarget) {
|
||||
$('#textSearchFilterHistory').hide();
|
||||
$('.apply-filters', $filterContainer).click();
|
||||
if (filtersEnabledFunction() || $filtersEnabled) {
|
||||
$('.apply-filters', $filterContainer).click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -97,16 +100,25 @@ var filterDropdown = (function() {
|
|||
});
|
||||
}
|
||||
|
||||
function initCloseButton() {
|
||||
$('.close', $filterContainer).click(function() {
|
||||
$(this).closest('.dropdown').removeClass('open');
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
init: function(filtersEnabledFunction) {
|
||||
$filterContainer = $('.filter-container');
|
||||
$filtersEnabled = false;
|
||||
initClearButton();
|
||||
preventDropdownClose();
|
||||
initApplyButton();
|
||||
initSearchField();
|
||||
initCloseButton();
|
||||
initSearchField(filtersEnabledFunction);
|
||||
return $filterContainer;
|
||||
},
|
||||
toggleFilterMark: function(filterContainer, filtersEnabled) {
|
||||
$filtersEnabled = filtersEnabled;
|
||||
if (filtersEnabled) {
|
||||
filterContainer.addClass('filters-applied');
|
||||
} else {
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#user_assignments_modal {
|
||||
|
||||
.btn-role-select {
|
||||
padding: 0;
|
||||
padding: 0 0 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-white;
|
||||
background-color: $color-concrete;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
tr {
|
||||
&.selected,
|
||||
.selected {
|
||||
background-color: $color-alto;
|
||||
background-color: $color-concrete;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,10 @@
|
|||
}
|
||||
|
||||
&.active {
|
||||
min-height: 36px;
|
||||
a {
|
||||
min-width: 36px;
|
||||
min-height: 36px;
|
||||
background-color: inherit;
|
||||
border: 1px solid $brand-primary;
|
||||
color: inherit;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
.btn-open-file {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin-top: 9px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
> input[type=file] {
|
||||
background: $color-white;
|
||||
|
@ -30,8 +32,36 @@
|
|||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.import-protocols-io {
|
||||
margin-bottom: 4px;
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
#protocols-index,
|
||||
#load-from-repository-modal {
|
||||
.header-separator {
|
||||
border-bottom: 1px solid $modal-header-border-color;
|
||||
margin-left: -1.15em;
|
||||
margin-right: -1.15em;
|
||||
position: relative;
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
.footer-separator {
|
||||
border-top: 1px solid $modal-header-border-color;
|
||||
bottom: 17px;
|
||||
margin-left: -1.15em;
|
||||
margin-right: -1.15em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.load-warning-message {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
|
|
|
@ -132,8 +132,11 @@
|
|||
background: $color-concrete;
|
||||
margin-left: .75em;
|
||||
text-align: center;
|
||||
line-height: 24px;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
.global-avatar-container,
|
||||
|
@ -307,3 +310,29 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#linked-children-modal {
|
||||
.dataTables_length .form-control {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.version-dropdown {
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
display: flex;
|
||||
margin-top: 16px;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.dropdown-selector-container {
|
||||
width: 86px;
|
||||
}
|
||||
}
|
||||
|
||||
.linked-children-datatable {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,14 @@
|
|||
margin-bottom: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-selector-container {
|
||||
width: 170px;
|
||||
|
||||
.search-field {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
.protocol-position-container {
|
||||
background-color: $color-white;
|
||||
border-radius: 2px;
|
||||
box-shadow: $flyout-shadow;
|
||||
margin: 20px auto;
|
||||
max-width: 900px;
|
||||
|
@ -74,6 +75,7 @@
|
|||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
padding: 0 25px;
|
||||
|
||||
.btn {
|
||||
margin-left: .25em;
|
||||
|
@ -82,6 +84,11 @@
|
|||
.caret {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
button,
|
||||
a {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,20 @@
|
|||
.btn {
|
||||
margin-right: .25em;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
box-shadow: 0 0 0 3px $brand-focus;
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
&:active {
|
||||
background-color: $color-alto !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-info,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
display: flex;
|
||||
height: 2.75em;
|
||||
margin-bottom: 1em;
|
||||
padding: 0 .3em 0 1em;
|
||||
padding: 1em;
|
||||
|
||||
.title {
|
||||
@include font-h2;
|
||||
|
@ -117,7 +117,7 @@
|
|||
}
|
||||
|
||||
.footer:not(.center) {
|
||||
.btn:last-child {
|
||||
:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,6 +148,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.user-name {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.notification {
|
||||
padding-right: 8px;
|
||||
word-wrap: break-word;
|
||||
|
|
|
@ -554,9 +554,12 @@ mark,.mark {
|
|||
|
||||
table tbody tr .breadcrumb {
|
||||
padding: 0 5px;
|
||||
position: relative;
|
||||
right: 15px;
|
||||
|
||||
& li + li::before {
|
||||
content: "> ";
|
||||
color: $color-volcano;
|
||||
content: "/ ";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,6 +576,10 @@ mark,.mark {
|
|||
}
|
||||
}
|
||||
|
||||
.no-linked-children {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
a.edit-name-link small {
|
||||
margin-left: 5px;
|
||||
display: none;
|
||||
|
|
|
@ -110,7 +110,7 @@ module AccessPermissions
|
|||
user_assignment.destroy!
|
||||
end
|
||||
|
||||
propagate_job(user_assignment)
|
||||
propagate_job(user_assignment, destroy: true)
|
||||
log_activity(:unassign_user_from_project, user_assignment)
|
||||
|
||||
render json: { flash: t('access_permissions.destroy.success', member_name: user.full_name) },
|
||||
|
@ -152,12 +152,13 @@ module AccessPermissions
|
|||
render_404 unless @project
|
||||
end
|
||||
|
||||
def propagate_job(user_assignment)
|
||||
def propagate_job(user_assignment, destroy: false)
|
||||
UserAssignments::PropagateAssignmentJob.perform_later(
|
||||
@project,
|
||||
user_assignment.user,
|
||||
user_assignment.user_role,
|
||||
current_user
|
||||
current_user,
|
||||
destroy: destroy
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ module AccessPermissions
|
|||
next unless user_assignment_params[:assign] == '1'
|
||||
|
||||
if user_assignment_params[:user_id] == 'all'
|
||||
@protocol.update!(visibility: :visible, default_public_user_role_id: user_assignment_params[:user_role_id])
|
||||
@protocol.update!(default_public_user_role_id: user_assignment_params[:user_role_id])
|
||||
else
|
||||
user_assignment = UserAssignment.find_or_initialize_by(
|
||||
assignable: @protocol,
|
||||
|
@ -87,31 +87,28 @@ module AccessPermissions
|
|||
def destroy
|
||||
user = @protocol.assigned_users.find(params[:user_id])
|
||||
user_assignment = @protocol.user_assignments.find_by(user: user, team: current_team)
|
||||
respond_to do |format|
|
||||
if user_assignment.destroy
|
||||
|
||||
log_activity(:protocol_template_access_revoked, user_assignment)
|
||||
format.json do
|
||||
render json: { flash: t('access_permissions.destroy.success', member_name: user.full_name) },
|
||||
status: :ok
|
||||
end
|
||||
Protocol.transaction do
|
||||
if @protocol.visible?
|
||||
user_assignment.update!(
|
||||
user_role: @protocol.default_public_user_role,
|
||||
assigned: :automatically
|
||||
)
|
||||
else
|
||||
format.json do
|
||||
render json: { flash: t('access_permissions.destroy.failure') },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
user_assignment.destroy!
|
||||
end
|
||||
log_activity(:protocol_template_access_revoked, user_assignment)
|
||||
end
|
||||
|
||||
render json: { flash: t('access_permissions.destroy.success', member_name: user.full_name) }
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
Rails.logger.error e.message
|
||||
render json: { flash: t('access_permissions.destroy.failure') }, status: :unprocessable_entity
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
def update_default_public_user_role
|
||||
Protocol.transaction do
|
||||
@protocol.visibility = :hidden if permitted_default_public_user_role_params[:default_public_user_role_id].blank?
|
||||
@protocol.assign_attributes(permitted_default_public_user_role_params)
|
||||
@protocol.save!
|
||||
|
||||
UserAssignments::ProtocolGroupAssignmentJob.perform_later(current_team, @protocol, current_user)
|
||||
end
|
||||
@protocol.update!(permitted_default_public_user_role_params)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -134,6 +131,8 @@ module AccessPermissions
|
|||
@protocol = current_team.protocols.includes(user_assignments: %i(user user_role)).find_by(id: params[:id])
|
||||
|
||||
render_404 unless @protocol
|
||||
|
||||
@protocol = @protocol.parent if @protocol.parent_id
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
|
|
|
@ -1074,7 +1074,7 @@ class ProtocolsController < ApplicationController
|
|||
|
||||
def set_inline_name_editing
|
||||
return unless @protocol.initial_draft?
|
||||
return unless can_manage_protocol_in_repository?(@protocol)
|
||||
return unless can_manage_protocol_draft_in_repository?(@protocol)
|
||||
|
||||
@inline_editable_title_config = {
|
||||
name: 'title',
|
||||
|
|
|
@ -295,7 +295,7 @@ class StepsController < ApplicationController
|
|||
end
|
||||
|
||||
def check_protocol_manage_permissions
|
||||
render_403 unless can_manage_protocol_in_module?(@protocol) || can_manage_protocol_in_repository?(@protocol)
|
||||
render_403 unless can_manage_protocol_in_module?(@protocol) || can_manage_protocol_draft_in_repository?(@protocol)
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
|
@ -306,7 +306,7 @@ class StepsController < ApplicationController
|
|||
if @my_module
|
||||
render_403 unless can_manage_my_module_steps?(@my_module)
|
||||
else
|
||||
render_403 unless can_manage_protocol_in_repository?(@protocol)
|
||||
render_403 unless can_manage_protocol_draft_in_repository?(@protocol)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ class TinyMceAssetsController < ApplicationController
|
|||
return render_403 unless can_manage_step?(@assoc.step)
|
||||
when Protocol
|
||||
return render_403 unless can_manage_protocol_in_module?(@protocol) ||
|
||||
can_manage_protocol_in_repository?(@protocol)
|
||||
can_manage_protocol_draft_in_repository?(@protocol)
|
||||
when ResultText, MyModule
|
||||
return render_403 unless can_manage_my_module?(@my_module)
|
||||
else
|
||||
|
|
|
@ -40,14 +40,16 @@ class ProtocolLinkedChildrenDatatable < CustomDatatable
|
|||
# Query database for records (this will be later paginated and filtered)
|
||||
# after that "data" function will return json
|
||||
def get_raw_records
|
||||
records =
|
||||
Protocol
|
||||
.joins(my_module: { experiment: :project })
|
||||
.includes(my_module: { experiment: :project })
|
||||
.references(my_module: { experiment: :project })
|
||||
.where(protocol_type: Protocol.protocol_types[:linked])
|
||||
.where(parent: @protocol)
|
||||
records.distinct
|
||||
if params[:version].present?
|
||||
records = @protocol.published_versions_with_original
|
||||
.find_by!(version_number: params[:version])
|
||||
.linked_children
|
||||
else
|
||||
records = Protocol.where(protocol_type: Protocol.protocol_types[:linked])
|
||||
records = records.where(parent_id: @protocol.published_versions)
|
||||
.or(records.where(parent_id: @protocol.id))
|
||||
end
|
||||
records.preload(my_module: { experiment: :project }).distinct
|
||||
end
|
||||
|
||||
# Helper methods
|
||||
|
|
|
@ -14,7 +14,8 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def user_names_with_roles(user_assignments)
|
||||
user_assignments.map { |up| user_name_with_role(up) }.join('
')
|
||||
names_with_roles = user_assignments.map { |up| user_name_with_role(up) }.join('
')
|
||||
sanitize_input(names_with_roles)
|
||||
end
|
||||
|
||||
def user_name_with_role(user_assignment)
|
||||
|
|
|
@ -6,7 +6,11 @@ module UserRolesHelper
|
|||
permissions = permission_group.constants.map { |const| permission_group.const_get(const) }
|
||||
|
||||
roles = user_roles_subset_by_permissions(permissions).order(id: :asc).pluck(:name, :id)
|
||||
roles = [[t('access_permissions.reset'), 'reset']] + roles if with_inherit
|
||||
if with_inherit
|
||||
roles = [[t('access_permissions.reset'), 'reset',
|
||||
t("access_permissions.partials.#{object.class.name.underscore}_member_field.reset_description")]] +
|
||||
roles
|
||||
end
|
||||
roles
|
||||
end
|
||||
|
||||
|
|
|
@ -189,6 +189,9 @@
|
|||
inRepository() {
|
||||
return this.protocol.attributes.in_repository
|
||||
},
|
||||
linked() {
|
||||
return this.protocol.attributes.linked;
|
||||
},
|
||||
urls() {
|
||||
return this.protocol.attributes.urls || {}
|
||||
}
|
||||
|
@ -232,21 +235,34 @@
|
|||
if (this.inRepository) return
|
||||
// legacy method from app/assets/javascripts/my_modules/protocols.js
|
||||
refreshProtocolStatusBar();
|
||||
|
||||
// Update protocol options drowpdown for linked tasks
|
||||
this.refreshProtocolDropdownOptions();
|
||||
},
|
||||
refreshProtocolDropdownOptions() {
|
||||
if (!this.linked && this.inRepository) return
|
||||
|
||||
$.get(this.protocolUrl, (result) => {
|
||||
this.protocol.attributes.urls = result.data.attributes.urls;
|
||||
});
|
||||
},
|
||||
updateProtocol(attributes) {
|
||||
this.protocol.attributes = attributes
|
||||
},
|
||||
updateName(newName) {
|
||||
this.protocol.attributes.name = newName;
|
||||
this.refreshProtocolStatus();
|
||||
$.ajax({
|
||||
type: 'PATCH',
|
||||
url: this.urls.update_protocol_name_url,
|
||||
data: { protocol: { name: newName } }
|
||||
data: { protocol: { name: newName } },
|
||||
success: () => {
|
||||
this.refreshProtocolStatus();
|
||||
}
|
||||
});
|
||||
},
|
||||
updateDescription(protocol) {
|
||||
this.protocol.attributes = protocol.attributes
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
addStep(position) {
|
||||
$.post(this.urls.add_step_url, {position: position}, (result) => {
|
||||
|
@ -257,8 +273,8 @@
|
|||
if(position === this.steps.length - 1) {
|
||||
this.$nextTick(() => this.scrollToBottom());
|
||||
}
|
||||
this.refreshProtocolStatus();
|
||||
})
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
updateStepsPosition(step, action = 'add') {
|
||||
let position = step.attributes.position;
|
||||
|
|
|
@ -86,7 +86,9 @@ export default {
|
|||
});
|
||||
filesUploadedCntr += 1;
|
||||
if (filesUploadedCntr === filesToUploadCntr) {
|
||||
this.$emit('stepUpdated');
|
||||
setTimeout(() => {
|
||||
this.$emit('stepUpdated');
|
||||
}, 1000);
|
||||
resolve('done');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,11 @@
|
|||
</div>
|
||||
<div class="sci-input-container">
|
||||
<label>{{ i18n.t('protocols.publish_modal.comment')}}</label>
|
||||
<textarea v-model="protocol.attributes.version_comment" class="sci-input-field" :placeholder="i18n.t('protocols.publish_modal.comment_placeholder')"></textarea>
|
||||
<textarea ref="textarea"
|
||||
v-model="protocol.attributes.version_comment"
|
||||
class="sci-input-field"
|
||||
:placeholder="i18n.t('protocols.publish_modal.comment_placeholder')">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -40,6 +44,7 @@
|
|||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
$(this.$refs.textarea).focus();
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<button class="btn btn-light" @click="openVersionsModal">{{ i18n.t("protocols.header.versions") }}</button>
|
||||
<button v-if="protocol.attributes.urls.publish_url" @click="$emit('publish')" class="btn btn-primary">{{ i18n.t("protocols.header.publish") }}</button>
|
||||
<button v-if="protocol.attributes.urls.save_as_draft_url" @click="saveAsdraft" class="btn btn-secondary">{{ i18n.t("protocols.header.save_as_draft") }}</button>
|
||||
<button v-bind:disabled="protocol.attributes.disabled_drafting" v-if="protocol.attributes.disabled_drafting" @click="saveAsdraft" class="btn btn-secondary">{{ i18n.t("protocols.header.save_as_draft") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="details-container" class="protocol-details collapse in">
|
||||
|
|
|
@ -419,6 +419,7 @@
|
|||
$.post(this.urls[`create_${elementType}_url`], (result) => {
|
||||
result.data.isNew = true;
|
||||
this.elements.push(result.data)
|
||||
this.$emit('stepUpdated')
|
||||
}).error(() => {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
||||
})
|
||||
|
|
|
@ -21,15 +21,19 @@ class Protocol < ApplicationRecord
|
|||
after_save :update_user_assignments, if: -> { saved_change_to_visibility? && in_repository? }
|
||||
after_save :update_linked_children
|
||||
skip_callback :create, :after, :create_users_assignments, if: -> { in_module? }
|
||||
before_update :sync_protocol_assignments, if: :visibility_changed?
|
||||
before_update :change_visibility, if: :default_public_user_role_id_changed?
|
||||
after_update :sync_protocol_assignments, if: :visibility_changed?
|
||||
|
||||
enum visibility: { hidden: 0, visible: 1 }
|
||||
enum protocol_type: {
|
||||
unlinked: 0,
|
||||
linked: 1,
|
||||
in_repository_published_original: 2,
|
||||
in_repository_draft: 3,
|
||||
in_repository_published_version: 4
|
||||
in_repository_private: 2, # Deprecated
|
||||
in_repository_public: 3, # Deprecated
|
||||
in_repository_archived: 4, # Deprecated
|
||||
in_repository_published_original: 5,
|
||||
in_repository_draft: 6,
|
||||
in_repository_published_version: 7
|
||||
}
|
||||
|
||||
auto_strip_attributes :name, :description, nullify: false
|
||||
|
@ -800,6 +804,10 @@ class Protocol < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def change_visibility
|
||||
self.visibility = default_public_user_role_id.present? ? :visible : :hidden
|
||||
end
|
||||
|
||||
def sync_protocol_assignments
|
||||
if visible?
|
||||
auto_assign_protocol_members
|
||||
|
|
|
@ -5,7 +5,7 @@ Canaid::Permissions.register_for(Step) do
|
|||
if step.my_module
|
||||
can_manage_my_module_steps?(user, step.my_module)
|
||||
else
|
||||
can_manage_protocol_in_repository?(user, step.protocol)
|
||||
can_manage_protocol_draft_in_repository?(user, step.protocol)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,6 +71,7 @@ end
|
|||
|
||||
Canaid::Permissions.register_for(Protocol) do
|
||||
%i(manage_protocol_in_repository
|
||||
manage_protocol_draft_in_repository
|
||||
manage_protocol_users
|
||||
clone_protocol_in_repository
|
||||
publish_protocol_in_repository
|
||||
|
@ -95,7 +96,8 @@ Canaid::Permissions.register_for(Protocol) do
|
|||
end
|
||||
|
||||
can :manage_protocol_draft_in_repository do |user, protocol|
|
||||
protocol.permission_granted?(user, ProtocolPermissions::MANAGE_DRAFT)
|
||||
protocol.in_repository_draft? &&
|
||||
protocol.permission_granted?(user, ProtocolPermissions::MANAGE_DRAFT)
|
||||
end
|
||||
|
||||
can :manage_protocol_users do |user, protocol|
|
||||
|
|
|
@ -8,7 +8,7 @@ class ProtocolSerializer < ActiveModel::Serializer
|
|||
|
||||
attributes :name, :id, :urls, :description, :description_view, :updated_at, :in_repository,
|
||||
:created_at_formatted, :updated_at_formatted, :added_by, :authors, :keywords, :version, :code,
|
||||
:published, :version_comment, :archived
|
||||
:published, :version_comment, :archived, :linked, :disabled_drafting
|
||||
|
||||
def updated_at
|
||||
object.updated_at.to_i
|
||||
|
@ -84,6 +84,17 @@ class ProtocolSerializer < ActiveModel::Serializer
|
|||
!object.in_module?
|
||||
end
|
||||
|
||||
def disabled_drafting
|
||||
return false unless can_create_protocols_in_repository?(object.team)
|
||||
|
||||
%(in_repository_published_original in_repository_published_version).include?(object.protocol_type) &&
|
||||
(object.parent || object).draft.present? && !object.archived
|
||||
end
|
||||
|
||||
def linked
|
||||
object.linked?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_from_repo_url
|
||||
|
@ -123,13 +134,13 @@ class ProtocolSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def reorder_steps_url
|
||||
return unless can_manage_protocol_in_module?(object) || can_manage_protocol_in_repository?(object)
|
||||
return unless can_manage_protocol_in_module?(object) || can_manage_protocol_draft_in_repository?(object)
|
||||
|
||||
reorder_protocol_steps_url(protocol_id: object.id)
|
||||
end
|
||||
|
||||
def add_step_url
|
||||
return unless can_manage_protocol_in_module?(object) || can_manage_protocol_in_repository?(object)
|
||||
return unless can_manage_protocol_in_module?(object) || can_manage_protocol_draft_in_repository?(object)
|
||||
|
||||
protocol_steps_path(protocol_id: object.id)
|
||||
end
|
||||
|
@ -153,7 +164,7 @@ class ProtocolSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def update_protocol_name_url
|
||||
if in_repository && can_manage_protocol_in_repository?(object)
|
||||
if in_repository && can_manage_protocol_draft_in_repository?(object)
|
||||
name_protocol_path(object)
|
||||
elsif can_manage_protocol_in_module?(object)
|
||||
protocol_my_module_path(object.my_module)
|
||||
|
@ -161,7 +172,7 @@ class ProtocolSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def update_protocol_description_url
|
||||
if in_repository && can_manage_protocol_in_repository?(object)
|
||||
if in_repository && can_manage_protocol_draft_in_repository?(object)
|
||||
description_protocol_path(object)
|
||||
elsif can_manage_protocol_in_module?(object)
|
||||
protocol_my_module_path(object.my_module)
|
||||
|
@ -169,15 +180,15 @@ class ProtocolSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def update_protocol_authors_url
|
||||
authors_protocol_path(object) if in_repository && can_manage_protocol_in_repository?(object)
|
||||
authors_protocol_path(object) if in_repository && can_manage_protocol_draft_in_repository?(object)
|
||||
end
|
||||
|
||||
def update_protocol_keywords_url
|
||||
keywords_protocol_path(object) if in_repository && can_manage_protocol_in_repository?(object)
|
||||
keywords_protocol_path(object) if in_repository && can_manage_protocol_draft_in_repository?(object)
|
||||
end
|
||||
|
||||
def delete_steps_url
|
||||
return unless can_manage_protocol_in_module?(object) || can_manage_protocol_in_repository?(object)
|
||||
return unless can_manage_protocol_in_module?(object) || can_manage_protocol_draft_in_repository?(object)
|
||||
|
||||
delete_steps_protocol_path(object)
|
||||
end
|
||||
|
|
|
@ -79,7 +79,7 @@ class StepSerializer < ActiveModel::Serializer
|
|||
urls_list[:state_url] = toggle_step_state_step_path(object)
|
||||
end
|
||||
|
||||
if can_manage_protocol_in_module?(object.protocol) || can_manage_protocol_in_repository?(object.protocol)
|
||||
if can_manage_protocol_in_module?(object.protocol) || can_manage_protocol_draft_in_repository?(object.protocol)
|
||||
urls_list[:duplicate_step_url] = duplicate_step_path(object)
|
||||
end
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ module Reports::Docx::DrawMyModuleProtocol
|
|||
end
|
||||
|
||||
if @settings.dig('task', 'protocol', 'description') && protocol.description.present?
|
||||
@docx.p I18n.t('projects.reports.elements.module.protocol.user_time', code: protocol.code,
|
||||
@docx.p I18n.t('projects.reports.elements.module.protocol.user_time', code: protocol.original_code,
|
||||
timestamp: I18n.l(protocol.created_at, format: :full)), color: @color[:gray]
|
||||
html = custom_auto_link(protocol.description, team: @report_team)
|
||||
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<% if assignable.top_level_assignable? %>
|
||||
<li role="separator" class="divider" data-hook="support-dropdown-separator"></li>
|
||||
<li>
|
||||
<a href="#" data-turbolinks="false" class="user-role-selector" data-role-id="">
|
||||
<a href="#" data-turbolinks="false" class="user-role-selector" data-role-id="" data-action='remote-destroy' data-target="#">
|
||||
<%= t('access_permissions.remove_access') %>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<ul class="dropdown-menu dropdown-menu-right user-assignment-dropdown" aria-labelledby="userAccess_<%= user.id %>">
|
||||
<% user_roles_collection(assignable, with_inherit: with_inherit).each do |role| %>
|
||||
<li>
|
||||
<a href="#" data-turbolinks="false" class="user-role-selector" data-role-id="<%= role[1] %>">
|
||||
<a href="#" data-turbolinks="false" class="user-role-selector" title="<%= role[2] %>" data-role-id="<%= role[1] %>">
|
||||
<%= role[0] %>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -44,6 +44,11 @@
|
|||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% else %>
|
||||
<button class="btn btn-light btn-role-select disabled" type="button">
|
||||
<%= f.object.user_role.name %>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -13,7 +13,10 @@
|
|||
<%= form_with(url: create_path, method: :post, remote: true, html: { id: 'new-user-assignment-form', data: { action: 'replace-form', target: '#user_assignments_modal', object_type: assignable.class.to_s.downcase} }) do |f| %>
|
||||
<div class="modal-body">
|
||||
<div class="sci-input-container left-icon">
|
||||
<%= text_field_tag :search_users, '', placeholder: t('.find_people_html'), class: 'sci-input-field', data: { action: 'filter-list', target: 'new-user-assignment-to-project-form' } %>
|
||||
<%= text_field_tag :search_users, '',
|
||||
placeholder: t('.find_people_html'),
|
||||
class: 'sci-input-field new-assignment-user-search',
|
||||
data: { action: 'filter-list', target: 'new-user-assignment-to-project-form' } %>
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
<% if assignable.visibility && assignable.visibility == 'hidden' %>
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
<h2 class="modal-title" id="load-from-repository-modal-label">
|
||||
<%= t("my_modules.protocols.load_from_repository_modal.title") %>
|
||||
</h2>
|
||||
<div class="header-separator"></div>
|
||||
</div>
|
||||
<div class="modal-body"></div>
|
||||
<div class="modal-footer">
|
||||
<div class="footer-separator"></div>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><%= t("general.cancel") %></button>
|
||||
<button type="button" class="btn btn-primary" data-action="submit"><%= t("my_modules.protocols.load_from_repository_modal.load") %></button>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div>
|
||||
<div class="load-warning-message">
|
||||
<p style="font-weight: bold;"><%= t("my_modules.protocols.load_from_repository_modal.text") %></p>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,36 +1,35 @@
|
|||
<template id="protocolGeneralToolbar">
|
||||
<div class="left-general-toolbar">
|
||||
<button data-toggle="modal"
|
||||
data-target="#newProtocolModal"
|
||||
<%= 'disabled' if !can_create_protocols_in_repository?(@current_team) %>
|
||||
class="btn btn-primary only-active"
|
||||
>
|
||||
<span class="fas fa-plus"></span>
|
||||
<span class="hidden-xs"><%= t("protocols.index.create_new") %></span>
|
||||
</button>
|
||||
|
||||
<div id="protocol-import-group" class="sci-btn-group only-active" role="group">
|
||||
<button class="btn btn-light btn-open-file <%= 'disabled' unless can_create_protocols_in_repository?(@current_team) %>"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<span class="fas fa-upload"></span><span class="hidden-xs"><%= t("protocols.index.import") %></span>
|
||||
<% if can_create_protocols_in_repository?(@current_team) %>
|
||||
<button data-toggle="modal"
|
||||
data-target="#newProtocolModal"
|
||||
class="btn btn-primary only-active"
|
||||
>
|
||||
<span class="fas fa-plus"></span>
|
||||
<span class="hidden-xs"><%= t("protocols.index.create_new") %></span>
|
||||
</button>
|
||||
<% if can_create_protocols_in_repository?(@current_team) %>
|
||||
|
||||
<div id="protocol-import-group" class="sci-btn-group only-active" role="group">
|
||||
<button class="btn btn-light btn-open-file"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<span class="fas fa-upload"></span><span class="hidden-xs"><%= t("protocols.index.import") %></span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a class="btn-link-alt btn-default-link btn-open-file" <%= can_create_protocols_in_repository?(@current_team) ? 'data-action="import"' : 'disabled="disabled"' %>>
|
||||
<span><%= t("protocols.index.import_alt") %></span>
|
||||
<input type="file" value="" accept=".eln" data-role="import-file-input"
|
||||
data-team-id="<%= @current_team.id %>" data-import-url="<%= import_protocols_path %>"
|
||||
<%= 'disabled="disabled"' unless can_create_protocols_in_repository?(@current_team) %>>
|
||||
data-team-id="<%= @current_team.id %>" data-import-url="<%= import_protocols_path %>">
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<li class="import-protocols-io">
|
||||
<%= link_to t("protocols.index.import_protocols_io"), '', data: { target: '#protocolsioModal', toggle: 'modal' } %>
|
||||
</li>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,17 +1,28 @@
|
|||
<% linked_children = protocol.linked_children %>
|
||||
<% published_versions = protocol.published_versions_with_original.order(version_number: :desc)%>
|
||||
|
||||
<% if protocol.nr_of_linked_children > 0 %>
|
||||
<div>
|
||||
<%= t("protocols.index.linked_children.used_in", nr: protocol.nr_of_linked_children) %>
|
||||
<div class="version-dropdown">
|
||||
<%= label_tag "version-selector".to_sym, t("protocols.index.linked_children.show_version") %>
|
||||
<%= select_tag "version",
|
||||
options_for_select(
|
||||
['All'].concat(
|
||||
published_versions.map { |p|
|
||||
[
|
||||
p.version_number
|
||||
]
|
||||
})),
|
||||
{
|
||||
id: 'version-selector'
|
||||
} %>
|
||||
</div>
|
||||
|
||||
<div class="linked-children-datatable">
|
||||
<table id="linked-children-table" class="table" data-source="<%= linked_children_datatable_protocol_path(protocol: protocol) %>">
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<% else %>
|
||||
<div>
|
||||
<div class="no-linked-children">
|
||||
<em><%= t("protocols.index.linked_children.no_linked_children") %></em>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
<%= render partial: 'shared/filter_dropdown/text_search', locals: {container_class: '', label_text: t('filters_modal.text.label_name_and_keywords')} %>
|
||||
<%= render partial: 'shared/filter_dropdown/datetime_search', locals: {container_class: 'published-on-filter', label: 'Published on', view_mode: nil } %>
|
||||
<%= render partial: 'shared/filter_dropdown/datetime_search', locals: {container_class: 'modified-on-filter', label: 'Modified on', view_mode: nil } %>
|
||||
<%= render partial: 'shared/filter_dropdown/datetime_search', locals: {container_class: 'archived-on-filter', label: 'Archived on', view_mode: 'archived' } %>
|
||||
<div class="select-block">
|
||||
<label>Published by</label>
|
||||
<select class="published-by-filter"
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
>
|
||||
<div class="protocol-comment-message">
|
||||
<div class="view-mode" data-placeholder="<%= t('protocols.index.versions.comment_placeholder') %>"><%= draft.version_comment %></div>
|
||||
<%= text_area_tag 'version_comment', draft.version_comment, disabled: true, class: 'smart-text-area hidden' %>
|
||||
<%= text_area_tag 'version_comment', draft.version_comment, disabled: can_publish_protocol_in_repository?(@protocol), class: 'smart-text-area hidden' %>
|
||||
</div>
|
||||
<div class="edit-buttons">
|
||||
<span class="cancel-button btn btn-secondary"><%= t('general.cancel') %></span>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<% protocol.user_assignments[0..3].each_with_index do |user_assigment, i| %>
|
||||
<% protocol.user_assignments[0..2].each_with_index do |user_assigment, i| %>
|
||||
<span class="global-avatar-container" style="z-index: <%= 5 - i %>">
|
||||
<%= image_tag(avatar_path(user_assigment.user, :icon_small), title: user_name_with_role(user_assigment)) %>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<% more_users = protocol.user_assignments[4..-1].to_a %>
|
||||
<% more_users = protocol.user_assignments[3..-1].to_a %>
|
||||
<% if more_users.any? %>
|
||||
<span class="more-users" title="<%= user_names_with_roles(more_users) %>">
|
||||
+<%= more_users.size %>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<% end %>
|
||||
</h4>
|
||||
<div class="user-time">
|
||||
<%= t('projects.reports.elements.module.protocol.user_time', code: protocol.code, timestamp: l(protocol.created_at, format: :full)) %>
|
||||
<%= t('projects.reports.elements.module.protocol.user_time', code: protocol.original_code, timestamp: l(protocol.created_at, format: :full)) %>
|
||||
</div>
|
||||
<div class="row module-protocol-description">
|
||||
<% if @settings.dig('task', 'protocol', 'description') && protocol.description.present? %>
|
||||
|
|
|
@ -3,14 +3,20 @@
|
|||
<div class="dropdown-menu dropdown-menu-right filter-dropdown <%= container_class %>" role="menu" data-team-id="<%= current_team.id %>" data-search-field-history-key="<%= search_field_history_key %>">
|
||||
<div class="header">
|
||||
<div class="title"><%= dropdown_title %></div>
|
||||
<div class="btn btn-light clear-button">
|
||||
<i class="fas fa-times-circle"></i><%= t("filters_modal.clear_btn") %></div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="body">
|
||||
<%= yield %>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="btn btn-primary apply-filters"><%= t("filters_modal.show_btn.one") %></div>
|
||||
<div class="buttons">
|
||||
<div class="btn btn-light clear-button">
|
||||
<%= t("filters_modal.clear_btn") %>
|
||||
</div>
|
||||
<div class="btn btn-primary apply-filters">
|
||||
<%= t("filters_modal.show_btn.one") %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
role="button"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<span>
|
||||
<span class="user-name">
|
||||
<%= t('nav.user_greeting', full_name: current_user.full_name) %>
|
||||
</span>
|
||||
<span class='global-avatar-container'>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</h4>
|
||||
</div>
|
||||
<% if !(preview) && (can_manage_protocol_in_module?(@protocol) ||
|
||||
can_manage_protocol_in_repository?(@protocol)) %>
|
||||
can_manage_protocol_draft_in_repository?(@protocol)) %>
|
||||
<div class="actions">
|
||||
<div class="dropdown sci-dropdown">
|
||||
<button class="btn btn-light dropdown-toggle" type="button" id="dropdownAttachmentsOptions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
|
|
|
@ -140,7 +140,7 @@ module PermissionExtends
|
|||
TeamPermissions::LABEL_TEMPLATES_MANAGE,
|
||||
ProtocolPermissions::READ,
|
||||
ProtocolPermissions::READ_ARCHIVED,
|
||||
ProtocolPermissions::MANAGE,
|
||||
ProtocolPermissions::MANAGE_DRAFT,
|
||||
ReportPermissions::READ,
|
||||
ReportPermissions::MANAGE,
|
||||
ProjectPermissions::READ,
|
||||
|
|
|
@ -2759,6 +2759,8 @@ en:
|
|||
title: "Tasks linked to protocol %{protocol}"
|
||||
used_in: "Number of tasks linked to this protocol: %{nr}"
|
||||
no_linked_children: "This protocol is not linked to any task."
|
||||
length_menu: "Show %{number} per page"
|
||||
show_version: "Show version"
|
||||
archive:
|
||||
description: "Archived protocols can only be seen by you. Restoring protocols will return them to their previous location (team/my protocols)."
|
||||
restore: "Restore"
|
||||
|
|
|
@ -87,8 +87,8 @@ en:
|
|||
uncomplete_task_html: "%{user} uncompleted task %{my_module}."
|
||||
assign_repository_record_html: "%{user} assigned inventory item(s) %{record_names} from inventory %{repository} to task %{my_module}."
|
||||
unassign_repository_record_html: "%{user} unassigned inventory item(s) %{record_names} from inventory %{repository} to task %{my_module}."
|
||||
assign_user_to_project_html: "%{user} assigned user %{user_target} with user role %{role} to project %{project}."
|
||||
unassign_user_from_project_html: "%{user} removed user %{user_target} with user role %{role} from project %{project}."
|
||||
assign_user_to_project_html: "%{user} granted with access %{user_target} with user role %{role} to project %{project}."
|
||||
unassign_user_from_project_html: "%{user} removed %{user_target} with user role %{role} from project %{project}."
|
||||
change_user_role_on_project_html: "%{user} changed %{user_target}'s role on project %{project} to %{role}."
|
||||
change_user_role_on_experiment_html: "%{user} changed %{user_target}'s role on experiment %{experiment} to %{role}."
|
||||
change_user_role_on_my_module_html: "%{user} changed %{user_target}'s role on task %{my_module} to %{role}."
|
||||
|
@ -254,14 +254,14 @@ en:
|
|||
label_template_edited_html: "%{user} edited %{type} label template %{label_template} in Label templates."
|
||||
label_template_deleted_html: "%{user} deleted %{type} label template %{label_template} in Label templates."
|
||||
label_template_copied_html: "%{user} copied %{type} label template %{label_template_new} from %{label_template_original} in Label templates."
|
||||
protocol_template_published_html: "%{user} published protocol template %{protocol} version %{version_number}"
|
||||
protocol_template_revision_notes_updated_html: "%{user} edited revision notes of %{protocol}"
|
||||
protocol_template_draft_deleted_html: "%{user} deleted draft of %{protocol}"
|
||||
protocol_template_draft_created_html: "%{user} created draft of %{protocol}"
|
||||
protocol_template_access_granted_html: "%{user} granted access to %{user_target} with user role %{role} to protocol template %{protocol}"
|
||||
protocol_template_access_changed_html: "%{user} changed %{user_target}’s role on protocol template %{protocol} to %{role}"
|
||||
protocol_template_access_revoked_html: "%{user} removed %{user_target} with user role %{role} from protocol template %{protocol}"
|
||||
task_protocol_save_to_template_html: "%{user} created a new protocol template %{protocol} from a task"
|
||||
protocol_template_published_html: "%{user} published protocol template %{protocol} version %{version_number}."
|
||||
protocol_template_revision_notes_updated_html: "%{user} edited revision notes of %{protocol}."
|
||||
protocol_template_draft_deleted_html: "%{user} deleted draft of %{protocol}."
|
||||
protocol_template_draft_created_html: "%{user} created draft of %{protocol}."
|
||||
protocol_template_access_granted_html: "%{user} granted with access %{user_target} with user role %{role} to protocol template %{protocol}."
|
||||
protocol_template_access_changed_html: "%{user} changed %{user_target}’s role on protocol template %{protocol} to %{role}."
|
||||
protocol_template_access_revoked_html: "%{user} removed %{user_target} with user role %{role} from protocol template %{protocol}."
|
||||
task_protocol_save_to_template_html: "%{user} created a new protocol template %{protocol} from a task."
|
||||
activity_name:
|
||||
create_project: "Project created"
|
||||
rename_project: "Project renamed"
|
||||
|
@ -487,7 +487,7 @@ en:
|
|||
experiment: "Experiment"
|
||||
reports: "Reports"
|
||||
inventories: "Inventories"
|
||||
protocol_repository: "Protocol repository"
|
||||
protocol_repository: "Protocol templates"
|
||||
team: "Team"
|
||||
exports: "Exports"
|
||||
label_repository: "Label repository"
|
||||
|
|
|
@ -14,13 +14,13 @@ class AddProtocolVersioning < ActiveRecord::Migration[6.1]
|
|||
end
|
||||
|
||||
execute(
|
||||
'UPDATE "protocols" SET "protocol_type" = 3, "archived" = TRUE WHERE "protocols"."protocol_type" = 4;'
|
||||
'UPDATE "protocols" SET "protocol_type" = 6, "archived" = TRUE WHERE "protocols"."protocol_type" = 4;'
|
||||
)
|
||||
execute(
|
||||
'UPDATE "protocols" SET "visibility" = 1 WHERE "protocols"."protocol_type" = 3;'
|
||||
)
|
||||
execute(
|
||||
'UPDATE "protocols" SET "protocol_type" = 2 ' \
|
||||
'UPDATE "protocols" SET "protocol_type" = 5 ' \
|
||||
'WHERE "id" IN (' \
|
||||
'SELECT DISTINCT "protocols"."id" FROM "protocols" ' \
|
||||
'JOIN "protocols" "linked_children" ON "linked_children"."parent_id" = "protocols"."id" ' \
|
||||
|
@ -28,7 +28,7 @@ class AddProtocolVersioning < ActiveRecord::Migration[6.1]
|
|||
');'
|
||||
)
|
||||
execute(
|
||||
'UPDATE "protocols" SET "protocol_type" = 3 ' \
|
||||
'UPDATE "protocols" SET "protocol_type" = 6 ' \
|
||||
'WHERE "id" IN (' \
|
||||
'SELECT DISTINCT "protocols"."id" FROM "protocols" ' \
|
||||
'LEFT OUTER JOIN "protocols" "linked_children" ON "linked_children"."parent_id" = "protocols"."id" ' \
|
||||
|
@ -37,16 +37,24 @@ class AddProtocolVersioning < ActiveRecord::Migration[6.1]
|
|||
)
|
||||
execute(
|
||||
'UPDATE "protocols" SET "published_on" = "created_at", "published_by_id" = "added_by_id" ' \
|
||||
'WHERE "protocols"."protocol_type" = 2 ' \
|
||||
'WHERE "protocols"."protocol_type" = 5 ' \
|
||||
'AND "protocols"."published_on" IS NULL;'
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
execute(
|
||||
'UPDATE "protocols" SET "protocol_type" = 4 WHERE "protocols"."protocol_type" IN (2, 3, 4) AND ' \
|
||||
'UPDATE "protocols" SET "protocol_type" = 4 WHERE "protocols"."protocol_type" IN (5, 6, 7) AND ' \
|
||||
'"protocols"."archived" = TRUE;'
|
||||
)
|
||||
execute(
|
||||
'UPDATE "protocols" SET "protocol_type" = 3 WHERE "protocols"."protocol_type" IN (5, 6, 7) AND ' \
|
||||
'"protocols"."visibility" = 1;'
|
||||
)
|
||||
execute(
|
||||
'UPDATE "protocols" SET "protocol_type" = 2 WHERE "protocols"."protocol_type" IN (5, 6, 7) AND ' \
|
||||
'"protocols"."visibility" != 1;'
|
||||
)
|
||||
change_table :protocols, bulk: true do |t|
|
||||
t.remove_references :published_by
|
||||
t.remove_references :last_modified_by
|
||||
|
|
|
@ -9,6 +9,10 @@ class AddProtocolVersioningPermissions < ActiveRecord::Migration[6.1]
|
|||
ProtocolPermissions::MANAGE_DRAFT
|
||||
].freeze
|
||||
|
||||
REMOVED_NORMAL_USER_PERMISSIONS = [
|
||||
ProtocolPermissions::MANAGE
|
||||
].freeze
|
||||
|
||||
def change
|
||||
reversible do |dir|
|
||||
dir.up do
|
||||
|
@ -16,6 +20,7 @@ class AddProtocolVersioningPermissions < ActiveRecord::Migration[6.1]
|
|||
@normal_user_role = UserRole.find_predefined_normal_user_role
|
||||
@owner_role.permissions = @owner_role.permissions | OWNER_PERMISSIONS
|
||||
@normal_user_role.permissions = @normal_user_role.permissions | NORMAL_USER_PERMISSIONS
|
||||
@normal_user_role.permissions = @normal_user_role.permissions - REMOVED_NORMAL_USER_PERMISSIONS
|
||||
@owner_role.save(validate: false)
|
||||
@normal_user_role.save(validate: false)
|
||||
end
|
||||
|
@ -25,6 +30,7 @@ class AddProtocolVersioningPermissions < ActiveRecord::Migration[6.1]
|
|||
@normal_user_role = UserRole.find_predefined_normal_user_role
|
||||
@owner_role.permissions = @owner_role.permissions - OWNER_PERMISSIONS
|
||||
@normal_user_role.permissions = @normal_user_role.permissions - NORMAL_USER_PERMISSIONS
|
||||
@normal_user_role.permissions = @normal_user_role.permissions | REMOVED_NORMAL_USER_PERMISSIONS
|
||||
@owner_role.save(validate: false)
|
||||
@normal_user_role.save(validate: false)
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue