Merge branch 'features/protocol_versioning' into sb_SCI-8108

This commit is contained in:
Soufiane 2023-03-16 09:20:36 +01:00 committed by GitHub
commit bc900cd26d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 485 additions and 220 deletions

View file

@ -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();
});

View file

@ -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);

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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

View file

@ -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);

View file

@ -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');

View file

@ -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 {

View file

@ -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 {

View file

@ -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;

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -54,6 +54,14 @@
margin-bottom: 0;
white-space: nowrap;
}
.dropdown-selector-container {
width: 170px;
.search-field {
padding-left: 8px;
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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,

View file

@ -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;
}
}

View file

@ -148,6 +148,10 @@
}
}
.user-name {
margin-right: 15px;
}
.notification {
padding-right: 8px;
word-wrap: break-word;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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',

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -14,7 +14,8 @@ module ProjectsHelper
end
def user_names_with_roles(user_assignments)
user_assignments.map { |up| user_name_with_role(up) }.join('&#013;')
names_with_roles = user_assignments.map { |up| user_name_with_role(up) }.join('&#013;')
sanitize_input(names_with_roles)
end
def user_name_with_role(user_assignment)

View file

@ -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

View file

@ -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;

View file

@ -86,7 +86,9 @@ export default {
});
filesUploadedCntr += 1;
if (filesUploadedCntr === filesToUploadCntr) {
this.$emit('stepUpdated');
setTimeout(() => {
this.$emit('stepUpdated');
}, 1000);
resolve('done');
}
}

View file

@ -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() {

View file

@ -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">

View file

@ -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');
})

View file

@ -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

View file

@ -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

View file

@ -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|

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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>

View file

@ -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 %>

View file

@ -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' %>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 %>

View file

@ -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"

View file

@ -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>

View file

@ -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 %>

View file

@ -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? %>

View file

@ -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">&times;</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>

View file

@ -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'>

View file

@ -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">

View file

@ -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,

View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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