diff --git a/Gemfile b/Gemfile index 69ed0c7e9..34ca6913d 100644 --- a/Gemfile +++ b/Gemfile @@ -38,7 +38,7 @@ gem 'rack-cors' gem 'uglifier', '>= 1.3.0' -gem 'activerecord-import' +gem 'activerecord-import', '~> 2.2.0' gem 'acts_as_list' gem 'ajax-datatables-rails', '~> 0.3.1' gem 'aspector' # Aspect-oriented programming for Rails @@ -70,7 +70,6 @@ gem 'rubyzip', '>= 2.3.0' # will load new rubyzip version gem 'scenic', '~> 1.4' gem 'sdoc', '~> 1.0', group: :doc gem 'silencer' # Silence certain Rails logs -gem 'sneaky-save', git: 'https://github.com/einzige/sneaky-save' gem 'turbolinks', '~> 5.2.0' gem 'underscore-rails' gem 'wicked_pdf' @@ -95,8 +94,12 @@ gem 'js-routes' gem 'tailwindcss-rails', '~> 2.4' gem 'base62' # Used for smart annotations -gem 'datadog' gem 'newrelic_rpm' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-instrumentation-pg' +gem 'opentelemetry-instrumentation-rails' +gem 'opentelemetry-propagator-xray' +gem 'opentelemetry-sdk' # Permission helper Gem gem 'canaid', git: 'https://github.com/scinote-eln/canaid' diff --git a/Gemfile.lock b/Gemfile.lock index 99f845814..7d2ec2351 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,10 +1,3 @@ -GIT - remote: https://github.com/einzige/sneaky-save - revision: ee71d0a00cd4ecdd575bd2a9aa8b8693915f4871 - specs: - sneaky-save (0.1.3) - activerecord (>= 3.2.0) - GIT remote: https://github.com/scinote-eln/canaid revision: bba1b817d1c9b0c7e0440a83d0f62848aabc0a1b @@ -43,29 +36,29 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.2.2.1) - actionpack (= 7.2.2.1) - activesupport (= 7.2.2.1) + actioncable (7.2.2.2) + actionpack (= 7.2.2.2) + activesupport (= 7.2.2.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.2.1) - actionpack (= 7.2.2.1) - activejob (= 7.2.2.1) - activerecord (= 7.2.2.1) - activestorage (= 7.2.2.1) - activesupport (= 7.2.2.1) + actionmailbox (7.2.2.2) + actionpack (= 7.2.2.2) + activejob (= 7.2.2.2) + activerecord (= 7.2.2.2) + activestorage (= 7.2.2.2) + activesupport (= 7.2.2.2) mail (>= 2.8.0) - actionmailer (7.2.2.1) - actionpack (= 7.2.2.1) - actionview (= 7.2.2.1) - activejob (= 7.2.2.1) - activesupport (= 7.2.2.1) + actionmailer (7.2.2.2) + actionpack (= 7.2.2.2) + actionview (= 7.2.2.2) + activejob (= 7.2.2.2) + activesupport (= 7.2.2.2) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.2.1) - actionview (= 7.2.2.1) - activesupport (= 7.2.2.1) + actionpack (7.2.2.2) + actionview (= 7.2.2.2) + activesupport (= 7.2.2.2) nokogiri (>= 1.8.5) racc rack (>= 2.2.4, < 3.2) @@ -74,15 +67,15 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (7.2.2.1) - actionpack (= 7.2.2.1) - activerecord (= 7.2.2.1) - activestorage (= 7.2.2.1) - activesupport (= 7.2.2.1) + actiontext (7.2.2.2) + actionpack (= 7.2.2.2) + activerecord (= 7.2.2.2) + activestorage (= 7.2.2.2) + activesupport (= 7.2.2.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.2.1) - activesupport (= 7.2.2.1) + actionview (7.2.2.2) + activesupport (= 7.2.2.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -92,16 +85,16 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (7.2.2.1) - activesupport (= 7.2.2.1) + activejob (7.2.2.2) + activesupport (= 7.2.2.2) globalid (>= 0.3.6) - activemodel (7.2.2.1) - activesupport (= 7.2.2.1) - activerecord (7.2.2.1) - activemodel (= 7.2.2.1) - activesupport (= 7.2.2.1) + activemodel (7.2.2.2) + activesupport (= 7.2.2.2) + activerecord (7.2.2.2) + activemodel (= 7.2.2.2) + activesupport (= 7.2.2.2) timeout (>= 0.4.0) - activerecord-import (1.4.1) + activerecord-import (2.2.0) activerecord (>= 4.2) activerecord-session_store (2.1.0) actionpack (>= 6.1) @@ -110,13 +103,13 @@ GEM multi_json (~> 1.11, >= 1.11.2) rack (>= 2.0.8, < 4) railties (>= 6.1) - activestorage (7.2.2.1) - actionpack (= 7.2.2.1) - activejob (= 7.2.2.1) - activerecord (= 7.2.2.1) - activesupport (= 7.2.2.1) + activestorage (7.2.2.2) + actionpack (= 7.2.2.2) + activejob (= 7.2.2.2) + activerecord (= 7.2.2.2) + activesupport (= 7.2.2.2) marcel (~> 1.0) - activesupport (7.2.2.1) + activesupport (7.2.2.2) base64 benchmark (>= 0.3) bigdecimal @@ -195,14 +188,14 @@ GEM aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) base62 (1.0.0) - base64 (0.2.0) + base64 (0.3.0) bcrypt (3.1.18) - benchmark (0.4.0) + benchmark (0.4.1) better_errors (2.10.1) erubi (>= 1.0.0) rack (>= 0.9.0) rouge (>= 1.0.0) - bigdecimal (3.2.0) + bigdecimal (3.2.2) bindata (2.5.0) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) @@ -292,13 +285,6 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - datadog (2.14.0) - datadog-ruby_core_source (~> 3.4) - libdatadog (~> 16.0.1.1.0) - libddwaf (~> 1.21.0.0.1) - logger - msgpack - datadog-ruby_core_source (3.4.0) date (3.4.1) debug_inspector (1.1.0) deface (1.9.0) @@ -361,6 +347,14 @@ GEM raabro (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) + google-protobuf (4.31.1-arm64-darwin) + bigdecimal + rake (>= 13) + google-protobuf (4.31.1-x86_64-linux-gnu) + bigdecimal + rake (>= 13) + googleapis-common-protos-types (1.20.0) + google-protobuf (>= 3.18, < 5.a) graphviz (1.2.1) process-pipeline grover (1.2.3) @@ -430,12 +424,6 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) - libdatadog (16.0.1.1.0) - libdatadog (16.0.1.1.0-x86_64-linux) - libddwaf (1.21.0.0.1-arm64-darwin) - ffi (~> 1.0) - libddwaf (1.21.0.0.1-x86_64-linux) - ffi (~> 1.0) listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -536,6 +524,82 @@ GEM validate_email validate_url webfinger (~> 2.0) + opentelemetry-api (1.5.0) + opentelemetry-common (0.22.0) + opentelemetry-api (~> 1.0) + opentelemetry-exporter-otlp (0.30.0) + google-protobuf (>= 3.18) + googleapis-common-protos-types (~> 1.3) + opentelemetry-api (~> 1.1) + opentelemetry-common (~> 0.20) + opentelemetry-sdk (~> 1.2) + opentelemetry-semantic_conventions + opentelemetry-helpers-sql (0.1.1) + opentelemetry-api (~> 1.0) + opentelemetry-helpers-sql-obfuscation (0.3.0) + opentelemetry-common (~> 0.21) + opentelemetry-instrumentation-action_mailer (0.4.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-active_support (~> 0.7) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-action_pack (0.12.3) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-rack (~> 0.21) + opentelemetry-instrumentation-action_view (0.9.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-active_support (~> 0.7) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-active_job (0.8.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-active_record (0.9.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-active_storage (0.1.1) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-active_support (~> 0.7) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-active_support (0.8.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-base (0.23.0) + opentelemetry-api (~> 1.0) + opentelemetry-common (~> 0.21) + opentelemetry-registry (~> 0.1) + opentelemetry-instrumentation-concurrent_ruby (0.22.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-pg (0.30.1) + opentelemetry-api (~> 1.0) + opentelemetry-helpers-sql + opentelemetry-helpers-sql-obfuscation + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-rack (0.26.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-rails (0.36.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-action_mailer (~> 0.4.0) + opentelemetry-instrumentation-action_pack (~> 0.12.0) + opentelemetry-instrumentation-action_view (~> 0.9.0) + opentelemetry-instrumentation-active_job (~> 0.8.0) + opentelemetry-instrumentation-active_record (~> 0.9.0) + opentelemetry-instrumentation-active_storage (~> 0.1.0) + opentelemetry-instrumentation-active_support (~> 0.8.0) + opentelemetry-instrumentation-base (~> 0.23.0) + opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0) + opentelemetry-propagator-xray (0.24.0) + opentelemetry-api (~> 1.0) + opentelemetry-registry (0.4.0) + opentelemetry-api (~> 1.1) + opentelemetry-sdk (1.8.0) + opentelemetry-api (~> 1.1) + opentelemetry-common (~> 0.20) + opentelemetry-registry (~> 0.2) + opentelemetry-semantic_conventions + opentelemetry-semantic_conventions (1.11.0) + opentelemetry-api (~> 1.0) orm_adapter (0.5.0) ostruct (0.6.0) overcommit (0.60.0) @@ -597,20 +661,20 @@ GEM rackup (1.0.1) rack (< 3) webrick - rails (7.2.2.1) - actioncable (= 7.2.2.1) - actionmailbox (= 7.2.2.1) - actionmailer (= 7.2.2.1) - actionpack (= 7.2.2.1) - actiontext (= 7.2.2.1) - actionview (= 7.2.2.1) - activejob (= 7.2.2.1) - activemodel (= 7.2.2.1) - activerecord (= 7.2.2.1) - activestorage (= 7.2.2.1) - activesupport (= 7.2.2.1) + rails (7.2.2.2) + actioncable (= 7.2.2.2) + actionmailbox (= 7.2.2.2) + actionmailer (= 7.2.2.2) + actionpack (= 7.2.2.2) + actiontext (= 7.2.2.2) + actionview (= 7.2.2.2) + activejob (= 7.2.2.2) + activemodel (= 7.2.2.2) + activerecord (= 7.2.2.2) + activestorage (= 7.2.2.2) + activesupport (= 7.2.2.2) bundler (>= 1.15.0) - railties (= 7.2.2.1) + railties (= 7.2.2.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -626,9 +690,9 @@ GEM actionview (> 3.1) activesupport (> 3.1) railties (> 3.1) - railties (7.2.2.1) - actionpack (= 7.2.2.1) - activesupport (= 7.2.2.1) + railties (7.2.2.2) + actionpack (= 7.2.2.2) + activesupport (= 7.2.2.2) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -698,7 +762,7 @@ GEM rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) ruby-progressbar (1.13.0) - ruby-saml (1.18.0) + ruby-saml (1.18.1) nokogiri (>= 1.13.10) rexml ruby-vips (2.1.4) @@ -810,7 +874,7 @@ PLATFORMS DEPENDENCIES active_model_serializers (~> 0.10.15) - activerecord-import + activerecord-import (~> 2.2.0) activerecord-session_store acts_as_list ajax-datatables-rails (~> 0.3.1) @@ -836,7 +900,6 @@ DEPENDENCIES cssbundling-rails cucumber-rails database_cleaner - datadog deface (~> 1.9) delayed_job_active_record devise (~> 4.9.4) @@ -876,6 +939,11 @@ DEPENDENCIES omniauth-rails_csrf_protection (~> 1.0) omniauth-saml omniauth_openid_connect + opentelemetry-exporter-otlp + opentelemetry-instrumentation-pg + opentelemetry-instrumentation-rails + opentelemetry-propagator-xray + opentelemetry-sdk overcommit pg (~> 1.5) pg_search @@ -907,7 +975,6 @@ DEPENDENCIES shoulda-matchers silencer simplecov - sneaky-save! sprockets-rails tailwindcss-rails (~> 2.4) timecop diff --git a/VERSION b/VERSION index 758e98242..372cf402c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.43.0.2 +1.44.0 diff --git a/app/assets/images/icon/group.svg b/app/assets/images/icon/group.svg new file mode 100644 index 000000000..e7057628d --- /dev/null +++ b/app/assets/images/icon/group.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/assets/images/user_groups/promo.svg b/app/assets/images/user_groups/promo.svg new file mode 100644 index 000000000..d8cba3029 --- /dev/null +++ b/app/assets/images/user_groups/promo.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/javascripts/my_modules/archived.js b/app/assets/javascripts/my_modules/archived.js index 19c2f4691..4096bea4b 100644 --- a/app/assets/javascripts/my_modules/archived.js +++ b/app/assets/javascripts/my_modules/archived.js @@ -82,13 +82,9 @@ id: data.data.id, type: data.data.type }; - const { rolesUrl } = container.dataset; - const params = { - object, - roles_path: rolesUrl - }; + const modal = $('#accessModalComponent').data('accessModal'); - modal.params = params; + modal.params = { object }; modal.open(); }); }); diff --git a/app/assets/javascripts/protocols/new_protocol.js b/app/assets/javascripts/protocols/new_protocol.js index 1b8735365..70493cdf7 100644 --- a/app/assets/javascripts/protocols/new_protocol.js +++ b/app/assets/javascripts/protocols/new_protocol.js @@ -3,17 +3,6 @@ const protocolModal = '#newProtocolModal'; const submitButton = $('.create-protocol-button'); - let roleSelector = `${protocolModal} #protocol_role_selector`; - dropdownSelector.init(roleSelector, { - noEmptyOption: true, - singleSelect: true, - closeOnSelect: true, - selectAppearance: 'simple', - onChange: function() { - $('#protocol_default_public_user_role_id').val(dropdownSelector.getValues(roleSelector)); - } - }); - $(protocolModal) .on('input', '#protocol_name', function() { if ($(this).val().length >= GLOBAL_CONSTANTS.NAME_MIN_LENGTH) { @@ -22,11 +11,6 @@ submitButton.attr('disabled', 'disabled'); } }) - .on('change', '#protocol_visibility', function() { - let checked = $(this)[0].checked; - $('#roleSelectWrapper').toggleClass('hidden', !checked); - $('#protocol_default_public_user_role_id').prop('disabled', !checked); - }) .on('submit', function() { submitButton.attr('disabled', 'disabled'); }) diff --git a/app/assets/javascripts/protocols/protocolsio.js b/app/assets/javascripts/protocols/protocolsio.js index 65dde90dd..ad2c1cda9 100644 --- a/app/assets/javascripts/protocols/protocolsio.js +++ b/app/assets/javascripts/protocols/protocolsio.js @@ -198,15 +198,6 @@ var protocolsIO = function() { e.stopPropagation(); animateSpinner(modal, true); - const visibility = $('#protocol-preview-modal .modal-footer #visibility').prop('checked'); - const defaultPublicUserRoleId = $('#protocol-preview-modal .modal-footer #default_public_user_role_id') - .prop('value'); - const visibilityField = $('#protocol-preview-modal #protocol_visibility'); - const defaultPublicUserRoleIdField = $('#protocol-preview-modal #protocol_default_public_user_role_id'); - - visibilityField.prop('value', visibility ? 'visible' : 'hidden'); - defaultPublicUserRoleIdField.prop('value', defaultPublicUserRoleId); - $.ajax({ type: 'POST', url: url, @@ -291,24 +282,6 @@ var protocolsIO = function() { $('form.protocols-search-bar').submit(); function initProtocolModalPreview() { - $('#protocol-preview-modal').on('change', '#visibility', function() { - const checkbox = this; - $('#protocol-preview-modal #roleSelectWrapper').toggleClass('hidden', !checkbox.checked); - }); - - - const roleSelector = '#protocol-preview-modal #role_selector'; - - dropdownSelector.init(roleSelector, { - noEmptyOption: true, - singleSelect: true, - closeOnSelect: true, - selectAppearance: 'simple', - onChange: function() { - $('#protocol-preview-modal #default_public_user_role_id').val(dropdownSelector.getValues(roleSelector)); - } - }); - $('#protocol-preview-modal') .on('ajax:error', 'form', function(e, error) { let msg = error.responseJSON.error; diff --git a/app/assets/javascripts/shared/inline_editing.js b/app/assets/javascripts/shared/inline_editing.js index 0d5241d1c..add20c34f 100644 --- a/app/assets/javascripts/shared/inline_editing.js +++ b/app/assets/javascripts/shared/inline_editing.js @@ -12,7 +12,9 @@ let inlineEditing = (function() { } if ($(container).data('params-group') === 'protocol' && $(container).hasClass('inline-editing-container')) { - $('.view-mode').text(I18n.t('protocols.draft_name', { name: $('.view-mode').text() })); + container.find('.view-mode').text(I18n.t('protocols.draft_name', { name: container.find('.view-mode').text() })); + } else if ($(container).data('params-group') === 'user_group' && $(container).hasClass('inline-editing-container')) { + container.find('.view-mode').text(`${I18n.t('user_groups.show.title')} ${container.find('.view-mode').text()}`); } } diff --git a/app/assets/javascripts/users/settings/teams/show.js b/app/assets/javascripts/users/settings/teams/show.js index d9a887b2a..cd213e4b6 100644 --- a/app/assets/javascripts/users/settings/teams/show.js +++ b/app/assets/javascripts/users/settings/teams/show.js @@ -150,7 +150,11 @@ if (data.status === 'done') { // Reload the whole table HelperModule.flashAlertMsg(jobData.success_message, 'success'); - usersDatatable.ajax.reload(); + if(jobData.redirect_url) { + window.location.href = jobData.redirect_url; + } else { + usersDatatable.ajax.reload(); + } animateSpinner(null, false); $('#destroy-user-team-modal').modal('hide'); clearInterval(jobStatusInterval); diff --git a/app/assets/stylesheets/shared/content_pane.scss b/app/assets/stylesheets/shared/content_pane.scss index b60b61ae0..7ce218414 100644 --- a/app/assets/stylesheets/shared/content_pane.scss +++ b/app/assets/stylesheets/shared/content_pane.scss @@ -17,6 +17,14 @@ .fixed-content-body { height: calc(100vh - var(--content-header-size) - var(--navbar-height)); width: 100%; + + &.user-groups-table-container { + height: calc(100vh - var(--content-header-size) - var(--navbar-height) - 72px); + } + + &.user-group-table-container { + height: calc(100vh - var(--content-header-size) - var(--navbar-height) - 104px); + } } .content-header { diff --git a/app/components/reports/repositories_input_component.rb b/app/components/reports/repositories_input_component.rb index f168cc396..f6dc1dc17 100644 --- a/app/components/reports/repositories_input_component.rb +++ b/app/components/reports/repositories_input_component.rb @@ -4,10 +4,10 @@ module Reports class RepositoriesInputComponent < TemplateValueComponent def initialize(report:, name:, label:, placeholder: nil, editing: true, displayed_field: :name, user: nil) super(report: report, name: name, label: label, placeholder: placeholder, editing: editing) - live_repositories = Repository.viewable_by_user(user, report.team).sort_by { |r| r.name.downcase } + live_repositories = Repository.readable_by_user(user, report.team).sort_by { |r| r.name.downcase } snapshots_of_deleted = RepositorySnapshot.left_outer_joins(:original_repository) .where(team: report.team) - .where.not(original_repository: live_repositories) + .where.not(original_repository: report.team.repositories) .select('DISTINCT ON ("repositories"."parent_id") "repositories".*') .sort_by { |r| r.name.downcase } @repositories = live_repositories + snapshots_of_deleted diff --git a/app/controllers/access_permissions/base_controller.rb b/app/controllers/access_permissions/base_controller.rb new file mode 100644 index 000000000..4b493fbee --- /dev/null +++ b/app/controllers/access_permissions/base_controller.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +module AccessPermissions + class BaseController < ApplicationController + include InputSanitizeHelper + include UserRolesHelper + + before_action :set_model + before_action :set_assignment, only: %i(create update destroy) + before_action :check_read_permissions, only: %i(show show_user_group_assignments user_roles) + before_action :check_manage_permissions, except: %i(show show_user_group_assignments user_roles) + before_action :load_available_users, only: %i(new create) + + def show + render json: @model.user_assignments.where(team: current_team).includes(:user_role, :user).order('users.full_name ASC'), + each_serializer: UserAssignmentSerializer, user: current_user + end + + def new + render json: @available_users, each_serializer: UserSerializer, user: current_user + end + + def edit; end + + def create + ActiveRecord::Base.transaction do + @assignment.update!( + user_role_id: permitted_params[:user_role_id], + assigned_by: current_user, + assigned: :manually + ) + + case assignment_type + when :team + log_activity(:"#{model_parameter}_access_granted_all_team_members", team: @assignment.team.id, role: @assignment.user_role.name) + when :user + log_activity(:"#{model_parameter}_access_granted", user_target: @assignment.user.id, role: @assignment.user_role.name) + when :user_group + log_activity(:"#{model_parameter}_access_granted_user_group", user_group: @assignment.user_group.id, role: @assignment.user_role.name) + end + + propagate_job + + @message = if assignment_type == :team + t('access_permissions.create.success', member_name: t('access_permissions.all_team')) + else + t('access_permissions.create.success', member_name: escape_input(assignment_type == :user_group ? @assignment.user_group.name : @assignment.user.name)) + end + render json: { message: @message } + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error e.message + errors = @model.errors.present? ? @model.errors&.map(&:message)&.join(',') : e.message + render json: { flash: errors }, status: :unprocessable_entity + raise ActiveRecord::Rollback + end + end + + def update + # prevent role change if it would result in no manually assigned users having the user management permission + new_user_role = UserRole.find(permitted_params[:user_role_id]) + if permitted_params[:user_id].present? && permitted_params[:user_id] != 'all' && !new_user_role.has_permission?(manage_permission_constant) && + @assignment.last_with_permission?(manage_permission_constant, assigned: :manually) + raise ActiveRecord::RecordInvalid + end + + @assignment.update!(user_role_id: permitted_params[:user_role_id], assigned_by: current_user) + + case assignment_type + when :team + log_activity(:"#{model_parameter}_access_changed_all_team_members", team: @assignment.team.id, role: @assignment.user_role.name) + when :user + log_activity(:"#{model_parameter}_access_changed", user_target: @assignment.user.id, role: @assignment.user_role.name) + when :user_group + log_activity(:"#{model_parameter}_access_changed_user_group", user_group: @assignment.user_group.id, role: @assignment.user_role.name) + end + + propagate_job + + render json: { user_role_id: @assignment.user_role_id }, status: :ok + rescue ActiveRecord::RecordInvalid + render json: { flash: t('access_permissions.update.failure') }, status: :unprocessable_entity + end + + def destroy + # prevent deletion of last manually assigned user that can manage users + if params[:user_id].present? && params[:user_id] != 'all' && @assignment.last_with_permission?(manage_permission_constant, assigned: :manually) + raise ActiveRecord::RecordInvalid + end + + UserAssignments::PropagateAssignmentJob.perform_now(@assignment, destroy: true) + + case assignment_type + when :team + @assigned_name = @assignment.team.name + log_activity(:"#{model_parameter}_access_revoked_all_team_members", team: @assignment.team.id, role: @assignment.user_role.name) + when :user_group + @assigned_name = @assignment.user_group.name + log_activity(:"#{model_parameter}_access_revoked_user_group", user_group: @assignment.user_group.id, role: @assignment.user_role.name) + when :user + @assigned_name = @assignment.user.full_name + log_activity(:"#{model_parameter}_access_revoked", user_target: @assignment.user.id, role: @assignment.user_role.name) + end + + render json: { message: t('access_permissions.destroy.success', member_name: escape_input(@assigned_name)) } + rescue ActiveRecord::RecordInvalid + render json: { message: t('access_permissions.destroy.failure') }, + status: :unprocessable_entity + end + + def show_user_group_assignments + render json: @model.user_group_assignments.where(team: current_team).includes(:user_role, :user_group).order('user_groups.name ASC'), + each_serializer: UserGroupAssignmentSerializer, user: current_user + end + + def unassigned_user_groups + render json: current_team.user_groups.where.not(id: @model.user_group_assignments.select(:user_group_id)), + each_serializer: UserGroupSerializer, user: current_user + end + + def user_roles + render json: { data: user_roles_collection(@model).map(&:reverse) } + end + + private + + def model_parameter + @model.class.permission_class.model_name.param_key + end + + def manage_permission_constant + "#{@model.class.permission_class.name}Permissions::USERS_MANAGE".constantize + end + + def permitted_default_public_user_role_params + params.require(:object).permit(:default_public_user_role_id) + end + + def permitted_params + params.require(:user_assignment) + .permit(%i(user_role_id user_id user_group_id team_id)) + end + + def load_available_users + @available_users = current_team.users.where.not(id: @model.user_assignments.where(team: current_team).select(:user_id)).order(users: { full_name: :asc }) + end + + def propagate_job(destroy: false) + return unless @model.has_permission_children? + + UserAssignments::PropagateAssignmentJob.perform_later( + @assignment, + destroy: destroy + ) + end + + def check_manage_permissions + raise NotImplementedError + end + + def check_read_permissions + raise NotImplementedError + end + + def log_activity(type_of, message_items = {}) + message_items = { model_parameter => @model.id }.merge(message_items) + + Activities::CreateActivityService + .call(activity_type: type_of, + owner: current_user, + subject: @model, + team: @model.team, + project: @project, + message_items: message_items) + end + + def set_assignment + case assignment_type + when :user, :user_group + @assignment = @model.public_send(:"#{assignment_type}_assignments").find_or_initialize_by( + "#{assignment_type}_id": permitted_params[:"#{assignment_type}_id"], + team: current_team + ) + when :team + @assignment = + @model.team_assignments + .find_or_initialize_by(team: current_team, assignable: @model) + end + end + + def assignment_type + if permitted_params[:team_id].present? || permitted_params[:user_id] == 'all' + :team + elsif permitted_params[:user_group_id].present? + :user_group + elsif permitted_params[:user_id].present? + :user + end + end + end +end diff --git a/app/controllers/access_permissions/experiments_controller.rb b/app/controllers/access_permissions/experiments_controller.rb index 0da893b75..0f661007e 100644 --- a/app/controllers/access_permissions/experiments_controller.rb +++ b/app/controllers/access_permissions/experiments_controller.rb @@ -1,90 +1,61 @@ # frozen_string_literal: true module AccessPermissions - class ExperimentsController < ApplicationController - before_action :set_experiment + class ExperimentsController < BaseController before_action :set_project - before_action :check_read_permissions, only: %i(show) - before_action :check_manage_permissions, only: %i(edit update) - - def show - render json: @experiment.user_assignments.includes(:user_role, :user).order('users.full_name ASC'), - each_serializer: UserAssignmentSerializer, user: current_user - end - - def new - render json: @available_users, each_serializer: UserSerializer, user: current_user - end - - - def edit; end def update - user_id = permitted_update_params[:user_id] - @user_assignment = @experiment.user_assignments.find_by(user_id: user_id, team: current_team) + if permitted_params[:user_role_id] == 'reset' + parent_assignment = @project.public_send(:"#{assignment_type}_assignments").find_or_initialize_by( + "#{assignment_type}_id": permitted_params[:"#{assignment_type}_id"] || current_team.id, + team: current_team + ) - if permitted_update_params[:user_role_id] == 'reset' - @user_assignment.update!( - user_role_id: @project.user_assignments.find_by(user_id: user_id, team: current_team).user_role_id, + @assignment.update!( + user_role_id: parent_assignment.user_role_id, + assigned_by: current_user, assigned: :automatically ) else - @user_assignment.update!( - user_role_id: permitted_update_params[:user_role_id], + @assignment.update!( + user_role_id: permitted_params[:user_role_id], + assigned_by: current_user, assigned: :manually ) end - UserAssignments::PropagateAssignmentJob.perform_later( - @experiment, - @user_assignment.user.id, - @user_assignment.user_role, - current_user.id - ) + UserAssignments::PropagateAssignmentJob.perform_later(@assignment) - log_change_activity + case assignment_type + when :team + log_activity(:experiment_access_changed_all_team_members, team: @assignment.team.id, role: @assignment.user_role.name) + when :user_group + log_activity(:experiment_access_changed_user_group, user_group: @assignment.user_group.id, role: @assignment.user_role.name) + when :user + log_activity(:change_user_role_on_experiment, user_target: @assignment.user.id, role: @assignment.user_role.name) + end - render json: {}, status: :ok + render json: { user_role_id: @assignment.user_role_id }, status: :ok end private - def permitted_update_params - params.require(:user_assignment) - .permit(%i(user_role_id user_id)) - end - def set_project - @project = @experiment.project + @project = @model.project end - def set_experiment - @experiment = Experiment.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) + def set_model + @model = Experiment.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) - render_404 unless @experiment + render_404 unless @model end def check_manage_permissions - render_403 unless can_manage_experiment_users?(@experiment) + render_403 unless can_manage_experiment_users?(@model) end def check_read_permissions - render_403 unless can_read_experiment?(@experiment) - end - - def log_change_activity - Activities::CreateActivityService.call( - activity_type: :change_user_role_on_experiment, - owner: current_user, - subject: @experiment, - team: @project.team, - project: @project, - message_items: { - experiment: @experiment.id, - user_target: @user_assignment.user_id, - role: @user_assignment.user_role.name - } - ) + render_403 unless can_read_experiment?(@model) end end end diff --git a/app/controllers/access_permissions/forms_controller.rb b/app/controllers/access_permissions/forms_controller.rb index b530fc444..ca30852e0 100644 --- a/app/controllers/access_permissions/forms_controller.rb +++ b/app/controllers/access_permissions/forms_controller.rb @@ -1,186 +1,21 @@ # frozen_string_literal: true module AccessPermissions - class FormsController < ApplicationController - include InputSanitizeHelper - - before_action :set_form - before_action :check_read_permissions, only: %i(show) - before_action :check_manage_permissions, except: %i(show) - before_action :available_users, only: %i(new create) - - def show - render json: @form.user_assignments.includes(:user_role, :user).order('users.full_name ASC'), - each_serializer: UserAssignmentSerializer, user: current_user - end - - def new - render json: @available_users, each_serializer: UserSerializer, user: current_user - end - - def edit; end - - def create - ActiveRecord::Base.transaction do - created_count = 0 - if permitted_create_params[:user_id] == 'all' - @form.update!(visibility: :visible, default_public_user_role_id: permitted_create_params[:user_role_id]) - log_activity(:form_access_granted_all_team_members, - { team: @form.team.id, role: @form.default_public_user_role&.name }) - else - user_assignment = UserAssignment.find_or_initialize_by( - assignable: @form, - user_id: permitted_create_params[:user_id], - team: current_team - ) - - user_assignment.update!( - user_role_id: permitted_create_params[:user_role_id], - assigned_by: current_user, - assigned: :manually - ) - - log_activity(:form_access_granted, { user_target: user_assignment.user.id, - role: user_assignment.user_role.name }) - created_count += 1 - end - - @message = if created_count.zero? - t('access_permissions.create.success', member_name: t('access_permissions.all_team')) - else - t('access_permissions.create.success', member_name: escape_input(user_assignment.user.name)) - end - render json: { message: @message } - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - errors = @form.errors.present? ? @form.errors&.map(&:message)&.join(',') : e.message - render json: { flash: errors }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - end - - def update - @user_assignment = @form.user_assignments.find_by( - user_id: permitted_update_params[:user_id], - team: current_team - ) - - # prevent role change if it would result in no manually assigned users having the user management permission - new_user_role = UserRole.find(permitted_update_params[:user_role_id]) - if !new_user_role.has_permission?(FormPermissions::USERS_MANAGE) && - @user_assignment.last_with_permission?(FormPermissions::USERS_MANAGE, assigned: :manually) - raise ActiveRecord::RecordInvalid - end - - @user_assignment.update!(permitted_update_params) - log_activity(:form_access_changed, { user_target: @user_assignment.user.id, - role: @user_assignment.user_role.name }) - - render :form_member - rescue ActiveRecord::RecordInvalid - render json: { flash: t('access_permissions.update.failure') }, status: :unprocessable_entity - end - - def destroy - user = @form.assigned_users.find(params[:user_id]) - user_assignment = @form.user_assignments.find_by(user: user, team: current_team) - - # prevent deletion of last manually assigned user that can manage users - raise ActiveRecord::RecordInvalid if user_assignment.last_with_permission?(FormPermissions::USERS_MANAGE, assigned: :manually) - - Protocol.transaction do - if @form.visible? - user_assignment.update!( - user_role: @form.default_public_user_role, - assigned: :automatically - ) - else - user_assignment.destroy! - end - log_activity(:form_access_revoked, { user_target: user_assignment.user.id, - role: user_assignment.user_role.name }) - end - - render json: { message: t('access_permissions.destroy.success', member_name: user.full_name) } - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - render json: { message: t('access_permissions.destroy.failure') }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - - def update_default_public_user_role - ActiveRecord::Base.transaction do - current_role = @form.default_public_user_role.name - @form.update!(permitted_default_public_user_role_params) - - # revoke all team members access - if permitted_default_public_user_role_params[:default_public_user_role_id].blank? - log_activity(:form_access_revoked_all_team_members, - { team: @form.team.id, role: current_role }) - render json: { flash: t('access_permissions.update.revoke_all_team_members') }, status: :ok - else - # update all team members access - log_activity(:form_access_changed_all_team_members, - { team: @form.team.id, role: @form.default_public_user_role&.name }) - end - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - render json: { flash: @form.errors&.map(&:message)&.join(',') }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - end - + class FormsController < BaseController private - def permitted_default_public_user_role_params - params.require(:object).permit(:default_public_user_role_id) - end + def set_model + @model = current_team.forms.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) - def permitted_update_params - params.require(:user_assignment) - .permit(%i(user_role_id user_id)) - end - - def permitted_create_params - params.require(:user_assignment) - .permit(%i(user_id user_role_id)) - end - - def available_users - # automatically assigned or not assigned to project - @available_users = current_team.users.where( - id: @form.user_assignments.automatically_assigned.select(:user_id) - ).or( - current_team.users.where.not(id: @form.users.select(:id)) - ).order('users.full_name ASC') - end - - def set_form - @form = current_team.forms.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) - - return render_404 unless @form - - @form = @form.parent if @form.parent_id + render_404 unless @model end def check_manage_permissions - render_403 unless can_manage_form_users?(@form) + render_403 unless can_manage_form_users?(@model) end def check_read_permissions - render_403 unless can_read_form?(@form) || can_manage_team?(@form.team) - end - - def log_activity(type_of, message_items = {}) - message_items = { form: @form.id }.merge(message_items) - - Activities::CreateActivityService - .call(activity_type: type_of, - owner: current_user, - subject: @form, - team: @form.team, - project: nil, - message_items: message_items) + render_403 unless can_read_form?(@model) || can_manage_team?(@model.team) end end end diff --git a/app/controllers/access_permissions/my_modules_controller.rb b/app/controllers/access_permissions/my_modules_controller.rb index 0e501fbaa..62cc95a0c 100644 --- a/app/controllers/access_permissions/my_modules_controller.rb +++ b/app/controllers/access_permissions/my_modules_controller.rb @@ -1,86 +1,64 @@ # frozen_string_literal: true module AccessPermissions - class MyModulesController < ApplicationController - before_action :set_my_module + class MyModulesController < BaseController before_action :set_experiment before_action :set_project - before_action :check_read_permissions, only: %i(show) - before_action :check_manage_permissions, only: %i(edit update) - - def show - render json: @my_module.user_assignments.includes(:user_role, :user).order('users.full_name ASC'), - each_serializer: UserAssignmentSerializer, user: current_user - end - - def new - render json: @available_users, each_serializer: UserSerializer, user: current_user - end - def edit; end def update - user_id = permitted_update_params[:user_id] - @user_assignment = @my_module.user_assignments.find_by(user_id: user_id, team: current_team) + if permitted_params[:user_role_id] == 'reset' + parent_assignment = @experiment.public_send(:"#{assignment_type}_assignments").find_or_initialize_by( + "#{assignment_type}_id": permitted_params[:"#{assignment_type}_id"] || current_team.id, + team: current_team + ) - if permitted_update_params[:user_role_id] == 'reset' - @user_assignment.update!( - user_role_id: @experiment.user_assignments.find_by(user_id: user_id, team: current_team).user_role_id, + @assignment.update!( + user_role_id: parent_assignment.user_role_id, + assigned_by: current_user, assigned: :automatically ) else - @user_assignment.update!( - user_role_id: permitted_update_params[:user_role_id], + @assignment.update!( + user_role_id: permitted_params[:user_role_id], + assigned_by: current_user, assigned: :manually ) end - log_change_activity + case assignment_type + when :team + log_activity(:my_module_access_changed_all_team_members, team: @assignment.team.id, role: @assignment.user_role.name) + when :user_group + log_activity(:my_module_access_changed_user_group, user_group: @assignment.user_group.id, role: @assignment.user_role.name) + when :user + log_activity(:change_user_role_on_my_module, user_target: @assignment.user.id, role: @assignment.user_role.name) + end - render :my_module_member + render json: { user_role_id: @assignment.user_role_id }, status: :ok end private - def permitted_update_params - params.require(:user_assignment) - .permit(%i(user_role_id user_id)) - end - def set_project @project = @experiment.project end def set_experiment - @experiment = @my_module.experiment + @experiment = @model.experiment end - def set_my_module - @my_module = MyModule.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) + def set_model + @model = MyModule.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) - render_404 unless @my_module + render_404 unless @model end def check_manage_permissions - render_403 unless can_manage_my_module_users?(@my_module) + render_403 unless can_manage_my_module_users?(@model) end def check_read_permissions - render_403 unless can_read_my_module?(@my_module) - end - - def log_change_activity - Activities::CreateActivityService.call( - activity_type: :change_user_role_on_my_module, - owner: current_user, - subject: @my_module, - team: @project.team, - project: @project, - message_items: { - my_module: @my_module.id, - user_target: @user_assignment.user_id, - role: @user_assignment.user_role.name - } - ) + render_403 unless can_read_my_module?(@model) end end end diff --git a/app/controllers/access_permissions/projects_controller.rb b/app/controllers/access_permissions/projects_controller.rb index 3527cfefe..23cf7068e 100644 --- a/app/controllers/access_permissions/projects_controller.rb +++ b/app/controllers/access_permissions/projects_controller.rb @@ -1,206 +1,36 @@ # frozen_string_literal: true module AccessPermissions - class ProjectsController < ApplicationController - include InputSanitizeHelper - - before_action :set_project - before_action :check_read_permissions, only: %i(show) - before_action :check_manage_permissions, except: %i(show) - before_action :available_users, only: %i(new create) - - def show - render json: @project.user_assignments.includes(:user_role, :user).order('users.full_name ASC'), - each_serializer: UserAssignmentSerializer, user: current_user - end - - def new - render json: @available_users, each_serializer: UserSerializer, user: current_user - end - - def edit; end - - def create - ActiveRecord::Base.transaction do - created_count = 0 - if permitted_create_params[:user_id] == 'all' - @project.update!(visibility: :visible, default_public_user_role_id: permitted_create_params[:user_role_id]) - log_activity(:project_grant_access_to_all_team_members, - { visibility: t('projects.activity.visibility_visible'), - role: @project.default_public_user_role.name, - team: @project.team.id }) - else - user_assignment = UserAssignment.find_or_initialize_by( - assignable: @project, - user_id: permitted_create_params[:user_id], - team: current_team - ) - - user_assignment.update!( - user_role_id: permitted_create_params[:user_role_id], - assigned_by: current_user, - assigned: :manually - ) - - log_activity(:assign_user_to_project, { user_target: user_assignment.user.id, - role: user_assignment.user_role.name }) - created_count += 1 - propagate_job(user_assignment) - end - - @message = if created_count.zero? - t('access_permissions.create.success', member_name: t('access_permissions.all_team')) - else - t('access_permissions.create.success', member_name: escape_input(user_assignment.user.name)) - end - render json: { message: @message } - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - errors = @project.errors.present? ? @project.errors&.map(&:message)&.join(',') : e.message - render json: { flash: errors }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - end - - def update - @user_assignment = @project.user_assignments.find_by( - user_id: permitted_update_params[:user_id], - team: current_team - ) - - # prevent role change if it would result in no manually assigned users having the user management permission - new_user_role = UserRole.find(permitted_update_params[:user_role_id]) - if !new_user_role.has_permission?(ProjectPermissions::USERS_MANAGE) && - @user_assignment.last_with_permission?(ProjectPermissions::USERS_MANAGE, assigned: :manually) - raise ActiveRecord::RecordInvalid - end - - @user_assignment.update!(permitted_update_params) - - log_activity(:change_user_role_on_project, { user_target: @user_assignment.user.id, - role: @user_assignment.user_role.name }) - propagate_job(@user_assignment) - - render :project_member - rescue ActiveRecord::RecordInvalid - render json: { flash: t('access_permissions.update.failure') }, status: :unprocessable_entity - end - - def destroy - user = @project.assigned_users.find(params[:user_id]) - user_assignment = @project.user_assignments.find_by(user: user, team: current_team) - - # prevent deletion of last manually assigned user that can manage users - if user_assignment.last_with_permission?(ProjectPermissions::USERS_MANAGE, assigned: :manually) - raise ActiveRecord::RecordInvalid - end - - UserAssignments::PropagateAssignmentJob.perform_now( - @project, - user_assignment.user.id, - user_assignment.user_role, - current_user.id, - destroy: true - ) - - log_activity(:unassign_user_from_project, { user_target: user_assignment.user.id, - role: user_assignment.user_role.name }) - - render json: { message: t('access_permissions.destroy.success', member_name: escape_input(user.full_name)) } - rescue ActiveRecord::RecordInvalid - render json: { message: t('access_permissions.destroy.failure') }, - status: :unprocessable_entity - end - - def update_default_public_user_role - Project.transaction do - @project.visibility_will_change! - @project.last_modified_by = current_user - if permitted_default_public_user_role_params[:default_public_user_role_id].blank? - # revoke all team members access - @project.visibility = :hidden - previous_user_role_name = @project.default_public_user_role.name - @project.default_public_user_role_id = nil - @project.save! - log_activity(:project_remove_access_from_all_team_members, - { visibility: t('projects.activity.visibility_hidden'), - role: previous_user_role_name, - team: @project.team.id }) - render json: { message: t('access_permissions.update.revoke_all_team_members') } - else - # update all team members access - @project.visibility = :visible - @project.assign_attributes(permitted_default_public_user_role_params) - @project.save! - log_activity(:project_access_changed_all_team_members, - { team: @project.team.id, role: @project.default_public_user_role&.name }) - end - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - render json: { flash: @project.errors&.map(&:message)&.join(',') }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - end + class ProjectsController < AccessPermissions::BaseController + # legacy activity map + ACTIVITY_TYPE_MAP = { + project_access_granted: :assign_user_to_project, + project_access_changed: :change_user_role_on_project, + project_access_revoked: :unassign_user_from_project, + project_access_granted_all_team_members: :project_grant_access_to_all_team_members, + project_access_changed_all_team_members: :project_access_changed_all_team_members, + project_access_revoked_all_team_members: :project_remove_access_from_all_team_members + }.freeze private - def permitted_default_public_user_role_params - params.require(:object).permit(:default_public_user_role_id) - end + def set_model + @model = current_team.projects.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) + @project = @model - def permitted_update_params - params.require(:user_assignment) - .permit(%i(user_role_id user_id)) - end - - def permitted_create_params - params.require(:user_assignment) - .permit(%i(user_id user_role_id)) - end - - def set_project - @project = current_team.projects.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) - - render_404 unless @project - end - - def propagate_job(user_assignment, destroy: false) - UserAssignments::PropagateAssignmentJob.perform_later( - @project, - user_assignment.user.id, - user_assignment.user_role, - current_user.id, - destroy: destroy - ) + render_404 unless @model end def check_manage_permissions - render_403 unless can_manage_project_users?(@project) + render_403 unless can_manage_project_users?(@model) end def check_read_permissions - render_403 unless can_read_project_users?(@project) - end - - def available_users - # automatically assigned or not assigned to project - @available_users = current_team.users.where( - id: @project.user_assignments.automatically_assigned.select(:user_id) - ).or( - current_team.users.where.not(id: @project.users.select(:id)) - ).order('users.full_name ASC') + render_403 unless can_read_project_users?(@model) end def log_activity(type_of, message_items = {}) - message_items = { project: @project.id }.merge(message_items) - - Activities::CreateActivityService - .call(activity_type: type_of, - owner: current_user, - subject: @project, - team: @project.team, - project: @project, - message_items: message_items) + super(ACTIVITY_TYPE_MAP[type_of] || type_of, message_items) end end end diff --git a/app/controllers/access_permissions/protocols_controller.rb b/app/controllers/access_permissions/protocols_controller.rb index 1614a93ae..32eede72d 100644 --- a/app/controllers/access_permissions/protocols_controller.rb +++ b/app/controllers/access_permissions/protocols_controller.rb @@ -1,188 +1,41 @@ # frozen_string_literal: true module AccessPermissions - class ProtocolsController < ApplicationController - include InputSanitizeHelper - - before_action :set_protocol - before_action :check_read_permissions, only: %i(show) - before_action :check_manage_permissions, except: %i(show) - before_action :available_users, only: %i(new create) - - def show - render json: @protocol.user_assignments.includes(:user_role, :user).order('users.full_name ASC'), - each_serializer: UserAssignmentSerializer, user: current_user - end - - def new - render json: @available_users, each_serializer: UserSerializer, user: current_user - end - - def edit; end - - def create - ActiveRecord::Base.transaction do - created_count = 0 - if permitted_create_params[:user_id] == 'all' - @protocol.update!(visibility: :visible, default_public_user_role_id: permitted_create_params[:user_role_id]) - log_activity(:protocol_template_access_granted_all_team_members, - { team: @protocol.team.id, role: @protocol.default_public_user_role&.name }) - else - user_assignment = UserAssignment.find_or_initialize_by( - assignable: @protocol, - user_id: permitted_create_params[:user_id], - team: current_team - ) - - user_assignment.update!( - user_role_id: permitted_create_params[:user_role_id], - assigned_by: current_user, - assigned: :manually - ) - - log_activity(:protocol_template_access_granted, { user_target: user_assignment.user.id, - role: user_assignment.user_role.name }) - created_count += 1 - end - - @message = if created_count.zero? - t('access_permissions.create.success', member_name: t('access_permissions.all_team')) - else - t('access_permissions.create.success', member_name: escape_input(user_assignment.user.name)) - end - render json: { message: @message } - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - errors = @protocol.errors.present? ? @protocol.errors&.map(&:message)&.join(',') : e.message - render json: { flash: errors }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - end - - def update - @user_assignment = @protocol.user_assignments.find_by( - user_id: permitted_update_params[:user_id], - team: current_team - ) - - # prevent role change if it would result in no manually assigned users having the user management permission - new_user_role = UserRole.find(permitted_update_params[:user_role_id]) - if !new_user_role.has_permission?(ProtocolPermissions::USERS_MANAGE) && - @user_assignment.last_with_permission?(ProtocolPermissions::USERS_MANAGE, assigned: :manually) - raise ActiveRecord::RecordInvalid - end - - @user_assignment.update!(permitted_update_params) - log_activity(:protocol_template_access_changed, { user_target: @user_assignment.user.id, - role: @user_assignment.user_role.name }) - - render :protocol_member - rescue ActiveRecord::RecordInvalid - render json: { flash: t('access_permissions.update.failure') }, status: :unprocessable_entity - end - - def destroy - user = @protocol.assigned_users.find(params[:user_id]) - user_assignment = @protocol.user_assignments.find_by(user: user, team: current_team) - - # prevent deletion of last manually assigned user that can manage users - if user_assignment.last_with_permission?(ProtocolPermissions::USERS_MANAGE, assigned: :manually) - raise ActiveRecord::RecordInvalid - end - - Protocol.transaction do - if @protocol.visible? - user_assignment.update!( - user_role: @protocol.default_public_user_role, - assigned: :automatically - ) - else - user_assignment.destroy! - end - log_activity(:protocol_template_access_revoked, { user_target: user_assignment.user.id, - role: user_assignment.user_role.name }) - end - - render json: { message: t('access_permissions.destroy.success', member_name: user.full_name) } - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - render json: { message: t('access_permissions.destroy.failure') }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - - def update_default_public_user_role - ActiveRecord::Base.transaction do - current_role = @protocol.default_public_user_role.name - @protocol.update!(permitted_default_public_user_role_params) - - # revoke all team members access - if permitted_default_public_user_role_params[:default_public_user_role_id].blank? - log_activity(:protocol_template_access_revoked_all_team_members, - { team: @protocol.team.id, role: current_role }) - render json: { flash: t('access_permissions.update.revoke_all_team_members') }, status: :ok - else - # update all team members access - log_activity(:protocol_template_access_changed_all_team_members, - { team: @protocol.team.id, role: @protocol.default_public_user_role&.name }) - end - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error e.message - render json: { flash: @protocol&.errors&.map(&:message)&.join(',') }, status: :unprocessable_entity - raise ActiveRecord::Rollback - end - end + class ProtocolsController < BaseController + # protocol template activity naming is inconsistent with model name (model_parameter in BaseController), + # so we need to map them for now + ACTIVITY_TYPE_MAP = { + protocol_access_granted_all_team_members: :protocol_template_access_granted_all_team_members, + protocol_access_revoked_all_team_members: :protocol_template_access_revoked_all_team_members, + protocol_access_changed_all_team_members: :protocol_template_access_changed_all_team_members, + protocol_access_granted: :protocol_template_access_granted, + protocol_access_revoked: :protocol_template_access_revoked, + protocol_access_changed: :protocol_template_access_changed, + protocol_access_granted_user_group: :protocol_template_access_granted_user_group, + protocol_access_revoked_user_group: :protocol_template_access_revoked_user_group, + protocol_access_changed_user_group: :protocol_template_access_changed_user_group + }.freeze private - def permitted_default_public_user_role_params - params.require(:object).permit(:default_public_user_role_id) - end + def set_model + @model = current_team.protocols.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) - def permitted_update_params - params.require(:user_assignment) - .permit(%i(user_role_id user_id)) - end + return render_404 unless @model - def permitted_create_params - params.require(:user_assignment) - .permit(%i(user_id user_role_id)) - end - - def available_users - # automatically assigned or not assigned to project - @available_users = current_team.users.where( - id: @protocol.user_assignments.automatically_assigned.select(:user_id) - ).or( - current_team.users.where.not(id: @protocol.users.select(:id)) - ).order('users.full_name ASC') - end - - def set_protocol - @protocol = current_team.protocols.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) - - return render_404 unless @protocol - - @protocol = @protocol.parent if @protocol.parent_id + @model = @model.parent if @model.parent_id end def check_manage_permissions - render_403 unless can_manage_protocol_users?(@protocol) + render_403 unless can_manage_protocol_users?(@model) end def check_read_permissions - render_403 unless can_read_protocol_in_repository?(@protocol) || can_manage_team?(@protocol.team) + render_403 unless can_read_protocol_in_repository?(@model) || can_manage_team?(@model.team) end def log_activity(type_of, message_items = {}) - message_items = { protocol: @protocol.id }.merge(message_items) - - Activities::CreateActivityService - .call(activity_type: type_of, - owner: current_user, - subject: @protocol, - team: @protocol.team, - project: nil, - message_items: message_items) + super(ACTIVITY_TYPE_MAP[type_of] || type_of, message_items) end end end diff --git a/app/controllers/access_permissions/repositories_controller.rb b/app/controllers/access_permissions/repositories_controller.rb new file mode 100644 index 000000000..7a1b6a136 --- /dev/null +++ b/app/controllers/access_permissions/repositories_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module AccessPermissions + class RepositoriesController < BaseController + private + + def set_model + @model = Repository.includes(user_assignments: %i(user user_role)).find_by(id: params[:id]) + + render_404 unless @model + end + + def check_manage_permissions + render_403 unless can_manage_repository_users?(@model) + end + + def check_read_permissions + render_403 unless can_manage_repository_users?(@model) || can_read_repository?(@model) + end + end +end diff --git a/app/controllers/api/v1/experiment_user_assignments_controller.rb b/app/controllers/api/v1/experiment_user_assignments_controller.rb index 65b0e238b..6ef48346c 100644 --- a/app/controllers/api/v1/experiment_user_assignments_controller.rb +++ b/app/controllers/api/v1/experiment_user_assignments_controller.rb @@ -35,12 +35,7 @@ module Api @user_assignment.update!(user_assignment_params.merge(assigned: :manually)) - UserAssignments::PropagateAssignmentJob.perform_later( - @experiment, - @user_assignment.user_id, - @user_assignment.user_role, - current_user.id - ) + UserAssignments::PropagateAssignmentJob.perform_later(@user_assignment) log_change_activity diff --git a/app/controllers/api/v1/inventories_controller.rb b/app/controllers/api/v1/inventories_controller.rb index 7c4089615..b148aa520 100644 --- a/app/controllers/api/v1/inventories_controller.rb +++ b/app/controllers/api/v1/inventories_controller.rb @@ -13,6 +13,7 @@ module Api def index inventories = timestamps_filter(@team.repositories).active + .readable_by_user(current_user) .page(params.dig(:page, :number)) .per(params.dig(:page, :size)) diff --git a/app/controllers/api/v1/project_user_assignments_controller.rb b/app/controllers/api/v1/project_user_assignments_controller.rb index a51814181..ba3218cab 100644 --- a/app/controllers/api/v1/project_user_assignments_controller.rb +++ b/app/controllers/api/v1/project_user_assignments_controller.rb @@ -110,10 +110,7 @@ module Api def propagate_job(user_assignment, destroy: false) UserAssignments::PropagateAssignmentJob.perform_later( - @project, - user_assignment.user.id, - user_assignment.user_role, - current_user.id, + user_assignment, destroy: destroy ) end diff --git a/app/controllers/api/v1/projects_controller.rb b/app/controllers/api/v1/projects_controller.rb index 19ec39c40..fd253aeff 100644 --- a/app/controllers/api/v1/projects_controller.rb +++ b/app/controllers/api/v1/projects_controller.rb @@ -11,7 +11,13 @@ module Api before_action :load_project_for_managing, only: %i(update) def index - projects = @team.projects.visible_to(current_user, @team) + projects = + if can_manage_team?(@team) + # Team owners see all projects in the team + @team.projects + else + @team.projects.readable_by_user(current_user, @team) + end projects = metadata_filter(timestamps_filter(archived_filter(projects))) .page(params.dig(:page, :number)) .per(params.dig(:page, :size)) @@ -26,32 +32,57 @@ module Api def create raise PermissionError.new(Project, :create) unless can_create_projects?(@team) - project = @team.projects.build(project_params.merge!(created_by: current_user)) + ActiveRecord::Base.transaction do + project = @team.projects.build(project_params.merge!(created_by: current_user)) - if project.visible? # set default viewer role for public projects - project.default_public_user_role = UserRole.predefined.find_by(name: I18n.t('user_roles.predefined.viewer')) + project.save! + + if project_params[:visibility] == 'visible' + project.team_assignments.create!( + team: project.team, + user_role: UserRole.find_predefined_viewer_role, + assigned_by: current_user, + assigned: :manually + ) + end + + render jsonapi: project, serializer: ProjectSerializer, scope: { metadata: params['with-metadata'] == 'true' }, status: :created end - - project.save! - - render jsonapi: project, serializer: ProjectSerializer, scope: { metadata: params['with-metadata'] == 'true' }, status: :created end def update @project.assign_attributes(project_params) - return render body: nil, status: :no_content unless @project.changed? + return render body: nil, status: :no_content if !@project.changed? && project_params[:visibility].blank? - if @project.archived_changed? - if @project.archived? - @project.archived_by = current_user - else - @project.restored_by = current_user + ActiveRecord::Base.transaction do + if @project.archived_changed? + if @project.archived? + @project.archived_by = current_user + else + @project.restored_by = current_user + end end + @project.last_modified_by = current_user + @project.save! + + if project_params[:visibility].present? + team_assignment = @project.team_assignments.find_by(team: @team) + + if project_params[:visibility] == 'hidden' && team_assignment.present? + team_assignment.destroy! + elsif project_params[:visibility] == 'visible' && team_assignment.blank? + @project.team_assignments.create!( + team: @project.team, + user_role: UserRole.find_predefined_viewer_role, + assigned_by: current_user, + assigned: :manually + ) + end + end + + render jsonapi: @project, serializer: ProjectSerializer, scope: { metadata: params['with-metadata'] == 'true' }, status: :ok end - @project.last_modified_by = current_user - @project.save! - render jsonapi: @project, serializer: ProjectSerializer, scope: { metadata: params['with-metadata'] == 'true' }, status: :ok end def activities diff --git a/app/controllers/api/v1/protocol_templates_controller.rb b/app/controllers/api/v1/protocol_templates_controller.rb index 725ee147d..dc739e718 100644 --- a/app/controllers/api/v1/protocol_templates_controller.rb +++ b/app/controllers/api/v1/protocol_templates_controller.rb @@ -11,13 +11,11 @@ module Api end def index - protocol_templates = - timestamps_filter( - Protocol.latest_available_versions(@team) - ) - .viewable_by_user(current_user, @team) - .page(params.dig(:page, :number)) - .per(params.dig(:page, :size)) + protocol_templates = timestamps_filter(Protocol.latest_available_versions(@team)) + # Team owners see all protocol templates in the team + protocol_templates = protocol_templates.readable_by_user(current_user, @team) unless can_manage_team?(@team) + protocol_templates = protocol_templates.page(params.dig(:page, :number)) + .per(params.dig(:page, :size)) render jsonapi: protocol_templates, each_serializer: ProtocolTemplateSerializer, rte_rendering: render_rte?, team: @team diff --git a/app/controllers/api/v1/task_inventory_items_controller.rb b/app/controllers/api/v1/task_inventory_items_controller.rb index c93ced8e5..e43d74ff9 100644 --- a/app/controllers/api/v1/task_inventory_items_controller.rb +++ b/app/controllers/api/v1/task_inventory_items_controller.rb @@ -7,16 +7,18 @@ module Api before_action :load_project before_action :load_experiment before_action :load_task - before_action :load_my_module_repository_row, only: :update + before_action :load_inventory_item, only: %i(show destroy update) + before_action :load_task_inventory_item, only: %i(update destroy) + before_action :check_repository_view_permissions, only: :show before_action :check_stock_consumption_update_permissions, only: :update before_action :check_task_assign_permissions, only: %i(create destroy) def index - items = - timestamps_filter(@task.repository_rows).includes(repository_cells: :repository_column) - .preload(repository_cells: :value) - .page(params.dig(:page, :number)) - .per(params.dig(:page, :size)) + items = @task.repository_rows.where(repository_id: Repository.readable_by_user(current_user).select(:id)) + items = timestamps_filter(items).includes(repository_cells: :repository_column) + .preload(repository_cells: :value) + .page(params.dig(:page, :number)) + .per(params.dig(:page, :size)) render jsonapi: items, each_serializer: TaskInventoryItemSerializer, show_repository: true, @@ -25,7 +27,7 @@ module Api end def show - render jsonapi: @task.repository_rows.find(params.require(:id)), + render jsonapi: @inventory_item, serializer: TaskInventoryItemSerializer, show_repository: true, my_module: @task, @@ -39,21 +41,21 @@ module Api @task.my_module_repository_rows.create!(repository_row: @inventory_item, assigned_by: current_user) - render jsonapi: @task.repository_rows, - each_serializer: TaskInventoryItemSerializer, + render jsonapi: @inventory_item, + serializer: TaskInventoryItemSerializer, show_repository: true, my_module: @task, include: include_params end def update - @my_module_repository_row.consume_stock( + @task_inventory_item.consume_stock( current_user, repository_row_params[:attributes][:stock_consumption], repository_row_params[:attributes][:stock_consumption_comment] ) - render jsonapi: @my_module_repository_row.repository_row, + render jsonapi: @inventory_item, serializer: TaskInventoryItemSerializer, show_repository: true, my_module: @task, @@ -61,29 +63,27 @@ module Api end def destroy - @inventory_item = @task.repository_rows.find(params.require(:id)) - - raise PermissionError.new(Repository, :read) unless @inventory_item && can_read_repository?(@inventory_item.repository) - - @task.my_module_repository_rows.find_by(repository_row: @inventory_item).destroy! + @task_inventory_item.destroy! render body: nil end private - def load_my_module_repository_row - @my_module_repository_row = @task.repository_rows - .find(params.require(:id)) - .my_module_repository_rows - .find_by(my_module: @task) + def load_inventory_item + @inventory_item = @task.repository_rows.find(params.require(:id)) + end + + def load_task_inventory_item + @task_inventory_item = @task.my_module_repository_rows.find_by!(repository_row: @inventory_item) + end + + def check_repository_view_permissions + raise PermissionError.new(RepositoryRow, :read_repository) unless can_read_repository?(@inventory_item.repository) end def check_stock_consumption_update_permissions - unless can_update_my_module_stock_consumption?(@task) && - can_manage_repository_rows?(@my_module_repository_row.repository_row.repository) - raise PermissionError.new(RepositoryRow, :update_stock_consumption) - end + raise PermissionError.new(RepositoryRow, :update_stock_consumption) if @inventory_item.archived? || !can_update_my_module_stock_consumption?(@task) end def check_task_assign_permissions diff --git a/app/controllers/api/v2/inventory_item_child_relationships_controller.rb b/app/controllers/api/v2/inventory_item_child_relationships_controller.rb index 0576e2027..2cd2327fb 100644 --- a/app/controllers/api/v2/inventory_item_child_relationships_controller.rb +++ b/app/controllers/api/v2/inventory_item_child_relationships_controller.rb @@ -21,7 +21,7 @@ module Api end def create - inventory_item_to_link = RepositoryRow.where(repository: Repository.viewable_by_user(current_user, @team)) + inventory_item_to_link = RepositoryRow.where(repository: Repository.readable_by_user(current_user, @team)) .find(connection_params[:child_id]) child_connection = @inventory_item.child_connections.create!( child: inventory_item_to_link, diff --git a/app/controllers/api/v2/inventory_item_parent_relationships_controller.rb b/app/controllers/api/v2/inventory_item_parent_relationships_controller.rb index c7929eb67..2aa5fa0ff 100644 --- a/app/controllers/api/v2/inventory_item_parent_relationships_controller.rb +++ b/app/controllers/api/v2/inventory_item_parent_relationships_controller.rb @@ -21,7 +21,7 @@ module Api end def create - inventory_item_to_link = RepositoryRow.where(repository: Repository.viewable_by_user(current_user, @team)) + inventory_item_to_link = RepositoryRow.where(repository: Repository.readable_by_user(current_user, @team)) .find(connection_params[:parent_id]) parent_connection = @inventory_item.parent_connections.create!( parent: inventory_item_to_link, diff --git a/app/controllers/at_who_controller.rb b/app/controllers/at_who_controller.rb index f48645f81..ba1a5e4b7 100644 --- a/app/controllers/at_who_controller.rb +++ b/app/controllers/at_who_controller.rb @@ -27,7 +27,7 @@ class AtWhoController < ApplicationController if params[:repository_id].present? Repository.find_by(id: params[:repository_id]) else - Repository.active.viewable_by_user(current_user, @team).first + Repository.active.readable_by_user(current_user, @team).first end items = [] @@ -36,7 +36,7 @@ class AtWhoController < ApplicationController if repository && can_read_repository?(repository) assignable_my_module = if params[:assignable_my_module_id].present? - MyModule.viewable_by_user(current_user, @team).find_by(id: params[:assignable_my_module_id]) + MyModule.readable_by_user(current_user, @team).find_by(id: params[:assignable_my_module_id]) end items = SmartAnnotation.new(current_user, current_team, @query) .repository_rows(repository, assignable_my_module&.id) @@ -54,7 +54,7 @@ class AtWhoController < ApplicationController end def menu - repositories = Repository.active.viewable_by_user(current_user, @team) + repositories = Repository.active.readable_by_user(current_user, @team) render json: { html: render_to_string(partial: 'shared/smart_annotation/menu', locals: { repositories: repositories }, diff --git a/app/controllers/concerns/steps_actions.rb b/app/controllers/concerns/steps_actions.rb index 428ee05ce..4ec796ff9 100644 --- a/app/controllers/concerns/steps_actions.rb +++ b/app/controllers/concerns/steps_actions.rb @@ -48,7 +48,7 @@ module StepsActions smart_annotation_notification( old_text: old_text, new_text: checklist_item.text, - subject: step.protocol, + subject: step, title: t('notifications.checklist_title', user: current_user.full_name, step: step.name), @@ -60,7 +60,7 @@ module StepsActions smart_annotation_notification( old_text: old_text, new_text: step_text.text, - subject: step.protocol, + subject: step, title: t('notifications.step_text_title', user: current_user.full_name, step: step.name), @@ -72,7 +72,7 @@ module StepsActions smart_annotation_notification( old_text: old_text, new_text: checklist.name, - subject: step.protocol, + subject: step, title: t('notifications.checklist_title', user: current_user.full_name, step: step.name), @@ -84,7 +84,7 @@ module StepsActions smart_annotation_notification( old_text: old_text, new_text: step.description, - subject: step.protocol, + subject: step, title: t('notifications.step_description_title', user: current_user.full_name, step: step.name), @@ -96,7 +96,7 @@ module StepsActions smart_annotation_notification( old_text: old_content, new_text: table.contents, - subject: step.protocol, + subject: step, title: t(table.metadata['plateTemplate'] ? 'notifications.step_well_plate_title' : 'notifications.step_table_title', user: current_user.full_name, step: step.name), diff --git a/app/controllers/dashboard/current_tasks_controller.rb b/app/controllers/dashboard/current_tasks_controller.rb index f548a8a4d..a61a2c787 100644 --- a/app/controllers/dashboard/current_tasks_controller.rb +++ b/app/controllers/dashboard/current_tasks_controller.rb @@ -30,7 +30,7 @@ module Dashboard def project_filter projects = current_team.projects .where(archived: false) - .viewable_by_user(current_user, current_team) + .readable_by_user(current_user, current_team) .search_by_name(current_user, current_team, params[:query]).select(:id, :name) unless params[:mode] == 'team' @@ -47,7 +47,7 @@ module Dashboard end experiments = @project.experiments .where(archived: false) - .viewable_by_user(current_user, current_team) + .readable_by_user(current_user, current_team) .search_by_name(current_user, current_team, params[:query]).select(:id, :name) unless params[:mode] == 'team' @@ -103,7 +103,7 @@ module Dashboard MyModule.active end - tasks = tasks.viewable_by_user(current_user, current_team) + tasks = tasks.readable_by_user(current_user, current_team) tasks = tasks.joins(experiment: :project) .where(experiments: { archived: false }) diff --git a/app/controllers/dashboard/quick_start_controller.rb b/app/controllers/dashboard/quick_start_controller.rb index a16cc8f2e..2fa8f186c 100644 --- a/app/controllers/dashboard/quick_start_controller.rb +++ b/app/controllers/dashboard/quick_start_controller.rb @@ -60,7 +60,7 @@ module Dashboard end def create_project_params - params.require(:project).permit(:name, :visibility, :default_public_user_role_id) + params.require(:project).permit(:name) end def create_experiment_params diff --git a/app/controllers/experiments_controller.rb b/app/controllers/experiments_controller.rb index b55090028..67cc028e6 100644 --- a/app/controllers/experiments_controller.rb +++ b/app/controllers/experiments_controller.rb @@ -47,7 +47,7 @@ class ExperimentsController < ApplicationController end def assigned_users - render json: User.where(id: @experiment.user_assignments.select(:user_id)), + render json: @experiment.users, each_serializer: UserSerializer, user: current_user end @@ -72,10 +72,13 @@ class ExperimentsController < ApplicationController def canvas @project = @experiment.project @active_modules = unless @experiment.archived_branch? - @experiment.my_modules.active.order(:name) + @experiment.my_modules + .active + .readable_by_user(current_user) .left_outer_joins(:designated_users, :task_comments) .preload(:tags, outputs: :to) .preload(:my_module_status, :my_module_group, user_assignments: %i(user user_role)) + .order(:name) .select('COUNT(DISTINCT users.id) as designated_users_count') .select('COUNT(DISTINCT comments.id) as task_comments_count') .select('my_modules.*').group(:id) @@ -202,7 +205,7 @@ class ExperimentsController < ApplicationController def projects_to_clone projects = @experiment.project.team.projects.active - .with_user_permission(current_user, ProjectPermissions::EXPERIMENTS_CREATE) + .with_granted_permissions(current_user, ProjectPermissions::EXPERIMENTS_CREATE) .where('trim_html_tags(projects.name) ILIKE ?', "%#{ActiveRecord::Base.sanitize_sql_like(params['query'])}%") .map { |p| [p.id, p.name] } @@ -355,10 +358,10 @@ class ExperimentsController < ApplicationController end def inventory_assigning_experiment_filter - viewable_experiments = Experiment.viewable_by_user(current_user, current_team) + viewable_experiments = Experiment.readable_by_user(current_user, current_team) assignable_my_modules = MyModule.repository_row_assignable_by_user(current_user) - project = Project.viewable_by_user(current_user, current_team) + project = Project.readable_by_user(current_user, current_team) .joins(experiments: :my_modules) .where(experiments: { id: viewable_experiments }) .where(my_modules: { id: assignable_my_modules }) diff --git a/app/controllers/external_protocols_controller.rb b/app/controllers/external_protocols_controller.rb index c38826e5f..8bfe34f37 100644 --- a/app/controllers/external_protocols_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -118,7 +118,7 @@ class ExternalProtocolsController < ApplicationController def create_protocol_params params .require(:protocol) - .permit(:name, :authors, :published_on, :protocol_type, :description, :visibility, :default_public_user_role_id) + .permit(:name, :authors, :published_on, :protocol_type, :description) .except(:steps) end diff --git a/app/controllers/form_field_values_controller.rb b/app/controllers/form_field_values_controller.rb index 4b4f577e7..6fb2b6305 100644 --- a/app/controllers/form_field_values_controller.rb +++ b/app/controllers/form_field_values_controller.rb @@ -18,7 +18,7 @@ class FormFieldValuesController < ApplicationController log_form_field_value_create_activity form_field_value_annotation if @form_field_value.is_a?(FormTextFieldValue) - render json: @form_field_value, serializer: FormFieldValueSerializer, user: current_user + render json: @form_field_value, serializer: FormFieldValueSerializer, scope: { user: current_user } end private @@ -52,7 +52,7 @@ class FormFieldValuesController < ApplicationController smart_annotation_notification( old_text: @form_field_value.text_previously_was, new_text: @form_field_value.text, - subject: step.protocol, + subject: step, title: t('notifications.form_field_value_title', user: current_user.full_name, field: @form_field_value.form_field.name, diff --git a/app/controllers/forms_controller.rb b/app/controllers/forms_controller.rb index cbb218d67..a7e130d32 100644 --- a/app/controllers/forms_controller.rb +++ b/app/controllers/forms_controller.rb @@ -2,9 +2,9 @@ class FormsController < ApplicationController include InputSanitizeHelper - include UserRolesHelper before_action :check_forms_enabled + before_action :load_forms, only: %i(index actions_toolbar) before_action :load_form, only: %i(show update publish unpublish export_form_responses duplicate) before_action :set_breadcrumbs_items, only: %i(index show) before_action :check_manage_permissions, only: :update @@ -14,11 +14,11 @@ class FormsController < ApplicationController respond_to do |format| format.html format.json do - forms = Lists::FormsService.new(current_user, current_team, params).call - render json: forms, + forms_list = Lists::FormsService.new(@forms, params, user: current_user).call + render json: forms_list, each_serializer: Lists::FormSerializer, user: current_user, - meta: pagination_dict(forms) + meta: pagination_dict(forms_list) end end end @@ -201,19 +201,16 @@ class FormsController < ApplicationController end def actions_toolbar + selected_forms = @forms.where(id: JSON.parse(params[:items]).pluck('id')) render json: { actions: Toolbars::FormsService.new( - current_user, - form_ids: JSON.parse(params[:items]).map { |i| i['id'] } + selected_forms, + current_user ).actions } end - def user_roles - render json: { data: user_roles_collection(Form.new).map(&:reverse) } - end - private def set_breadcrumbs_items @@ -235,6 +232,12 @@ class FormsController < ApplicationController end end + def load_forms + # Team owners see all forms in the team + @forms = current_team.forms + @forms = @forms.readable_by_user(current_user) unless can_manage_team?(current_team) + end + def load_form @form = current_team.forms.readable_by_user(current_user).find_by(id: params[:id]) diff --git a/app/controllers/hidden_repository_cell_reminders_controller.rb b/app/controllers/hidden_repository_cell_reminders_controller.rb index ad7472d8c..5ca5c19fc 100644 --- a/app/controllers/hidden_repository_cell_reminders_controller.rb +++ b/app/controllers/hidden_repository_cell_reminders_controller.rb @@ -15,7 +15,7 @@ class HiddenRepositoryCellRemindersController < ApplicationController private def load_repository - @repository = Repository.viewable_by_user(current_user).find_by(id: params[:repository_id]) + @repository = Repository.readable_by_user(current_user).find_by(id: params[:repository_id]) render_404 unless @repository end diff --git a/app/controllers/label_templates_controller.rb b/app/controllers/label_templates_controller.rb index dcb312dac..710b71d42 100644 --- a/app/controllers/label_templates_controller.rb +++ b/app/controllers/label_templates_controller.rb @@ -5,7 +5,7 @@ class LabelTemplatesController < ApplicationController include TeamsHelper before_action :check_feature_enabled, except: %i(index zpl_preview list) - before_action :load_label_templates, only: %i(index datatable list) + before_action :load_label_templates, only: %i(index list) before_action :load_label_template, only: %i(show set_default update template_tags) before_action :check_view_permissions, except: %i(create duplicate set_default delete update) before_action :check_manage_permissions, only: %i(create duplicate set_default delete update) diff --git a/app/controllers/my_module_repositories_controller.rb b/app/controllers/my_module_repositories_controller.rb index 547ea2fd0..700bfb01f 100644 --- a/app/controllers/my_module_repositories_controller.rb +++ b/app/controllers/my_module_repositories_controller.rb @@ -6,7 +6,7 @@ class MyModuleRepositoriesController < ApplicationController before_action :load_my_module, except: :assign_my_modules before_action :load_repository, except: %i(repositories_dropdown_list repositories_list_html repositories_list create) before_action :check_my_module_view_permissions, except: %i(update consume_modal update_consumption assign_my_modules) - before_action :check_repository_view_permissions, except: %i(repositories_dropdown_list repositories_list_html repositories_list create) + before_action :check_repository_view_permissions, except: %i(index_dt repositories_dropdown_list repositories_list_html repositories_list create) before_action :check_repository_row_consumption_permissions, only: %i(consume_modal update_consumption) before_action :check_assign_repository_records_permissions, only: %i(update create) before_action :load_my_modules, only: :assign_my_modules @@ -19,6 +19,8 @@ class MyModuleRepositoriesController < ApplicationController rows_view = 'repository_rows/simple_view_index' preload_cells = false else + return render_403 unless can_read_repository?(@repository) + rows_view = 'repository_rows/index' preload_cells = true end @@ -150,8 +152,8 @@ class MyModuleRepositoriesController < ApplicationController end def repositories_list - @assigned_repositories = @my_module.readable_live_and_snapshot_repositories_list(current_user) - render json: @assigned_repositories, each_serializer: AssignedRepositorySerializer, scope: {user: current_user, my_module: @my_module } + @assigned_repositories = @my_module.live_and_snapshot_repositories_list + render json: @assigned_repositories, each_serializer: AssignedRepositorySerializer, scope: { user: current_user, my_module: @my_module } end def full_view_table @@ -164,7 +166,7 @@ class MyModuleRepositoriesController < ApplicationController end def repositories_dropdown_list - @repositories = Repository.viewable_by_user(current_user).joins(" + @repositories = Repository.readable_by_user(current_user).joins(" LEFT OUTER JOIN repository_rows ON repository_rows.repository_id = repositories.id LEFT OUTER JOIN my_module_repository_rows ON diff --git a/app/controllers/my_module_repository_snapshots_controller.rb b/app/controllers/my_module_repository_snapshots_controller.rb index 7c45cd093..323a24164 100644 --- a/app/controllers/my_module_repository_snapshots_controller.rb +++ b/app/controllers/my_module_repository_snapshots_controller.rb @@ -67,7 +67,7 @@ class MyModuleRepositorySnapshotsController < ApplicationController end def full_view_sidebar - @repository = Repository.viewable_by_user(current_user, current_team).find_by(id: params[:repository_id]) + @repository = Repository.readable_by_user(current_user, current_team).find_by(id: params[:repository_id]) @repository_snapshots = @my_module.repository_snapshots .where(parent_id: params[:repository_id]) .order(created_at: :desc) diff --git a/app/controllers/my_module_shareable_links_controller.rb b/app/controllers/my_module_shareable_links_controller.rb index 625bce799..fe9055901 100644 --- a/app/controllers/my_module_shareable_links_controller.rb +++ b/app/controllers/my_module_shareable_links_controller.rb @@ -71,7 +71,8 @@ class MyModuleShareableLinksController < ApplicationController my_module: @my_module, include_stock_consumption: @repository.has_stock_management? && params[:assigned].present?, disable_reminders: true, # reminders are always disabled for shareable links - disable_stock_management: true # stock management is always disabled in MyModule context + disable_stock_management: true, # stock management is always disabled in MyModule context + shareable_link_view: true } @all_rows_count = datatable_service.all_count @@ -89,6 +90,7 @@ class MyModuleShareableLinksController < ApplicationController page = (params[:start].to_i / per_page) + 1 datatable_service = RepositorySnapshotDatatableService.new(@repository_snapshot, params, nil, @my_module, preload_cells: false) + @datatable_params = { shareable_link_view: true } @all_rows_count = datatable_service.all_count @filtered_rows_count = datatable_service.filtered_count @columns_mappings = datatable_service.mappings diff --git a/app/controllers/my_modules_controller.rb b/app/controllers/my_modules_controller.rb index 8398698f4..781e4f3d5 100644 --- a/app/controllers/my_modules_controller.rb +++ b/app/controllers/my_modules_controller.rb @@ -47,7 +47,7 @@ class MyModulesController < ApplicationController def new @my_module = @experiment.my_modules.new - assigned_users = User.where(id: @experiment.user_assignments.select(:user_id)) + assigned_users = @experiment.users render json: { html: render_to_string( @@ -423,10 +423,10 @@ class MyModulesController < ApplicationController end def inventory_assigning_my_module_filter - viewable_experiments = Experiment.viewable_by_user(current_user, current_team) + viewable_experiments = Experiment.readable_by_user(current_user, current_team) assignable_my_modules = MyModule.repository_row_assignable_by_user(current_user) - experiment = Experiment.viewable_by_user(current_user, current_team) + experiment = Experiment.readable_by_user(current_user, current_team) .joins(:my_modules) .where(experiments: { id: viewable_experiments }) .where(my_modules: { id: assignable_my_modules }) diff --git a/app/controllers/navigations_controller.rb b/app/controllers/navigations_controller.rb index 964ca12e4..b11fa6f9d 100644 --- a/app/controllers/navigations_controller.rb +++ b/app/controllers/navigations_controller.rb @@ -60,22 +60,16 @@ class NavigationsController < ApplicationController end def settings_menu_links - links = [ - { - name: I18n.t('users.settings.sidebar.teams'), url: teams_path - }, { - name: I18n.t('users.settings.sidebar.account_nav.addons'), url: addons_path - } - ] - - if can_create_acitivity_filters? - links.push({ name: I18n.t('users.settings.sidebar.webhooks'), url: users_settings_webhooks_path }) - end + links = [{ name: I18n.t('users.settings.sidebar.teams'), url: teams_path }] + links << { name: I18n.t('users.settings.sidebar.groups'), url: users_settings_team_user_groups_path(current_team) } if can_manage_team?(current_team) + links << { name: I18n.t('users.settings.sidebar.account_nav.addons'), url: addons_path } private_methods.select { |i| i.to_s[/^settings_menu_links_[a-z]*_extension$/] }.each do |method| links = __send__(method, links) end + links << { name: I18n.t('users.settings.sidebar.webhooks'), url: users_settings_webhooks_path } if can_create_acitivity_filters? + links end diff --git a/app/controllers/navigator/base_controller.rb b/app/controllers/navigator/base_controller.rb index df82e27f2..828ed098f 100644 --- a/app/controllers/navigator/base_controller.rb +++ b/app/controllers/navigator/base_controller.rb @@ -53,7 +53,7 @@ module Navigator end def fetch_projects(object = nil, archived = false) - if object&.is_a?(ProjectFolder) + if object.is_a?(ProjectFolder) folder = object project = nil else @@ -70,37 +70,65 @@ module Navigator (projects.archived IS TRUE AND experiments.id IS NOT NULL) THEN 1 ELSE 0 END) > 0 AS has_children' end - disabled_sql = 'SUM(CASE WHEN project_user_roles IS NULL THEN 0 ELSE 1 END) < 1 AS disabled' + disabled_sql = 'SUM(CASE + WHEN project_user_roles IS NULL AND + project_user_group_roles IS NULL AND + project_team_roles IS NULL + THEN 0 ELSE 1 END) < 1 AS disabled' - current_team.projects - .where(project_folder_id: folder) - .visible_to(current_user, current_team) - .with_children_viewable_by_user(current_user) - .joins("LEFT OUTER JOIN user_assignments project_user_assignments - ON project_user_assignments.assignable_type = 'Project' - AND project_user_assignments.assignable_id = projects.id - AND project_user_assignments.user_id = #{current_user.id} - LEFT OUTER JOIN user_roles project_user_roles - ON project_user_roles.id = project_user_assignments.user_role_id - AND project_user_roles.permissions @> ARRAY['#{ProjectPermissions::READ}']::varchar[]") - .where('projects.archived = :archived OR - ( - ( - experiments.archived = :archived OR - my_modules.archived = :archived - ) AND - :archived IS TRUE - ) OR - projects.id = :project_id', - archived: archived, - project_id: project&.id || -1) - .select( - 'projects.id', - 'projects.name', - 'projects.archived', - disabled_sql, - has_children_sql - ).group('projects.id') + projects = + if can_manage_team?(current_team) + # Team owners see all projects in the team + current_team.projects + else + current_team.projects.readable_by_user(current_user, current_team) + end + + projects.where(project_folder_id: folder) + .with_children_viewable_by_user(current_user) + .joins("LEFT OUTER JOIN user_assignments project_user_assignments + ON project_user_assignments.assignable_type = 'Project' + AND project_user_assignments.assignable_id = projects.id + AND project_user_assignments.user_id = #{current_user.id} + LEFT OUTER JOIN user_roles project_user_roles + ON project_user_roles.id = project_user_assignments.user_role_id + AND project_user_roles.permissions @> ARRAY['#{ProjectPermissions::READ}']::varchar[] + LEFT OUTER JOIN user_group_assignments project_user_group_assignments + ON project_user_group_assignments.assignable_type = 'Project' + AND project_user_group_assignments.assignable_id = projects.id + AND project_user_group_assignments.user_group_id IN ( + SELECT user_group_memberships.user_group_id FROM user_group_memberships + WHERE user_group_memberships.user_id = #{current_user.id} + ) + LEFT OUTER JOIN user_roles project_user_group_roles + ON project_user_group_roles.id = project_user_group_assignments.user_role_id + AND project_user_group_roles.permissions @> ARRAY['#{ProjectPermissions::READ}']::varchar[] + LEFT OUTER JOIN team_assignments project_team_assignments + ON project_team_assignments.assignable_type = 'Project' + AND project_team_assignments.assignable_id = projects.id + AND project_team_assignments.team_id = #{current_team.id} + LEFT OUTER JOIN user_roles project_team_roles + ON project_team_roles.id = project_team_assignments.user_role_id + AND project_team_roles.permissions @> ARRAY['#{ProjectPermissions::READ}']::varchar[] + ") + .where('projects.archived = :archived OR + ( + ( + experiments.archived = :archived OR + my_modules.archived = :archived + ) AND + :archived IS TRUE + ) OR + projects.id = :project_id', + archived: archived, + project_id: project&.id || -1) + .select( + 'projects.id', + 'projects.name', + 'projects.archived', + disabled_sql, + has_children_sql + ).group('projects.id') end def fetch_project_folders(object = nil, archived = false) @@ -109,13 +137,20 @@ module Navigator else object&.project_folder end - has_children_sql = 'SUM(CASE WHEN viewable_projects.id IS NOT NULL OR project_folders_project_folders.id IS NOT NULL + has_children_sql = 'SUM(CASE WHEN visible_projects.id IS NOT NULL OR project_folders_project_folders.id IS NOT NULL THEN 1 ELSE 0 END) > 0' + visible_projects = + if can_manage_team?(current_team) + # Team owners see all projects in the team + current_team.projects + else + current_team.projects.readable_by_user(current_user, current_team) + end current_team.project_folders.where(parent_folder: folder) .left_outer_joins(:projects, project_folders: {}) .joins( - "LEFT OUTER JOIN (#{Project.viewable_by_user(current_user, current_team).where(archived: archived).to_sql}) " \ - "viewable_projects ON viewable_projects.project_folder_id = project_folders.id" + "LEFT OUTER JOIN (#{visible_projects.where(archived: archived).to_sql}) " \ + "visible_projects ON visible_projects.project_folder_id = project_folders.id" ) .select( 'project_folders.id', @@ -147,7 +182,7 @@ module Navigator THEN 1 ELSE 0 END) > 0 AS has_children' end experiments = project.experiments - .viewable_by_user(current_user, current_team) + .readable_by_user(current_user, current_team) .with_children_viewable_by_user(current_user) .select( 'experiments.id', @@ -169,8 +204,7 @@ module Navigator end def fetch_my_modules(experiment, archived = false) - my_modules = experiment.my_modules - .viewable_by_user(current_user, current_team) + my_modules = experiment.my_modules.readable_by_user(current_user, current_team) my_modules = my_modules.where(archived: archived) unless experiment.archived_branch? my_modules diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index e622fd917..a864845a2 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -8,7 +8,6 @@ class ProjectsController < ApplicationController include CardsViewHelper include ExperimentsHelper include Breadcrumbs - include UserRolesHelper include FavoritesActions attr_reader :current_folder @@ -16,11 +15,12 @@ class ProjectsController < ApplicationController helper_method :current_folder before_action :switch_team_with_param, only: :index - before_action :load_vars, only: %i(update create_tag assigned_users_list show) + before_action :load_projects, only: %i(index actions_toolbar) + before_action :load_project, only: %i(update create_tag assigned_users_list show) before_action :load_current_folder, only: :index before_action :check_read_permissions, except: %i(index create update archive_group restore_group inventory_assigning_project_filter - actions_toolbar user_roles users_filter head_of_project_users_list + actions_toolbar users_filter head_of_project_users_list favorite unfavorite) before_action :check_create_permissions, only: :create before_action :check_manage_permissions, only: :update @@ -32,9 +32,9 @@ class ProjectsController < ApplicationController def index respond_to do |format| format.json do - projects = Lists::ProjectsService.new(current_team, current_user, current_folder, params).call - render json: projects, each_serializer: Lists::ProjectAndFolderSerializer, user: current_user, - meta: pagination_dict(projects) + projects_list = Lists::ProjectsService.new(current_team, @projects, @current_folder, params, user: current_user).call + render json: projects_list, each_serializer: Lists::ProjectAndFolderSerializer, user: current_user, + meta: pagination_dict(projects_list) end format.html do render 'projects/index' @@ -54,10 +54,10 @@ class ProjectsController < ApplicationController end def inventory_assigning_project_filter - viewable_experiments = Experiment.viewable_by_user(current_user, current_team) + viewable_experiments = Experiment.readable_by_user(current_user, current_team) assignable_my_modules = MyModule.repository_row_assignable_by_user(current_user) - projects = Project.viewable_by_user(current_user, current_team) + projects = Project.readable_by_user(current_user, current_team) .active .joins(experiments: :my_modules) .where(experiments: { id: viewable_experiments }) @@ -86,7 +86,6 @@ class ProjectsController < ApplicationController end def update - default_public_user_role_name_before_update = @project.default_public_user_role&.name old_status = @project.status @project.assign_attributes(project_update_params) return_error = false @@ -105,14 +104,6 @@ class ProjectsController < ApplicationController end message_edited = @project.name_changed? || @project.description_changed? - message_visibility = if !@project.visibility_changed? - nil - elsif @project.visible? - t('projects.activity.visibility_visible') - else - t('projects.activity.visibility_hidden') - end - message_archived = if !@project.archived_changed? nil elsif @project.archived? @@ -124,41 +115,14 @@ class ProjectsController < ApplicationController start_date_changes = @project.changes[:start_date] due_date_changes = @project.changes[:due_date] - default_public_user_role_name = nil - if !@project.visibility_changed? && @project.default_public_user_role_id_changed? - @project.visibility_will_change! # triggers assignment sync - default_public_user_role_name = UserRole.find(project_params[:default_public_user_role_id]).name - end - @project.last_modified_by = current_user if !return_error && @project.save # Add activities if needed - if message_visibility.present? && @project.visible? - log_activity(:project_grant_access_to_all_team_members, - @project, - { visibility: message_visibility, - role: @project.default_public_user_role.name, - team: @project.team.id }) - end - if message_visibility.present? && !@project.visible? - log_activity(:project_remove_access_from_all_team_members, - @project, - { visibility: message_visibility, - role: default_public_user_role_name_before_update, - team: @project.team.id }) - end - log_activity(:edit_project) if message_edited.present? log_activity(:archive_project) if message_archived == 'archive' log_activity(:restore_project) if message_archived == 'restore' - if default_public_user_role_name.present? - log_activity(:project_access_changed_all_team_members, - @project, - { team: @project.team.id, role: default_public_user_role_name }) - end - if supervised_by_id_changes.present? log_activity(:remove_head_of_project, @project, { user_target: supervised_by_id_changes[0] }) if supervised_by_id_changes[0].present? # remove head of project log_activity(:set_head_of_project, @project, { user_target: supervised_by_id_changes[1] }) if supervised_by_id_changes[1].present? # add head of project @@ -300,16 +264,17 @@ class ProjectsController < ApplicationController render json: { data: users.map { |u| [u.id, u.name, { avatar_url: avatar_path(u, :icon_small) }] } }, status: :ok end - def user_roles - render json: { data: user_roles_collection(Project.new).map(&:reverse) } - end - def actions_toolbar + project_ids = JSON.parse(params[:items]).select { |i| i['type'] == 'projects' }.pluck('id') + project_folder_ids = JSON.parse(params[:items]).select { |i| i['type'] == 'project_folders' }.pluck('id') + selected_projects = @projects.where(id: project_ids) + selected_project_folders = current_user.current_team.project_folders.where(id: project_folder_ids) render json: { actions: Toolbars::ProjectsService.new( - current_user, - items: JSON.parse(params[:items]) + selected_projects, + selected_project_folders, + current_user ).actions } end @@ -319,9 +284,8 @@ class ProjectsController < ApplicationController def project_params params.require(:project) .permit( - :name, :visibility, + :name, :archived, :project_folder_id, - :default_public_user_role_id, :due_date, :start_date, :description @@ -330,14 +294,23 @@ class ProjectsController < ApplicationController def project_update_params params.require(:project) - .permit(:name, :visibility, :archived, :default_public_user_role_id, :due_date, :start_date, :description, :status, :supervised_by_id) + .permit(:name, :archived, :due_date, :start_date, :description, :status, :supervised_by_id) end def view_type_params params.require(:project).require(:view_type) end - def load_vars + def load_projects + @projects = if can_manage_team?(current_team) + # Team owners see all projects in the team + current_team.projects + else + current_team.projects.readable_by_user(current_user, current_team) + end + end + + def load_project @project = Project.find_by(id: params[:id] || params[:project_id]) render_404 unless @project diff --git a/app/controllers/protocols_controller.rb b/app/controllers/protocols_controller.rb index 4731f91db..a96270be6 100644 --- a/app/controllers/protocols_controller.rb +++ b/app/controllers/protocols_controller.rb @@ -7,7 +7,7 @@ class ProtocolsController < ApplicationController include ProtocolsIoHelper include TeamsHelper include ProtocolsExporterV2 - include UserRolesHelper + include FormFieldValuesHelper before_action :check_create_permissions, only: %i( create @@ -79,11 +79,17 @@ class ProtocolsController < ApplicationController def index respond_to do |format| format.json do - protocols = Lists::ProtocolsService.new(Protocol.viewable_by_user(current_user, @current_team), params).call - render json: protocols, + protocols = if can_manage_team?(current_team) + # Team owners see all protocol templates in the team + current_team.repository_protocols + else + current_team.repository_protocols.readable_by_user(current_user, current_team) + end + protocols_list = Lists::ProtocolsService.new(protocols, params).call + render json: protocols_list, each_serializer: Lists::ProtocolSerializer, user: current_user, - meta: pagination_dict(protocols) + meta: pagination_dict(protocols_list) end format.html do render 'index' @@ -531,6 +537,7 @@ class ProtocolsController < ApplicationController Protocol.transaction do protocol = @importer.import_new_protocol(@protocol_json) rescue StandardError => e + Rails.logger.error e.message Rails.logger.error e.backtrace.join("\n") transaction_error = true raise ActiveRecord::Rollback @@ -876,10 +883,6 @@ class ProtocolsController < ApplicationController render json: { job_id: @job.job_id } end - def user_roles - render json: { data: user_roles_collection(Protocol.new).map(&:reverse) } - end - private def set_importer @@ -1089,7 +1092,7 @@ class ProtocolsController < ApplicationController end def create_params - params.require(:protocol).permit(:name, :default_public_user_role_id, :visibility) + params.require(:protocol).permit(:name) end def check_protocolsio_import_permissions diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index fab290fd7..d2d95001e 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -26,7 +26,7 @@ class ReportsController < ApplicationController def index respond_to do |format| format.json do - reports = Lists::ReportsService.new(Report.viewable_by_user(current_user, current_team), params).call + reports = Lists::ReportsService.new(Report.readable_by_user(current_user, current_team), params).call render json: reports, each_serializer: Lists::ReportSerializer, user: current_user, meta: pagination_dict(reports) end @@ -370,10 +370,10 @@ class ReportsController < ApplicationController end def load_repositories_vars - live_repositories = Repository.viewable_by_user(current_user).sort_by { |r| r.name.downcase } + live_repositories = Repository.readable_by_user(current_user).sort_by { |r| r.name.downcase } snapshots_of_deleted = RepositorySnapshot.left_outer_joins(:original_repository) .where(team: current_team) - .where.not(original_repository: live_repositories) + .where.not(original_repository: current_team.repositories) .select('DISTINCT ON ("repositories"."parent_id") "repositories".*') .sort_by { |r| r.name.downcase } @repositories = live_repositories + snapshots_of_deleted @@ -398,7 +398,7 @@ class ReportsController < ApplicationController def load_available_repositories @available_repositories = [] repositories = Repository.active - .viewable_by_user(current_user) + .readable_by_user(current_user) .name_like(search_params[:query]) .limit(Constants::SEARCH_LIMIT) repositories.each do |repository| diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 4baf20faa..34c01b9c7 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -10,14 +10,13 @@ class RepositoriesController < ApplicationController include MyModulesHelper before_action :switch_team_with_param, only: %i(index) - before_action :load_repository, except: %i(index create create_modal sidebar archive restore actions_toolbar + before_action :load_repository, except: %i(index create create_modal archive restore actions_toolbar export_repositories list) - before_action :load_repositories, only: %i(index list) + before_action :load_repositories, only: %i(index actions_toolbar) before_action :load_repositories_for_archiving, only: :archive before_action :load_repositories_for_restoring, only: :restore - before_action :check_view_all_permissions, only: %i(index sidebar list) before_action :check_view_permissions, except: %i(index create_modal create update destroy parse_sheet - import_records sidebar archive restore actions_toolbar + import_records archive restore actions_toolbar export_repositories list) before_action :check_manage_permissions, only: %i(rename_modal update) before_action :check_delete_permissions, only: %i(destroy destroy_modal) @@ -45,7 +44,12 @@ class RepositoriesController < ApplicationController end def list - results = @repositories.select(:id, :name, 'LOWER(repositories.name)') + repositories = if params[:manageable] == 'true' + Repository.with_granted_permissions(current_user, RepositoryPermissions::ROWS_UPDATE, current_team) + else + Repository.readable_by_user(current_user, current_team) + end + results = repositories.select(:id, :name, 'LOWER(repositories.name)') results = results.name_like(params[:query]) if params[:query].present? results = results.joins(:repository_rows).distinct if params[:non_empty].present? results = results.active if params[:active].present? @@ -74,15 +78,6 @@ class RepositoriesController < ApplicationController } end - def sidebar - render json: { - html: render_to_string(partial: 'repositories/sidebar', locals: { - repositories: @repositories, - archived: params[:archived] == 'true' - }) - } - end - def show respond_to do |format| format.html do @@ -332,14 +327,14 @@ class RepositoriesController < ApplicationController end def import_records - render_403 unless can_create_repository_rows?(Repository.viewable_by_user(current_user) + render_403 unless can_create_repository_rows?(Repository.readable_by_user(current_user) .find_by(id: import_params[:id])) # Check if there exist mapping for repository record (it's mandatory) if import_params[:mappings].present? && import_params[:mappings].value?('-1') status = ImportRepository::ImportRecords .new( temp_file: TempFile.find_by(id: import_params[:file_id]), - repository: Repository.viewable_by_user(current_user).find_by(id: import_params[:id]), + repository: Repository.readable_by_user(current_user).find_by(id: import_params[:id]), mappings: import_params[:mappings], session: session, user: current_user, @@ -404,7 +399,7 @@ class RepositoriesController < ApplicationController end def export_repositories - repositories = Repository.viewable_by_user(current_user, current_team).where(id: params[:repository_ids]) + repositories = Repository.readable_by_user(current_user, current_team).where(id: params[:repository_ids]) if repositories.present? RepositoriesExportJob .perform_later(params[:file_type], repositories.pluck(:id), user_id: current_user.id, team_id: current_team.id) @@ -472,12 +467,12 @@ class RepositoriesController < ApplicationController end def actions_toolbar + selected_repositories = @repositories.where(id: JSON.parse(params[:items]).pluck('id')) render json: { actions: Toolbars::RepositoriesService.new( - current_user, - current_team, - repository_ids: JSON.parse(params[:items]).map { |i| i['id'] } + selected_repositories, + current_user ).actions } end @@ -486,16 +481,20 @@ class RepositoriesController < ApplicationController def load_repository repository_id = params[:id] || params[:repository_id] - @repository = Repository.viewable_by_user(current_user, current_user.teams).find_by(id: repository_id) + @repository = Repository.readable_by_user(current_user, current_user.teams).find_by(id: repository_id) render_404 unless @repository end def load_repositories - @repositories = if params[:appendable] == 'true' - Repository.appendable_by_user(current_user) - else - Repository.viewable_by_user(current_user) - end + @repositories = + if params[:appendable] == 'true' + Repository.appendable_by_user(current_user) + elsif can_manage_team?(current_team) + # Team owners see all repositories in the team + current_team.repositories.or(Repository.shared_with_team(current_team)) + else + Repository.readable_by_user(current_user, current_team) + end end def load_repositories_for_archiving @@ -530,10 +529,6 @@ class RepositoriesController < ApplicationController } end - def check_view_all_permissions - render_403 unless can_read_team?(current_team) - end - def check_view_permissions current_team_switch(@repository.team) unless @repository.shared_with?(current_team) render_403 unless can_read_repository?(@repository) diff --git a/app/controllers/repository_columns_controller.rb b/app/controllers/repository_columns_controller.rb index 73cefa87b..c9fa98253 100644 --- a/app/controllers/repository_columns_controller.rb +++ b/app/controllers/repository_columns_controller.rb @@ -108,7 +108,7 @@ class RepositoryColumnsController < ApplicationController AvailableRepositoryColumn = Struct.new(:id, :name) def load_repository - @repository = Repository.viewable_by_user(current_user).find_by(id: params[:repository_id]) + @repository = Repository.readable_by_user(current_user).find_by(id: params[:repository_id]) render_404 unless @repository end diff --git a/app/controllers/repository_row_connections_controller.rb b/app/controllers/repository_row_connections_controller.rb index 4398959bd..3e6955ae7 100644 --- a/app/controllers/repository_row_connections_controller.rb +++ b/app/controllers/repository_row_connections_controller.rb @@ -56,7 +56,7 @@ class RepositoryRowConnectionsController < ApplicationController end def repositories - repositories = Repository.viewable_by_user(current_user) + repositories = Repository.readable_by_user(current_user) .search_by_name_and_id(current_user, current_user.teams, params[:query]) .order(name: :asc) .page(params[:page] || 1) @@ -70,7 +70,7 @@ class RepositoryRowConnectionsController < ApplicationController end def repository_rows - selected_repository = Repository.viewable_by_user(current_user).find(params[:selected_repository_id]) + selected_repository = Repository.readable_by_user(current_user).find(params[:selected_repository_id]) repository_rows = selected_repository.repository_rows .where.not(id: @repository_row.id) @@ -95,14 +95,14 @@ class RepositoryRowConnectionsController < ApplicationController return render_422(t('.invalid_params')) unless @relation_type - @connection_repository = Repository.viewable_by_user(current_user) + @connection_repository = Repository.readable_by_user(current_user) .find_by(id: connection_params[:connection_repository_id]) return render_404 unless @connection_repository return render_403 unless can_connect_repository_rows?(@connection_repository) end def load_repository - @repository = Repository.viewable_by_user(current_user).find_by(id: params[:repository_id]) + @repository = Repository.readable_by_user(current_user).find_by(id: params[:repository_id]) render_404 unless @repository end @@ -145,19 +145,23 @@ class RepositoryRowConnectionsController < ApplicationController repository_connections.map do |connection| repository_row = @relation_type == 'parent' ? connection.parent : connection.child - - { - name: repository_row.name_with_label, - code: repository_row.code, - path: repository_repository_row_path(repository_row.repository, repository_row), - repository_name: repository_row.repository.name_with_label, - repository_path: repository_path(repository_row.repository), - unlink_path: repository_repository_row_repository_row_connection_path( - repository_row.repository, - repository_row, - connection - ) - } + if can_read_repository?(repository_row.repository) + { + name: repository_row.name_with_label, + code: repository_row.code, + path: repository_repository_row_path(repository_row.repository, repository_row), + repository_name: repository_row.repository.name_with_label, + repository_path: repository_path(repository_row.repository), + can_connect_rows: can_connect_repository_rows?(repository_row.repository), + unlink_path: repository_repository_row_repository_row_connection_path( + repository_row.repository, + repository_row, + connection + ) + } + else + { name: I18n.t('repositories.item_card.relationships.private_item_name') } + end end end diff --git a/app/controllers/repository_rows_controller.rb b/app/controllers/repository_rows_controller.rb index 02ca7d6de..3b5f0866d 100644 --- a/app/controllers/repository_rows_controller.rb +++ b/app/controllers/repository_rows_controller.rb @@ -58,13 +58,11 @@ class RepositoryRowsController < ApplicationController end end - @assigned_modules = @repository_row.my_modules - .joins(experiment: :project) - .joins(:my_module_repository_rows) - .select('my_module_repository_rows.created_at, my_modules.*') - .order('my_module_repository_rows.created_at': :desc) - .distinct - @viewable_modules = @assigned_modules.viewable_by_user(current_user, current_user.teams) + @assigned_modules = @repository_row.my_modules.distinct + @viewable_modules = @assigned_modules.readable_by_user(current_user, current_user.teams) + .joins(:my_module_repository_rows) + .select('my_module_repository_rows.created_at, my_modules.*') + .order(my_module_repository_rows: { created_at: :desc }) @reminders_present = @repository_row.repository_cells.with_active_reminder(@current_user).any? end end @@ -83,7 +81,7 @@ class RepositoryRowsController < ApplicationController end if update_params[:my_module_id].present? - my_module = MyModule.viewable_by_user(current_user, current_team).find_by(id: update_params[:my_module_id]) + my_module = MyModule.readable_by_user(current_user, current_team).find_by(id: update_params[:my_module_id]) return render_403 unless my_module.present? && can_read_my_module?(my_module) @@ -215,6 +213,8 @@ class RepositoryRowsController < ApplicationController { repository_row: @repository_row.id, repository_column: update_params['repository_cells']&.keys&.first || I18n.t('repositories.table.row_name') }) + + record_annotation_notification(@repository_row, row_cell_update.cell) if row_cell_update.cell && row_cell_update.cell.value_type == 'RepositoryTextValue' end @reminders_present = @repository_row.repository_cells.with_active_reminder(@current_user).any? @@ -299,7 +299,7 @@ class RepositoryRowsController < ApplicationController params[:query], whole_phrase: true ) - viewable_modules = assigned_modules.viewable_by_user(current_user, current_user.teams) + viewable_modules = assigned_modules.readable_by_user(current_user, current_user.teams) private_modules_number = assigned_modules.where.not(id: viewable_modules).count render json: { html: render_to_string(partial: 'shared/my_modules_list_partial', locals: { @@ -372,7 +372,7 @@ class RepositoryRowsController < ApplicationController AvailableRepositoryRow = Struct.new(:id, :name, :has_file_attached) def load_repository - @repository = Repository.viewable_by_user(current_user) + @repository = Repository.readable_by_user(current_user) .eager_load(:repository_columns) .find_by(id: params[:repository_id]) render_404 unless @repository @@ -384,7 +384,7 @@ class RepositoryRowsController < ApplicationController FormRepositoryRowsFieldValue.find_by(id: params[:form_repository_rows_field_value_id]) end - @repository = Repository.viewable_by_user(current_user).find_by(id: params[:repository_id]) || + @repository = Repository.readable_by_user(current_user).find_by(id: params[:repository_id]) || RepositorySnapshot.find_by(id: params[:repository_id]) render_404 unless @form_repository_rows_field_value || @repository @@ -476,7 +476,7 @@ class RepositoryRowsController < ApplicationController smart_annotation_notification( old_text: old_text, new_text: cell.value.data, - subject: cell.repository_column.repository, + subject: cell.repository_row, title: t('notifications.repository_annotation_title', user: current_user.full_name, column: cell.repository_column.name, diff --git a/app/controllers/repository_table_filters_controller.rb b/app/controllers/repository_table_filters_controller.rb index a5009529a..7e231a2ee 100644 --- a/app/controllers/repository_table_filters_controller.rb +++ b/app/controllers/repository_table_filters_controller.rb @@ -70,7 +70,7 @@ class RepositoryTableFiltersController < ApplicationController private def load_repository - @repository = Repository.viewable_by_user(current_user).find_by(id: params[:repository_id]) + @repository = Repository.readable_by_user(current_user).find_by(id: params[:repository_id]) render_403 unless can_read_repository?(@repository) end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 5623e71cc..e2d3006e4 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -171,8 +171,7 @@ class SearchController < ApplicationController current_team, params[:query], search_object_classes, - limit: Constants::QUICK_SEARCH_LIMIT, - fetch_latest_versions: class_name == 'protocol') + limit: Constants::QUICK_SEARCH_LIMIT) .order(updated_at: :desc) end diff --git a/app/controllers/smart_annotations_controller.rb b/app/controllers/smart_annotations_controller.rb index 239cbd1b7..85b9fe9a8 100644 --- a/app/controllers/smart_annotations_controller.rb +++ b/app/controllers/smart_annotations_controller.rb @@ -84,7 +84,7 @@ class SmartAnnotationsController < ApplicationController when MyModule protocols_my_module_path(resource) when RepositoryRow - repository_repository_row_path(resource.repository, resource) + repository_repository_row_path(resource.repository, resource, my_module_id: params[:my_module_id]) end end diff --git a/app/controllers/storage_locations_controller.rb b/app/controllers/storage_locations_controller.rb index 088fb1a7f..ef819e974 100644 --- a/app/controllers/storage_locations_controller.rb +++ b/app/controllers/storage_locations_controller.rb @@ -8,6 +8,7 @@ class StorageLocationsController < ApplicationController before_action :switch_team_with_param, only: %i(index show) before_action :check_storage_locations_enabled, except: :unassign_rows + before_action :load_storage_locations, only: %i(index actions_toolbar) before_action :load_storage_location, only: %i(update destroy duplicate move show available_positions unassign_rows export_container import_container) before_action :set_inline_name_editing, only: %i(show) before_action :check_read_permissions, except: %i(index create tree actions_toolbar import_container unassign_rows) @@ -26,7 +27,7 @@ class StorageLocationsController < ApplicationController respond_to do |format| format.html format.json do - storage_locations = Lists::StorageLocationsService.new(current_user, current_team, params).call + storage_locations = Lists::StorageLocationsService.new(@storage_locations, params, user: current_user).call render json: storage_locations, each_serializer: Lists::StorageLocationSerializer, user: current_user, @@ -131,7 +132,7 @@ class StorageLocationsController < ApplicationController end def tree - records = StorageLocation.viewable_by_user(current_user, current_team) + records = StorageLocation.readable_by_user(current_user, current_team) .where( parent: nil, container: [false, params[:container] == 'true'] @@ -188,11 +189,12 @@ class StorageLocationsController < ApplicationController end def actions_toolbar + selected_storage_locations = @storage_locations.where(id: JSON.parse(params[:items]).pluck('id')) render json: { actions: Toolbars::StorageLocationsService.new( - current_user, - storage_location_ids: JSON.parse(params[:items]).pluck('id') + selected_storage_locations, + current_user ).actions } end @@ -212,6 +214,10 @@ class StorageLocationsController < ApplicationController params.permit(:id, :destination_storage_location_id) end + def load_storage_locations + @storage_locations = StorageLocation.readable_by_user(current_user, current_team).or(StorageLocation.shared_with_team(current_team)) + end + def load_storage_location @storage_location = StorageLocation.find(storage_location_params[:id]) @parent_location = @storage_location.parent diff --git a/app/controllers/team_shared_objects_controller.rb b/app/controllers/team_shared_objects_controller.rb index 3276b5721..1132bb993 100644 --- a/app/controllers/team_shared_objects_controller.rb +++ b/app/controllers/team_shared_objects_controller.rb @@ -22,6 +22,14 @@ class TeamSharedObjectsController < ApplicationController if @model.permission_level_changed? @model.save! @model.team_shared_objects.each(&:destroy!) unless global_permission_level == :not_shared + + case global_permission_level + when :shared_read + @model.demote_all_sharing_assignments_to_viewer! + when :not_shared + @model.destroy_all_sharing_assignments! + end + case @model when Repository setup_repository_global_share_activity @@ -66,9 +74,9 @@ class TeamSharedObjectsController < ApplicationController def load_vars case params[:object_type] when 'Repository' - @model = Repository.viewable_by_user(current_user).find_by(id: params[:object_id]) + @model = Repository.readable_by_user(current_user).find_by(id: params[:object_id]) when 'StorageLocation' - @model = StorageLocation.viewable_by_user(current_user).find_by(id: params[:object_id]) + @model = StorageLocation.readable_by_user(current_user).find_by(id: params[:object_id]) end render_404 unless @model @@ -88,7 +96,7 @@ class TeamSharedObjectsController < ApplicationController def check_sharing_permissions object_name = @model.is_a?(RepositoryBase) ? 'repository' : @model.model_name.param_key - render_403 unless public_send("can_share_#{object_name}?", @model) + render_403 unless public_send(:"can_share_#{object_name}?", @model) render_403 if !@model.shareable_write? && update_params[:write_permissions].present? end diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb index 61707f01b..1b67ceeb1 100644 --- a/app/controllers/teams_controller.rb +++ b/app/controllers/teams_controller.rb @@ -9,7 +9,7 @@ class TeamsController < ApplicationController before_action :load_vars, only: %i(sidebar export_projects export_projects_modal disable_tasks_sharing_modal shared_tasks_toggle) before_action :load_current_folder, only: :sidebar - before_action :check_read_permissions, except: %i(view_type visible_teams visible_users) + before_action :check_read_permissions, except: %i(view_type visible_teams visible_users current_team_users) before_action :check_export_projects_permissions, only: %i(export_projects_modal export_projects) def visible_teams @@ -22,7 +22,12 @@ class TeamsController < ApplicationController if params[:teams].present? teams = teams.where(id: params[:teams]) end - users = User.where(id: teams.joins(:users).select('users.id')).order(:full_name) + users = User.where(id: UserAssignment.where(assignable: teams).select(:user_id)).order(:full_name) + render json: users, each_serializer: UserSerializer, user: current_user + end + + def current_team_users + users = current_team.users.order(:full_name) render json: users, each_serializer: UserSerializer, user: current_user end @@ -154,7 +159,7 @@ class TeamsController < ApplicationController if export_projects_params[:project_folder_ids] folders = @team.project_folders.where(id: export_projects_params[:project_folder_ids]) folders.each do |folder| - @exp_projects += folder.inner_projects.visible_to(current_user, @team) + @exp_projects += folder.inner_projects.readable_by_user(current_user, @team) end end diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb index f10e61f31..99c38f15e 100644 --- a/app/controllers/users/passwords_controller.rb +++ b/app/controllers/users/passwords_controller.rb @@ -35,7 +35,7 @@ class Users::PasswordsController < Devise::PasswordsController flash_message = resource.active_for_authentication? ? :updated : :updated_not_active set_flash_message!(:notice, flash_message) resource.after_database_authentication if check_database_authentication?(resource) - sign_in(resource_name, resource) + sign_in(resource_name, resource, event: :authentication) else set_flash_message!(:notice, :updated_not_active) end diff --git a/app/controllers/users/settings/teams_controller.rb b/app/controllers/users/settings/teams_controller.rb index 6208fc2f8..dce1104b2 100644 --- a/app/controllers/users/settings/teams_controller.rb +++ b/app/controllers/users/settings/teams_controller.rb @@ -15,6 +15,7 @@ module Users create show users_datatable + members ) before_action :load_team, only: %i( @@ -24,12 +25,13 @@ module Users description_html update destroy + members ) before_action :check_create_team_permission, only: %i(new create) - before_action :set_breadcrumbs_items, only: %i(index show) + before_action :set_breadcrumbs_items, only: %i(index show members) layout 'fluid' @@ -57,7 +59,13 @@ module Users end end - def show; end + def show + @active_tab = :details + end + + def members + @active_tab = :members + end def users_datatable render json: ::TeamUsersDatatable.new(view_context, @team, @user) diff --git a/app/controllers/users/settings/user_group_memberships_controller.rb b/app/controllers/users/settings/user_group_memberships_controller.rb new file mode 100644 index 000000000..a64379dde --- /dev/null +++ b/app/controllers/users/settings/user_group_memberships_controller.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Users + module Settings + class UserGroupMembershipsController < ApplicationController + before_action :check_user_groups_enabled + before_action :load_team + before_action :load_user_group + before_action :check_manage_permissions, except: %i(index show) + + def index + memberships = Lists::UserGroupMembershipsService.new(@user_group.user_group_memberships, params).call + render json: memberships, each_serializer: Lists::UserGroupMembershipSerializer, user: current_user, meta: pagination_dict(memberships) + end + + def actions_toolbar + render json: { + actions: + Toolbars::UserGroupMembershipsService.new( + current_user, + @user_group, + user_group_membership_ids: JSON.parse(params[:items]).pluck('id') + ).actions + } + end + + def show; end + + def create + ActiveRecord::Base.transaction do + new_users = @team.users.where(id: params[:user_ids]) + + new_users.each do |user| + @user_group.user_group_memberships.create!(user: user, created_by: current_user) + log_activity(:add_group_user_member, user) + end + + render json: { message: :success }, status: :created + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error e.message + head :unprocessable_entity + raise ActiveRecord::Rollback + end + end + + def destroy_multiple + members = @user_group.user_group_memberships.where(id: params[:membership_ids]) + + members.each do |member| + log_activity(:remove_group_user_member, member.user) + end + + if members.destroy_all + render json: { message: :success }, status: :ok + else + head :unprocessable_entity + end + end + + private + + def check_user_groups_enabled + render '/users/settings/user_groups/promo' unless UserGroup.enabled? + end + + def load_team + @team = Team.find(params[:team_id]) + end + + def load_user_group + @user_group = @team.user_groups.find(params[:user_group_id]) + end + + def load_user_group_membership + @user_group_membership = @user_group.user_group_memberships.find(params[:id]) + end + + def check_manage_permissions + render_403 unless can_manage_team?(@team) + end + + def log_activity(type_of, user_target) + Activities::CreateActivityService + .call(activity_type: type_of, + owner: current_user, + subject: @user_group.team, + team: @user_group.team, + message_items: { + user_group: @user_group.id, + team: @user_group.team.id, + user_target: user_target.id + }) + end + end + end +end diff --git a/app/controllers/users/settings/user_groups_controller.rb b/app/controllers/users/settings/user_groups_controller.rb new file mode 100644 index 000000000..12e8530f7 --- /dev/null +++ b/app/controllers/users/settings/user_groups_controller.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +module Users + module Settings + class UserGroupsController < ApplicationController + before_action :load_team + before_action :set_breadcrumbs_items, only: %i(index show) + before_action :check_user_groups_enabled, except: :users + before_action :load_user_group, except: %i(index unassigned_users actions_toolbar create) + before_action :check_read_permissions, only: %i(users) + before_action :check_manage_permissions, except: %i(users) + + def index + respond_to do |format| + format.html do + @active_tab = :user_groups + end + format.json do + user_groups = Lists::UserGroupsService.new(@team.user_groups, params).call + render json: user_groups, each_serializer: Lists::UserGroupSerializer, user: current_user, meta: pagination_dict(user_groups) + end + end + end + + def actions_toolbar + render json: { + actions: + Toolbars::UserGroupsService.new( + current_user, + @team, + user_group_ids: JSON.parse(params[:items]).pluck('id') + ).actions + } + end + + def unassigned_users + @unassigned_users = @team.users.search(false, params[:query]) + if params[:user_group_id].present? + @user_group = @team.user_groups.find(params[:user_group_id]) + @unassigned_users = @unassigned_users.where.not(id: @user_group.users.select(:id)) + end + end + + def show + @active_tab = :user_groups + end + + def create + @user_group = @team.user_groups.new + @user_group.created_by = current_user + @user_group.last_modified_by = current_user + @user_group.assign_attributes(user_group_params) + + if @user_group.save + log_activity(:create_user_group) + @user_group.users.each do |user| + log_activity(:add_group_user_member, { user_target: user.id }) + end + render json: { message: t('user_groups.create.success') }, status: :created + else + render json: { error: @user_group.errors.full_messages.join(", ") }, status: :unprocessable_entity + end + end + + def update + ActiveRecord::Base.transaction do + @user_group.last_modified_by = current_user + @user_group.assign_attributes(user_group_params) + @user_group.save! + log_activity(:update_user_group) + render json: {}, status: :ok + rescue ActiveRecord::RecordInvalid => e + render json: { errors: e.message }, status: :unprocessable_entity + raise ActiveRecord::Rollback + end + end + + def destroy + log_activity(:delete_user_group) + if @user_group.destroy + render json: { message: t('user_groups.delete.success') }, status: :ok + else + render json: { errors: t('user_groups.delete.error') }, status: :unprocessable_entity + end + end + + def users + render json: @user_group.users, each_serializer: UserSerializer, user: current_user + end + + private + + def check_user_groups_enabled + @active_tab = :user_groups + render '/users/settings/user_groups/promo' unless UserGroup.enabled? + end + + def user_group_params + params.require(:user_group).permit( + :name, + user_group_memberships_attributes: %i(id user_id) + ) + end + + def load_team + @team = Team.find(params[:team_id]) + end + + def load_user_group + @user_group = @team.user_groups.find(params[:user_group_id] || params[:id]) + end + + def check_read_permissions + render_403 unless can_read_team?(@team) + end + + def check_manage_permissions + render_403 unless can_manage_team?(@team) + end + + def log_activity(type_of, message_items = {}) + Activities::CreateActivityService + .call(activity_type: type_of, + owner: current_user, + subject: @user_group.team, + team: @user_group.team, + message_items: { + user_group: @user_group.id, + team: @user_group.team.id + }.merge(message_items)) + end + + def set_breadcrumbs_items + @breadcrumbs_items = [ + { label: t('breadcrumbs.teams'), url: teams_path }, + { label: @team.name, url: team_path(@team) } + ] + end + end + end +end diff --git a/app/controllers/users/settings/user_teams_controller.rb b/app/controllers/users/settings/user_teams_controller.rb index ebf1ccbe4..f316ee7a6 100644 --- a/app/controllers/users/settings/user_teams_controller.rb +++ b/app/controllers/users/settings/user_teams_controller.rb @@ -109,11 +109,13 @@ module Users ) end + redirect_url = teams_path if params[:leave] + generate_notification(current_user, @user_assignment.user, @user_assignment.assignable, false) - render json: { status: :ok, job_id: job_id, success_message: success_message } + render json: { status: :ok, job_id: job_id, success_message: success_message, redirect_url: redirect_url } end end diff --git a/app/datatables/teams_datatable.rb b/app/datatables/teams_datatable.rb index 4bca0126e..7601d4758 100644 --- a/app/datatables/teams_datatable.rb +++ b/app/datatables/teams_datatable.rb @@ -2,7 +2,7 @@ class TeamsDatatable < CustomDatatable include InputSanitizeHelper def_delegator :@view, :link_to - def_delegator :@view, :team_path + def_delegator :@view, :members_users_settings_team_path def_delegator :@view, :leave_user_team_html_path MEMEBERS_SORT_COL = 'members'.freeze @@ -28,7 +28,7 @@ class TeamsDatatable < CustomDatatable { 'DT_RowId': record.id, '0': if current_user_team_role(record)&.owner? - link_to(escape_input(record.name), team_path(record)) + link_to(escape_input(record.name), members_users_settings_team_path(record)) else escape_input(record.name) end, diff --git a/app/helpers/comment_helper.rb b/app/helpers/comment_helper.rb index 81aa82646..ba6dc2566 100644 --- a/app/helpers/comment_helper.rb +++ b/app/helpers/comment_helper.rb @@ -162,7 +162,7 @@ module CommentHelper smart_annotation_notification( old_text: old_text, new_text: comment.message, - subject: step.protocol, + subject: step, title: t('notifications.step_comment_annotation_title', step: step.name, user: current_user.full_name), diff --git a/app/helpers/form_field_values_helper.rb b/app/helpers/form_field_values_helper.rb new file mode 100644 index 000000000..545c320b3 --- /dev/null +++ b/app/helpers/form_field_values_helper.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module FormFieldValuesHelper + include Canaid::Helpers::PermissionsHelper + + def form_repository_rows_field_value_formatter(field_values, user = current_user) + field_values&.value&.map do |value| + row_code = "#{RepositoryRow::ID_PREFIX}#{value['id']}" + repository = Repository.find_by(id: value['repository_id']) + + if repository.nil? || can_read_repository?(user, repository) + "#{value['name']} (#{row_code})" + else + I18n.t('my_modules.assigned_items.repository.private_repository_row_name', repository_row_code: row_code) + end + end&.join(' | ') + end +end diff --git a/app/helpers/global_activities_helper.rb b/app/helpers/global_activities_helper.rb index 585495a46..3a17f2c67 100644 --- a/app/helpers/global_activities_helper.rb +++ b/app/helpers/global_activities_helper.rb @@ -53,6 +53,8 @@ module GlobalActivitiesHelper path = '' case obj + when UserGroup + path = users_settings_team_user_group_path(obj.team, obj) when User return "[@#{obj.full_name}~#{obj.id.base62_encode}]" when Tag @@ -61,6 +63,8 @@ module GlobalActivitiesHelper when Team path = projects_path(team: obj.id) when Repository + return I18n.t('repositories.private') unless can_read_repository?(obj) + path = repository_path(obj, team: obj.team.id) when RepositoryRow # Handle private repository rows @@ -70,6 +74,8 @@ module GlobalActivitiesHelper path = repository_path(obj.repository, team: obj.repository.team.id) when RepositoryColumn + return I18n.t('repositories.repository_column.private') unless can_read_repository?(obj.repository) + return current_value unless obj.repository path = repository_path(obj.repository, team: obj.repository.team.id) diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb index fc90a2b3a..7f4c5b7f5 100644 --- a/app/helpers/reports_helper.rb +++ b/app/helpers/reports_helper.rb @@ -2,6 +2,7 @@ module ReportsHelper include StringUtility + include FormFieldValuesHelper include Canaid::Helpers::PermissionsHelper def render_report_element(element, provided_locals = nil) @@ -10,6 +11,8 @@ module ReportsHelper return unless can_read_experiment?(element.experiment) when 'my_module' return unless can_read_my_module?(element.my_module) + when 'my_module_repository' + return unless can_read_repository?(element.repository) end # Determine partial @@ -22,7 +25,7 @@ module ReportsHelper # First, recursively render element's children if element.children.active.present? element.children.active.find_each do |child| - children_html.safe_concat render_report_element(child, provided_locals) + children_html.safe_concat render_report_element(child, provided_locals) || '' end end locals[:report_element] = element @@ -139,6 +142,8 @@ module ReportsHelper ) elsif form_field_value.is_a?(FormDatetimeFieldValue) form_field_value&.formatted_localize + elsif form_field_value.is_a?(FormRepositoryRowsFieldValue) + form_repository_rows_field_value_formatter(form_field_value) else form_field_value&.formatted end diff --git a/app/helpers/repository_datatable_helper.rb b/app/helpers/repository_datatable_helper.rb index 14505d311..490623075 100644 --- a/app/helpers/repository_datatable_helper.rb +++ b/app/helpers/repository_datatable_helper.rb @@ -101,11 +101,14 @@ module RepositoryDatatableHelper # otherwise it will result in duplicated SQL queries has_stock_management = repository.has_stock_management? reminders_enabled = !options[:disable_reminders] && Repository.reminders_enabled? + shareable_link_view = options[:shareable_link_view] && my_module.shared? # Always disabled in a simple view stock_managable = false stock_consumption_permitted = has_stock_management && stock_consumption_permitted?(repository, my_module) repository_rows.map do |record| + next { code: record.code } unless shareable_link_view || can_read_repository?(record.repository) + row = { DT_RowId: record.id, DT_RowAttr: { 'data-state': row_style(record, my_module) }, @@ -156,7 +159,7 @@ module RepositoryDatatableHelper consumed_stock_formatted = number_with_precision( record.consumed_stock, - precision: (record.repository.repository_stock_column.metadata['decimals'].to_i || 0), + precision: record.repository.repository_stock_column.metadata['decimals'].to_i || 0, strip_insignificant_zeros: true ) row['consumedStock'][:value] = { diff --git a/app/helpers/table_helper.rb b/app/helpers/table_helper.rb index e5a64911d..0842cf1af 100644 --- a/app/helpers/table_helper.rb +++ b/app/helpers/table_helper.rb @@ -4,7 +4,7 @@ module TableHelper def table_data content = {} data = JSON.parse(contents)['data'] - return [] if data.blank? + return {} if data.blank? cells = metadata.fetch('cells', []) diff --git a/app/helpers/user_roles_helper.rb b/app/helpers/user_roles_helper.rb index 17bf224dd..94f24db15 100644 --- a/app/helpers/user_roles_helper.rb +++ b/app/helpers/user_roles_helper.rb @@ -2,15 +2,23 @@ module UserRolesHelper def user_roles_collection(object, with_inherit: false) - permission_group = "#{object.class.name}Permissions".constantize - permissions = permission_group.constants.map { |const| permission_group.const_get(const) } + if (object.respond_to?(:private_shared_with_read?) && object.private_shared_with_read?(current_team)) || + (object.respond_to?(:shared_with_read?) && object.shared_with_read?(current_team)) + viewer_role = UserRole.find_predefined_viewer_role + roles = [[viewer_role.name, viewer_role.id]] + else + permission_group = "#{object.class.permission_class}Permissions".constantize + permissions = permission_group.constants.map { |const| permission_group.const_get(const) } + + roles = user_roles_subset_by_permissions(permissions).order(id: :asc).pluck(:name, :id) + end - roles = user_roles_subset_by_permissions(permissions).order(id: :asc).pluck(:name, :id) 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 diff --git a/app/javascript/packs/vue/user_groups_show.js b/app/javascript/packs/vue/user_groups_show.js new file mode 100644 index 000000000..6d5ede78a --- /dev/null +++ b/app/javascript/packs/vue/user_groups_show.js @@ -0,0 +1,10 @@ +import { createApp } from 'vue/dist/vue.esm-bundler.js'; +import { PerfectScrollbar } from 'vue3-perfect-scrollbar'; +import UserGroupShow from '../../vue/user_groups/show.vue'; +import { mountWithTurbolinks } from './helpers/turbolinks.js'; + +const app = createApp(); +app.component('UserGroupShow', UserGroupShow); +app.config.globalProperties.i18n = window.I18n; +app.use(PerfectScrollbar); +mountWithTurbolinks(app, '#userGroupShow'); diff --git a/app/javascript/packs/vue/user_groups_table.js b/app/javascript/packs/vue/user_groups_table.js new file mode 100644 index 000000000..abb4e6a37 --- /dev/null +++ b/app/javascript/packs/vue/user_groups_table.js @@ -0,0 +1,10 @@ +import { createApp } from 'vue/dist/vue.esm-bundler.js'; +import { PerfectScrollbar } from 'vue3-perfect-scrollbar'; +import UserGroupsTable from '../../vue/user_groups/index.vue'; +import { mountWithTurbolinks } from './helpers/turbolinks.js'; + +const app = createApp(); +app.component('UserGroupsTable', UserGroupsTable); +app.config.globalProperties.i18n = window.I18n; +app.use(PerfectScrollbar); +mountWithTurbolinks(app, '#userGroupsTable'); diff --git a/app/javascript/vue/dashboard/new_task.vue b/app/javascript/vue/dashboard/new_task.vue index 92cf6853a..fc09d5913 100644 --- a/app/javascript/vue/dashboard/new_task.vue +++ b/app/javascript/vue/dashboard/new_task.vue @@ -22,24 +22,6 @@ @change="changeProject" /> -
-
-
- - -
- -
-
- - -
-
{ this.$emit('close'); }); @@ -113,10 +94,6 @@ export default { type: String, required: true }, - rolesUrl: { - type: String, - required: true - }, createUrl: { type: String, required: true @@ -130,10 +107,7 @@ export default { experiments: [], selectedExperiment: null, newExperimentName: '', - userRoles: [], taskName: '', - publicProject: false, - defaultRole: null, creatingTask: false }; }, @@ -153,9 +127,7 @@ export default { }, project: { id: this.selectedProject, - name: this.newProjectName, - visibility: (this.publicProject ? 'visible' : 'hidden'), - default_public_user_role_id: this.defaultRole + name: this.newProjectName } }) .then((response) => { @@ -163,9 +135,6 @@ export default { window.location.href = response.data.my_module_path; }); }, - changeRole(value) { - this.defaultRole = value; - }, changeProject(value, label) { this.selectedProject = value; this.newProjectName = label; @@ -204,12 +173,6 @@ export default { this.selectedExperiment = null; this.newExperimentName = ''; }, - fetchUserRoles() { - axios.get(this.rolesUrl) - .then((response) => { - this.userRoles = response.data.data; - }); - }, focusInput() { this.$refs.taskName.focus(); } diff --git a/app/javascript/vue/experiments/list.vue b/app/javascript/vue/experiments/list.vue index 87e335554..4e08b764c 100644 --- a/app/javascript/vue/experiments/list.vue +++ b/app/javascript/vue/experiments/list.vue @@ -171,6 +171,7 @@ export default { cellRendererParams: { statusesList: this.statusesList }, + notSelectable: true, minWidth: 180 }, { diff --git a/app/javascript/vue/experiments/renderers/status.vue b/app/javascript/vue/experiments/renderers/status.vue index f92b408e2..ef31f362a 100644 --- a/app/javascript/vue/experiments/renderers/status.vue +++ b/app/javascript/vue/experiments/renderers/status.vue @@ -12,7 +12,11 @@
+ :class="{ + 'bg-sn-grey-500': params.data.status_cell.status === 'not_started', + 'bg-sn-science-blue': params.data.status_cell.status === 'in_progress', + 'bg-sn-alert-green': params.data.status_cell.status === 'done' + }">
{{ this.i18n.t(`experiments.table.column.status.${params.data.status_cell.status}`) }} @@ -34,20 +38,17 @@ export default { } }, methods: { - statusColor(status) { + optionRenderer(option) { let color = 'bg-sn-grey-500'; - if (status === 'in_progress') { + if (option[0] === 'in_progress') { color = 'bg-sn-science-blue'; - } else if (status === 'done') { + } else if (option[0] === 'done') { color = 'bg-sn-alert-green'; } - return color; - }, - optionRenderer(option) { return `
-
+
${option[1]}
`; }, diff --git a/app/javascript/vue/forms/fields/repository_rows.vue b/app/javascript/vue/forms/fields/repository_rows.vue index 1a068a590..8a7826a19 100644 --- a/app/javascript/vue/forms/fields/repository_rows.vue +++ b/app/javascript/vue/forms/fields/repository_rows.vue @@ -15,10 +15,11 @@ diff --git a/app/javascript/vue/item_relationships/RepositoryItemRelationshipsModal.vue b/app/javascript/vue/item_relationships/RepositoryItemRelationshipsModal.vue index 6e1d1bc1a..c641e324a 100644 --- a/app/javascript/vue/item_relationships/RepositoryItemRelationshipsModal.vue +++ b/app/javascript/vue/item_relationships/RepositoryItemRelationshipsModal.vue @@ -33,6 +33,7 @@ diff --git a/app/javascript/vue/my_module/assigned_items/repository.vue b/app/javascript/vue/my_module/assigned_items/repository.vue index 174eccd87..8efa1217c 100644 --- a/app/javascript/vue/my_module/assigned_items/repository.vue +++ b/app/javascript/vue/my_module/assigned_items/repository.vue @@ -19,6 +19,7 @@ - - - - - <% if assignable.top_level_assignable? %> - - <% end %> - - - diff --git a/app/views/access_permissions/my_modules/edit.json.jbuilder b/app/views/access_permissions/my_modules/edit.json.jbuilder deleted file mode 100644 index 7296100fd..000000000 --- a/app/views/access_permissions/my_modules/edit.json.jbuilder +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -json.modal controller.render_to_string( - partial: 'access_permissions/modals/edit_modal', - formats: [:html], - locals: { - assignable: @my_module, - top_level_assignable: @project, - manually_assigned_users: @project.manually_assigned_users - }, - layout: false -) diff --git a/app/views/access_permissions/my_modules/my_module_member.json.jbuilder b/app/views/access_permissions/my_modules/my_module_member.json.jbuilder deleted file mode 100644 index 43b413999..000000000 --- a/app/views/access_permissions/my_modules/my_module_member.json.jbuilder +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -json.form controller.render_to_string( - partial: 'access_permissions/partials/member_field', - formats: [:html], - locals: { - user: @user_assignment.user, - update_path: access_permissions_my_module_path(@my_module), - with_inherit: true, - assignable: @my_module - }, - layout: false -) diff --git a/app/views/access_permissions/my_modules/show.json.jbuilder b/app/views/access_permissions/my_modules/show.json.jbuilder deleted file mode 100644 index d7bb02d85..000000000 --- a/app/views/access_permissions/my_modules/show.json.jbuilder +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -json.modal controller.render_to_string( - partial: 'access_permissions/modals/show_modal', - formats: [:html], - locals: { - assignable: @my_module, - top_level_assignable: @project, - manually_assigned_users: @project.manually_assigned_users - }, - layout: false -) diff --git a/app/views/access_permissions/partials/_member_field.html.erb b/app/views/access_permissions/partials/_member_field.html.erb deleted file mode 100644 index 421ad7900..000000000 --- a/app/views/access_permissions/partials/_member_field.html.erb +++ /dev/null @@ -1,55 +0,0 @@ -<% # frozen_string_literal: true %> - -<% - with_inherit ||= false - assignment = assignable.user_assignments.find_by(user_id: user.id, team: current_team) - item_id = dom_id(user, :assignment_member) -%> - -<% if assignment.present? %> - <%= form_with(model: assignment, url: update_path, method: :put, html: { class: 'member-item', id: item_id, data: { remote: true, action: 'replace-form autosave-form', object_type: :assignment_member } }) do |f| %> - <%= f.hidden_field :user_id, value: f.object.user.id %> -
-
- <%= image_tag avatar_path(user, :icon_small), title: current_assignee_name(user), class: 'img-circle pull-left' %> -
-
- <%= current_assignee_name(user) %> -
- <%= user_assignment_resource_role_name(user, assignable) %> -
-
-
- <% unless with_inherit && current_user == user %> - - <% else %> - - <% end %> -
- <% end %> -<% end %> diff --git a/app/views/access_permissions/partials/_new_assignments_form.html.erb b/app/views/access_permissions/partials/_new_assignments_form.html.erb deleted file mode 100644 index ced4e15ef..000000000 --- a/app/views/access_permissions/partials/_new_assignments_form.html.erb +++ /dev/null @@ -1,37 +0,0 @@ -<% # frozen_string_literal: true %> - - diff --git a/app/views/access_permissions/partials/_public_assignment_field.html.erb b/app/views/access_permissions/partials/_public_assignment_field.html.erb deleted file mode 100644 index 19c644c72..000000000 --- a/app/views/access_permissions/partials/_public_assignment_field.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -<% # frozen_string_literal: true %> - -
- <%= user_form.hidden_field :user_id, value: :all, name:"access_permissions_new_user_form[resource_members][0][user_id]" %> -
-
- <%= user_form.check_box :assign, - name: "access_permissions_new_user_form[resource_members][0][assign]", - data: { action: 'toggle-visibility', target: 'usersAll' }, - class: "sci-checkbox" - %> - -
-
- <%= image_tag "icon/team.png", class: 'img-circle pull-left' %> -
-
- <%= t('user_assignment.assign_all_team_members') %> -
-
-
- -
-
diff --git a/app/views/access_permissions/partials/_user_assignment_field.html.erb b/app/views/access_permissions/partials/_user_assignment_field.html.erb deleted file mode 100644 index 97cb97b4a..000000000 --- a/app/views/access_permissions/partials/_user_assignment_field.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -<% # frozen_string_literal: true %> - -<% - user = user_form.object.user - id = dom_id(user, :new_protocol_member) -%> - -
- <%= user_form.hidden_field :user_id, value: user.id, name:"access_permissions_new_user_form[resource_members][#{user.id}][user_id]" %> -
-
- <%= user_form.check_box :assign, - name: "access_permissions_new_user_form[resource_members][#{user.id}][assign]", - data: { action: 'toggle-visibility', target: id }, - class: "sci-checkbox" - %> - -
-
- <%= image_tag avatar_path(user, :icon_small), title: current_assignee_name(user), class: 'img-circle pull-left' %> -
-
- <%= current_assignee_name(user) %> -
-
-
- -
-
diff --git a/app/views/access_permissions/projects/edit.json.jbuilder b/app/views/access_permissions/projects/edit.json.jbuilder deleted file mode 100644 index 68411cec1..000000000 --- a/app/views/access_permissions/projects/edit.json.jbuilder +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -json.html controller.render_to_string( - partial: 'access_permissions/modals/edit_modal', - formats: [:html], - locals: { - assignable: @project, - manually_assigned_users: @project.manually_assigned_users, - top_level_assignable: @project, - new_assignment_path: new_access_permissions_project_path(id: @project) - }, - layout: false -) - -json.flash @message diff --git a/app/views/access_permissions/projects/new.json.jbuilder b/app/views/access_permissions/projects/new.json.jbuilder deleted file mode 100644 index b23ecd272..000000000 --- a/app/views/access_permissions/projects/new.json.jbuilder +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -json.html controller.render_to_string( - partial: 'access_permissions/partials/new_assignments_form', - formats: [:html], - locals: { - assignable: @project, - form_object: @user_assignment, - users: @available_users, - create_path: access_permissions_projects_path(id: @project.id), - assignable_path: edit_access_permissions_project_path(@project) - }, - layout: false -) diff --git a/app/views/access_permissions/projects/project_member.json.jbuilder b/app/views/access_permissions/projects/project_member.json.jbuilder deleted file mode 100644 index b6ee40aa6..000000000 --- a/app/views/access_permissions/projects/project_member.json.jbuilder +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -json.form controller.render_to_string( - partial: 'access_permissions/partials/member_field', - formats: [:html], - locals: { - user: @user_assignment.user, - assignable: @project, - update_path: access_permissions_project_path(@project), - delete_path: access_permissions_project_path(@project, user_id: @user_assignment.user.id) - }, - layout: false -) diff --git a/app/views/access_permissions/projects/show.json.jbuilder b/app/views/access_permissions/projects/show.json.jbuilder deleted file mode 100644 index 40744273a..000000000 --- a/app/views/access_permissions/projects/show.json.jbuilder +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -json.modal controller.render_to_string( - partial: 'access_permissions/modals/show_modal', - formats: [:html], - locals: { - assignable: @project, - manually_assigned_users: @project.manually_assigned_users, - top_level_assignable: @project - }, - layout: false -) diff --git a/app/views/access_permissions/projects/update_default_public_user_role.json.jbuilder b/app/views/access_permissions/projects/update_default_public_user_role.json.jbuilder deleted file mode 100644 index 0cba94518..000000000 --- a/app/views/access_permissions/projects/update_default_public_user_role.json.jbuilder +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -json.form controller.render_to_string( - partial: 'access_permissions/partials/default_public_user_role_form', - formats: [:html], - locals: { - assignable: @project, - editable: true - }, - layout: false -) diff --git a/app/views/access_permissions/protocols/edit.json.jbuilder b/app/views/access_permissions/protocols/edit.json.jbuilder deleted file mode 100644 index aa065c0b2..000000000 --- a/app/views/access_permissions/protocols/edit.json.jbuilder +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -json.html controller.render_to_string( - partial: 'access_permissions/modals/edit_modal', - formats: [:html], - locals: { - assignable: @protocol, - top_level_assignable: @protocol, - manually_assigned_users: @protocol.manually_assigned_users, - update_path: access_permissions_protocol_path(@protocol), - new_assignment_path: new_access_permissions_protocol_path(id: @protocol) - }, - layout: false -) - -json.flash @message diff --git a/app/views/access_permissions/protocols/new.json.jbuilder b/app/views/access_permissions/protocols/new.json.jbuilder deleted file mode 100644 index c626e01fe..000000000 --- a/app/views/access_permissions/protocols/new.json.jbuilder +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -json.html controller.render_to_string( - partial: 'access_permissions/partials/new_assignments_form', - formats: [:html], - locals: { - assignable: @protocol, - form_object: @user_assignment, - users: current_team.users.where.not(id: @protocol.manually_assigned_users.select(:id)), - create_path: access_permissions_protocols_path(id: @protocol.id), - assignable_path: edit_access_permissions_protocol_path(@protocol) - }, - layout: false -) diff --git a/app/views/access_permissions/protocols/protocol_member.json.jbuilder b/app/views/access_permissions/protocols/protocol_member.json.jbuilder deleted file mode 100644 index 8d0e8916e..000000000 --- a/app/views/access_permissions/protocols/protocol_member.json.jbuilder +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -json.form controller.render_to_string( - partial: 'access_permissions/partials/member_field', - formats: [:html], - locals: { - user: @user_assignment.user, - assignable: @protocol, - update_path: access_permissions_protocol_path(@protocol), - delete_path: access_permissions_protocol_path(@protocol, user_id: @user_assignment.user_id) - }, - layout: false -) diff --git a/app/views/access_permissions/protocols/show.json.jbuilder b/app/views/access_permissions/protocols/show.json.jbuilder deleted file mode 100644 index 9353a07da..000000000 --- a/app/views/access_permissions/protocols/show.json.jbuilder +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -json.modal controller.render_to_string( - partial: 'access_permissions/modals/show_modal', - formats: [:html], - locals: { - assignable: @protocol, - top_level_assignable: @protocol, - manually_assigned_users: @protocol.manually_assigned_users - }, - layout: false -) diff --git a/app/views/access_permissions/protocols/update_default_public_user_role.json.jbuilder b/app/views/access_permissions/protocols/update_default_public_user_role.json.jbuilder deleted file mode 100644 index d151934ff..000000000 --- a/app/views/access_permissions/protocols/update_default_public_user_role.json.jbuilder +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -json.form controller.render_to_string( - partial: 'access_permissions/partials/default_public_user_role_form', - formats: [:html], - locals: { - assignable: @protocol, - editable: true - }, - layout: false -) diff --git a/app/views/canvas/edit/_my_module_dropdown_menu.html.erb b/app/views/canvas/edit/_my_module_dropdown_menu.html.erb index fb6448e0f..7c2afbe89 100644 --- a/app/views/canvas/edit/_my_module_dropdown_menu.html.erb +++ b/app/views/canvas/edit/_my_module_dropdown_menu.html.erb @@ -17,8 +17,7 @@ <%= link_to t('experiments.canvas.edit.task_access'), nil, class: "openAccessModal", data: { - url: my_module_path(@my_module, format: :json), - roles_url: user_roles_projects_path + url: my_module_path(@my_module, format: :json) } %> diff --git a/app/views/dashboards/_create_task_modal.html.erb b/app/views/dashboards/_create_task_modal.html.erb index 42036198c..9f320d16d 100644 --- a/app/views/dashboards/_create_task_modal.html.erb +++ b/app/views/dashboards/_create_task_modal.html.erb @@ -15,7 +15,6 @@ projects-url="<%= dashboard_quick_start_project_filter_path %>" experiments-url="<%= dashboard_quick_start_experiment_filter_path %>" create-url="<%= dashboard_quick_start_create_task_path %>" - roles-url="<%= user_roles_projects_path %>" > diff --git a/app/views/experiments/index.html.erb b/app/views/experiments/index.html.erb index d18dee907..2e72bfd15 100644 --- a/app/views/experiments/index.html.erb +++ b/app/views/experiments/index.html.erb @@ -11,7 +11,6 @@ active-page-url="<%= experiments_path(project_id: @project, view_mode: :active) %>" archived-page-url="<%= experiments_path(project_id: @project, view_mode: :archived) %>" current-view-mode="<%= params[:view_mode] || :active %>" - user-roles-url="<%= user_roles_projects_path %>" :archived="<%= @project.archived?%>" /> diff --git a/app/views/experiments/module_archive.html.erb b/app/views/experiments/module_archive.html.erb index 4acd02b19..df4cdedf6 100644 --- a/app/views/experiments/module_archive.html.erb +++ b/app/views/experiments/module_archive.html.erb @@ -32,8 +32,7 @@ -
+
<% if subject %> - <%= route_to_other_team(repository_path(subject.id, team: subject.team.id), - team, - subject.name&.truncate(Constants::NAME_TRUNCATION_LENGTH), - title: subject.name) %> + <% if can_read_repository?(subject) %> + <%= route_to_other_team(repository_path(subject.id, team: subject.team.id), + team, + subject.name&.truncate(Constants::NAME_TRUNCATION_LENGTH), + title: subject.name) %> + <% else %> + <%= I18n.t('repositories.private') %> + <% end %> <% else %> <%= breadcrumbs['repository']&.truncate(Constants::NAME_TRUNCATION_LENGTH) %> diff --git a/app/views/global_activities/references/_repository_row.html.erb b/app/views/global_activities/references/_repository_row.html.erb index a8076ad73..326504f6f 100644 --- a/app/views/global_activities/references/_repository_row.html.erb +++ b/app/views/global_activities/references/_repository_row.html.erb @@ -3,15 +3,23 @@
<% if subject %> - <%= route_to_other_team(repository_path(subject.repository.id, team: subject.repository.team.id), - team, - subject.repository.name&.truncate(Constants::NAME_TRUNCATION_LENGTH), - title: subject.repository.name) %> + <% if can_read_repository?(subject.repository) %> + <%= route_to_other_team(repository_path(subject.repository.id, team: subject.repository.team.id), + team, + subject.repository.name&.truncate(Constants::NAME_TRUNCATION_LENGTH), + title: subject.repository.name) %> + <% else %> + <%= I18n.t('repositories.private') %> + <% end %> <% elsif repository = Repository.find_by(id: values.dig('message_items', 'repository', 'id')) %> - <%= route_to_other_team(repository_path(repository.id, team: repository.team.id), - team, - repository.name&.truncate(Constants::NAME_TRUNCATION_LENGTH), - title: repository.name) %> + <% if can_read_repository?(repository) %> + <%= route_to_other_team(repository_path(repository.id, team: repository.team.id), + team, + repository.name&.truncate(Constants::NAME_TRUNCATION_LENGTH), + title: repository.name) %> + <% else %> + <%= I18n.t('repositories.private') %> + <% end %> <% else %> <%= breadcrumbs['repository']&.truncate(Constants::NAME_TRUNCATION_LENGTH) %> diff --git a/app/views/my_modules/index.html.erb b/app/views/my_modules/index.html.erb index 521c5eabe..8a714d750 100644 --- a/app/views/my_modules/index.html.erb +++ b/app/views/my_modules/index.html.erb @@ -14,8 +14,7 @@ current-view-mode="<%= view_mode %>" assigned-users-url="<%= (assigned_users_experiment_path(@experiment) if can_designate_users_to_new_task?(@experiment)) %>" current-user-id="<%= current_user.id %>" - users-filter-url="<%= users_filter_projects_path %>"v - user-roles-url="<%= user_roles_projects_path %>" + users-filter-url="<%= users_filter_projects_path %>" :tags-colors="<%= Constants::TAG_COLORS.to_json %>" project-name="<%= @experiment.project.name %>" :statuses-list="<%= MyModuleStatus.all.order(:id).map{ |i| [i.id, i.name] }.to_json %>" diff --git a/app/views/my_modules/modals/_assign_repository_records_modal_content.html.erb b/app/views/my_modules/modals/_assign_repository_records_modal_content.html.erb index 6403e49f6..7a076fe5f 100644 --- a/app/views/my_modules/modals/_assign_repository_records_modal_content.html.erb +++ b/app/views/my_modules/modals/_assign_repository_records_modal_content.html.erb @@ -1,17 +1,30 @@