mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-01-01 13:13:22 +08:00
Merge pull request #4734 from aignatov-bio/ai-sci-7518-manage-access-modal
Add access modal for protocols [SCI-7518]
This commit is contained in:
commit
296ea04b62
18 changed files with 338 additions and 4 deletions
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
function initNewUserAssignmentFormListener() {
|
||||
$(document).on('change', 'form#new-user-assignment-to-project-form', function() {
|
||||
$(document).on('change', 'form#new-user-assignment-to-project-form, form#new-user-assignment-to-protocol-form', function() {
|
||||
let values = [];
|
||||
let count = 0;
|
||||
let submitBtn = $(this).find('input[type="submit"]');
|
106
app/controllers/access_permissions/protocols_controller.rb
Normal file
106
app/controllers/access_permissions/protocols_controller.rb
Normal file
|
@ -0,0 +1,106 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AccessPermissions
|
||||
class ProtocolsController < ApplicationController
|
||||
before_action :set_protocol
|
||||
before_action :check_read_permissions, only: %i(show)
|
||||
before_action :check_manage_permissions, except: %i(show)
|
||||
|
||||
def new
|
||||
@user_assignment = UserAssignment.new(assignable: @project, assigned_by: current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.json
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.json
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
respond_to do |format|
|
||||
format.json
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@user_assignment = UserAssignment.find_by(user_id: permitted_update_params[:user_id], assignable: @protocol)
|
||||
@user_assignment.update(permitted_update_params)
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render :protocol_member
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
ActiveRecord::Base.transaction do
|
||||
permitted_create_params[:resource_members].each do |_k, user_assignment_params|
|
||||
next unless user_assignment_params[:assign] == '1'
|
||||
|
||||
user_assignment = UserAssignment.new(user_assignment_params)
|
||||
user_assignment.assignable = @protocol
|
||||
user_assignment.assigned_by = current_user
|
||||
user_assignment.save!
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
@message = t('access_permissions.create.success', count: @protocol.user_assignments.count)
|
||||
format.json { render :edit }
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
respond_to do |format|
|
||||
@message = t('access_permissions.create.failure')
|
||||
format.json { render :new }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user = @protocol.assigned_users.find(params[:user_id])
|
||||
user_assignment = @protocol.user_assignments.find_by(user_id: params[:user_id])
|
||||
respond_to do |format|
|
||||
if user_assignment.destroy
|
||||
format.json do
|
||||
render json: { flash: t('access_permissions.destroy.success', member_name: user.full_name) },
|
||||
status: :ok
|
||||
end
|
||||
else
|
||||
format.json do
|
||||
render json: { flash: t('access_permissions.destroy.failure') },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def permitted_update_params
|
||||
params.require(:user_assignment)
|
||||
.permit(%i(user_role_id user_id))
|
||||
end
|
||||
|
||||
def permitted_create_params
|
||||
params.require(:access_permissions_new_user_protocol_form)
|
||||
.permit(resource_members: %i(assign user_id user_role_id))
|
||||
end
|
||||
|
||||
def set_protocol
|
||||
@protocol = current_team.protocols.includes(user_assignments: %i(user user_role)).find_by(id: params[:id])
|
||||
|
||||
render_404 unless @protocol
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
render_403 unless can_manage_protocol_users?(@protocol)
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 unless can_read_protocol_in_repository?(@protocol)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,7 +12,7 @@ module UserAssignmentsHelper
|
|||
|
||||
def user_assignment_resource_role_name(user, resource, inherit = '')
|
||||
user_assignment = resource.user_assignments.find_by(user: user)
|
||||
if resource.class != Project && user_assignment.automatically_assigned?
|
||||
if ![Project, Protocol].include?(resource.class) && user_assignment.automatically_assigned?
|
||||
parent = resource.permission_parent
|
||||
return user_assignment_resource_role_name(user, parent, '_inherit')
|
||||
end
|
||||
|
|
|
@ -42,6 +42,10 @@ module Assignable
|
|||
User.joins(:user_assignments).where(user_assignments: { assigned: :manually, assignable: self })
|
||||
end
|
||||
|
||||
def assigned_users
|
||||
User.joins(:user_assignments).where(user_assignments: { assignable: self })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_users_assignments
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UserAssignment < ApplicationRecord
|
||||
attr_accessor :assign
|
||||
|
||||
before_validation -> { self.team ||= (assignable.is_a?(Team) ? assignable : assignable.team) }
|
||||
after_create :assign_team_child_objects, if: -> { assignable.is_a?(Team) }
|
||||
after_update :update_team_children_assignments, if: -> { assignable.is_a?(Team) && saved_change_to_user_role_id? }
|
||||
|
@ -16,6 +18,10 @@ class UserAssignment < ApplicationRecord
|
|||
|
||||
validates :user, uniqueness: { scope: %i(assignable team_id) }
|
||||
|
||||
def last_assignable_owner?
|
||||
assignable_owners.count == 1 && user_role.owner?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assign_team_child_objects
|
||||
|
@ -29,4 +35,10 @@ class UserAssignment < ApplicationRecord
|
|||
def unassign_team_child_objects
|
||||
UserAssignments::RemoveTeamUserAssignmentsService.new(self).call
|
||||
end
|
||||
|
||||
def assignable_owners
|
||||
@assignable_owners ||= assignable.user_assignments
|
||||
.includes(:user_role)
|
||||
.where(user_roles: { name: I18n.t('user_roles.predefined.owner') })
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</div>
|
||||
<div>
|
||||
<%= t('access_permissions.everyone_else', team_name: project.team.name) %>
|
||||
<%= render 'access_permissions/partials/public_members_dropdown', team: project.team, project: project %>
|
||||
<%= render 'access_permissions/partials/public_members_dropdown', team: project.team, assignable: project %>
|
||||
<br>
|
||||
<small class="text-muted">
|
||||
<%= project.default_public_user_role.name %>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<div class="modal-content" id="user_assignments_modal" data-action="modal-close" data-target="<%= projects_path %>">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<%= link_to resource_path, remote: true, class: 'pull-left spacer', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
<% end %>
|
||||
<%= t '.title', resource_name: resource.name %>
|
||||
</h4>
|
||||
</div>
|
||||
<%= form_with(url: create_path, method: :post, remote: true, html: { id: 'new-user-assignment-to-protocol-form', data: { action: 'replace-form', target: '#user_assignments_modal', object_type: resource.class.to_s.downcase} }) do |f| %>
|
||||
<div class="modal-body">
|
||||
<div class="sci-input-container left-icon">
|
||||
<%= text_field_tag :search_users, '', placeholder: t('.find_people_html'), class: 'sci-input-field', data: { action: 'filter-list', target: 'new-user-assignment-to-project-form' } %>
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
<% users.each do |user| %>
|
||||
<%= f.fields_for :users, UserAssignment.new(user: user) do |user_form| %>
|
||||
<%= render 'access_permissions/partials/protocol_user_assignment_field.html.erb', user_form: user_form %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<%= f.submit t('.submit'), class: "btn btn-primary", disabled: true, data: { 'label-default' => t('.submit'), 'label-singular' => t('.submit_singular'), 'label-plural' => t('.submit_plural') }%>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
|
@ -0,0 +1,33 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<%
|
||||
protocol_assignment = UserAssignment.find_by(user_id: user.id, assignable: protocol)
|
||||
item_id = dom_id(user, :protocol_member)
|
||||
%>
|
||||
|
||||
<%= form_with(model: protocol_assignment, url: update_path, method: :put, remote: true, html: { class: 'row member-item', id: item_id, data: { action: 'replace-form autosave-form', object_type: :protocol } }) do |f| %>
|
||||
<%= f.hidden_field :user_id, value: f.object.user.id %>
|
||||
<div class="user-assignment-info">
|
||||
<div class="global-avatar-container">
|
||||
<%= image_tag avatar_path(user, :icon_small), title: current_assignee_name(user), class: 'img-circle pull-left' %>
|
||||
</div>
|
||||
<div>
|
||||
<%= current_assignee_name(user) %>
|
||||
<br>
|
||||
<small class="text-muted"><%= user_assignment_resource_role_name(user, protocol) %></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-assignment-controls">
|
||||
<div class="user-assignment-role">
|
||||
<%= f.select :user_role_id, options_for_select(user_roles_collection, selected: f.object.user_role.id), {}, class: 'form-control selectpicker', title: t('user_assignment.change_protocol_role'), data: { 'selected-text-format' => 'static' } %>
|
||||
</div>
|
||||
<div class="user-assignment-remove">
|
||||
<% unless protocol_assignment.last_assignable_owner? %>
|
||||
<%= link_to access_permissions_protocol_path(protocol, user_id: user), remote: true, method: :delete, class: 'btn btn-secondary', data: { action: 'remote-destroy', target: "##{item_id}" } do %>
|
||||
<span class="fas fa-times"></span>
|
||||
<%= t 'general.remove' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
|
@ -0,0 +1,36 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<%
|
||||
user = user_form.object.user
|
||||
id = dom_id(user, :new_protocol_member)
|
||||
%>
|
||||
|
||||
<div class="row member-item new-member-item" data-filter-item="<%= user.full_name %>">
|
||||
<%= user_form.hidden_field :user_id, value: user.id, name:"access_permissions_new_user_protocol_form[resource_members][#{user.id}][user_id]" %>
|
||||
<div class="user-assignment-info">
|
||||
<div class="sci-checkbox-container">
|
||||
<%= user_form.check_box :assign,
|
||||
name: "access_permissions_new_user_protocol_form[resource_members][#{user.id}][assign]",
|
||||
data: { action: 'toggle-visibility', target: id },
|
||||
class: "sci-checkbox"
|
||||
%>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</div>
|
||||
<div class="global-avatar-container">
|
||||
<%= image_tag avatar_path(user, :icon_small), title: current_assignee_name(user), class: 'img-circle pull-left' %>
|
||||
</div>
|
||||
<div>
|
||||
<%= current_assignee_name(user) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-assignment-controls">
|
||||
<div class="user-assignment-role hidden" id="<%= id %>">
|
||||
<%= user_form.select :user_role_id,
|
||||
options_for_select(user_roles_collection),
|
||||
{},
|
||||
name: "access_permissions_new_user_protocol_form[resource_members][#{user.id}][user_role_id]",
|
||||
class: 'form-control selectpicker pull-right',
|
||||
title: t('user_assignment.select_role') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -5,7 +5,8 @@
|
|||
<div class="title">
|
||||
<%= t('.title', team: team.name) %>
|
||||
</div>
|
||||
<% team.users.order(full_name: :asc).where.not(id: project.manually_assigned_users.select(:id)).each do |user| %>
|
||||
<% users_excluded_id = assignable.manually_assigned_users.select(:id) %>
|
||||
<% team.users.order(full_name: :asc).where.not(id: users_excluded_id).each do |user| %>
|
||||
<div class="name">
|
||||
<%= user.full_name %>
|
||||
</div>
|
||||
|
|
14
app/views/access_permissions/protocols/edit.json.jbuilder
Normal file
14
app/views/access_permissions/protocols/edit.json.jbuilder
Normal file
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.modal controller.render_to_string(
|
||||
partial: 'access_permissions/protocols/modals/edit_modal',
|
||||
formats: [:html],
|
||||
locals: {
|
||||
protocol: @protocol,
|
||||
update_path: access_permissions_protocol_path(@protocol),
|
||||
new_resource_path: new_access_permissions_protocol_path(id: @protocol)
|
||||
},
|
||||
layout: false
|
||||
)
|
||||
|
||||
json.flash @message
|
|
@ -0,0 +1,24 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<div class="modal fade user-assignments-modal protocol-assignments-modal" tabindex="-1" role="dialog" data-action="modal-close">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="user_assignments_modal">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><%= t '.title', resource_name: protocol.name %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<% protocol.assigned_users.order(full_name: :asc).each do |user| %>
|
||||
<%= render('access_permissions/partials/protocol_member_field', user: user, protocol: protocol, update_path: update_path) %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<%= link_to new_resource_path, class: 'btn btn-default pull-left', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
|
||||
<i class="fas fa-plus"></i>
|
||||
<%= t('.new_resource_assignments', resource: protocol.model_name.human.downcase) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<div class="modal fade protocol-assignments-modal" tabindex="-1" role="dialog" data-action="modal-close">
|
||||
<div class="modal-dialog modal-md" role="document">
|
||||
<div class="modal-content" id="user_assignments_modal">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><%= t '.title', resource_name: protocol.name %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<% users.order(full_name: :asc).each do |user| %>
|
||||
<% user_assignment = protocol.user_assignments.find_by(user: user) %>
|
||||
<%= render partial: 'access_permissions/partials/user_assignment', locals: { user_assignment: user_assignment, user: user, resource: protocol } %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
14
app/views/access_permissions/protocols/new.json.jbuilder
Normal file
14
app/views/access_permissions/protocols/new.json.jbuilder
Normal file
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.html controller.render_to_string(
|
||||
partial: 'access_permissions/partials/new_protocol_assignments_form',
|
||||
formats: [:html],
|
||||
locals: {
|
||||
resource: @protocol,
|
||||
form_object: @user_assignment,
|
||||
users: current_team.users.where.not(id: @protocol.assigned_users.select(:id)),
|
||||
create_path: access_permissions_protocols_path(id: @protocol.id),
|
||||
resource_path: edit_access_permissions_protocol_path(@protocol)
|
||||
},
|
||||
layout: false
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.form controller.render_to_string(
|
||||
partial: 'access_permissions/partials/protocol_member_field',
|
||||
formats: [:html],
|
||||
locals: {
|
||||
user: @user_assignment.user,
|
||||
protocol: @protocol,
|
||||
update_path: access_permissions_protocol_path(@protocol)
|
||||
},
|
||||
layout: false
|
||||
)
|
12
app/views/access_permissions/protocols/show.json.jbuilder
Normal file
12
app/views/access_permissions/protocols/show.json.jbuilder
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.modal controller.render_to_string(
|
||||
partial: 'access_permissions/protocols/modals/show_modal',
|
||||
formats: [:html],
|
||||
locals: {
|
||||
protocol: @protocol,
|
||||
users: @protocol.assigned_users,
|
||||
can_manage_resource: can_manage_protocol_users?(@protocol)
|
||||
},
|
||||
layout: false
|
||||
)
|
|
@ -3036,6 +3036,7 @@ en:
|
|||
user_assignment:
|
||||
current_assignee: "(you)"
|
||||
change_project_role: "Change project role"
|
||||
change_protocol_role: "Change protocol role"
|
||||
select_default_user_role: "Select default user role"
|
||||
change_experiment_role: "Change experiment role"
|
||||
change_my_module_role: "Change task role"
|
||||
|
@ -3069,6 +3070,12 @@ en:
|
|||
submit_singular: "Grant access to 1 user"
|
||||
submit_plural: "Grant access to {num} users"
|
||||
find_people_html: "Find people"
|
||||
new_protocol_assignments_form:
|
||||
title: "Select members"
|
||||
submit: "Grant access"
|
||||
submit_singular: "Grant access to 1 user"
|
||||
submit_plural: "Grant access to {num} users"
|
||||
find_people_html: "Find people"
|
||||
experiment_member_field:
|
||||
reset: "Inherit role"
|
||||
reset_description: "The inherited role from project will be applied"
|
||||
|
@ -3078,6 +3085,9 @@ en:
|
|||
project: "Project"
|
||||
project_tooltip: "This role was set on this project."
|
||||
project_tooltip_inherit: "This role was inherited from the project."
|
||||
protocol: "Protocol"
|
||||
protocol_tooltip: "This role was set on this protocol."
|
||||
protocol_tooltip_inherit: "This role was inherited from the protocol."
|
||||
experiment: "Experiment"
|
||||
experiment_tooltip: "This role was set on this experiment."
|
||||
experiment_tooltip_inherit: "This role was inherited from the experiment."
|
||||
|
@ -3091,6 +3101,14 @@ en:
|
|||
show_modal:
|
||||
title: "Access to %{resource_name}"
|
||||
new_resource_assignments: "Grant new access to %{resource}"
|
||||
protocols:
|
||||
modals:
|
||||
show_modal:
|
||||
title: "Access to %{resource_name}"
|
||||
new_resource_assignments: "Grant new access to %{resource}"
|
||||
edit_modal:
|
||||
title: "Manage access for %{resource_name}"
|
||||
new_resource_assignments: "Grant new access to %{resource}"
|
||||
experiments:
|
||||
modals:
|
||||
show_modal:
|
||||
|
|
|
@ -280,6 +280,7 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
namespace :access_permissions do
|
||||
resources :protocols, defaults: { format: 'json' }
|
||||
resources :projects, defaults: { format: 'json' } do
|
||||
put :update_default_public_user_role, on: :member
|
||||
resources :experiments, only: %i(show update edit) do
|
||||
|
|
Loading…
Reference in a new issue