mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-06 21:24:23 +08:00
commit
200dc89dcd
345 changed files with 5197 additions and 3798 deletions
9
Gemfile
9
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'
|
||||
|
|
229
Gemfile.lock
229
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
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.43.0.2
|
||||
1.44.0
|
||||
|
|
5
app/assets/images/icon/group.svg
Normal file
5
app/assets/images/icon/group.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" fill="#F9F9F9"/>
|
||||
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" stroke="#EAECF0"/>
|
||||
<path d="M4.50001 17.8331V16.6663C4.50001 16.2744 4.60001 15.9412 4.80001 15.6662C5.00001 15.3912 5.26689 15.1663 5.60001 14.9994C6.32501 14.6494 7.04189 14.3744 7.75814 14.1744C8.47439 13.9744 9.33314 13.8744 10.3331 13.8744C11.3331 13.8744 12.1913 13.9744 12.9081 14.1744C13.625 14.3744 14.3413 14.6494 15.0663 14.9994C15.3994 15.1663 15.6663 15.3912 15.8663 15.6662C16.0663 15.9412 16.1663 16.2744 16.1663 16.6663V17.8331H4.50001ZM10.3331 12C9.64126 12 9.05001 11.7581 8.56626 11.2669C8.08314 10.7837 7.83314 10.1919 7.83314 9.5C7.83314 8.80813 8.07501 8.23313 8.56626 7.74188C9.04939 7.25 9.64126 7 10.3331 7C11.025 7 11.6 7.25 12.0913 7.74188C12.5831 8.23375 12.8331 8.825 12.8331 9.5C12.8331 10.175 12.5831 10.7831 12.0913 11.2669C11.5994 11.75 11.0081 12 10.3331 12ZM16.3919 9.5C16.3919 10.1919 16.1419 10.7831 15.65 11.2669C15.1581 11.7506 14.5669 12 13.8919 12C13.85 12 13.8 12 13.75 11.9919C13.6919 11.9919 13.65 11.9837 13.625 11.9837C13.9 11.6337 14.1169 11.2506 14.275 10.8256C14.425 10.4006 14.5 9.95875 14.5 9.50063C14.5 9.0425 14.4169 8.62563 14.2581 8.20875C14.1 7.79188 13.8831 7.40063 13.625 7.04188C13.6669 7.025 13.7081 7.01687 13.75 7.00875C13.7919 7.00875 13.8419 7.00063 13.8919 7.00063C14.575 7.00063 15.1588 7.25062 15.65 7.7425C16.1413 8.23437 16.3919 8.82563 16.3919 9.50063V9.5ZM5.33314 17H15.3331V16.6669C15.3331 16.475 15.2831 16.3 15.1831 16.1587C15.0831 16.0087 14.9163 15.8669 14.6663 15.7338C14.0413 15.4006 13.3831 15.1506 12.6994 14.9756C12.0075 14.8006 11.2244 14.7175 10.3325 14.7175C9.44064 14.7175 8.65752 14.8006 7.96564 14.9756C7.27376 15.1506 6.62376 15.4006 5.99876 15.7338C5.74876 15.8756 5.57376 16.0169 5.48189 16.1587C5.38189 16.3006 5.34001 16.4756 5.34001 16.6669V17H5.33314ZM10.3331 11.1669C10.7913 11.1669 11.1831 11 11.5081 10.675C11.8331 10.35 12 9.95812 12 9.5C12 9.04188 11.8331 8.65 11.5081 8.325C11.1831 8 10.7913 7.83313 10.3331 7.83313C9.87501 7.83313 9.48314 8 9.15814 8.325C8.83314 8.65 8.66627 9.04188 8.66627 9.5C8.66627 9.95812 8.83314 10.35 9.15814 10.675C9.48314 11 9.87501 11.1669 10.3331 11.1669ZM17.25 16.3169V17.8337H19.5V16.3169C19.5 15.9587 19.4 15.6337 19.2081 15.325C19.0163 15.025 18.7413 14.775 18.3913 14.575C18.0331 14.3831 17.6663 14.2 17.2994 14.0419C16.9325 13.875 16.5494 13.75 16.1663 13.6669C16.5163 13.9919 16.7831 14.3837 16.9663 14.85C17.1494 15.3081 17.25 15.8 17.25 16.3169Z" fill="#1D2939"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
100
app/assets/images/user_groups/promo.svg
Normal file
100
app/assets/images/user_groups/promo.svg
Normal file
|
@ -0,0 +1,100 @@
|
|||
<svg width="399" height="240" viewBox="0 0 399 240" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2686_13418)">
|
||||
<path d="M180.722 32.686L199.497 31.6412L194.696 17.9363L176.738 16.3633L180.722 32.686Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M176.55 16.2012L194.802 17.8L199.698 31.7755L180.61 32.8377L176.55 16.2012ZM176.927 16.5256L180.834 32.5345L199.297 31.5071L194.59 18.0728L176.927 16.5256Z" fill="#B7BBCE"/>
|
||||
<path d="M182.287 30.0739H196.894L193.763 19.6313L179.162 18.5864L182.287 30.0739Z" fill="#074C9C"/>
|
||||
<path d="M233.47 32.541L213.385 52.6832C213.385 52.6832 212.625 47.3719 211.488 41.6543C210.213 35.1879 208.462 28.2049 206.815 27.7812C203.707 26.9801 196.749 25.8192 197.433 27.4097C198.117 29.0001 201.312 29.6909 201.312 29.6909C201.312 29.6909 199.26 35.1705 203.475 35.8555C203.475 35.8555 203.197 38.0264 202.895 41.3467C201.892 51.4584 200.46 72.2566 205.215 74.7874C211.593 78.1483 229.707 71.8212 229.707 71.8212L233.47 32.541Z" fill="#DBDBDC"/>
|
||||
<path d="M173.485 169.653L215.42 185.866L218.191 213.722L194.238 226.051L195.838 215.545L173.485 169.653Z" fill="#4175BA"/>
|
||||
<path d="M216.904 200.795L218.192 213.722L194.238 226.051L195.839 215.545L189.53 202.6L216.904 200.795Z" fill="#DBDBDC"/>
|
||||
<path d="M205.215 162.461H241.113V239.083H213.385L219.879 230.277C219.879 230.277 206.589 180.937 205.191 162.461" fill="#6693CD"/>
|
||||
<path d="M228.333 4.51001L234.131 21.094L226.779 23.9905C226.527 24.0921 226.255 24.1334 225.985 24.111C225.714 24.0887 225.453 24.0033 225.221 23.8617C224.99 23.7201 224.795 23.5263 224.651 23.2957C224.508 23.065 224.42 22.804 224.396 22.5335C223.967 18.2206 223.659 8.89254 228.333 4.53903" fill="#DBDBDC"/>
|
||||
<path d="M231.661 16.9668L230.623 16.7753C230.623 16.7753 231.864 15.3938 231.064 8.88673C230.264 2.3797 239.646 0.59766 244.748 6.24561C252.292 14.5985 247.131 34.4389 247.131 34.4389H233.117C233.117 34.4389 235.587 26.0976 235.587 23.2707C235.587 23.2707 239.147 23.8222 240.985 21.8079C231.064 24.2923 231.661 16.9436 231.661 16.9436" fill="#4071B7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M233.559 14.1123C233.839 13.832 234.219 13.6746 234.615 13.6746C235.012 13.6746 235.392 13.832 235.672 14.1123L235.582 14.2026C235.326 13.9463 234.978 13.8023 234.615 13.8023C234.253 13.8023 233.905 13.9463 233.649 14.2026L233.559 14.1123Z" fill="#20499A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M232.969 17.8259H235.294L235.185 17.9349C235.047 18.0735 234.883 18.1835 234.702 18.2585C234.521 18.3336 234.327 18.3722 234.131 18.3722C233.936 18.3722 233.742 18.3336 233.561 18.2585C233.38 18.1835 233.216 18.0735 233.077 17.9349L232.969 17.8259ZM233.29 17.9536C233.388 18.03 233.495 18.093 233.61 18.1406C233.775 18.2092 233.952 18.2445 234.131 18.2445C234.31 18.2445 234.488 18.2092 234.653 18.1406C234.768 18.093 234.875 18.03 234.972 17.9536H233.29Z" fill="#20499A"/>
|
||||
<path d="M236.236 8.25414C236.236 7.98132 236.236 11.1565 231.325 13.3971C226.414 15.6377 224.175 10.8198 227.266 5.93808C230.357 1.05635 237.274 -0.969483 241.623 0.458467C245.972 1.88642 253.075 6.26314 248.859 20.1944L244.192 16.8799C244.192 16.8799 246.355 13.9137 243.96 13.0024C241.565 12.091 241.171 14.5406 241.171 14.5406C241.171 14.5406 236.491 11.8008 236.236 8.23673" fill="#F0F0F6"/>
|
||||
<path d="M244.232 27.6127C256.989 29.6676 251.115 69.9868 251.115 69.9868H215.82C218.099 38.9318 231.441 25.5753 244.232 27.6127Z" fill="#F0F0F6"/>
|
||||
<path d="M215.82 69.9695H251.098L248.871 190.202C248.871 190.202 222.987 205.294 159.354 178.9L215.82 69.9695Z" fill="#F0F0F6"/>
|
||||
<path d="M215.82 69.9695L246.801 44.2664C246.801 44.2664 253.011 75.4258 204.809 91.2029L215.82 69.9695Z" fill="#DBDBDC"/>
|
||||
<path d="M184.775 29.2148C184.241 27.4444 182.49 28.1583 182.49 28.1583C182.49 28.1583 179.817 23.7642 178.397 24.0486C176.976 24.3331 174.129 31.0142 178.054 37.9044L185.749 33.6902C185.499 32.183 185.174 30.6894 184.775 29.2148Z" fill="#4071B7"/>
|
||||
<path d="M243.49 35.5652C243.49 35.5652 252.292 64.5886 224.355 78.6127C196.418 92.6368 179.527 47.935 177.388 39.228L186.439 33.9283L211.285 57.0774C211.285 57.0774 233.847 14.5523 243.49 35.571" fill="#F0F0F6"/>
|
||||
<path d="M241.113 223.886V239.094H213.385L219.879 230.277C219.879 230.277 218.719 226.028 217.078 219.567L241.113 223.886Z" fill="#DBDBDC"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M228.938 32.9135C229.254 32.6653 229.533 32.4871 229.762 32.4043L229.861 32.6773C229.676 32.7439 229.427 32.8984 229.117 33.1418C228.81 33.3831 228.453 33.7045 228.054 34.0952C227.256 34.8765 226.298 35.9283 225.246 37.1555C223.144 39.6095 220.677 42.7575 218.399 45.8286C216.121 48.9 214.033 51.8915 212.685 54.0325C212.01 55.105 211.526 55.9568 211.295 56.4968C211.246 56.6115 211.209 56.7096 211.185 56.791L211.547 57.1074L211.348 57.2024C211.257 57.2456 211.156 57.2788 211.063 57.2615C211.01 57.2518 210.961 57.2264 210.924 57.1824C210.888 57.1405 210.871 57.0915 210.864 57.0466C210.857 57.0003 210.858 56.9495 210.865 56.8963L201.229 48.4795L201.419 48.2608L210.948 56.5842C210.971 56.5213 210.998 56.4539 211.028 56.3826C211.269 55.8195 211.765 54.9507 212.44 53.8778C213.793 51.728 215.886 48.7299 218.166 45.6556C220.447 42.581 222.918 39.4273 225.026 36.9665C226.08 35.7364 227.045 34.6775 227.852 33.8878C228.255 33.493 228.62 33.1637 228.938 32.9135Z" fill="white"/>
|
||||
<path d="M202.988 40.1798C203.249 37.5387 203.452 35.8844 203.452 35.8844C199.393 35.1878 201.364 29.6385 201.364 29.6385C201.364 29.6385 198.291 28.942 197.63 27.3167C196.969 25.6913 203.672 26.8755 206.67 27.6881C208.137 28.0887 209.691 34.172 210.891 40.2727L202.988 40.1798Z" fill="#4071B7"/>
|
||||
<path d="M363.843 84.0574C363.843 84.0574 370.163 115.937 347.688 120.772C325.213 125.607 309.998 111.09 309.998 111.09C309.998 111.09 306.995 112.396 303.73 108.187C303.73 108.187 300.332 109.534 300.46 107.485C300.587 105.436 304.455 94.5871 341.177 104.275L349.184 85.3983L363.843 84.0574Z" fill="#DBDBDC"/>
|
||||
<path d="M368.319 71.6354C361.674 69.0407 352.663 76.2094 348.245 87.633L372.303 97.0134C376.744 85.5898 374.964 74.2243 368.319 71.6354Z" fill="#DBDBDC"/>
|
||||
<path d="M320.337 152.32L355.667 136.108L365.315 81.4569C365.315 81.4569 362.677 57.0192 379.928 65.1225C397.178 73.2258 410.474 144.618 381.603 161.561C352.733 178.505 300.616 163.976 320.337 152.344" fill="#1F3563"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M332.808 167.374C346.939 170.33 367.105 169.719 381.457 161.311L381.75 161.812C367.231 170.318 346.904 170.915 332.689 167.942C329.132 167.198 325.949 166.228 323.329 165.099C320.713 163.971 318.64 162.675 317.319 161.269C315.995 159.858 315.402 158.303 315.844 156.696C316.28 155.112 317.7 153.562 320.19 152.094L320.484 152.594C318.044 154.033 316.779 155.481 316.403 156.85C316.033 158.197 316.502 159.551 317.742 160.871C318.985 162.196 320.978 163.453 323.558 164.566C326.135 165.676 329.279 166.635 332.808 167.374Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M350.327 235.356V169.235H350.907V235.356H350.327Z" fill="#B7BBCE"/>
|
||||
<path d="M314.185 228.164L310.259 208.057L329.58 207.052L329.365 231.751L311.836 232.657L314.185 228.164Z" fill="#1F3563"/>
|
||||
<path d="M335.169 234.265L329.435 214.599L348.581 211.842L350.617 236.459L333.244 238.955L335.169 234.265Z" fill="#0B4B9B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M365.124 240H283.325V239.129H365.124V240Z" fill="#B7BBCE"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M242.4 240H160.601V239.129H242.4V240Z" fill="#B7BBCE"/>
|
||||
<path d="M367.27 68.4834C345.056 71.0317 345.873 124.371 345.873 124.371H384.653C381.754 83.3956 383.726 66.6027 367.258 68.4834" fill="#F0F0F6"/>
|
||||
<path d="M379.493 131.122L344.238 123.744C344.238 123.744 288.231 127.128 307.285 209.74L330.304 214.564L335.697 164.609C335.697 164.609 377.684 176.067 379.493 131.11" fill="#4175BA"/>
|
||||
<path d="M372.883 116.285L347.114 108.28C347.114 108.28 356.108 112.785 359.111 96.2763C360.886 86.4896 374.187 92.718 374.187 92.718L372.883 116.285Z" fill="#DBDBDC"/>
|
||||
<path d="M385.198 123.686H346.418C346.418 123.686 288.156 138.627 326.42 216.358H351.736L346.418 165.857C346.418 165.857 393.189 168.486 385.198 123.674" fill="#6693CD"/>
|
||||
<path d="M338.016 123.726L384.665 120.168C384.665 120.168 401.098 157.08 347.346 172.201C347.346 172.201 334.908 171.754 317.136 166.861C295.328 160.859 306.032 150.852 306.032 150.852C306.032 150.852 310.132 135.249 338.016 123.726Z" fill="#F0F0F6"/>
|
||||
<path d="M366.469 110.399L383.088 114.602C383.088 114.602 387.408 125.903 371.798 131.087C356.189 136.27 331.082 112.872 331.082 112.872L366.469 110.399Z" fill="#DBDBDC"/>
|
||||
<path d="M387.524 88.4224C389.698 95.7398 389.062 103.606 385.74 110.479C382.419 117.351 376.652 122.732 369.571 125.567C362.491 128.402 354.607 128.486 347.468 125.802C340.329 123.118 334.449 117.861 330.983 111.061C330.983 111.061 327.979 112.372 324.709 108.158C324.709 108.158 321.317 109.511 321.444 107.462C321.572 105.413 326.541 99.2655 363.263 108.953L361.836 89.4208L387.524 88.4224Z" fill="#F0F0F6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M323.03 105.733C322.03 106.407 321.759 107.1 321.734 107.503C321.72 107.721 321.755 107.87 321.807 107.972C321.858 108.073 321.934 108.147 322.038 108.204C322.261 108.324 322.601 108.354 323.001 108.316C323.391 108.278 323.791 108.179 324.098 108.088C324.251 108.042 324.378 107.999 324.467 107.968C324.511 107.952 324.546 107.939 324.569 107.93L324.595 107.92L324.601 107.918L324.602 107.918L324.804 107.837L324.938 108.009C326.535 110.064 328.043 110.745 329.122 110.931C329.665 111.024 330.109 110.994 330.414 110.942C330.567 110.916 330.684 110.884 330.762 110.86C330.8 110.848 330.829 110.838 330.847 110.831C330.856 110.828 330.862 110.826 330.866 110.824L330.868 110.823C330.868 110.823 330.868 110.823 330.983 111.09C331.099 111.356 331.098 111.356 331.098 111.356L331.096 111.357L331.093 111.358L331.083 111.362C331.075 111.366 331.064 111.37 331.05 111.375C331.022 111.385 330.983 111.399 330.933 111.415C330.834 111.446 330.691 111.483 330.511 111.514C330.151 111.575 329.64 111.609 329.023 111.503C327.815 111.295 326.239 110.559 324.612 108.532C324.52 108.564 324.401 108.603 324.264 108.644C323.939 108.741 323.499 108.851 323.057 108.893C322.625 108.935 322.141 108.918 321.763 108.715C321.567 108.609 321.4 108.453 321.29 108.234C321.18 108.019 321.137 107.761 321.155 107.467C321.194 106.845 321.592 106.002 322.707 105.251C323.818 104.502 325.651 103.836 328.602 103.544C334.503 102.958 344.966 103.855 363.337 108.702L363.189 109.263C344.838 104.422 334.456 103.546 328.66 104.121C325.762 104.409 324.034 105.057 323.03 105.733ZM330.869 110.823C330.869 110.823 330.869 110.823 330.869 110.823V110.823Z" fill="white"/>
|
||||
<path d="M330.919 103.7C322.801 103.863 321.166 106.342 321.062 107.508C320.934 109.552 324.332 108.205 324.332 108.205C327.597 112.39 330.6 111.107 330.6 111.107L330.919 103.7Z" fill="#4071B7"/>
|
||||
<path d="M308.468 101.019C301.016 104.24 300.442 107.16 300.814 108.263C301.469 110.207 304.101 107.682 304.101 107.682C308.74 110.318 310.996 107.967 310.996 107.967L308.468 101.019Z" fill="#4071B7"/>
|
||||
<path d="M292.626 111.554L296.453 107.984H334.381L337.222 113.882L282.763 113.777C282.55 113.752 282.355 113.645 282.221 113.478C282.086 113.31 282.023 113.097 282.044 112.883C282.024 112.67 282.087 112.458 282.22 112.29C282.353 112.122 282.545 112.013 282.757 111.984L292.626 111.554Z" fill="#0B4B9B"/>
|
||||
<path d="M313.588 104.438L251.776 103.422L242.504 62.824L306.374 61.2974L313.588 104.438Z" fill="#0B4B9B"/>
|
||||
<path d="M287.512 113.481H278.142L273.213 92.9966H282.577L287.512 113.481Z" fill="#0B4B9B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M272.845 92.7063H282.806L287.88 113.771H277.913L272.845 92.7063ZM273.581 93.2868L278.37 113.191H287.144L282.349 93.2868H273.581Z" fill="white"/>
|
||||
<path d="M353.475 55.3009L352.408 55.2719C352.408 55.2719 353.458 53.8033 351.759 47.8361C350.06 41.8689 359.21 38.8215 365.095 43.3433C373.793 50.0245 373.793 68.9942 373.793 68.9942L359.042 71.1767C359.042 71.1767 358.653 63.2708 358.265 60.6296C358.265 60.6296 361.906 60.6296 363.483 58.4645C353.893 62.2549 353.475 55.3009 353.475 55.3009Z" fill="#4071B7"/>
|
||||
<path d="M353.04 55.2894C353.04 55.2894 351.741 63.027 355.261 64.6697C358.781 66.3124 366.568 64.1937 366.04 57.4603C365.513 50.7269 365.849 49.8214 367.415 50.1522C368.98 50.4831 368.041 52.4218 368.041 52.4218L371.41 53.5247C371.41 53.5247 368.284 40.232 363.129 39.3091C357.975 38.3861 347.52 45.8684 347.52 45.8684C347.52 45.8684 349.741 47.8304 351.776 47.4182L348.877 48.5327C348.877 48.5327 352.414 52.5321 361.054 49.8388L363.373 51.1564C363.373 51.1564 363.756 56.3052 361.923 56.3806C360.091 56.4561 357.285 54.1284 353.052 55.3126" fill="#1F3563"/>
|
||||
<path d="M357.059 52.0852C356.744 51.8755 356.363 51.7902 355.989 51.846C355.615 51.9018 355.275 52.0946 355.035 52.3871L357.059 52.0852Z" fill="white"/>
|
||||
<path d="M355.064 55.9454C355.379 56.1551 355.76 56.2404 356.134 56.1846C356.508 56.1289 356.848 55.936 357.088 55.6436L355.064 55.9454Z" fill="#0B4B9B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M357.789 55.2454L357.312 55.8277C357.026 56.1757 356.621 56.4052 356.176 56.4716C355.731 56.538 355.278 56.4365 354.903 56.1869L354.277 55.7693L357.789 55.2454Z" fill="#2354A2"/>
|
||||
<path d="M374.628 63.4973L356.722 69.4065C356.722 69.4065 356.253 77.4227 356.224 77.6027L377.73 73.8122L374.628 63.4973Z" fill="#DBDBDC"/>
|
||||
<path d="M372.906 68.9827C380.009 68.3616 386.631 77.7594 387.692 89.9782L361.97 92.2246C360.909 80.0058 365.803 69.5864 372.906 69.0059" fill="#F0F0F6"/>
|
||||
<path d="M36.7404 85.6245C36.7404 85.6245 31.1681 115.101 50.9988 119.576C70.8295 124.051 84.2703 110.619 84.2703 110.619C84.2703 110.619 86.926 111.827 89.8078 107.955C89.8078 107.955 92.8056 109.203 92.707 107.311C92.6085 105.418 89.1816 95.3822 56.7566 104.339L48.3373 86.3095L36.7404 85.6245Z" fill="#DBDBDC"/>
|
||||
<path d="M31.5103 75.4492C37.6567 73.0518 45.9717 79.6808 50.0654 90.2511L27.8109 98.9233C23.6998 88.3529 25.3524 77.8465 31.4929 75.4492" fill="#DBDBDC"/>
|
||||
<path d="M72.7894 149.517L31.1913 83.9934C31.1913 83.9934 33.6325 61.3958 17.6751 68.9013C1.71779 76.4067 -10.5749 142.406 16.1269 158.038C42.8288 173.67 91.0255 160.273 72.7894 149.517Z" fill="#0B4B9B"/>
|
||||
<path d="M123.155 203.622L115.884 186.133L102.315 192.866L115.124 211.772L127.26 205.88L123.155 203.622Z" fill="#4175BA"/>
|
||||
<path d="M154.142 178.9L138.509 168.219L130.814 181.274L151.851 190.114L158.815 178.546L154.142 178.9Z" fill="#0B4B9B"/>
|
||||
<path d="M52.5413 112.233L76.3729 159.843L131.4 182.882L141.495 168.538L52.5413 112.233Z" fill="#1F3563"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M100.877 239.53H25.2246V238.659H100.877V239.53Z" fill="#B7BBCE"/>
|
||||
<path d="M52.5412 112.222H18.9681C18.9681 112.222 9.62101 156.68 74.4187 159.82L103.075 196.227L118.052 187.102C118.052 187.102 100.999 129.578 52.5296 112.222" fill="#0B4B9B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M44.3538 239.094V165.474H45.2235V239.094H44.3538Z" fill="#B7BBCE"/>
|
||||
<path d="M57.638 114.108H19.3334C19.3334 114.108 9.54559 157.91 76.2336 161.184C76.2336 161.184 73.4735 208.933 79.2488 201.097C102.773 169.171 92.5331 136.891 92.5331 136.891C92.5331 136.891 81.4522 123.947 57.638 114.108Z" fill="#F0F0F6"/>
|
||||
<path d="M32.1249 109.54L19.4553 113.104C19.4553 113.104 16.8634 125.508 29.4055 132.416C41.9475 139.324 59.9111 114.776 59.9111 114.776L32.1249 109.54Z" fill="#DBDBDC"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M67.759 104.403C73.1904 104.943 74.5916 106.872 74.6676 108.053C74.6853 108.328 74.6451 108.569 74.5421 108.773C74.4379 108.979 74.2794 109.127 74.0947 109.226C73.7393 109.417 73.2861 109.431 72.8852 109.392C72.474 109.352 72.0651 109.249 71.7635 109.159C71.6425 109.123 71.5373 109.088 71.4543 109.06C69.9516 110.919 68.4936 111.595 67.3737 111.786C66.801 111.883 66.3262 111.852 65.9911 111.794C65.8236 111.765 65.6909 111.73 65.598 111.701C65.5516 111.687 65.5151 111.674 65.4891 111.664C65.4761 111.659 65.4658 111.655 65.4581 111.652L65.4487 111.648L65.4455 111.647L65.4443 111.647C65.444 111.646 65.4433 111.646 65.5588 111.38C65.6743 111.114 65.6741 111.114 65.6739 111.114L65.6756 111.114C65.6787 111.115 65.6843 111.118 65.6924 111.121C65.7085 111.127 65.7346 111.136 65.7701 111.147C65.8409 111.169 65.9488 111.198 66.0889 111.222C66.3689 111.27 66.7773 111.298 67.2766 111.213C68.2681 111.045 69.6566 110.424 71.1288 108.537L71.2624 108.366L71.464 108.446L71.4653 108.446L71.471 108.448L71.4948 108.458C71.5162 108.466 71.5481 108.477 71.589 108.492C71.6709 108.521 71.7884 108.561 71.9293 108.603C72.2132 108.688 72.5824 108.779 72.9416 108.814C73.3112 108.85 73.6208 108.822 73.8205 108.715C73.9133 108.665 73.9803 108.599 74.0247 108.511C74.0703 108.421 74.1017 108.288 74.0889 108.09C74.0431 107.378 73.0873 105.516 67.7016 104.98C62.3454 104.447 52.7472 105.255 35.7765 109.733L35.6287 109.172C52.6195 104.688 62.2982 103.859 67.759 104.403Z" fill="white"/>
|
||||
<path d="M48.8244 74.1721C51.6424 71.8908 43.3507 69.8534 46.8239 63.1432C50.2972 56.433 46.0817 51.7661 46.0817 51.7661L34.6994 49.5312L33.8006 59.7533L27.6484 65.0181C27.6484 65.0181 35.5575 75.2692 41.6401 81.0332C48.9172 87.9234 54.2285 77.2892 48.5345 74.2533L48.8244 74.1721Z" fill="#1F3563"/>
|
||||
<path d="M38.88 59.9796L39.831 59.8055C39.831 59.8055 38.6713 58.5284 39.4251 52.5264C40.1789 46.5244 31.5102 44.8874 26.7902 50.1117C19.8321 57.8203 24.5752 76.1456 24.5752 76.1456H37.5232C37.5232 76.1456 35.2386 68.4544 35.2386 65.8423C35.2386 65.8423 31.9567 66.3473 30.2577 64.4898C39.4135 66.8117 38.8684 60.0028 38.8684 60.0028" fill="#4071B7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M36.149 57.3119C35.8758 57.3119 35.6137 57.4204 35.4202 57.6135L35.0107 57.2025C35.3129 56.9009 35.7223 56.7314 36.149 56.7314C36.5758 56.7314 36.9851 56.9009 37.2873 57.2025L36.8778 57.6135C36.6844 57.4204 36.4223 57.3119 36.149 57.3119Z" fill="#D2D2D2"/>
|
||||
<path d="M37.529 60.8503C37.2804 61.0964 36.945 61.2343 36.5954 61.2343C36.2459 61.2343 35.9104 61.0964 35.6619 60.8503H37.529Z" fill="#0B4B9B"/>
|
||||
<path d="M28.9589 58.0001C28.9589 58.0001 38.2944 54.7902 39.4425 51.3132C39.4425 51.3132 39.541 56.6883 45.4844 56.7928C51.4279 56.8973 42.6432 44.3069 33.7484 43.9354C24.8535 43.5639 15.2571 52.3638 23.0328 62.0054L26.3553 59.4223C26.3553 59.4223 25.4102 58.3368 26.0654 57.4081C26.7206 56.4793 27.7528 56.3458 28.9646 57.9885" fill="#1F3563"/>
|
||||
<path d="M25.5146 71.9255C13.3378 73.8759 18.9681 112.233 18.9681 112.233H52.5412C50.3726 82.6815 37.6739 69.9693 25.5146 71.9255Z" fill="#F0F0F6"/>
|
||||
<path d="M82.2351 100.438C91.2691 101.93 92.6317 105.419 92.4983 106.939C92.2664 109.615 88.7235 107.328 88.7235 107.328C84.3051 112.239 81.1855 110.062 81.1855 110.062L82.2351 100.438Z" fill="#4071B7"/>
|
||||
<path d="M115.286 80.0288L100.471 108.28H76.8078L73.5491 113.731H135.308L151.103 80.0288H115.286Z" fill="#6693CD"/>
|
||||
<path d="M21.3281 69.447L21.85 76.6216L36.8274 78.0321L37.8247 71.5135L21.3281 69.447Z" fill="#DBDBDC"/>
|
||||
<path d="M25.5552 72.4017C18.9623 72.6223 13.6451 80.3715 14.022 91.708L38.1726 92.5265C37.7899 81.19 32.1422 72.2101 25.5552 72.4017Z" fill="#F0F0F6"/>
|
||||
<path d="M25.0508 113.383L47.0501 106.545C47.0501 106.545 39.3672 110.393 36.8101 96.2936C35.2909 87.9406 23.9143 93.2577 23.9143 93.2577L25.0508 113.383Z" fill="#DBDBDC"/>
|
||||
<path d="M14.0337 91.3945C17.1417 114.445 23.5779 122.589 30.0838 124.667C43.2289 128.87 66.1386 111.363 66.1386 111.363C66.1386 111.363 68.9276 112.57 71.937 108.698C71.937 108.698 75.0914 109.946 74.9696 108.054C74.8479 106.162 70.2381 100.473 36.12 109.43C35.7566 103.42 35.3933 97.4101 35.0299 91.4003L14.0337 91.3945Z" fill="#F0F0F6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M73.2618 106.052C74.2811 106.739 74.6492 107.513 74.685 108.088C74.7019 108.36 74.6617 108.598 74.5596 108.8C74.4564 109.004 74.2996 109.149 74.1171 109.248C73.7659 109.437 73.3188 109.451 72.9239 109.413C72.5188 109.374 72.1163 109.274 71.8194 109.185C71.7019 109.15 71.5995 109.117 71.5182 109.089C70.0414 110.916 68.6064 111.581 67.5031 111.768C66.9389 111.863 66.4707 111.832 66.1403 111.775C65.9751 111.747 65.8442 111.712 65.7526 111.683C65.7068 111.669 65.6708 111.656 65.6451 111.647C65.6323 111.642 65.622 111.638 65.6145 111.635L65.6051 111.631L65.602 111.63L65.6008 111.629C65.6005 111.629 65.5998 111.629 65.7153 111.363C65.8308 111.096 65.8306 111.096 65.8304 111.096L65.832 111.097C65.835 111.098 65.8405 111.1 65.8484 111.103C65.8643 111.109 65.89 111.118 65.9248 111.129C65.9945 111.151 66.1006 111.179 66.2384 111.203C66.5139 111.25 66.9155 111.278 67.4065 111.195C68.3813 111.031 69.7465 110.421 71.1924 108.566L71.3259 108.395L71.5276 108.475L71.5289 108.475L71.5343 108.477L71.5578 108.486C71.5787 108.494 71.6101 108.506 71.6503 108.52C71.7307 108.549 71.8463 108.588 71.9847 108.629C72.2637 108.712 72.6266 108.801 72.9796 108.835C73.3429 108.87 73.6467 108.842 73.8424 108.736C73.9333 108.688 73.999 108.623 74.0425 108.537C74.0872 108.449 74.1183 108.319 74.1062 108.124C74.0841 107.768 73.8433 107.144 72.938 106.534C72.0298 105.922 70.462 105.333 67.8285 105.071C62.5599 104.547 53.1182 105.342 36.4258 109.745L36.2781 109.184C52.9905 104.775 62.5127 103.959 67.8858 104.493C70.5731 104.761 72.2454 105.368 73.2618 106.052Z" fill="white"/>
|
||||
<path d="M159.963 62.2028L163.46 60.1015L161.935 58.2324L158.438 60.3337L159.963 62.2028Z" fill="#0B4B9B"/>
|
||||
<path d="M162.822 67.752L167.727 64.8032L164.272 59.7996L159.372 62.7483L162.822 67.752Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M164.397 59.2161L168.349 64.9375L162.696 68.3356L158.751 62.6142L164.397 59.2161ZM159.993 62.8827L162.948 67.1685L167.106 64.669L164.146 60.3832L159.993 62.8827Z" fill="#B7BBCE"/>
|
||||
<path d="M179.736 92.242L184.636 89.299L181.18 84.2954L176.28 87.2384L179.736 92.242Z" fill="#0B4B9B"/>
|
||||
<path d="M177.498 90.5005L183.859 86.6753L165.884 60.6355L159.517 64.4608L177.498 90.5005Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M165.926 60.4412L184.066 86.7201L177.456 90.6952L159.31 64.4161L165.926 60.4412ZM159.724 64.5057L177.54 90.3062L183.652 86.6306L165.842 60.8301L159.724 64.5057Z" fill="#DBDBDC"/>
|
||||
<path d="M169.241 75.2807C169.206 75.7725 169.32 76.2635 169.567 76.6896C169.815 77.1158 170.185 77.4574 170.629 77.6701C171.074 77.8828 171.572 77.9566 172.059 77.882C172.545 77.8074 172.999 77.5877 173.359 77.2517C173.719 76.9157 173.97 76.4789 174.079 75.998C174.188 75.5172 174.15 75.0147 173.97 74.5559C173.789 74.0971 173.475 73.7033 173.068 73.4257C172.661 73.1481 172.18 72.9996 171.688 72.9995C171.061 72.9776 170.452 73.2055 169.993 73.6332C169.534 74.061 169.263 74.6535 169.241 75.2807Z" fill="#0B4B9B"/>
|
||||
<path d="M179.307 101.21L186.99 97.4604L186.578 96.7349L178.901 100.485L179.307 101.21Z" fill="#2757A0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M166.01 60.052L184.48 86.8094L177.372 91.0839L158.896 64.3263L166.01 60.052ZM160.138 64.595L177.624 89.9169L183.238 86.5409L165.758 61.2188L160.138 64.595Z" fill="#B7BBCE"/>
|
||||
<path d="M176.64 105.007L177.277 106.464L184.868 102.754L182.977 99.3818L177.254 102.18C171.572 99.7243 168.325 96.6884 167.6 93.1534C166.44 87.4822 171.781 82.3973 171.838 82.3393L170.012 79.7156C169.734 79.9826 162.897 86.4838 164.376 93.7106C164.909 96.2879 166.376 98.6156 168.765 100.676H168.649V109.795H178.437L177.277 106.469L177.19 106.51L176.64 105.007Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M170.078 79.0491L172.404 82.3893L172.146 82.6471C172.145 82.6478 172.144 82.6497 172.14 82.6527C172.042 82.7485 170.768 83.9852 169.635 85.8662C168.462 87.813 167.478 90.3856 168.026 93.0658C168.699 96.3496 171.716 99.2767 177.244 101.7L183.156 98.8101L185.472 102.943L177.814 106.686L179.05 110.231H168.215V100.771C165.924 98.7176 164.486 96.3886 163.95 93.7989L163.95 93.7979C163.179 90.0282 164.58 86.4793 166.164 83.8766C167.734 81.296 169.522 79.5828 169.706 79.4066C169.708 79.4046 169.71 79.4028 169.711 79.4012L170.078 79.0491ZM169.084 101.112H169.936L169.049 100.346C166.718 98.3356 165.312 96.0882 164.802 93.6227C164.095 90.1658 165.372 86.8508 166.907 84.3296C168.061 82.4335 169.338 81.0224 169.952 80.3899L171.275 82.2905C171.121 82.4512 170.908 82.6815 170.659 82.9727C170.156 83.5595 169.504 84.3973 168.89 85.4165C167.669 87.4444 166.562 90.2497 167.174 93.2406C167.943 96.9908 171.362 100.108 177.082 102.579L177.266 102.659L182.799 99.9534L184.263 102.565L177.494 105.873L177.038 104.832L176.232 105.156L176.945 107.105L177.026 107.067L177.825 109.36H169.084V101.112Z" fill="#B7BBCE"/>
|
||||
<path d="M186.375 109.766H161.714V114.033H186.375V109.766Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M161.28 109.331H186.81V114.468H161.28V109.331ZM162.149 110.202V113.597H185.94V110.202H162.149Z" fill="#B7BBCE"/>
|
||||
<path d="M170.163 104.385C170.127 104.881 170.24 105.376 170.489 105.805C170.738 106.235 171.11 106.58 171.558 106.795C172.005 107.01 172.507 107.085 172.997 107.01C173.488 106.935 173.945 106.715 174.308 106.377C174.671 106.038 174.925 105.599 175.035 105.114C175.145 104.63 175.107 104.124 174.926 103.661C174.744 103.199 174.428 102.802 174.018 102.522C173.608 102.242 173.123 102.093 172.627 102.092C172.315 102.081 172.003 102.131 171.71 102.24C171.418 102.349 171.149 102.515 170.92 102.727C170.691 102.94 170.507 103.196 170.377 103.481C170.247 103.765 170.174 104.073 170.163 104.385Z" fill="#0B4B9B"/>
|
||||
<path d="M66.0283 104.333C73.6069 104.478 75.1319 106.852 75.2015 107.955C75.3232 109.911 72.1515 108.623 72.1515 108.623C69.1015 112.616 66.2951 111.368 66.2951 111.368L66.0283 104.333Z" fill="#4071B7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.387 113.609H336.567V114.48H62.387V113.609Z" fill="#B7BBCE"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2686_13418">
|
||||
<rect width="398" height="240" fill="white" transform="translate(0.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 25 KiB |
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
200
app/controllers/access_permissions/base_controller.rb
Normal file
200
app/controllers/access_permissions/base_controller.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
141
app/controllers/users/settings/user_groups_controller.rb
Normal file
141
app/controllers/users/settings/user_groups_controller.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
18
app/helpers/form_field_values_helper.rb
Normal file
18
app/helpers/form_field_values_helper.rb
Normal file
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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] = {
|
||||
|
|
|
@ -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', [])
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
10
app/javascript/packs/vue/user_groups_show.js
Normal file
10
app/javascript/packs/vue/user_groups_show.js
Normal file
|
@ -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');
|
10
app/javascript/packs/vue/user_groups_table.js
Normal file
10
app/javascript/packs/vue/user_groups_table.js
Normal file
|
@ -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');
|
|
@ -22,24 +22,6 @@
|
|||
@change="changeProject"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="selectedProject == 0">
|
||||
<div class="flex gap-2 text-xs items-center">
|
||||
<div class="sci-checkbox-container">
|
||||
<input type="checkbox" class="sci-checkbox" v-model="publicProject" value="visible"/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</div>
|
||||
<span v-html="i18n.t('projects.index.modal_new_project.visibility_html')"></span>
|
||||
</div>
|
||||
<div class="mt-4" :class="{'hidden': !publicProject}">
|
||||
<label class="sci-label">{{ i18n.t("dashboard.create_task_modal.user_role") }}</label>
|
||||
<SelectDropdown
|
||||
:options="userRoles"
|
||||
:value="defaultRole"
|
||||
@change="changeRole"
|
||||
:placeholder="i18n.t('dashboard.create_task_modal.user_role_placeholder')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<label class="sci-label">{{ i18n.t("dashboard.create_task_modal.experiment") }}</label>
|
||||
<SelectDropdown
|
||||
|
@ -94,7 +76,6 @@ export default {
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchUserRoles();
|
||||
$('#create-task-modal').on('hidden.bs.modal', () => {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -171,6 +171,7 @@ export default {
|
|||
cellRendererParams: {
|
||||
statusesList: this.statusesList
|
||||
},
|
||||
notSelectable: true,
|
||||
minWidth: 180
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,7 +12,11 @@
|
|||
</div>
|
||||
<div v-else class="flex items-center gap-2 py-0.5">
|
||||
<div class="w-3 h-3 rounded-full"
|
||||
:class="this.statusColor(params.data.status_cell.status)"></div>
|
||||
: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'
|
||||
}"></div>
|
||||
<span class="truncate">
|
||||
{{ this.i18n.t(`experiments.table.column.status.${params.data.status_cell.status}`) }}
|
||||
</span>
|
||||
|
@ -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 `
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="${this.statusColor(option[0])} w-3 h-3 rounded-full"></div>
|
||||
<div class="${color} w-3 h-3 rounded-full"></div>
|
||||
<span>${option[1]}</span>
|
||||
</div>`;
|
||||
},
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
<template v-for="(row, index) in field.field_value.value" :key="row.id">
|
||||
<div class="flex items-center gap-2">
|
||||
<span v-if="index > 0">|</span>
|
||||
<a
|
||||
<a v-if="row.has_access"
|
||||
class="cursor-pointer text-sn-blue record-info-link"
|
||||
:href="itemCardUrl(row)"
|
||||
>{{ row.name }} (ID{{ row.id }})</a>
|
||||
>{{ row.name }}</a>
|
||||
<span v-else class="cursor-pointer"> {{ row.name }}</span>
|
||||
<i v-if="!disabled" @click="removeValue(row.id)" class="sn-icon sn-icon-unlink-italic-s cursor-pointer"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
<div class="modal-body flex flex-col gap-6" :class="{ '!pb-3': notification }">
|
||||
<RepositoryRowSelector
|
||||
:multiple="true"
|
||||
:manageableRepositoriesOnly="true"
|
||||
@change="selectedItemValues = $event"
|
||||
@repositoryChange="changeSelectedInventory"
|
||||
/>
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
<template>
|
||||
<div class="bg-white px-4 my-4 task-section">
|
||||
<div class="py-4 flex items-center gap-4">
|
||||
<i ref="openHandler" @click="toggleContainer" class="sn-icon sn-icon-right cursor-pointer"></i>
|
||||
<h2 class="my-0 flex items-center gap-1">
|
||||
<i ref="openHandler"
|
||||
@click="toggleContainer"
|
||||
class="sn-icon sn-icon-right cursor-pointer"
|
||||
data-e2e="e2e-IC-task-assignedItems-visibilityToggle">
|
||||
</i>
|
||||
<h2 class="my-0 flex items-center gap-1" data-e2e="e2e-TX-task-assignedItems-title">
|
||||
{{ i18n.t('my_modules.assigned_items.title') }}
|
||||
<span class="text-sn-grey-500 font-normal text-base">[{{ totalRows }}]</span>
|
||||
</h2>
|
||||
|
|
|
@ -10,14 +10,22 @@
|
|||
<ul ref="reminders" v-html="reminders" class="list-none pl-0"></ul>
|
||||
</template>
|
||||
</GeneralDropdown>
|
||||
<a class="hover:no-underline record-info-link truncate block"
|
||||
<a v-if="params.data.recordInfoUrl"
|
||||
class="hover:no-underline record-info-link truncate block"
|
||||
:title="params.data[0]"
|
||||
:href="params.data.recordInfoUrl"
|
||||
>
|
||||
{{ params.data[0] }}
|
||||
</a>
|
||||
<span v-else
|
||||
:title="i18n.t('my_modules.assigned_items.repository.private_repository_row_name', {repository_row_code: params.data.code })"
|
||||
class="text-sn-grey truncate"
|
||||
>
|
||||
<i class="sn-icon sn-icon-locked-task"></i>
|
||||
{{ i18n.t('my_modules.assigned_items.repository.private_repository_row_name', {repository_row_code: params.data.code }) }}
|
||||
</span>
|
||||
|
||||
<span v-for="state in params.data.DT_RowAttr['data-state']" class="text-sn-grey bg-sn-light-grey text-xs px-1.5 py-1 ">
|
||||
<span v-if="params.data.DT_RowAttr" v-for="state in params.data.DT_RowAttr['data-state']" class="text-sn-grey bg-sn-light-grey text-xs px-1.5 py-1 ">
|
||||
{{ state }}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
</h3>
|
||||
</div>
|
||||
<button
|
||||
v-if="repository.attributes.urls.full_view"
|
||||
class="btn btn-light icon-btn ml-auto full-screen"
|
||||
:data-table-url="fullViewUrl"
|
||||
:data-e2e="`e2e-BT-task-assignedItems-inventory${ repository.id }-expand`"
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
<img :src="user.avatar" class="w-7 h-7 rounded-full" />
|
||||
</div>
|
||||
<div v-if="hiddenUsers.length > 0" :title="hiddenUsersTitle"
|
||||
class="flex shrink-0 items-center justify-center w-7 h-7 text-xs
|
||||
rounded-full bg-sn-dark-grey text-sn-white">
|
||||
class="flex shrink-0 items-center justify-center w-7 h-7 text-xs rounded-full bg-sn-dark-grey text-sn-white">
|
||||
+{{ hiddenUsers.length }}
|
||||
</div>
|
||||
<div v-if="canManage" class="flex items-center shrink-0 justify-center w-7 h-7 rounded-full bg-sn-light-grey text-sn-dark-grey">
|
||||
<div v-if="canManage" class="flex items-center shrink-0 justify-center w-7 h-7 rounded-full border-dashed bg-sn-white text-sn-sleepy-grey border-sn-sleepy-grey">
|
||||
<i class="sn-icon sn-icon-new-task"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,15 +3,16 @@
|
|||
<template v-if="params.data.tags.length > 0 || params.data.permissions.manage_tags">
|
||||
<GeneralDropdown v-if="params.data.tags.length > 0">
|
||||
<template v-slot:field>
|
||||
<div
|
||||
class="sci-tag text-white max-w-[150px]"
|
||||
:style="{'background': params.data.tags[0].color}">
|
||||
<div class="truncate">{{ params.data.tags[0].name }}</div>
|
||||
</div>
|
||||
<div v-if="params.data.tags.length > 1"
|
||||
class="h-6 min-w-[24px] text-sn-dark-grey inline-flex items-center justify-center rounded-full text-[.625rem]
|
||||
ml-1.5 bg-sn-light-grey border !border-sn-sleepy-grey cursor-pointer">
|
||||
<span>+{{ params.data.tags.length - 1 }}</span>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div
|
||||
class="sci-tag text-white max-w-[150px]"
|
||||
:style="{'background': params.data.tags[0].color}">
|
||||
<div class="truncate">{{ params.data.tags[0].name }}</div>
|
||||
</div>
|
||||
<div v-if="params.data.tags.length > 1"
|
||||
class="flex shrink-0 items-center justify-center w-7 h-7 text-xs rounded-full bg-sn-dark-grey text-sn-white">
|
||||
<span>+{{ params.data.tags.length - 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:flyout>
|
||||
|
@ -29,8 +30,7 @@
|
|||
</template>
|
||||
</GeneralDropdown>
|
||||
<div v-if="params.data.permissions.manage_tags" @click.stop="openModal"
|
||||
class="cursor-pointer text-sn-sleep-grey border !border-dashed h-6 w-6 flex items-center
|
||||
justify-center !border-sn-sleep-grey rounded-full ">
|
||||
class="flex items-center shrink-0 justify-center w-7 h-7 rounded-full border-dashed bg-sn-white text-sn-sleepy-grey border-sn-sleepy-grey">
|
||||
<i class="sn-icon sn-icon-new-task"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
<img :src="logoUrl" alt="SciNote" class="h-full block">
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="currentTeam" class="w-64" :data-e2e="'e2e-DD-topMenu-teams'">
|
||||
<div v-if="currentTeam" class="w-64">
|
||||
<SelectDropdown
|
||||
:value="currentTeam"
|
||||
:options="teams"
|
||||
@change="switchTeam"
|
||||
:e2eValue="'e2e-DD-topMenu-teams'"
|
||||
></SelectDropdown>
|
||||
</div>
|
||||
<QuickSearch
|
||||
|
@ -144,11 +145,16 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
settingsMenuItems() {
|
||||
return this.settingsMenu.map((item) => ({ text: item.name, url: item.url })).concat(
|
||||
return this.settingsMenu.map((item) => ({
|
||||
text: item.name,
|
||||
url: item.url,
|
||||
data_e2e: `e2e-DO-topMenu-settings-${item.name.replace(' ','').toLowerCase()}`
|
||||
})).concat(
|
||||
{
|
||||
text: this.i18n.t('left_menu_bar.support_links.core_version'),
|
||||
modalTarget: '#aboutModal',
|
||||
url: ''
|
||||
url: '',
|
||||
data_e2e: `e2e-DO-topMenu-settings-scinoteVersion`
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@
|
|||
@update="updateDescription"
|
||||
@close="descriptionModalObject = null"/>
|
||||
<ExportLimitExceededModal v-if="exportLimitExceded" :description="exportDescription" @close="exportLimitExceded = false"/>
|
||||
<ProjectFormModal v-if="editProject" :userRolesUrl="userRolesUrl"
|
||||
<ProjectFormModal v-if="editProject"
|
||||
:project="editProject" @close="editProject = null" @update="updateTable(); updateNavigator()" />
|
||||
<EditFolderModal v-if="editFolder" :folder="editFolder"
|
||||
@close="editFolder = null" @update="updateTable(); updateNavigator()" />
|
||||
<ProjectFormModal v-if="newProject" :createUrl="createUrl"
|
||||
:currentFolderId="currentFolderId" :userRolesUrl="userRolesUrl"
|
||||
:currentFolderId="currentFolderId"
|
||||
@close="newProject = false" @create="updateTable(); updateNavigator()" />
|
||||
<NewFolderModal v-if="newFolder" :createFolderUrl="createFolderUrl"
|
||||
:currentFolderId="currentFolderId" :viewMode="currentViewMode"
|
||||
|
|
|
@ -51,22 +51,6 @@
|
|||
:placeholder="i18n.t('projects.index.add_description')"
|
||||
></TinymceEditor>
|
||||
</div>
|
||||
<div class="flex gap-2 text-xs items-center">
|
||||
<div class="sci-checkbox-container">
|
||||
<input type="checkbox" class="sci-checkbox" v-model="visible" value="visible" data-e2e="e2e-CB-projects-newProjectModal-access"/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</div>
|
||||
<span v-html="i18n.t('projects.index.modal_new_project.visibility_html')"></span>
|
||||
</div>
|
||||
<div class="mt-6" :class="{'hidden': !visible}">
|
||||
<label class="sci-label">{{ i18n.t("user_assignment.select_default_user_role") }}</label>
|
||||
<SelectDropdown
|
||||
:options="userRoles"
|
||||
:value="defaultRole"
|
||||
@change="changeRole"
|
||||
:e2eValue="'e2e-DD-projects-newProjectModal-defaultRole'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
|
@ -80,7 +64,7 @@
|
|||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
:disabled="submitting || (visible && !defaultRole) || !validName"
|
||||
:disabled="submitting || !validName"
|
||||
data-e2e="e2e-BT-projects-newProjectModal-create"
|
||||
>
|
||||
{{ submitButtonLabel }}
|
||||
|
@ -94,7 +78,6 @@
|
|||
|
||||
<script>
|
||||
|
||||
import SelectDropdown from '../../shared/select_dropdown.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
import TinymceEditor from '../../shared/tinymce_editor.vue';
|
||||
import axios from '../../../packs/custom_axios.js';
|
||||
|
@ -104,25 +87,14 @@ export default {
|
|||
name: 'ProjectFormModal',
|
||||
props: {
|
||||
project: Object,
|
||||
userRolesUrl: String,
|
||||
currentFolderId: String,
|
||||
createUrl: String
|
||||
},
|
||||
mixins: [modalMixin],
|
||||
components: {
|
||||
SelectDropdown,
|
||||
DateTimePicker,
|
||||
TinymceEditor
|
||||
},
|
||||
watch: {
|
||||
visible(newValue) {
|
||||
if (newValue) {
|
||||
[this.defaultRole] = this.userRoles.find((role) => role[1] === 'Viewer');
|
||||
} else {
|
||||
this.defaultRole = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
validName() {
|
||||
return this.name.length >= GLOBAL_CONSTANTS.NAME_MIN_LENGTH;
|
||||
|
@ -142,16 +114,10 @@ export default {
|
|||
return this.i18n.t('projects.index.modal_edit_project.submit');
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchUserRoles();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: this.project?.name || '',
|
||||
visible: this.project ? !this.project.hidden : false,
|
||||
defaultRole: this.project?.default_public_user_role_id,
|
||||
error: null,
|
||||
userRoles: [],
|
||||
submitting: false,
|
||||
startDate: null,
|
||||
dueDate: null,
|
||||
|
@ -175,9 +141,7 @@ export default {
|
|||
name: this.name,
|
||||
start_date: this.startDate,
|
||||
due_date: this.dueDate,
|
||||
description: this.description,
|
||||
visibility: (this.visible ? 'visible' : 'hidden'),
|
||||
default_public_user_role_id: this.defaultRole
|
||||
description: this.description
|
||||
};
|
||||
|
||||
if (this.createUrl) {
|
||||
|
@ -209,23 +173,12 @@ export default {
|
|||
});
|
||||
this.submitting = false;
|
||||
},
|
||||
changeRole(role) {
|
||||
this.defaultRole = role;
|
||||
},
|
||||
updateStartDate(startDate) {
|
||||
this.startDate = this.stripTime(startDate);
|
||||
},
|
||||
updateDueDate(dueDate) {
|
||||
this.dueDate = this.stripTime(dueDate);
|
||||
},
|
||||
fetchUserRoles() {
|
||||
if (this.userRolesUrl) {
|
||||
axios.get(this.userRolesUrl)
|
||||
.then((response) => {
|
||||
this.userRoles = response.data.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
stripTime(date) {
|
||||
if (date) {
|
||||
return new Date(Date.UTC(
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
<template>
|
||||
<div v-if="params.data.status" class="py-0.5">
|
||||
<SelectDropdown
|
||||
:options="params.statusesList"
|
||||
:value="params.data.status"
|
||||
@change="changeStatus"
|
||||
size="xs"
|
||||
:borderless="true"
|
||||
:optionRenderer="optionRenderer"
|
||||
:labelRenderer="optionRenderer"
|
||||
:disabled="!params.data.urls.update"
|
||||
/>
|
||||
<div v-if="params.data.status">
|
||||
<div v-if="params.data.urls.update" class="py-0.5">
|
||||
<SelectDropdown
|
||||
:options="params.statusesList"
|
||||
:value="params.data.status"
|
||||
@change="changeStatus"
|
||||
size="xs"
|
||||
:borderless="true"
|
||||
:optionRenderer="optionRenderer"
|
||||
:labelRenderer="optionRenderer"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="flex items-center gap-2 py-0.5">
|
||||
<div class="w-3 h-3 rounded-full"
|
||||
:class="{
|
||||
'bg-sn-grey-500': params.data.status === 'not_started',
|
||||
'bg-sn-science-blue': params.data.status === 'in_progress',
|
||||
'bg-sn-alert-green': params.data.status === 'done'
|
||||
}"></div>
|
||||
<span class="truncate">
|
||||
{{ this.i18n.t(`projects.index.status.${params.data.status}`) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div v-if="params.data.supervised_by">
|
||||
<GeneralDropdown v-if="canManage" @open="openFlyout" @close="closeFlyout" position="right">
|
||||
<template v-slot:field>
|
||||
<div class="flex items-center gap-1 cursor-pointer h-9">
|
||||
<div class="flex items-center gap-1 cursor-pointer h-9 px-1" :class="{ 'bg-sn-super-light-blue': opened }">
|
||||
<div v-if="params.data.supervised_by.id" class="flex items-center gap-2" :title="params.data.supervised_by.name">
|
||||
<img :src="params.data.supervised_by.avatar" class="w-7 h-7 rounded-full" />
|
||||
<span>{{ params.data.supervised_by.name }}</span>
|
||||
|
@ -67,7 +67,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
allUsers: [],
|
||||
query: ''
|
||||
query: '',
|
||||
opened: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
@ -83,6 +84,7 @@ export default {
|
|||
methods: {
|
||||
openFlyout() {
|
||||
this.loadUsers();
|
||||
this.opened = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.searchInput) {
|
||||
this.$refs.searchInput.focus();
|
||||
|
@ -102,6 +104,7 @@ export default {
|
|||
},
|
||||
closeFlyout() {
|
||||
this.query = '';
|
||||
this.opened = false;
|
||||
},
|
||||
selectUser(user) {
|
||||
if (user[0] === this.params.data.supervised_by.id) {
|
||||
|
|
|
@ -9,10 +9,18 @@
|
|||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a class="task-section-caret" tabindex="0" role="button" data-toggle="collapse" href="#protocol-content" aria-expanded="true" aria-controls="protocol-content">
|
||||
<a class="task-section-caret"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
data-toggle="collapse"
|
||||
href="#protocol-content"
|
||||
aria-expanded="true"
|
||||
aria-controls="protocol-content"
|
||||
data-e2e="e2e-IC-task-protocol-visibilityToggle"
|
||||
>
|
||||
<i class="sn-icon sn-icon-right"></i>
|
||||
<div class="task-section-title truncate">
|
||||
<h2>{{ i18n.t('Protocol') }}</h2>
|
||||
<h2 data-e2e="e2e-TX-task-protocol-sectionTitle">{{ i18n.t('Protocol') }}</h2>
|
||||
</div>
|
||||
</a>
|
||||
</template>
|
||||
|
@ -29,16 +37,30 @@
|
|||
:title="i18n.t('protocols.steps.new_step_title')"
|
||||
@keyup.enter="addStep(steps.length)"
|
||||
@click="addStep(steps.length)"
|
||||
tabindex="0">
|
||||
tabindex="0"
|
||||
data-e2e="e2e-BT-task-protocol-newStep">
|
||||
<span class="sn-icon sn-icon-new-task" aria-hidden="true"></span>
|
||||
<span class="tw-hidden xl:inline">{{ i18n.t("protocols.steps.new_step") }}</span>
|
||||
</a>
|
||||
<template v-if="steps.length > 0">
|
||||
<button :title="i18n.t('protocols.steps.collapse_label')" v-if="!stepCollapsed" class="btn btn-secondary icon-btn xl:!px-4" @click="collapseSteps" tabindex="0">
|
||||
<button
|
||||
:title="i18n.t('protocols.steps.collapse_label')"
|
||||
v-if="!stepCollapsed"
|
||||
class="btn btn-secondary icon-btn xl:!px-4"
|
||||
@click="collapseSteps"
|
||||
tabindex="0"
|
||||
data-e2e="e2e-BT-task-protocol-collapseAll"
|
||||
>
|
||||
<i class="sn-icon sn-icon-collapse-all"></i>
|
||||
<span class="tw-hidden xl:inline">{{ i18n.t("protocols.steps.collapse_label") }}</span>
|
||||
</button>
|
||||
<button v-else :title="i18n.t('protocols.steps.expand_label')" class="btn btn-secondary icon-btn xl:!px-4" @click="expandSteps" tabindex="0">
|
||||
<button v-else
|
||||
:title="i18n.t('protocols.steps.expand_label')"
|
||||
class="btn btn-secondary icon-btn xl:!px-4"
|
||||
@click="expandSteps"
|
||||
tabindex="0"
|
||||
data-e2e="e2e-BT-task-protocol-expandAll"
|
||||
>
|
||||
<i class="sn-icon sn-icon-expand-all"></i>
|
||||
<span class="tw-hidden xl:inline">{{ i18n.t("protocols.steps.expand_label") }}</span>
|
||||
</button>
|
||||
|
@ -51,7 +73,13 @@
|
|||
@protocol:add_protocol_steps="addSteps"
|
||||
:canDeleteSteps="steps.length > 0 && urls.delete_steps_url !== null"
|
||||
/>
|
||||
<button class="btn btn-light icon-btn" data-toggle="modal" data-target="#print-protocol-modal" tabindex="0">
|
||||
<button
|
||||
class="btn btn-light icon-btn"
|
||||
data-toggle="modal"
|
||||
data-target="#print-protocol-modal"
|
||||
tabindex="0"
|
||||
data-e2e="e2e-BT-task-protocol-print"
|
||||
>
|
||||
<span class="sn-icon sn-icon-printer" aria-hidden="true"></span>
|
||||
</button>
|
||||
<a v-if="steps.length > 0 && urls.reorder_steps_url"
|
||||
|
@ -60,13 +88,20 @@
|
|||
@click="startStepReorder"
|
||||
@keyup.enter="startStepReorder"
|
||||
:class="{'disabled': steps.length == 1}"
|
||||
tabindex="0" >
|
||||
tabindex="0"
|
||||
data-e2e="e2e-BT-task-protocol-reorderSteps"
|
||||
>
|
||||
<i class="sn-icon sn-icon-sort" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="protocol-content" class="protocol-content collapse in" aria-expanded="true">
|
||||
<div
|
||||
id="protocol-content"
|
||||
class="protocol-content collapse in"
|
||||
aria-expanded="true"
|
||||
data-e2e="e2e-CO-task-protocol-content"
|
||||
>
|
||||
<div class="sci-divider" v-if="!inRepository"></div>
|
||||
<div class="mb-4">
|
||||
<div class="protocol-name mt-4" v-if="!inRepository">
|
||||
|
@ -78,6 +113,7 @@
|
|||
:allowBlank="!inRepository"
|
||||
:attributeName="`${i18n.t('Protocol')} ${i18n.t('name')}`"
|
||||
@update="updateName"
|
||||
:dataE2e="'task-protocol-title'"
|
||||
/>
|
||||
<span v-else>
|
||||
{{ protocol.attributes.name }}
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
<template>
|
||||
<div ref="modal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-dialog" role="document" data-e2e="e2e-MD-protocol-addProtocolSteps">
|
||||
<form @submit.prevent="submit">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
data-e2e="e2e-BT-protocol-addProtocolStepsModal-close"
|
||||
>
|
||||
<i class="sn-icon sn-icon-close"></i>
|
||||
</button>
|
||||
<h4 class="modal-title truncate !block" >
|
||||
<h4 class="modal-title truncate !block" data-e2e="e2e-TX-protocol-addProtocolStepsModal-title">
|
||||
{{ i18n.t('protocols.steps.modals.add_protocol_steps.title') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="mb-6">{{ i18n.t('protocols.steps.modals.add_protocol_steps.description')}}</p>
|
||||
<p class="mb-6" data-e2e="e2e-TX-protocol-addProtocolStepsModal-description">
|
||||
{{ i18n.t('protocols.steps.modals.add_protocol_steps.description')}}
|
||||
</p>
|
||||
<div class="mb-6">
|
||||
<label class="sci-label">{{ i18n.t('protocols.steps.modals.add_protocol_steps.protocol_label') }}</label>
|
||||
<SelectDropdown
|
||||
|
@ -21,6 +29,7 @@
|
|||
:searchable="true"
|
||||
:value="selectedProtocol"
|
||||
@change="selectedProtocol = $event"
|
||||
:e2eValue="'e2e-DD-protocol-addProtocolStepsModal-selectProtocol'"
|
||||
></SelectDropdown>
|
||||
</div>
|
||||
<div class="relative">
|
||||
|
@ -36,12 +45,25 @@
|
|||
:searchable="true"
|
||||
:value="selectedSteps"
|
||||
@change="selectedSteps= $event"
|
||||
:e2eValue="'e2e-DD-protocol-addProtocolStepsModal-selectSteps'"
|
||||
></SelectDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" :disabled="submitting || !validObject" type="submit">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal"
|
||||
data-e2e="e2e-BT-protocol-addProtocolStepsModal-cancel"
|
||||
>
|
||||
{{ i18n.t('general.cancel') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
:disabled="submitting || !validObject"
|
||||
type="submit"
|
||||
data-e2e="e2e-BT-protocol-addProtocolStepsModal-addSteps"
|
||||
>
|
||||
{{ i18n.t('protocols.steps.modals.add_protocol_steps.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,20 +1,44 @@
|
|||
<template>
|
||||
<div ref="modal" @keydown.esc="cancel" class="modal delete-steps-modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-content" data-e2e="e2e-MD-task-protocol-deleteAllSteps">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
|
||||
<h4 class="modal-title">
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
data-e2e="e2e-BT-task-protocol-deleteAllStepsModal-close"
|
||||
>
|
||||
<i class="sn-icon sn-icon-close"></i>
|
||||
</button>
|
||||
<h4 class="modal-title" data-e2e="e2e-TX-task-protocol-deleteAllStepsModal-title">
|
||||
{{ i18n.t('protocols.steps.modals.delete_steps.title')}}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ i18n.t('protocols.steps.modals.delete_steps.description_1')}}</p>
|
||||
<p class="warning">{{ i18n.t('protocols.steps.modals.delete_steps.description_2')}}</p>
|
||||
<p data-e2e="e2e-TX-task-protocol-deleteAllStepsModal-description">
|
||||
{{ i18n.t('protocols.steps.modals.delete_steps.description_1')}}
|
||||
</p>
|
||||
<p class="warning" data-e2e="e2e-TX-task-protocol-deleteAllStepsModal-warning">
|
||||
{{ i18n.t('protocols.steps.modals.delete_steps.description_2')}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-danger" @click="confirm">{{ i18n.t('protocols.steps.modals.delete_steps.confirm')}}</button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
@click="cancel"
|
||||
data-e2e="e2e-BT-task-protocol-deleteAllStepsModal-cancel"
|
||||
>
|
||||
{{ i18n.t('general.cancel') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="confirm"
|
||||
data-e2e="e2e-BT-task-protocol-deleteAllStepsModal-delete"
|
||||
>
|
||||
{{ i18n.t('protocols.steps.modals.delete_steps.confirm')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
tabindex="0"
|
||||
data-e2e="e2e-DD-task-protocol-actions"
|
||||
>
|
||||
<span>{{ i18n.t("my_modules.protocol.options_dropdown.title") }}</span>
|
||||
<span class="sn-icon sn-icon-down"></span>
|
||||
|
@ -22,6 +23,7 @@
|
|||
ref="loadProtocol"
|
||||
data-action="load-from-repository"
|
||||
@click="loadProtocol"
|
||||
data-e2e="e2e-DO-task-protocol-actions-loadFromRepository"
|
||||
>
|
||||
<span>{{ i18n.t("my_modules.protocol.options_dropdown.load_from_repo") }}</span>
|
||||
</a>
|
||||
|
@ -30,6 +32,7 @@
|
|||
<a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
|
||||
data-turbolinks="false"
|
||||
@click.prevent="openAddStepsModal()"
|
||||
data-e2e="e2e-DO-task-protocol-actions-addProtocolSteps"
|
||||
>
|
||||
<span>{{
|
||||
i18n.t("my_modules.protocol.options_dropdown.add_protocol_steps")
|
||||
|
@ -42,6 +45,7 @@
|
|||
data-target="#newProtocolModal"
|
||||
v-bind:data-protocol-name="protocol.attributes.name"
|
||||
:class="{ disabled: !protocol.attributes.urls.save_to_repo_url }"
|
||||
data-e2e="e2e-DO-task-protocol-actions-saveAsNewTemplate"
|
||||
>
|
||||
<span>{{
|
||||
i18n.t("my_modules.protocol.options_dropdown.save_to_repo")
|
||||
|
@ -53,6 +57,7 @@
|
|||
data-turbolinks="false"
|
||||
:href="protocol.attributes.urls.export_url"
|
||||
:class="{ disabled: !protocol.attributes.urls.export_url }"
|
||||
data-e2e="e2e-DO-task-protocol-actions-exportProtocol"
|
||||
>
|
||||
<span>{{
|
||||
i18n.t("my_modules.protocol.options_dropdown.export")
|
||||
|
@ -64,6 +69,7 @@
|
|||
ref="updateProtocol"
|
||||
data-action="update-self"
|
||||
@click="updateProtocol"
|
||||
data-e2e="e2e-DO-task-protocol-actions-updateProtocol"
|
||||
>
|
||||
<span>{{
|
||||
i18n.t("my_modules.protocol.options_dropdown.update_protocol")
|
||||
|
@ -75,6 +81,7 @@
|
|||
ref="unlinkProtocol"
|
||||
data-action="unlink"
|
||||
@click="unlinkProtocol"
|
||||
data-e2e="e2e-DO-task-protocol-actions-unlinkProtocol"
|
||||
>
|
||||
<span>{{
|
||||
i18n.t("my_modules.protocol.options_dropdown.unlink")
|
||||
|
@ -86,6 +93,7 @@
|
|||
ref="revertProtocol"
|
||||
data-action="revert"
|
||||
@click="revertProtocol"
|
||||
data-e2e="e2e-DO-task-protocol-actions-revertProtocol"
|
||||
>
|
||||
<span>{{
|
||||
i18n.t("my_modules.protocol.options_dropdown.revert_protocol")
|
||||
|
@ -96,6 +104,7 @@
|
|||
<a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
|
||||
data-turbolinks="false"
|
||||
@click.prevent="openStepsDeletingModal()"
|
||||
data-e2e="e2e-DO-task-protocol-actions-deleteAllSteps"
|
||||
>
|
||||
<span>{{
|
||||
i18n.t("my_modules.protocol.options_dropdown.delete_steps")
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
:position="'right'"
|
||||
:caret="true"
|
||||
:dataE2e="`e2e-DD-protocol-step${step.id}-insertContent`"
|
||||
:disableOverflow="true"
|
||||
@create:custom_well_plate="openCustomWellPlateModal"
|
||||
@create:table="(...args) => this.createElement('table', ...args)"
|
||||
@create:checklist="createElement('checklist')"
|
||||
|
@ -661,7 +662,7 @@
|
|||
}
|
||||
}
|
||||
}).fail(() => {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general_saving_data'), 'danger');
|
||||
})
|
||||
}
|
||||
},
|
||||
|
|
|
@ -22,26 +22,10 @@
|
|||
:placeholder="i18n.t('protocols.new_protocol_modal.name_placeholder')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 text-xs items-center">
|
||||
<div class="sci-checkbox-container">
|
||||
<input type="checkbox" class="sci-checkbox" v-model="visible" value="visible" data-e2e="e2e-CB-newProtocolModal-grantAccess"/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</div>
|
||||
<span v-html="i18n.t('protocols.new_protocol_modal.access_label')" data-e2e="e2e-TX-newProtocolModal-grantAccess"></span>
|
||||
</div>
|
||||
<div class="mt-6" :class="{'hidden': !visible}">
|
||||
<label class="sci-label">{{ i18n.t("protocols.new_protocol_modal.role_label") }}</label>
|
||||
<SelectDropdown
|
||||
:options="userRoles"
|
||||
:value="defaultRole"
|
||||
:data-e2e="`e2e-DD-newProtocolModal-defaultUserRole`"
|
||||
@change="changeRole"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" data-e2e="e2e-BT-newProtocolModal-cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting || (visible && !defaultRole) || !validName" data-e2e="e2e-BT-newProtocolModal-create">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting || !validName" data-e2e="e2e-BT-newProtocolModal-create">
|
||||
{{ i18n.t('protocols.new_protocol_modal.create_new') }}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -54,32 +38,15 @@
|
|||
<script>
|
||||
/* global GLOBAL_CONSTANTS */
|
||||
|
||||
import SelectDropdown from '../../shared/select_dropdown.vue';
|
||||
import axios from '../../../packs/custom_axios.js';
|
||||
import modalMixin from '../../shared/modal_mixin';
|
||||
|
||||
export default {
|
||||
name: 'NewProtocolModal',
|
||||
props: {
|
||||
createUrl: String,
|
||||
userRolesUrl: String
|
||||
createUrl: String
|
||||
},
|
||||
mixins: [modalMixin],
|
||||
components: {
|
||||
SelectDropdown
|
||||
},
|
||||
watch: {
|
||||
visible(newValue) {
|
||||
if (newValue) {
|
||||
[this.defaultRole] = this.userRoles.find((role) => role[1] === 'Viewer');
|
||||
} else {
|
||||
this.defaultRole = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchUserRoles();
|
||||
},
|
||||
computed: {
|
||||
validName() {
|
||||
return this.name.length >= GLOBAL_CONSTANTS.NAME_MIN_LENGTH;
|
||||
|
@ -88,9 +55,6 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
name: '',
|
||||
visible: false,
|
||||
defaultRole: null,
|
||||
userRoles: [],
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
|
@ -101,9 +65,7 @@ export default {
|
|||
|
||||
axios.post(this.createUrl, {
|
||||
protocol: {
|
||||
name: this.name,
|
||||
visibility: (this.visible ? 'visible' : 'hidden'),
|
||||
default_public_user_role_id: this.defaultRole
|
||||
name: this.name
|
||||
}
|
||||
}).then(() => {
|
||||
this.error = null;
|
||||
|
@ -113,17 +75,6 @@ export default {
|
|||
this.submitting = false;
|
||||
this.error = error.response.data.error;
|
||||
});
|
||||
},
|
||||
changeRole(role) {
|
||||
this.defaultRole = role;
|
||||
},
|
||||
fetchUserRoles() {
|
||||
if (this.userRolesUrl) {
|
||||
axios.get(this.userRolesUrl)
|
||||
.then((response) => {
|
||||
this.userRoles = response.data.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
/>
|
||||
</div>
|
||||
<NewProtocolModal v-if="newProtocol" :createUrl="createUrl"
|
||||
:userRolesUrl="userRolesUrl"
|
||||
@close="newProtocol = false" @create="updateTable" />
|
||||
<AccessModal v-if="accessModalParams" :params="accessModalParams"
|
||||
@close="accessModalParams = null" @refresh="this.reloadingTable = true" />
|
||||
|
|
|
@ -108,7 +108,7 @@ export default {
|
|||
mounted() {
|
||||
document.addEventListener('mouseover', this.loadColumnsInfo);
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('mouseover', this.loadColumnsInfo);
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<a class="hover:no-underline flex items-center gap-1"
|
||||
:title="params.data.name"
|
||||
:class="{
|
||||
'pointer-events-none text-sn-grey': !params.data.urls.show
|
||||
}"
|
||||
:href="params.data.urls.show"
|
||||
>
|
||||
<span class="truncate">
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
@share="share"
|
||||
@create="newRepository = true"
|
||||
@tableReloaded="reloadingTable = false"
|
||||
@access="access"
|
||||
/>
|
||||
</div>
|
||||
<ConfirmationModal
|
||||
|
@ -75,6 +76,8 @@
|
|||
confirm: 'e2e-BT-confirmSharingChangesModal-delete'
|
||||
}"
|
||||
></ConfirmationModal>
|
||||
<AccessModal v-if="accessModalParams" :params="accessModalParams"
|
||||
@close="accessModalParams = null" @refresh="reloadingTable = true" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -89,6 +92,9 @@ import DuplicateRepositoryModal from './modals/duplicate.vue';
|
|||
import ShareObjectModal from '../shared/share_modal.vue';
|
||||
import DataTable from '../shared/datatable/table.vue';
|
||||
import NameRenderer from './renderers/name.vue';
|
||||
import AccessModal from '../shared/access_modal/modal.vue';
|
||||
import UsersRenderer from '../projects/renderers/users.vue';
|
||||
import escapeHtml from '../shared/escape_html.js';
|
||||
|
||||
export default {
|
||||
name: 'RepositoriesTable',
|
||||
|
@ -100,7 +106,9 @@ export default {
|
|||
EditRepositoryModal,
|
||||
DuplicateRepositoryModal,
|
||||
NameRenderer,
|
||||
ShareObjectModal
|
||||
ShareObjectModal,
|
||||
AccessModal,
|
||||
UsersRenderer
|
||||
},
|
||||
props: {
|
||||
dataSource: {
|
||||
|
@ -125,11 +133,16 @@ export default {
|
|||
archivedPageUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
userRolesUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
reloadingTable: false,
|
||||
accessModalParams: null,
|
||||
exportRepository: null,
|
||||
newRepository: false,
|
||||
editRepository: null,
|
||||
|
@ -186,8 +199,14 @@ export default {
|
|||
field: 'created_at',
|
||||
headerName: this.i18n.t('libraries.index.table.added_on'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
}, {
|
||||
field: 'assigned_users',
|
||||
headerName: this.i18n.t('repositories.index.table.access'),
|
||||
sortable: true,
|
||||
cellRenderer: 'UsersRenderer',
|
||||
minWidth: 210,
|
||||
notSelectable: true
|
||||
}, {
|
||||
field: 'created_by',
|
||||
headerName: this.i18n.t('libraries.index.table.added_by'),
|
||||
sortable: true
|
||||
|
@ -252,6 +271,12 @@ export default {
|
|||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
});
|
||||
},
|
||||
access(_event, rows) {
|
||||
this.accessModalParams = {
|
||||
object: rows[0],
|
||||
roles_path: this.userRolesUrl
|
||||
};
|
||||
},
|
||||
async deleteRepository(event, rows) {
|
||||
const [repository] = rows;
|
||||
this.deleteModal.e2eAttributes = {
|
||||
|
@ -263,13 +288,14 @@ export default {
|
|||
};
|
||||
this.deleteModal.title = this.i18n.t('repositories.index.modal_delete.title_html', { name: repository.name });
|
||||
this.deleteModal.description = `
|
||||
<p data-e2e="e2e-TX-deleteInventoryModal-info">${this.i18n.t('repositories.index.modal_delete.message_html', { name: repository.name })}</p>
|
||||
<p data-e2e="e2e-TX-deleteInventoryModal-info">${this.i18n.t('repositories.index.modal_delete.message_html', { name: escapeHtml(repository.name) })}</p>
|
||||
<div class="alert alert-danger" role="alert" data-e2e="e2e-TX-deleteInventoryModal-warning">
|
||||
<span class="fas fa-exclamation-triangle"></span>
|
||||
${this.i18n.t('repositories.index.modal_delete.alert_heading')}
|
||||
<ul>
|
||||
<li>${this.i18n.t('repositories.index.modal_delete.alert_line_1')}</li>
|
||||
<li>${this.i18n.t('repositories.index.modal_delete.alert_line_2')}</li>
|
||||
<li>${this.i18n.t('repositories.index.modal_delete.alert_line_3')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -23,7 +23,7 @@ export default {
|
|||
archivedUrl: { type: String, required: true },
|
||||
disabled: { type: String, default: 'false' }
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
delete window.initRepositoryStateMenu;
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -184,14 +184,15 @@
|
|||
</a>
|
||||
</div>
|
||||
<div v-if="parentsCount">
|
||||
<details v-for="(parent) in parents" @toggle="updateOpenState(parent.code, $event.target.open)" :key="parent.code" class="flex flex-col font-normal gap-4 group cursor-default">
|
||||
<details v-for="(parent) in parents" @toggle="updateOpenState(parent.code, $event.target.open)" :key="parent.code" class="flex flex-col font-normal group cursor-default">
|
||||
<summary class="flex flex-row gap-3 mb-4 relative group">
|
||||
<img :src="icons.delimiter_path" class="w-3 h-3 cursor-pointer flex-shrink-0 relative top-1"
|
||||
:class="{ 'rotate-90': relationshipDetailsState[parent.code] }" />
|
||||
<span>
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.item') }}</span>
|
||||
<a :href="parent.path" class="record-info-link btn-text-link !text-sn-science-blue">{{ parent.name }}</a>
|
||||
<button v-if="permissions.can_connect_rows" @click="openUnlinkModal(parent)"
|
||||
<a v-if="parent.path" :href="parent.path" class="record-info-link btn-text-link !text-sn-science-blue">{{ parent.name }}</a>
|
||||
<span v-else>{{ parent.name }}</span>
|
||||
<button v-if="permissions.can_connect_rows && parent.can_connect_rows" @click="openUnlinkModal(parent)"
|
||||
class=" ml-2 bg-transparent border-none opacity-0 group-hover:opacity-100 cursor-pointer">
|
||||
<img :src="icons.unlink_path" />
|
||||
</button>
|
||||
|
@ -201,7 +202,7 @@
|
|||
<span>
|
||||
{{ i18n.t('repositories.item_card.relationships.id', { code: parent.code }) }}
|
||||
</span>
|
||||
<span>
|
||||
<span v-if="parent.repository_name && parent.repository_path">
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.inventory') }}</span>
|
||||
<a :href="parent.repository_path" class="btn-text-link !text-sn-science-blue">
|
||||
{{ parent.repository_name }}
|
||||
|
@ -233,15 +234,16 @@
|
|||
</div>
|
||||
<div v-if="childrenCount">
|
||||
<details v-for="(child) in children" :key="child.code" @toggle="updateOpenState(child.code, $event.target.open)"
|
||||
class="flex flex-col font-normal gap-4 group-last-of-type:[&>p:last-child]:mb-0">
|
||||
class="flex flex-col font-normal group-last-of-type:[&>p:last-child]:mb-0">
|
||||
<summary class="flex flex-row gap-3 mb-4 relative group"
|
||||
:class="{ 'group-last-of-type:mb-0': !relationshipDetailsState[child.code] }">
|
||||
<img :src="icons.delimiter_path" class="w-3 h-3 flex-shrink-0 cursor-pointer relative top-1"
|
||||
:class="{ 'rotate-90': relationshipDetailsState[child.code] }"/>
|
||||
<span class="group/child">
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.item') }}</span>
|
||||
<a :href="child.path" class="record-info-link btn-text-link !text-sn-science-blue">{{ child.name }}</a>
|
||||
<button v-if="permissions.can_connect_rows" @click="openUnlinkModal(child)"
|
||||
<a v-if="child.path" :href="child.path" class="record-info-link btn-text-link !text-sn-science-blue">{{ child.name }}</a>
|
||||
<span v-else>{{ child.name }}</span>
|
||||
<button v-if="permissions.can_connect_rows && child.can_connect_rows" @click="openUnlinkModal(child)"
|
||||
class="ml-2 bg-transparent border-none opacity-0 group-hover:opacity-100 cursor-pointer">
|
||||
<img :src="icons.unlink_path" />
|
||||
</button>
|
||||
|
@ -249,7 +251,7 @@
|
|||
</summary>
|
||||
<p class="flex flex-col gap-3 ml-6 mb-4">
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.id', { code: child.code }) }}</span>
|
||||
<span>
|
||||
<span v-if="child.repository_name && child.repository_path">
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.inventory') }}</span>
|
||||
<a :href="child.repository_path" class="btn-text-link !text-sn-science-blue">
|
||||
{{ child.repository_name }}
|
||||
|
|
|
@ -60,7 +60,7 @@ export default {
|
|||
}
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
this.removeScrollListener();
|
||||
},
|
||||
|
|
|
@ -222,7 +222,7 @@ export default {
|
|||
created() {
|
||||
window.manageStockModalComponent = this;
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
delete window.manageStockModalComponent;
|
||||
},
|
||||
mounted() {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue