From 416d4f0f57336b2225bcc82b0f4db8873adf8cd2 Mon Sep 17 00:00:00 2001 From: Stephen Smith Date: Tue, 30 Nov 2021 14:15:11 -0500 Subject: [PATCH] 18123: Change edit permission level to context menu. Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- cypress/integration/group-manage.spec.js | 9 +-- src/components/icon/icon.tsx | 5 ++ src/index.tsx | 2 + .../context-menu/context-menu-actions.ts | 14 +++++ .../group-details-panel-actions.ts | 30 +--------- .../action-sets/permission-edit-action-set.ts | 28 +++++++++ .../context-menu/context-menu.tsx | 1 + .../data-explorer/renderers.tsx | 58 +++++++++---------- .../edit-permission-level-dialog.tsx | 55 ------------------ .../group-details-panel.tsx | 15 ----- src/views/workbench/workbench.tsx | 2 - 11 files changed, 82 insertions(+), 137 deletions(-) create mode 100644 src/views-components/context-menu/action-sets/permission-edit-action-set.ts delete mode 100644 src/views-components/dialog-forms/edit-permission-level-dialog.tsx diff --git a/cypress/integration/group-manage.spec.js b/cypress/integration/group-manage.spec.js index 8f630033..690102c0 100644 --- a/cypress/integration/group-manage.spec.js +++ b/cypress/integration/group-manage.spec.js @@ -93,16 +93,9 @@ describe('Group manage tests', function() { cy.get('button').click(); }); }); - cy.get('[data-cy=form-dialog]') - .should('contain', 'Edit permission') - .within(() => { - cy.contains('Read').click(); - }); - cy.get('li span') + cy.get('[data-cy=context-menu]') .contains('Write') - .parents('li') .click(); - cy.get('[data-cy=form-dialog] button[type=submit]').click(); cy.get('[data-cy=group-members-data-explorer]') .contains('Other User') .parents('tr') diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx index 26ce4fea..f4a1b1f3 100644 --- a/src/components/icon/icon.tsx +++ b/src/components/icon/icon.tsx @@ -61,6 +61,8 @@ import StarBorder from '@material-ui/icons/StarBorder'; import Warning from '@material-ui/icons/Warning'; import VpnKey from '@material-ui/icons/VpnKey'; import LinkOutlined from '@material-ui/icons/LinkOutlined'; +import RemoveRedEye from '@material-ui/icons/RemoveRedEye'; +import Computer from '@material-ui/icons/Computer'; // Import FontAwesome icons import { library } from '@fortawesome/fontawesome-svg-core'; @@ -148,3 +150,6 @@ export const WorkflowIcon: IconType = (props) => ; export const WarningIcon: IconType = (props) => ; export const Link: IconType = (props) => ; export const FolderSharedIcon: IconType = (props) => ; +export const CanReadIcon: IconType = (props) => ; +export const CanWriteIcon: IconType = (props) => ; +export const CanManageIcon: IconType = (props) => ; diff --git a/src/index.tsx b/src/index.tsx index 6ad22a55..0b04c29e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -59,6 +59,7 @@ import { linkActionSet } from 'views-components/context-menu/action-sets/link-ac import { loadFileViewersConfig } from 'store/file-viewers/file-viewers-actions'; import { processResourceAdminActionSet } from 'views-components/context-menu/action-sets/process-resource-admin-action-set'; import { filterGroupAdminActionSet, projectAdminActionSet } from 'views-components/context-menu/action-sets/project-admin-action-set'; +import { permissionEditActionSet } from 'views-components/context-menu/action-sets/permission-edit-action-set'; import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions"; import { openNotFoundDialog } from './store/not-found-panel/not-found-panel-action'; import { storeRedirects } from './common/redirect-to'; @@ -99,6 +100,7 @@ addMenuActionSet(ContextMenuKind.COLLECTION_ADMIN, collectionAdminActionSet); addMenuActionSet(ContextMenuKind.PROCESS_ADMIN, processResourceAdminActionSet); addMenuActionSet(ContextMenuKind.PROJECT_ADMIN, projectAdminActionSet); addMenuActionSet(ContextMenuKind.FILTER_GROUP_ADMIN, filterGroupAdminActionSet); +addMenuActionSet(ContextMenuKind.PERMISSION_EDIT, permissionEditActionSet); storeRedirects(); diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts index 59a6813b..9a8733ba 100644 --- a/src/store/context-menu/context-menu-actions.ts +++ b/src/store/context-menu/context-menu-actions.ts @@ -20,6 +20,7 @@ import { ProcessResource } from 'models/process'; import { CollectionResource } from 'models/collection'; import { GroupClass, GroupResource } from 'models/group'; import { GroupContentsResource } from 'services/groups-service/groups-service'; +import { LinkResource } from 'models/link'; export const contextMenuActions = unionize({ OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(), @@ -193,6 +194,19 @@ export const openProcessContextMenu = (event: React.MouseEvent, pro } }; +export const openPermissionEditContextMenu = (event: React.MouseEvent, link: LinkResource) => + (dispatch: Dispatch, getState: () => RootState) => { + if (link) { + dispatch(openContextMenu(event, { + name: link.name, + uuid: link.uuid, + kind: link.kind, + menuKind: ContextMenuKind.PERMISSION_EDIT, + ownerUuid: link.ownerUuid, + })); + } + }; + export const resourceUuidToContextMenuKind = (uuid: string, readonly = false) => (dispatch: Dispatch, getState: () => RootState) => { const { isAdmin: isAdminUser, uuid: userUuid } = getState().auth.user!; diff --git a/src/store/group-details-panel/group-details-panel-actions.ts b/src/store/group-details-panel/group-details-panel-actions.ts index 43349fa9..916d68a7 100644 --- a/src/store/group-details-panel/group-details-panel-actions.ts +++ b/src/store/group-details-panel/group-details-panel-actions.ts @@ -8,16 +8,14 @@ import { propertiesActions } from 'store/properties/properties-actions'; import { getProperty } from 'store/properties/properties'; import { Participant } from 'views-components/sharing-dialog/participant-select'; import { dialogActions } from 'store/dialog/dialog-actions'; -import { initialize, reset, startSubmit } from 'redux-form'; +import { reset, startSubmit } from 'redux-form'; import { addGroupMember, deleteGroupMember } from 'store/groups-panel/groups-panel-actions'; import { getResource } from 'store/resources/resources'; import { GroupResource } from 'models/group'; -import { Resource } from 'models/resource'; import { RootState } from 'store/store'; import { ServiceRepository } from 'services/services'; import { PermissionResource, PermissionLevel } from 'models/permission'; import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions'; -import { PermissionSelectValue, parsePermissionLevel, formatPermissionLevel } from 'views-components/sharing-dialog/permission-select'; import { LinkResource } from 'models/link'; import { deleteResources } from 'store/resources/resources-actions'; @@ -28,10 +26,6 @@ export const ADD_GROUP_MEMBERS_FORM = 'addGroupMembers'; export const ADD_GROUP_MEMBERS_USERS_FIELD_NAME = 'users'; export const MEMBER_ATTRIBUTES_DIALOG = 'memberAttributesDialog'; export const MEMBER_REMOVE_DIALOG = 'memberRemoveDialog'; -export const EDIT_PERMISSION_LEVEL_DIALOG = 'editPermissionLevel'; -export const EDIT_PERMISSION_LEVEL_FORM = 'editPermissionLevel'; -export const EDIT_PERMISSION_LEVEL_FIELD_NAME = 'name'; -export const EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME = 'uuid'; export const GroupMembersPanelActions = bindDataExplorerActions(GROUP_DETAILS_MEMBERS_PANEL_ID); export const GroupPermissionsPanelActions = bindDataExplorerActions(GROUP_DETAILS_PERMISSIONS_PANEL_ID); @@ -50,11 +44,6 @@ export interface AddGroupMembersFormData { [ADD_GROUP_MEMBERS_USERS_FIELD_NAME]: Participant[]; } -export interface EditPermissionLevelFormData { - [EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME]: string; - [EDIT_PERMISSION_LEVEL_FIELD_NAME]: PermissionSelectValue; -} - export const openAddGroupMembersDialog = () => (dispatch: Dispatch) => { dispatch(dialogActions.OPEN_DIALOG({ id: ADD_GROUP_MEMBERS_DIALOG, data: {} })); @@ -93,23 +82,10 @@ export const addGroupMembers = ({ users }: AddGroupMembersFormData) => } }; -export const openEditPermissionLevelDialog = (linkUuid: string, resourceUuid: string) => - async (dispatch: Dispatch, getState: () => RootState) => { - const link = getResource(linkUuid)(getState().resources); - const resource = getResource(resourceUuid)(getState().resources); - - if (link) { - dispatch(reset(EDIT_PERMISSION_LEVEL_FORM)); - dispatch(initialize(EDIT_PERMISSION_LEVEL_FORM, {[EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME]: link.uuid, [EDIT_PERMISSION_LEVEL_FIELD_NAME]: formatPermissionLevel(link.name as PermissionLevel)})); - dispatch(dialogActions.OPEN_DIALOG({ id: EDIT_PERMISSION_LEVEL_DIALOG, data: resource })); - } - }; - -export const editPermissionLevel = (data: EditPermissionLevelFormData) => +export const editPermissionLevel = (uuid: string, level: PermissionLevel) => async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { try { - await permissionService.update(data[EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME], {name: parsePermissionLevel(data[EDIT_PERMISSION_LEVEL_FIELD_NAME])}); - dispatch(dialogActions.CLOSE_DIALOG({ id: EDIT_PERMISSION_LEVEL_DIALOG })); + await permissionService.update(uuid, {name: level}); dispatch(GroupMembersPanelActions.REQUEST_ITEMS()); dispatch(GroupPermissionsPanelActions.REQUEST_ITEMS()); dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Permission level changed.', hideDuration: 2000 })); diff --git a/src/views-components/context-menu/action-sets/permission-edit-action-set.ts b/src/views-components/context-menu/action-sets/permission-edit-action-set.ts new file mode 100644 index 00000000..8663d3c7 --- /dev/null +++ b/src/views-components/context-menu/action-sets/permission-edit-action-set.ts @@ -0,0 +1,28 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { ContextMenuActionSet } from "views-components/context-menu/context-menu-action-set"; +import { CanReadIcon, CanManageIcon, CanWriteIcon } from "components/icon/icon"; +import { editPermissionLevel } from 'store/group-details-panel/group-details-panel-actions'; +import { PermissionLevel } from "models/permission"; + +export const permissionEditActionSet: ContextMenuActionSet = [[{ + name: "Read", + icon: CanReadIcon, + execute: (dispatch, { uuid }) => { + dispatch(editPermissionLevel(uuid, PermissionLevel.CAN_READ)); + } +}, { + name: "Write", + icon: CanWriteIcon, + execute: (dispatch, { uuid }) => { + dispatch(editPermissionLevel(uuid, PermissionLevel.CAN_WRITE)); + } +}, { + name: "Manage", + icon: CanManageIcon, + execute: (dispatch, { uuid }) => { + dispatch(editPermissionLevel(uuid, PermissionLevel.CAN_MANAGE)); + } +}]]; diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx index 603ee90b..f2c43ced 100644 --- a/src/views-components/context-menu/context-menu.tsx +++ b/src/views-components/context-menu/context-menu.tsx @@ -98,5 +98,6 @@ export enum ContextMenuKind { USER = "User", GROUPS = "Group", GROUP_MEMBER = "GroupMember", + PERMISSION_EDIT = "PermissionEdit", LINK = "Link", } diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx index 44326a85..39893392 100644 --- a/src/views-components/data-explorer/renderers.tsx +++ b/src/views-components/data-explorer/renderers.tsx @@ -29,10 +29,12 @@ import { CollectionResource } from 'models/collection'; import { IllegalNamingWarning } from 'components/warning/warning'; import { loadResource } from 'store/resources/resources-actions'; import { GroupClass, GroupResource } from 'models/group'; -import { openRemoveGroupMemberDialog, openEditPermissionLevelDialog } from 'store/group-details-panel/group-details-panel-actions'; +import { openRemoveGroupMemberDialog } from 'store/group-details-panel/group-details-panel-actions'; import { setMemberIsHidden } from 'store/group-details-panel/group-details-panel-actions'; import { formatPermissionLevel } from 'views-components/sharing-dialog/permission-select'; import { PermissionLevel } from 'models/permission'; +import { openPermissionEditContextMenu } from 'store/context-menu/context-menu-actions'; +import { getUserUuid } from 'common/getuser'; const renderName = (dispatch: Dispatch, item: GroupContentsResource) => { @@ -357,14 +359,6 @@ const renderResourceLink = (dispatch: Dispatch, item: Resource) => { ; }; -const renderResource = (dispatch: Dispatch, item: Resource) => { - var displayName = getResourceDisplayName(item); - - return - {resourceLabel(item.kind)}: {displayName || item.uuid} - ; -}; - export const ResourceLinkTail = connect( (state: RootState, props: { uuid: string }) => { const resource = getResource(props.uuid)(state.resources); @@ -446,48 +440,52 @@ export const ResourceLinkTailUsername = connect( return resource || { username: '' }; })(renderUsername); -const renderPermissionLevel = (dispatch: Dispatch, link: LinkResource, resource: Resource) => { +const renderPermissionLevel = (dispatch: Dispatch, link: LinkResource, canManage: boolean) => { return {formatPermissionLevel(link.name as PermissionLevel)} - dispatch(openEditPermissionLevelDialog(link.uuid, resource.uuid))}> - - + {canManage ? + dispatch(openPermissionEditContextMenu(event, link))}> + + : + '' + } ; } export const ResourceLinkHeadPermissionLevel = connect( (state: RootState, props: { uuid: string }) => { const link = getResource(props.uuid)(state.resources); - const resource = getResource(link?.headUuid || '')(state.resources); return { link: link || { uuid: '', name: '', kind: ResourceKind.NONE }, - resource: resource || { uuid: '', kind: ResourceKind.NONE } + canManage: link && getResourceLinkCanManage(state, link), }; - })((props: { link: LinkResource, resource: Resource } & DispatchProp) => - renderPermissionLevel(props.dispatch, props.link, props.resource)); + })((props: { link: LinkResource, canManage: boolean } & DispatchProp) => + renderPermissionLevel(props.dispatch, props.link, props.canManage)); export const ResourceLinkTailPermissionLevel = connect( (state: RootState, props: { uuid: string }) => { const link = getResource(props.uuid)(state.resources); - const resource = getResource(link?.tailUuid || '')(state.resources); return { link: link || { uuid: '', name: '', kind: ResourceKind.NONE }, - resource: resource || { uuid: '', kind: ResourceKind.NONE } + canManage: link && getResourceLinkCanManage(state, link), }; - })((props: { link: LinkResource, resource: Resource } & DispatchProp) => - renderPermissionLevel(props.dispatch, props.link, props.resource)); + })((props: { link: LinkResource, canManage: boolean } & DispatchProp) => + renderPermissionLevel(props.dispatch, props.link, props.canManage)); -// Displays resource type and display name without link -export const ResourceLabel = connect( - (state: RootState, props: { uuid: string }) => { - const resource = getResource(props.uuid)(state.resources); - return { - item: resource || { uuid: '', kind: ResourceKind.NONE } - }; - })((props: { item: Resource } & DispatchProp) => - renderResource(props.dispatch, props.item)); +const getResourceLinkCanManage = (state: RootState, link: LinkResource) => { + const headResource = getResource(link.headUuid)(state.resources); + // const tailResource = getResource(link.tailUuid)(state.resources); + const userUuid = getUserUuid(state); + + if (headResource && headResource.kind === ResourceKind.GROUP) { + return userUuid ? (headResource as GroupResource).writableBy?.includes(userUuid) : false; + } else { + // true for now + return true; + } +} // Process Resources const resourceRunProcess = (dispatch: Dispatch, uuid: string) => { diff --git a/src/views-components/dialog-forms/edit-permission-level-dialog.tsx b/src/views-components/dialog-forms/edit-permission-level-dialog.tsx deleted file mode 100644 index 5479a0c6..00000000 --- a/src/views-components/dialog-forms/edit-permission-level-dialog.tsx +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) The Arvados Authors. All rights reserved. -// -// SPDX-License-Identifier: AGPL-3.0 - -import React from 'react'; -import { compose } from "redux"; -import { reduxForm, InjectedFormProps, WrappedFieldProps, Field } from 'redux-form'; -import { withDialog, WithDialogProps } from "store/dialog/with-dialog"; -import { FormDialog } from 'components/form-dialog/form-dialog'; -import { EDIT_PERMISSION_LEVEL_DIALOG, EDIT_PERMISSION_LEVEL_FORM, EditPermissionLevelFormData, EDIT_PERMISSION_LEVEL_FIELD_NAME, editPermissionLevel } from 'store/group-details-panel/group-details-panel-actions'; -import { require } from 'validators/require'; -import { PermissionSelect } from 'views-components/sharing-dialog/permission-select'; -import { Grid } from '@material-ui/core'; -import { Resource } from 'models/resource'; -import { ResourceLabel } from 'views-components/data-explorer/renderers'; - -export const EditPermissionLevelDialog = compose( - withDialog(EDIT_PERMISSION_LEVEL_DIALOG), - reduxForm({ - form: EDIT_PERMISSION_LEVEL_FORM, - onSubmit: (data, dispatch) => { - dispatch(editPermissionLevel(data)); - }, - }) -)( - (props: EditPermissionLevelDialogProps) => - -); - -interface EditPermissionLevelDataProps { - data: Resource; -} - -type EditPermissionLevelDialogProps = EditPermissionLevelDataProps & WithDialogProps<{}> & InjectedFormProps; - -const PermissionField = (props: EditPermissionLevelDialogProps) => - - - - - - - - ; - -const PermissionSelectComponent = ({ input }: WrappedFieldProps) => - ; diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx index 7c173b27..a44304c4 100644 --- a/src/views/group-details-panel/group-details-panel.tsx +++ b/src/views/group-details-panel/group-details-panel.tsx @@ -14,8 +14,6 @@ import { RootState } from 'store/store'; import { GROUP_DETAILS_MEMBERS_PANEL_ID, GROUP_DETAILS_PERMISSIONS_PANEL_ID, openAddGroupMembersDialog, getCurrentGroupDetailsPanelUuid } from 'store/group-details-panel/group-details-panel-actions'; import { openContextMenu } from 'store/context-menu/context-menu-actions'; import { ResourcesState, getResource } from 'store/resources/resources'; -import { ContextMenuKind } from 'views-components/context-menu/context-menu'; -import { PermissionResource } from 'models/permission'; import { Grid, Button, Tabs, Tab, Paper } from '@material-ui/core'; import { AddIcon } from 'components/icon/icon'; import { getUserUuid } from 'common/getuser'; @@ -215,19 +213,6 @@ export const GroupDetailsPanel = connect( ); } - handleContextMenu = (event: React.MouseEvent, resourceUuid: string) => { - const resource = getResource(resourceUuid)(this.props.resources); - if (resource) { - this.props.onContextMenu(event, { - name: '', - uuid: resource.uuid, - ownerUuid: resource.ownerUuid, - kind: resource.kind, - menuKind: ContextMenuKind.GROUP_MEMBER - }); - } - } - handleChange = (event: React.MouseEvent, value: number) => { this.setState({ value }); } diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 50194f9e..9ce93bf2 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -89,7 +89,6 @@ import { GroupDetailsPanel } from 'views/group-details-panel/group-details-panel import { RemoveGroupMemberDialog } from 'views-components/groups-dialog/member-remove-dialog'; import { GroupMemberAttributesDialog } from 'views-components/groups-dialog/member-attributes-dialog'; import { AddGroupMembersDialog } from 'views-components/dialog-forms/add-group-member-dialog'; -import { EditPermissionLevelDialog } from 'views-components/dialog-forms/edit-permission-level-dialog'; import { PartialCopyToCollectionDialog } from 'views-components/dialog-forms/partial-copy-to-collection-dialog'; import { PublicFavoritePanel } from 'views/public-favorites-panel/public-favorites-panel'; import { LinkAccountPanel } from 'views/link-account-panel/link-account-panel'; @@ -214,7 +213,6 @@ export const WorkbenchPanel = - -- 2.30.2