1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { Dispatch } from 'redux';
6 import { RootState } from 'store/store';
7 import { AuthState } from 'store/auth/auth-reducer';
8 import { getResource } from 'store/resources/resources';
9 import { Resource, ResourceKind } from 'models/resource';
10 import { resourceIsFrozen } from 'common/frozen-resources';
11 import { GroupResource, GroupClass, isGroupResource, isUserGroup } from 'models/group';
12 import { ContextMenuKind } from 'views-components/context-menu/menu-item-sort';
13 import { getProcess, isProcessCancelable } from 'store/processes/process';
14 import { isCollectionResource } from 'models/collection';
15 import { ResourcesState } from 'store/resources/resources';
17 type ProjectToMenuArgs = {
23 isFilterGroup: boolean;
24 unfreezeRequiresAdmin: boolean;
28 type CollectionToMenuArgs = {
31 isOnlyWriteable: boolean;
32 isOldVersion: boolean;
36 type ProcessToMenuArgs = {
39 canWriteProcess: boolean;
42 type ProjectMenuKind = ContextMenuKind.PROJECT
43 | ContextMenuKind.PROJECT_ADMIN
44 | ContextMenuKind.FROZEN_PROJECT
45 | ContextMenuKind.FROZEN_PROJECT_ADMIN
46 | ContextMenuKind.FROZEN_MANAGEABLE_PROJECT
47 | ContextMenuKind.MANAGEABLE_PROJECT
48 | ContextMenuKind.READONLY_PROJECT
49 | ContextMenuKind.WRITEABLE_PROJECT
50 | ContextMenuKind.FILTER_GROUP
51 | ContextMenuKind.FILTER_GROUP_ADMIN;
53 type CollectionMenuKind = ContextMenuKind.COLLECTION
54 | ContextMenuKind.READONLY_COLLECTION
55 | ContextMenuKind.WRITEABLE_COLLECTION
56 | ContextMenuKind.OLD_VERSION_COLLECTION
57 | ContextMenuKind.TRASHED_COLLECTION
58 | ContextMenuKind.COLLECTION_ADMIN;
60 type ProcessMenuKind = ContextMenuKind.PROCESS_RESOURCE
61 | ContextMenuKind.PROCESS_ADMIN
62 | ContextMenuKind.RUNNING_PROCESS_RESOURCE
63 | ContextMenuKind.RUNNING_PROCESS_ADMIN
64 | ContextMenuKind.READONLY_PROCESS_RESOURCE;
66 export const resourceToMenuKind = (uuid: string, readonly = false) =>
67 (dispatch: Dispatch, getState: () => RootState): ContextMenuKind | undefined => {
68 const { auth, resources } = getState();
69 const resource = getResource<Resource>(uuid)(resources);
70 if (!resource) return;
71 const isAdmin = auth.user?.isAdmin || false;
72 const isFrozen = resourceIsFrozen(resource, resources);
73 const isEditable = getIsEditable(isAdmin, resource, resources, readonly, isFrozen);
75 if (isUserGroup(resource)) {
76 return ContextMenuKind.GROUPS
78 if (isGroupResource(resource)) {
79 const { canManage = false, canWrite = false } = resource;
80 const unfreezeRequiresAdmin = getUnfreezeRequiresAdmin(auth);
81 const isFilterGroup = resource.groupClass === GroupClass.FILTER;
82 return getProjectMenuKind({ isAdmin, isFrozen, isEditable, canManage, canWrite, unfreezeRequiresAdmin, isFilterGroup, readonly });
84 if (isCollectionResource(resource)){
85 const collectionParent = getResource<GroupResource>(resource.ownerUuid)(resources);
86 const isOnlyWriteable = collectionParent?.canWrite === true && collectionParent.canManage === false;
87 const isOldVersion = resource.uuid !== resource.currentVersionUuid;
88 const isTrashed = resource.isTrashed || false;
89 return getCollectionMenuKind({ isAdmin, isEditable, isOldVersion, isTrashed, isOnlyWriteable });
91 switch (resource.kind) {
92 case ResourceKind.PROCESS:
93 const process = getProcess(uuid)(resources);
94 const canWriteProcess = !!(process && getResource<GroupResource>(process.containerRequest.ownerUuid)(resources)?.canWrite);
95 const isRunning = process ? isProcessCancelable(process) : false;
96 return getProcessMenuKind({ isAdmin, isRunning, canWriteProcess });
97 case ResourceKind.USER:
98 return ContextMenuKind.USER_DETAILS;
99 case ResourceKind.LINK:
100 return ContextMenuKind.LINK;
101 case ResourceKind.WORKFLOW:
102 return isEditable ? ContextMenuKind.WORKFLOW : ContextMenuKind.READONLY_WORKFLOW;
108 const getProjectMenuKind = ({ isAdmin, readonly, isFrozen, canManage, canWrite, unfreezeRequiresAdmin, isEditable, isFilterGroup }: ProjectToMenuArgs): ProjectMenuKind => {
111 return ContextMenuKind.FROZEN_PROJECT_ADMIN;
114 if (unfreezeRequiresAdmin) return ContextMenuKind.MANAGEABLE_PROJECT;
115 return ContextMenuKind.FROZEN_MANAGEABLE_PROJECT;
118 return ContextMenuKind.FROZEN_PROJECT;
120 return ContextMenuKind.READONLY_PROJECT;
123 if (isAdmin && !readonly) {
124 if (isFilterGroup) return ContextMenuKind.FILTER_GROUP_ADMIN;
125 return ContextMenuKind.PROJECT_ADMIN;
128 if (canManage === false && canWrite === true) {
129 return ContextMenuKind.WRITEABLE_PROJECT;
133 return ContextMenuKind.READONLY_PROJECT;
136 if (isFilterGroup) return ContextMenuKind.FILTER_GROUP;
138 return ContextMenuKind.PROJECT;
141 const getCollectionMenuKind = ({ isAdmin, isEditable, isOnlyWriteable, isOldVersion, isTrashed }: CollectionToMenuArgs): CollectionMenuKind => {
143 return ContextMenuKind.OLD_VERSION_COLLECTION;
146 if (isTrashed && isEditable) {
147 return ContextMenuKind.TRASHED_COLLECTION;
150 if (isAdmin && isEditable) {
151 return ContextMenuKind.COLLECTION_ADMIN;
155 return ContextMenuKind.READONLY_COLLECTION;
158 return isOnlyWriteable ? ContextMenuKind.WRITEABLE_COLLECTION : ContextMenuKind.COLLECTION;
161 const getProcessMenuKind = ({ isAdmin, isRunning, canWriteProcess }: ProcessToMenuArgs): ProcessMenuKind => {
163 return isRunning ? ContextMenuKind.RUNNING_PROCESS_ADMIN : ContextMenuKind.PROCESS_ADMIN;
167 return ContextMenuKind.RUNNING_PROCESS_RESOURCE;
170 return canWriteProcess ? ContextMenuKind.PROCESS_RESOURCE : ContextMenuKind.READONLY_PROCESS_RESOURCE;
173 //Utils--------------------------------------------------------------
174 const getUnfreezeRequiresAdmin = (auth: AuthState) => {
175 const { remoteHostsConfig } = auth;
176 if (!remoteHostsConfig) return false;
177 return Object.keys(remoteHostsConfig).some((k) => remoteHostsConfig[k].clusterConfig.API.UnfreezeProjectRequiresAdmin);
180 const getIsEditable = (isAdmin: boolean, resource: Resource, resources: ResourcesState, readonly: boolean, isFrozen: boolean) => {
181 const isEditable = (resources[resource.ownerUuid] as GroupResource)?.canWrite || (isGroupResource(resource) && resource.canWrite);
182 return (isAdmin || isEditable) && !readonly && !isFrozen;