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')
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';
export const WarningIcon: IconType = (props) => <Warning style={{ color: '#fbc02d', height: '30px', width: '30px' }} {...props} />;
export const Link: IconType = (props) => <LinkOutlined {...props} />;
export const FolderSharedIcon: IconType = (props) => <FolderShared {...props} />;
+export const CanReadIcon: IconType = (props) => <RemoveRedEye {...props} />;
+export const CanWriteIcon: IconType = (props) => <Edit {...props} />;
+export const CanManageIcon: IconType = (props) => <Computer {...props} />;
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';
addMenuActionSet(ContextMenuKind.PROCESS_ADMIN, processResourceAdminActionSet);
addMenuActionSet(ContextMenuKind.PROJECT_ADMIN, projectAdminActionSet);
addMenuActionSet(ContextMenuKind.FILTER_GROUP_ADMIN, filterGroupAdminActionSet);
+addMenuActionSet(ContextMenuKind.PERMISSION_EDIT, permissionEditActionSet);
storeRedirects();
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 }>(),
}
};
+export const openPermissionEditContextMenu = (event: React.MouseEvent<HTMLElement>, link: LinkResource) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ if (link) {
+ dispatch<any>(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!;
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';
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);
[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: {} }));
}
};
-export const openEditPermissionLevelDialog = (linkUuid: string, resourceUuid: string) =>
- async (dispatch: Dispatch, getState: () => RootState) => {
- const link = getResource<PermissionResource>(linkUuid)(getState().resources);
- const resource = getResource<Resource>(resourceUuid)(getState().resources);
-
- if (link) {
- dispatch(reset(EDIT_PERMISSION_LEVEL_FORM));
- dispatch<any>(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 }));
--- /dev/null
+// 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<any>(editPermissionLevel(uuid, PermissionLevel.CAN_READ));
+ }
+}, {
+ name: "Write",
+ icon: CanWriteIcon,
+ execute: (dispatch, { uuid }) => {
+ dispatch<any>(editPermissionLevel(uuid, PermissionLevel.CAN_WRITE));
+ }
+}, {
+ name: "Manage",
+ icon: CanManageIcon,
+ execute: (dispatch, { uuid }) => {
+ dispatch<any>(editPermissionLevel(uuid, PermissionLevel.CAN_MANAGE));
+ }
+}]];
USER = "User",
GROUPS = "Group",
GROUP_MEMBER = "GroupMember",
+ PERMISSION_EDIT = "PermissionEdit",
LINK = "Link",
}
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) => {
</Typography>;
};
-const renderResource = (dispatch: Dispatch, item: Resource) => {
- var displayName = getResourceDisplayName(item);
-
- return <Typography variant='body2'>
- {resourceLabel(item.kind)}: {displayName || item.uuid}
- </Typography>;
-};
-
export const ResourceLinkTail = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<LinkResource>(props.uuid)(state.resources);
return resource || { username: '' };
})(renderUsername);
-const renderPermissionLevel = (dispatch: Dispatch, link: LinkResource, resource: Resource) => {
+const renderPermissionLevel = (dispatch: Dispatch, link: LinkResource, canManage: boolean) => {
return <Typography noWrap>
{formatPermissionLevel(link.name as PermissionLevel)}
- <IconButton onClick={() => dispatch<any>(openEditPermissionLevelDialog(link.uuid, resource.uuid))}>
- <RenameIcon />
- </IconButton>
+ {canManage ?
+ <IconButton onClick={(event) => dispatch<any>(openPermissionEditContextMenu(event, link))}>
+ <RenameIcon />
+ </IconButton> :
+ ''
+ }
</Typography>;
}
export const ResourceLinkHeadPermissionLevel = connect(
(state: RootState, props: { uuid: string }) => {
const link = getResource<LinkResource>(props.uuid)(state.resources);
- const resource = getResource<Resource>(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<any>) =>
- renderPermissionLevel(props.dispatch, props.link, props.resource));
+ })((props: { link: LinkResource, canManage: boolean } & DispatchProp<any>) =>
+ renderPermissionLevel(props.dispatch, props.link, props.canManage));
export const ResourceLinkTailPermissionLevel = connect(
(state: RootState, props: { uuid: string }) => {
const link = getResource<LinkResource>(props.uuid)(state.resources);
- const resource = getResource<Resource>(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<any>) =>
- renderPermissionLevel(props.dispatch, props.link, props.resource));
+ })((props: { link: LinkResource, canManage: boolean } & DispatchProp<any>) =>
+ 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<Resource>(props.uuid)(state.resources);
- return {
- item: resource || { uuid: '', kind: ResourceKind.NONE }
- };
- })((props: { item: Resource } & DispatchProp<any>) =>
- renderResource(props.dispatch, props.item));
+const getResourceLinkCanManage = (state: RootState, link: LinkResource) => {
+ const headResource = getResource<Resource>(link.headUuid)(state.resources);
+ // const tailResource = getResource<Resource>(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) => {
+++ /dev/null
-// 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<EditPermissionLevelFormData>({
- form: EDIT_PERMISSION_LEVEL_FORM,
- onSubmit: (data, dispatch) => {
- dispatch(editPermissionLevel(data));
- },
- })
-)(
- (props: EditPermissionLevelDialogProps) =>
- <FormDialog
- dialogTitle='Edit permission'
- formFields={PermissionField}
- submitLabel='Update'
- {...props}
- />
-);
-
-interface EditPermissionLevelDataProps {
- data: Resource;
-}
-
-type EditPermissionLevelDialogProps = EditPermissionLevelDataProps & WithDialogProps<{}> & InjectedFormProps<EditPermissionLevelFormData>;
-
-const PermissionField = (props: EditPermissionLevelDialogProps) =>
- <Grid container spacing={8}>
- <Grid item xs={8}>
- <ResourceLabel uuid={props.data.uuid} />
- </Grid>
- <Grid item xs={4} container wrap='nowrap'>
- <Field
- name={EDIT_PERMISSION_LEVEL_FIELD_NAME}
- component={PermissionSelectComponent as any}
- validate={require} />
- </Grid>
- </Grid>;
-
-const PermissionSelectComponent = ({ input }: WrappedFieldProps) =>
- <PermissionSelect fullWidth disableUnderline {...input} />;
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';
);
}
- handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
- const resource = getResource<PermissionResource>(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<HTMLElement>, value: number) => {
this.setState({ value });
}
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';
<DetailsPanel />
</Grid>
<AddGroupMembersDialog />
- <EditPermissionLevelDialog />
<AdvancedTabDialog />
<AttributesApiClientAuthorizationDialog />
<AttributesKeepServiceDialog />