name: string;
groupClass: GroupClass | null;
description: string;
- writableBy: string[];
ensure_unique_name: boolean;
canWrite: boolean;
canManage: boolean;
"group_class",
"description",
"properties",
- "writable_by",
"can_write",
"can_manage",
"trash_at",
properties: "",
trashAt: "",
uuid: "",
- writableBy: [],
ensure_unique_name: true,
canWrite: false,
canManage: false,
prefs: UserPrefs;
isAdmin: boolean;
isActive: boolean;
+ canWrite: boolean;
+ canManage: boolean;
}
export const getUserFullname = (user: User) => {
export interface UserResource extends Resource, User {
kind: ResourceKind.USER;
defaultOwnerUuid: string;
- writableBy: string[];
}
is_active: boolean;
username: string;
prefs: UserPrefs;
+ can_write: boolean;
+ can_manage: boolean;
}
export class AuthService {
isAdmin: resp.data.is_admin,
isActive: resp.data.is_active,
username: resp.data.username,
+ canWrite: resp.data.can_write,
+ canManage: resp.data.can_manage,
prefs
};
})
constructor(serverApi: AxiosInstance, actions: ApiActions, readOnlyFields: string[] = []) {
super(serverApi, "users", actions, readOnlyFields.concat([
'fullName',
- 'isInvited',
- 'writableBy',
+ 'isInvited'
]));
}
const groupRequestApiResponse = (apiResponse: ProjectResource): JSX.Element => {
const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name,
- description, groupClass, trashAt, isTrashed, deleteAt, properties, writableBy,
+ description, groupClass, trashAt, isTrashed, deleteAt, properties,
canWrite, canManage } = apiResponse;
const response = `
"uuid": "${uuid}",
"delete_at": ${stringify(deleteAt)},
"properties": ${stringifyObject(properties)},
"can_write": ${stringify(canWrite)},
-"can_manage": ${stringify(canManage)},
-"writable_by": ${stringifyObject(writableBy)}`;
+"can_manage": ${stringify(canManage)}`;
return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
};
[projectUuid]: {
uuid: projectUuid,
ownerUuid: isEditable ? userUuid : otherUserUuid,
- writableBy: isEditable ? [userUuid] : [otherUserUuid],
+ canWrite: isEditable,
groupClass: GroupClass.PROJECT,
},
[filterGroupUuid]: {
uuid: filterGroupUuid,
ownerUuid: isEditable ? userUuid : otherUserUuid,
- writableBy: isEditable ? [userUuid] : [otherUserUuid],
+ canWrite: isEditable,
groupClass: GroupClass.FILTER,
},
[linkUuid]: {
modifiedAt: 'string',
href: 'string',
kind: ResourceKind.PROJECT,
- writableBy: [groupFixtures.user_uuid],
+ canWrite: true,
etag: 'string',
},
[groupFixtures.editable_collection_resource_uuid]: {
modifiedAt: 'string',
href: 'string',
kind: ResourceKind.PROJECT,
- writableBy: [groupFixtures.unknown_user_resource_uuid],
+ canWrite: false,
etag: 'string',
},
[groupFixtures.not_editable_collection_resource_uuid]: {
expect(result!.isEditable).toBeFalsy();
});
});
-});
\ No newline at end of file
+});
export type ResourcesState = { [key: string]: Resource };
-const getResourceWritableBy = (state: ResourcesState, id: string, userUuid: string): string[] => {
- if (!id) {
- return [];
- }
-
- if (id === userUuid) {
- return [userUuid];
- }
-
- const resource = (state[id] as ProjectResource);
-
- if (!resource) {
- return [];
- }
-
- const { writableBy } = resource;
-
- return writableBy || getResourceWritableBy(state, resource.ownerUuid, userUuid);
-};
-
export const getResourceWithEditableStatus = <T extends EditableResource & GroupResource>(id: string, userUuid?: string) =>
(state: ResourcesState): T | undefined => {
if (state[id] === undefined) { return; }
const resource = JSON.parse(JSON.stringify(state[id] as T));
if (resource) {
- resource.isEditable = userUuid ? getResourceWritableBy(state, id, userUuid).indexOf(userUuid) > -1 : false;
+ resource.isEditable = resource.canWrite;
}
return resource;
let owner = getResource<ProjectResource | UserResource>(getState().runProcessPanel.processOwnerUuid)(getState().resources);
const userUuid = getUserUuid(getState());
- if (!owner || !userUuid || owner.writableBy.indexOf(userUuid) === -1) {
+ if (!owner || !owner.canWrite) {
owner = undefined;
}
id: 'Favorites',
pickerId,
data: items.filter((item) => {
- if (options.showOnlyWritable && (item as GroupResource).writableBy && (item as GroupResource).writableBy.indexOf(uuid) === -1) {
+ if (options.showOnlyWritable && !(item as GroupResource).canWrite) {
return false;
}
// Must be writable.
const userUuid = getUserUuid(getState());
owner = getResource<ProjectResource | UserResource>(ownerUuid)(getState().resources);
- if (!owner || !userUuid || owner.writableBy.indexOf(userUuid) === -1) {
+ if (!owner || !owner.canWrite) {
owner = undefined;
}
}
};
-const FrozenProject = (props: {item: ProjectResource}) => {
+const FrozenProject = (props: { item: ProjectResource }) => {
const [fullUsername, setFullusername] = React.useState<any>(null);
const getFullName = React.useCallback(() => {
if (props.item.frozenByUuid) {
if (props.item.frozenByUuid) {
return <Tooltip onOpen={getFullName} enterDelay={500} title={<span>Project was frozen by {fullUsername}</span>}>
- <FreezeIcon style={{ fontSize: "inherit" }}/>
+ <FreezeIcon style={{ fontSize: "inherit" }} />
</Tooltip>;
} else {
return null;
return resource;
})((resource: GroupContentsResource & DispatchProp<any>) => renderName(resource.dispatch, resource));
-
+
const renderIcon = (item: GroupContentsResource) => {
switch (item.kind) {
case ResourceKind.PROJECT:
const renderUuid = (item: { uuid: string }) =>
<Typography data-cy="uuid" noWrap>
{item.uuid}
- {(item.uuid && <CopyToClipboardSnackbar value={item.uuid} />) || '-' }
+ {(item.uuid && <CopyToClipboardSnackbar value={item.uuid} />) || '-'}
</Typography>;
const renderUuidCopyIcon = (item: { uuid: string }) =>
<Typography data-cy="uuid" noWrap>
- {(item.uuid && <CopyToClipboardSnackbar value={item.uuid} />) || '-' }
+ {(item.uuid && <CopyToClipboardSnackbar value={item.uuid} />) || '-'}
</Typography>;
export const ResourceUuid = connect((state: RootState, props: { uuid: string }) => (
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;
+ return (headResource as GroupResource).canManage;
} else {
// true for now
return true;
const selectedColumnUuid = item[column]
return <Grid container alignItems="center" wrap="nowrap" >
<Grid item>
- {selectedColumnUuid ?
- <Typography color="primary" style={{ width: 'auto', cursor: 'pointer' }} noWrap
+ {selectedColumnUuid ?
+ <Typography color="primary" style={{ width: 'auto', cursor: 'pointer' }} noWrap
onClick={() => dispatch<any>(navigateTo(selectedColumnUuid))}>
- {selectedColumnUuid}
- </Typography>
- : '-' }
+ {selectedColumnUuid}
+ </Typography>
+ : '-'}
</Grid>
<Grid item>
{selectedColumnUuid && renderUuidCopyIcon({ uuid: selectedColumnUuid })}
(state: RootState, props: { uuid: string }) => {
const process = getProcess(props.uuid)(state.resources)
return { parentProcess: process?.containerRequest?.requestingContainerUuid || '' };
- })((props: { parentProcess: string }) => renderUuid({uuid: props.parentProcess}));
+ })((props: { parentProcess: string }) => renderUuid({ uuid: props.parentProcess }));
export const ResourceModifiedByUserUuid = connect(
(state: RootState, props: { uuid: string }) => {
const process = getProcess(props.uuid)(state.resources)
return { userUuid: process?.containerRequest?.modifiedByUserUuid || '' };
- })((props: { userUuid: string }) => renderUuid({uuid: props.userUuid}));
+ })((props: { userUuid: string }) => renderUuid({ uuid: props.userUuid }));
- export const ResourceCreatedAtDate = connect(
+export const ResourceCreatedAtDate = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<GroupContentsResource>(props.uuid)(state.resources);
return { date: resource ? resource.createdAt : '' };
})((props: { date: string }) => renderDate(props.date));
-
+
export const ResourceLastModifiedDate = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<GroupContentsResource>(props.uuid)(state.resources);
(state: RootState, props: { uuid: string }) => {
const resource = getResource<CollectionResource>(props.uuid)(state.resources);
return { uuid: resource ? resource.uuid : '' };
- })((props: { uuid: string }) => renderUuid({uuid: props.uuid}));
+ })((props: { uuid: string }) => renderUuid({ uuid: props.uuid }));
-const renderVersion = (version: number) =>{
+const renderVersion = (version: number) => {
return <Typography>{version ?? '-'}</Typography>
}
export const ResourceVersion = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<CollectionResource>(props.uuid)(state.resources);
- return { version: resource ? resource.version: '' };
+ return { version: resource ? resource.version : '' };
})((props: { version: number }) => renderVersion(props.version));
-
-const renderPortableDataHash = (portableDataHash:string | null) =>
+
+const renderPortableDataHash = (portableDataHash: string | null) =>
<Typography noWrap>
{portableDataHash ? <>{portableDataHash}
- <CopyToClipboardSnackbar value={portableDataHash} /></> : '-' }
+ <CopyToClipboardSnackbar value={portableDataHash} /></> : '-'}
</Typography>
-
+
export const ResourcePortableDataHash = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<CollectionResource>(props.uuid)(state.resources);
- return { portableDataHash: resource ? resource.portableDataHash : '' };
+ return { portableDataHash: resource ? resource.portableDataHash : '' };
})((props: { portableDataHash: string }) => renderPortableDataHash(props.portableDataHash));
-const renderFileCount = (fileCount: number) =>{
+const renderFileCount = (fileCount: number) => {
return <Typography>{fileCount ?? '-'}</Typography>
}
export const ResourceFileCount = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<CollectionResource>(props.uuid)(state.resources);
- return { fileCount: resource ? resource.fileCount: '' };
+ return { fileCount: resource ? resource.fileCount : '' };
})((props: { fileCount: number }) => renderFileCount(props.fileCount));
const userFromID =
export const CollectionName = connect((state: RootState, props: { uuid: string, className?: string }) => {
return {
- collection: getResource<CollectionResource>(props.uuid)(state.resources),
- uuid: props.uuid,
- className: props.className,
- };
+ collection: getResource<CollectionResource>(props.uuid)(state.resources),
+ uuid: props.uuid,
+ className: props.className,
+ };
})((props: { collection: CollectionResource, uuid: string, className?: string }) =>
- <Typography className={props.className}>{props.collection?.name || props.uuid}</Typography>
+ <Typography className={props.className}>{props.collection?.name || props.uuid}</Typography>
);
export const ProcessStatus = compose(
}}
/>
: <Typography>-</Typography>
- );
+ );
export const ProcessStartDate = connect(
(state: RootState, props: { uuid: string }) => {
enabled = true;
} else if (matchProjectRoute(location ? location.pathname : '')) {
const currentProject = getResource<ProjectResource>(currentItemId)(resources);
- if (currentProject && currentProject.writableBy &&
- currentProject.writableBy.indexOf(currentUserUUID || '') >= 0 &&
+ if (currentProject && currentProject.canWrite &&
!currentProject.frozenByUuid &&
!isProjectTrashed(currentProject, resources) &&
currentProject.groupClass !== GroupClass.FILTER) {
isWritable = true;
} else {
const itemOwner = getResource<GroupResource | UserResource>(item.ownerUuid)(state.resources);
- if (itemOwner && itemOwner.writableBy) {
- isWritable = itemOwner.writableBy.indexOf(currentUserUUID || '') >= 0;
+ if (itemOwner) {
+ isWritable = itemOwner.canWrite;
}
}
}
{/*
NOTE: The property list should be kept at the bottom, because it spans
the entire available width, without regards of the twoCol prop.
- */}
+ */}
<Grid item xs={12} md={12}>
<DetailsAttribute classLabel={classes.label} classValue={classes.value}
label='Properties' />
return {
resources: state.resources,
groupCanManage: userUuid && !isBuiltinGroup(group?.uuid || '')
- ? group?.writableBy?.includes(userUuid)
- : false,
+ ? group?.canManage
+ : false,
};
};
)(
class GroupDetailsPanel extends React.Component<GroupDetailsPanelProps & WithStyles<CssRules>> {
state = {
- value: 0,
+ value: 0,
};
componentDidMount() {
const { value } = this.state;
return (
<Paper className={this.props.classes.root}>
- <Tabs value={value} onChange={this.handleChange} variant="fullWidth">
- <Tab data-cy="group-details-members-tab" label="MEMBERS" />
- <Tab data-cy="group-details-permissions-tab" label="PERMISSIONS" />
- </Tabs>
- <div className={this.props.classes.content}>
- {value === 0 &&
- <DataExplorer
- id={GROUP_DETAILS_MEMBERS_PANEL_ID}
- data-cy="group-members-data-explorer"
- onRowClick={noop}
- onRowDoubleClick={noop}
- onContextMenu={noop}
- contextMenuColumn={false}
- defaultViewIcon={UserPanelIcon}
- defaultViewMessages={[MEMBERS_DEFAULT_MESSAGE]}
- hideColumnSelector
- hideSearchInput
- actions={
+ <Tabs value={value} onChange={this.handleChange} variant="fullWidth">
+ <Tab data-cy="group-details-members-tab" label="MEMBERS" />
+ <Tab data-cy="group-details-permissions-tab" label="PERMISSIONS" />
+ </Tabs>
+ <div className={this.props.classes.content}>
+ {value === 0 &&
+ <DataExplorer
+ id={GROUP_DETAILS_MEMBERS_PANEL_ID}
+ data-cy="group-members-data-explorer"
+ onRowClick={noop}
+ onRowDoubleClick={noop}
+ onContextMenu={noop}
+ contextMenuColumn={false}
+ defaultViewIcon={UserPanelIcon}
+ defaultViewMessages={[MEMBERS_DEFAULT_MESSAGE]}
+ hideColumnSelector
+ hideSearchInput
+ actions={
this.props.groupCanManage &&
<Grid container justify='flex-end'>
<Button
- data-cy="group-member-add"
- variant="contained"
- color="primary"
- onClick={this.props.onAddUser}>
- <AddIcon /> Add user
+ data-cy="group-member-add"
+ variant="contained"
+ color="primary"
+ onClick={this.props.onAddUser}>
+ <AddIcon /> Add user
</Button>
</Grid>
- }
- paperProps={{
- elevation: 0,
- }} />
- }
- {value === 1 &&
- <DataExplorer
- id={GROUP_DETAILS_PERMISSIONS_PANEL_ID}
- data-cy="group-permissions-data-explorer"
- onRowClick={noop}
- onRowDoubleClick={noop}
- onContextMenu={noop}
- contextMenuColumn={false}
- defaultViewIcon={KeyIcon}
- defaultViewMessages={[PERMISSIONS_DEFAULT_MESSAGE]}
- hideColumnSelector
- hideSearchInput
- paperProps={{
- elevation: 0,
- }} />
- }
- </div>
+ }
+ paperProps={{
+ elevation: 0,
+ }} />
+ }
+ {value === 1 &&
+ <DataExplorer
+ id={GROUP_DETAILS_PERMISSIONS_PANEL_ID}
+ data-cy="group-permissions-data-explorer"
+ onRowClick={noop}
+ onRowDoubleClick={noop}
+ onContextMenu={noop}
+ contextMenuColumn={false}
+ defaultViewIcon={KeyIcon}
+ defaultViewMessages={[PERMISSIONS_DEFAULT_MESSAGE]}
+ hideColumnSelector
+ hideSearchInput
+ paperProps={{
+ elevation: 0,
+ }} />
+ }
+ </div>
</Paper>
);
}
}
}
- invalid = () => (!this.state.project || this.state.project.writableBy.indexOf(this.props.userUuid) === -1);
+ invalid = () => (!this.state.project || !this.state.project.canWrite);
renderInput() {
return <GenericInput