mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-20 06:35:56 +08:00
Implement storage location sharing logic and permissions [SCI-10865]
This commit is contained in:
parent
40355ae6c8
commit
5ba07aec28
|
@ -93,7 +93,7 @@ class StorageLocationRepositoryRowsController < ApplicationController
|
|||
end
|
||||
|
||||
def load_storage_location
|
||||
@storage_location = StorageLocation.where(team: current_team).find(
|
||||
@storage_location = StorageLocation.viewable_by_user(current_user).find(
|
||||
storage_location_repository_row_params[:storage_location_id]
|
||||
)
|
||||
render_404 unless @storage_location
|
||||
|
@ -110,12 +110,10 @@ class StorageLocationRepositoryRowsController < ApplicationController
|
|||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 unless can_read_storage_location_containers?(current_team)
|
||||
render_403 unless can_read_storage_location?(@storage_location)
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
unless can_manage_storage_location_containers?(current_team) && can_read_repository?(@repository_row.repository)
|
||||
render_403
|
||||
end
|
||||
render_403 unless can_manage_storage_location?(@storage_location)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class StorageLocationsController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
storage_locations = Lists::StorageLocationsService.new(current_team, params).call
|
||||
storage_locations = Lists::StorageLocationsService.new(current_user, current_team, params).call
|
||||
render json: storage_locations, each_serializer: Lists::StorageLocationSerializer,
|
||||
user: current_user, meta: pagination_dict(storage_locations)
|
||||
end
|
||||
|
@ -35,9 +35,11 @@ class StorageLocationsController < ApplicationController
|
|||
|
||||
def create
|
||||
@storage_location = StorageLocation.new(
|
||||
storage_location_params.merge({ team: current_team, created_by: current_user })
|
||||
storage_location_params.merge({ created_by: current_user })
|
||||
)
|
||||
|
||||
@storage_location.team = @storage_location.root_storage_location.team
|
||||
|
||||
@storage_location.image.attach(params[:signed_blob_id]) if params[:signed_blob_id]
|
||||
|
||||
if @storage_location.save
|
||||
|
@ -122,7 +124,7 @@ class StorageLocationsController < ApplicationController
|
|||
end
|
||||
|
||||
def load_storage_location
|
||||
@storage_location = current_team.storage_locations.find_by(id: storage_location_params[:id])
|
||||
@storage_location = StorageLocation.viewable_by_user(current_user).find_by(id: storage_location_params[:id])
|
||||
render_404 unless @storage_location
|
||||
end
|
||||
|
||||
|
|
|
@ -38,9 +38,9 @@ class TeamSharedObjectsController < ApplicationController
|
|||
def load_vars
|
||||
case params[:object_type]
|
||||
when 'Repository'
|
||||
@model = current_team.repositories.find_by(id: params[:object_id])
|
||||
@model = Repository.viewable_by_user(current_user).find_by(id: params[:object_id])
|
||||
when 'StorageLocation'
|
||||
@model = current_team.storage_locations.find_by(id: params[:object_id])
|
||||
@model = StorageLocation.viewable_by_user(current_user).find_by(id: params[:object_id])
|
||||
end
|
||||
|
||||
render_404 unless @model
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
<div class="sci-divider my-4" v-if="index > 0"></div>
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
{{ i18n.t('repositories.locations.container') }}:
|
||||
<a :href="containerUrl(location.id)">{{ location.name }}</a>
|
||||
<a v-if="location.readable" :href="containerUrl(location.id)">{{ location.name }}</a>
|
||||
<span v-else>{{ location.name }}</span>
|
||||
<span v-if="location.metadata.display_type !== 'grid'">
|
||||
({{ location.positions.length }})
|
||||
</span>
|
||||
|
|
|
@ -66,6 +66,10 @@ export default {
|
|||
RemindersRender
|
||||
},
|
||||
props: {
|
||||
canManage: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
dataSource: {
|
||||
type: String,
|
||||
required: true
|
||||
|
@ -143,13 +147,15 @@ export default {
|
|||
toolbarActions() {
|
||||
const left = [];
|
||||
|
||||
left.push({
|
||||
name: 'assign',
|
||||
icon: 'sn-icon sn-icon-new-task',
|
||||
label: this.i18n.t('storage_locations.show.toolbar.assign'),
|
||||
type: 'emit',
|
||||
buttonStyle: 'btn btn-primary'
|
||||
});
|
||||
if (this.canManage) {
|
||||
left.push({
|
||||
name: 'assign',
|
||||
icon: 'sn-icon sn-icon-new-task',
|
||||
label: this.i18n.t('storage_locations.show.toolbar.assign'),
|
||||
type: 'emit',
|
||||
buttonStyle: 'btn btn-primary'
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
left,
|
||||
|
|
|
@ -120,11 +120,6 @@ export default {
|
|||
headerName: this.i18n.t('storage_locations.index.table.items'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'free_spaces',
|
||||
headerName: this.i18n.t('storage_locations.index.table.free_spaces'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'shared',
|
||||
headerName: this.i18n.t('storage_locations.index.table.shared'),
|
||||
|
|
|
@ -32,8 +32,18 @@ module Shareable
|
|||
.where(team: teams)
|
||||
.or(readable.where(team_shared_objects: { team: teams }))
|
||||
.or(readable
|
||||
.where(permission_level: [Extends::SHARED_OBJECTS_PERMISSION_LEVELS[:shared_read], Extends::SHARED_OBJECTS_PERMISSION_LEVELS[:shared_write]])
|
||||
.where.not(team: teams))
|
||||
.where(
|
||||
if column_names.include?('permission_level')
|
||||
{
|
||||
permission_level: [
|
||||
Extends::SHARED_OBJECTS_PERMISSION_LEVELS[:shared_read],
|
||||
Extends::SHARED_OBJECTS_PERMISSION_LEVELS[:shared_write]
|
||||
]
|
||||
}
|
||||
else
|
||||
{}
|
||||
end
|
||||
).where.not(team: teams))
|
||||
.distinct
|
||||
}
|
||||
end
|
||||
|
|
|
@ -93,10 +93,6 @@ class Repository < RepositoryBase
|
|||
['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL, 'users.full_name']
|
||||
end
|
||||
|
||||
def self.viewable_by_user(_user, teams)
|
||||
accessible_by_teams(teams)
|
||||
end
|
||||
|
||||
def self.name_like(query)
|
||||
where('repositories.name ILIKE ?', "%#{query}%")
|
||||
end
|
||||
|
|
|
@ -174,17 +174,6 @@ class RepositoryRow < ApplicationRecord
|
|||
self[:archived]
|
||||
end
|
||||
|
||||
def grouped_storage_locations
|
||||
storage_location_repository_rows.joins(:storage_location).group(:storage_location_id).select(
|
||||
"storage_location_id as id,
|
||||
(ARRAY_AGG(storage_locations.metadata))[1] as metadata,
|
||||
MAX(storage_locations.name) as name,
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'id', storage_location_repository_rows.id,
|
||||
'metadata', storage_location_repository_rows.metadata)
|
||||
) as positions").as_json
|
||||
end
|
||||
|
||||
def has_reminders?(user)
|
||||
stock_reminders = RepositoryCell.stock_reminder_repository_cells_scope(
|
||||
repository_cells.joins(:repository_column), user)
|
||||
|
|
|
@ -17,16 +17,42 @@ class StorageLocation < ApplicationRecord
|
|||
|
||||
has_many :storage_location_repository_rows, inverse_of: :storage_location, dependent: :destroy
|
||||
has_many :storage_locations, foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy
|
||||
has_many :repository_rows, through: :storage_location_repository_row
|
||||
has_many :repository_rows, through: :storage_location_repository_rows
|
||||
|
||||
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
validate :parent_validation, if: -> { parent.present? }
|
||||
|
||||
scope :readable_by_user, (lambda do |user, team = user.current_team|
|
||||
next StorageLocation.none unless team.permission_granted?(user, TeamPermissions::STORAGE_LOCATIONS_READ)
|
||||
|
||||
where(team: team)
|
||||
end)
|
||||
|
||||
after_discard do
|
||||
StorageLocation.where(parent_id: id).find_each(&:discard)
|
||||
storage_location_repository_rows.each(&:discard)
|
||||
end
|
||||
|
||||
def shared_with?(team)
|
||||
return false if self.team == team
|
||||
|
||||
(root? ? self : root_storage_location).private_shared_with?(team)
|
||||
end
|
||||
|
||||
def root?
|
||||
parent_id.nil?
|
||||
end
|
||||
|
||||
def root_storage_location
|
||||
return self if root?
|
||||
|
||||
storage_location = self
|
||||
|
||||
storage_location = storage_location.parent while storage_location.parent_id
|
||||
|
||||
storage_location
|
||||
end
|
||||
|
||||
def duplicate!
|
||||
ActiveRecord::Base.transaction do
|
||||
new_storage_location = dup
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
Canaid::Permissions.register_for(StorageLocation) do
|
||||
can :read_storage_location do |user, storage_location|
|
||||
storage_location.team.permission_granted?(
|
||||
root_storage_location = storage_location.root_storage_location
|
||||
|
||||
next true if root_storage_location.shared_with?(user.current_team)
|
||||
|
||||
user.current_team == root_storage_location.team && root_storage_location.team.permission_granted?(
|
||||
user,
|
||||
if @storage_location.container
|
||||
if root_storage_location.container?
|
||||
TeamPermissions::STORAGE_LOCATION_CONTAINERS_READ
|
||||
else
|
||||
TeamPermissions::STORAGE_LOCATIONS_READ
|
||||
|
@ -13,9 +17,13 @@ Canaid::Permissions.register_for(StorageLocation) do
|
|||
end
|
||||
|
||||
can :manage_storage_location do |user, storage_location|
|
||||
storage_location.team.permission_granted?(
|
||||
root_storage_location = storage_location.root_storage_location
|
||||
|
||||
next true if root_storage_location.shared_with_write?(user.current_team)
|
||||
|
||||
user.current_team == root_storage_location.team && root_storage_location.team.permission_granted?(
|
||||
user,
|
||||
if @storage_location.container
|
||||
if root_storage_location.container?
|
||||
TeamPermissions::STORAGE_LOCATION_CONTAINERS_MANAGE
|
||||
else
|
||||
TeamPermissions::STORAGE_LOCATIONS_MANAGE
|
||||
|
@ -24,6 +32,8 @@ Canaid::Permissions.register_for(StorageLocation) do
|
|||
end
|
||||
|
||||
can :share_storage_location do |user, storage_location|
|
||||
storage_location.team.permission_granted?(user, TeamPermissions::STORAGE_LOCATIONS_MANAGE)
|
||||
user.current_team == storage_location.team &&
|
||||
storage_location.root? &&
|
||||
can_manage_storage_location?(user, storage_location)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ module Lists
|
|||
:have_reminders, :reminders_url
|
||||
|
||||
def row_id
|
||||
object.repository_row.id unless hidden
|
||||
object.repository_row.code
|
||||
end
|
||||
|
||||
def row_name
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
module Lists
|
||||
class StorageLocationsService < BaseService
|
||||
def initialize(team, params)
|
||||
def initialize(user, team, params)
|
||||
@user = user
|
||||
@team = team
|
||||
@parent_id = params[:parent_id]
|
||||
@filters = params[:filters] || {}
|
||||
|
@ -13,8 +14,8 @@ module Lists
|
|||
@records =
|
||||
StorageLocation.joins('LEFT JOIN storage_locations AS sub_locations ' \
|
||||
'ON storage_locations.id = sub_locations.parent_id')
|
||||
.viewable_by_user(@user, @team)
|
||||
.select('storage_locations.*, COUNT(sub_locations.id) AS sub_location_count')
|
||||
.accessible_by_teams(@team)
|
||||
.group(:id)
|
||||
end
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ module Toolbars
|
|||
private
|
||||
|
||||
def unassign_action
|
||||
return unless can_manage_storage_location?(@storage_location)
|
||||
|
||||
{
|
||||
name: 'unassign',
|
||||
label: I18n.t('storage_locations.show.toolbar.unassign'),
|
||||
|
@ -37,7 +39,7 @@ module Toolbars
|
|||
end
|
||||
|
||||
def move_action
|
||||
return unless @single
|
||||
return unless @single && can_manage_storage_location?(@storage_location)
|
||||
|
||||
{
|
||||
name: 'move',
|
||||
|
|
|
@ -29,9 +29,7 @@ module Toolbars
|
|||
private
|
||||
|
||||
def edit_action
|
||||
return unless @single
|
||||
|
||||
return unless can_manage_storage_locations?(current_user.current_team)
|
||||
return unless @single && can_manage_storage_location?(@storage_locations.first)
|
||||
|
||||
{
|
||||
name: 'edit',
|
||||
|
@ -43,13 +41,11 @@ module Toolbars
|
|||
end
|
||||
|
||||
def move_action
|
||||
return unless @single
|
||||
|
||||
return unless can_manage_storage_locations?(current_user.current_team)
|
||||
return unless @single && can_manage_storage_location?(@storage_locations.first)
|
||||
|
||||
{
|
||||
name: 'move',
|
||||
label: I18n.t("storage_locations.index.toolbar.move"),
|
||||
label: I18n.t('storage_locations.index.toolbar.move'),
|
||||
icon: 'sn-icon sn-icon-move',
|
||||
path: move_storage_location_path(@storage_locations.first),
|
||||
type: :emit
|
||||
|
@ -57,9 +53,7 @@ module Toolbars
|
|||
end
|
||||
|
||||
def duplicate_action
|
||||
return unless @single
|
||||
|
||||
return unless can_manage_storage_locations?(current_user.current_team)
|
||||
return unless @single && can_manage_storage_location?(@storage_locations.first)
|
||||
|
||||
{
|
||||
name: 'duplicate',
|
||||
|
@ -71,9 +65,7 @@ module Toolbars
|
|||
end
|
||||
|
||||
def delete_action
|
||||
return unless @single
|
||||
|
||||
return unless can_manage_storage_locations?(current_user.current_team)
|
||||
return unless @single && can_manage_storage_location?(@storage_locations.first)
|
||||
|
||||
storage_location = @storage_locations.first
|
||||
|
||||
|
|
|
@ -38,7 +38,19 @@ json.actions do
|
|||
end
|
||||
|
||||
json.storage_locations do
|
||||
json.locations @repository_row.grouped_storage_locations
|
||||
json.locations(
|
||||
@repository_row.storage_locations.distinct.map do |storage_location|
|
||||
readable = can_read_storage_location?(storage_location)
|
||||
|
||||
{
|
||||
id: storage_location.id,
|
||||
readable: readable,
|
||||
name: readable ? storage_location.name : storage_location.code,
|
||||
metadata: storage_location.metadata,
|
||||
positions: readable ? storage_location.storage_location_repository_rows.where(repository_row: @repository_row) : []
|
||||
}
|
||||
end
|
||||
)
|
||||
json.enabled StorageLocation.storage_locations_enabled?
|
||||
end
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<storage-locations-container
|
||||
actions-url="<%= actions_toolbar_storage_location_storage_location_repository_rows_path(@storage_location) %>"
|
||||
data-source="<%= storage_location_storage_location_repository_rows_path(@storage_location) %>"
|
||||
:can-manage="<%= can_manage_storage_location?(@storage_location) %>"
|
||||
:with-grid="<%= @storage_location.with_grid? %>"
|
||||
:grid-size="<%= @storage_location.grid_size.to_json %>"
|
||||
:container-id="<%= @storage_location.id %>"
|
||||
|
|
|
@ -1082,7 +1082,4 @@ Rails.application.routes.draw do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/rest-v1/license/grant", to: "application#grant"
|
||||
post "/marvin4js-license", to: "application#grant", format: :cxl
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue