add assign user permission to experiment level

This commit is contained in:
zmagoD 2021-05-08 18:06:07 +02:00
parent 331a68e9b6
commit ac91d9c2c4
19 changed files with 275 additions and 13 deletions

View file

@ -0,0 +1,60 @@
# frozen_string_literal: true
module AccessPermissions
class ExperimentsController < ApplicationController
before_action :set_project
before_action :set_experiment
before_action :check_read_permissions, only: %i(show)
before_action :check_manage_permissions, only: %i(create edit update)
def show
respond_to do |format|
format.json
end
end
def edit
respond_to do |format|
format.json
end
end
def update
@experiment_member = ExperimentMember.new(current_user, @experiment, @project)
@experiment_member.update(permitted_update_params)
respond_to do |format|
format.json do
render :experiment_member
end
end
end
private
def permitted_update_params
params.require(:experiment_member)
.permit(%i(user_role_id user_id))
end
def set_project
@project = current_team.projects.find_by(id: params[:project_id])
render_404 unless @project
end
def set_experiment
@experiment = @project.experiments.includes(user_assignments: %i(user user_role)).find_by(id: params[:id])
render_404 unless @experiment
end
def check_manage_permissions
render_403 unless can_manage_experiment?(@experiment)
end
def check_read_permissions
render_403 unless can_read_experiment?(@experiment)
end
end
end

View file

@ -264,7 +264,7 @@ class ProjectsController < ApplicationController
render json: {
cards_html: render_to_string(
partial: 'projects/show/experiments_list.html.erb',
locals: { cards: overview_service.experiments }
locals: { cards: overview_service.experiments, project: @project }
)
}

View file

@ -9,4 +9,23 @@ module UserAssignmentsHelper
end
sanitize_input(display_name)
end
def user_assignment_resource_role_name(user_assignment, user, resource)
# Triggers N+1 but the partial is cached
if resource.is_a?(Experiment)
project_user_assignment = resource.permission_parent.user_assignments.find_by(user: user)
current_user_assignment_name = user_assignment&.user_role&.name
[
t('user_assignment.from_project', user_role: project_user_assignment.user_role.name),
current_user_assignment_name
].compact.join(' / ')
elsif resource.is_a?(MyModule)
# TODO
else
user_assignment.user_role.name
end
end
end

View file

@ -10,4 +10,8 @@ module UserRolesHelper
def new_user_roles_collection
[[t('user_assignment.select_role'), nil]] + user_roles_collection
end
def experiment_user_roles_collection
[[t('user_assignment.select_role'), nil]] + user_roles_collection
end
end

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
class ExperimentMember
include ActiveModel::Model
attr_accessor :user_id, :user_role_id
attr_reader :current_user, :experiment, :user, :project, :user_role
def initialize(current_user, experiment, project, user = nil)
@experiment = experiment
@current_user = current_user
@project = project
if user
@user = user
@user_role = UserAssignment.find_by(assignable: experiment, user: user)&.user_role
end
end
def update(params)
self.user_role_id = params[:user_role_id]
self.user_id = params[:user_id]
ActiveRecord::Base.transaction do
@user = @project.users.find(user_id)
@user_role = UserRole.find_by(id: user_role_id)
user_assignment = UserAssignment.find_by(assignable: experiment, user: user)
if user_assignment.present? && user_role.nil?
user_assignment.destroy
elsif user_assignment.present?
user_assignment.update!(user_role: user_role)
else
UserAssignment.create!(
assignable: experiment,
user: user,
user_role: user_role,
assigned_by: current_user
)
end
end
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
json.modal controller.render_to_string(
partial: 'access_permissions/experiments/modals/edit_modal',
formats: [:html],
locals: {
experiment: @experiment,
project: @project,
users: @project.users,
project_path: project_path(@project)
},
layout: false
)

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
json.form controller.render_to_string(
partial: 'access_permissions/partials/experiment_member_field',
formats: [:html],
locals: {
user: @experiment_member.user,
experiment: @experiment_member.experiment,
project: @experiment_member.project,
update_path: access_permissions_project_experiment_path(@experiment_member.project, @experiment_member.experiment)
},
layout: false
)

View file

@ -0,0 +1,30 @@
<% # frozen_string_literal: true %>
<div class="modal fade" tabindex="-1" role="dialog" data-action="modal-close" data-target="<%= project_path %>">
<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">&times;</span></button>
<h4 class="modal-title"><%= t '.title', resource_name: experiment.name %></h4>
</div>
<div class="modal-body">
<% users.each do |user| %>
<% user_assignment = experiment.user_assignments.find_by(user: user) %>
<% cache [user_assignment, experiment, user] do %>
<%= render partial: 'access_permissions/partials/experiment_member_field',
locals: {
user: user,
project: project,
experiment: experiment,
update_path: access_permissions_project_experiment_path(project, experiment)
} %>
<% end %>
<% end %>
</div>
<div class="modal-footer">
<button type='button' class='btn btn-default' data-dismiss='modal'><%= t('general.cancel')%></button>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,24 @@
<% # frozen_string_literal: true %>
<div class="modal fade" tabindex="-1" role="dialog" data-action="modal-close" data-target="<%= project_path %>">
<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">&times;</span></button>
<h4 class="modal-title"><%= t '.title', resource_name: experiment.name %></h4>
</div>
<div class="modal-body">
<% users.each do |user| %>
<% user_assignment = experiment.user_assignments.find_by(user: user) %>
<% cache [user_assignment, experiment, current_user, user] do %>
<%= render partial: 'access_permissions/partials/user_assignment', locals: { user_assignment: user_assignment, user: user, resource: experiment } %>
<% end %>
<% end %>
</div>
<div class="modal-footer">
<button type='button' class='btn btn-default' data-dismiss='modal'><%= t('general.cancel')%></button>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
json.modal controller.render_to_string(
partial: 'access_permissions/experiments/modals/show_modal',
formats: [:html],
locals: {
experiment: @experiment,
users: @project.users,
project_path: project_path(@project)
},
layout: false
)

View file

@ -0,0 +1,25 @@
<% # frozen_string_literal: true %>
<%
experiment_member = ExperimentMember.new(current_user, experiment, project, user)
item_id = dom_id(user, :experiment_member)
%>
<%= form_with(model: experiment_member, url: update_path, method: :put, remote: true, html: { class: 'row experiment-member-item', id: item_id, data: { action: 'replace-form autosave-form' } }) do |f| %>
<%= f.hidden_field :user_id, value: f.object.user.id %>
<div class="col-xs-1">
<span class="global-avatar-container">
<%= image_tag avatar_path(user, :icon_small), title: current_assignee_name(user), class: 'img-circle pull-left' %>
</span>
</div>
<div class="col-xs-6">
<span><%= current_assignee_name(user) %></span>
<br>
<small class="text-muted"><%= user_assignment_resource_role_name(experiment_member, user, experiment) %></small>
</div>
<% unless user == current_user %>
<div class="col-xs-5">
<%= f.select :user_role_id, options_for_select(experiment_user_roles_collection, selected: f.object.user_role&.id), {}, class: 'form-control selectpicker' %>
</div>
<% end %>
<% end %>

View file

@ -15,7 +15,7 @@
<div class="col-xs-3">
<span><%= current_assignee_name(user) %></span>
<br>
<small class="text-muted"><%= project_member.user_role.name %></small>
<small class="text-muted"><%= user_assignment_resource_role_name(project_member, user, project) %></small>
</div>
<% unless user == current_user %>
<div class="col-xs-7">

View file

@ -3,13 +3,13 @@
<div class="row project-member-item">
<div class="col-xs-2">
<span class="global-avatar-container">
<%= image_tag avatar_path(user_assignment.user, :icon_small), title: current_assignee_name(user_assignment.user), class: 'img-circle pull-left' %>
<%= image_tag avatar_path(user, :icon_small), title: current_assignee_name(user), class: 'img-circle pull-left' %>
</span>
</div>
<div class="col-xs-10 col-sm-4">
<span><%= current_assignee_name(user_assignment.user) %></span>
<span><%= current_assignee_name(user) %></span>
<br>
<small class="text-muted"><%= user_assignment.user_role.name %></small>
<small class="text-muted"><%= user_assignment_resource_role_name(user_assignment, user, resource) %></small>
</div>
</div>

View file

@ -8,7 +8,11 @@
<h4 class="modal-title"><%= t '.title', resource_name: resource.name %></h4>
</div>
<div class="modal-body">
<%= render partial: 'access_permissions/partials/user_assignment', collection: resource.user_assignments, cached: ->(user_assignment) { [user_assignment. resource, current_user] } %>
<% resource.user_assignments.each do |user_assignment| %>
<% cache [user_assignment, resource, current_user, user_assignment.user] do %>
<%= render partial: 'access_permissions/partials/user_assignment', locals: { user_assignment: user_assignment, user: user_assignment.user, resource: resource } %>
<% end %>
<% end %>
</div>
<div class="modal-footer">
<% if can_manage_resource %>

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
json.modal controller.render_to_string(
partial: 'access_permissions/modals/show_modal',
partial: 'access_permissions/projects/modals/show_modal',
formats: [:html],
locals: {
resource: @project,

View file

@ -12,6 +12,7 @@
<span class="sci-checkbox-label"></span>
</div>
</div>
<div class="experiment-name-cell table-cell">
<% if experiment.archived_branch? %>
<%= link_to experiment.name, module_archive_experiment_path(experiment), title: experiment.name %>
@ -55,6 +56,8 @@
</div>
</div>
<div class="description-cell table-cell">
<%= link_to 'Experiment Accesss', edit_access_permissions_project_experiment_path(project, experiment), remote: true, data: { action: 'remote-modal'} %>
<%= link_to 'Show Experiment Accesss', access_permissions_project_experiment_path(project, experiment), remote: true, data: { action: 'remote-modal'} %>
<div class="description-text">
<%= custom_auto_link(experiment.description, team: current_team) %>
</div>

View file

@ -1,5 +1,5 @@
<% cards.each do |card| %>
<% cache [current_user, card] do %>
<%= render partial: 'projects/show/experiment_card', locals: { experiment: card } %>
<%= render partial: 'projects/show/experiment_card', locals: { experiment: card, project: project } %>
<% end %>
<% end %>

View file

@ -2318,6 +2318,8 @@ en:
user_assignment:
current_assignee: "(you)"
select_role: "Select role"
from_project: "%{user_role} [from project]"
experiment_select_role: "Change experiment role"
access_permissions:
create:
success:
@ -2337,10 +2339,17 @@ en:
submit_singular: "Grand access to 1 user"
submit_plural: "Grand access to {num} users"
find_people_html: "&#128269; Find people"
modals:
show_modal:
title: "Access to %{resource_name}"
new_resource_assignments: "Grant new access to %{resource}"
projects:
modals:
show_modal:
title: "Access to %{resource_name}"
new_resource_assignments: "Grant new access to %{resource}"
experiments:
modals:
show_modal:
title: "Access to %{resource_name}"
edit_modal:
title: "Manage access for %{resource_name}"
zip_export:
modal_label: 'Export inventory'
notification_title: 'Your requested export package is ready!'

View file

@ -228,7 +228,9 @@ Rails.application.routes.draw do
end
namespace :access_permissions do
resources :projects
resources :projects, defaults: { format: 'json' } do
resources :experiments, only: %i(show update edit)
end
end
resources :projects, except: [:destroy] do