[arvados-workbench2] updated: 2.4.0-57-g6fbb4eca

git repository hosting git at public.arvados.org
Mon May 23 18:45:52 UTC 2022


Summary of changes:
 src/store/sharing-dialog/sharing-dialog-actions.ts | 69 +++++++++++++++++++---
 src/store/sharing-dialog/sharing-dialog-types.ts   | 16 +++++
 .../sharing-dialog/sharing-dialog-component.tsx    | 10 +++-
 .../sharing-management-form-component.tsx          |  9 +--
 .../sharing-public-access-form-component.tsx       | 54 +++++++++++++++++
 .../sharing-dialog/sharing-public-access-form.tsx  | 24 ++++++++
 .../sharing-dialog/visibility-level-select.tsx     | 55 +++++++++++++++++
 7 files changed, 220 insertions(+), 17 deletions(-)
 create mode 100644 src/views-components/sharing-dialog/sharing-public-access-form-component.tsx
 create mode 100644 src/views-components/sharing-dialog/sharing-public-access-form.tsx
 create mode 100644 src/views-components/sharing-dialog/visibility-level-select.tsx

       via  6fbb4eca48ea5907887e61d60b60987cf2d1c8ca (commit)
      from  0bd774be1714eb7bb815cef3d44803eac6c735ec (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 6fbb4eca48ea5907887e61d60b60987cf2d1c8ca
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Mon May 23 15:43:58 2022 -0300

    16115: Brings back the Visibility Level form.
    
    To avoid confusing users, this form is back and it's rendered as the first
    row of the permissions list instead of being hidden inside an 'Advanced' mode.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/store/sharing-dialog/sharing-dialog-actions.ts b/src/store/sharing-dialog/sharing-dialog-actions.ts
index ffd81fb7..367eea81 100644
--- a/src/store/sharing-dialog/sharing-dialog-actions.ts
+++ b/src/store/sharing-dialog/sharing-dialog-actions.ts
@@ -10,6 +10,9 @@ import {
     SharingManagementFormData,
     SharingInvitationFormData,
     getSharingMangementFormData,
+    SharingPublicAccessFormData,
+    VisibilityLevel,
+    SHARING_PUBLIC_ACCESS_FORM_NAME,
 } from './sharing-dialog-types';
 import { Dispatch } from 'redux';
 import { ServiceRepository } from "services/services";
@@ -18,7 +21,7 @@ import { initialize, getFormValues, reset } from 'redux-form';
 import { SHARING_MANAGEMENT_FORM_NAME } from 'store/sharing-dialog/sharing-dialog-types';
 import { RootState } from 'store/store';
 import { getDialog } from 'store/dialog/dialog-reducer';
-import { PermissionLevel } from 'models/permission';
+import { PermissionLevel, PermissionResource } from 'models/permission';
 import { differenceWith } from "lodash";
 import { withProgress } from "store/progress-indicator/with-progress";
 import { progressIndicatorActions } from 'store/progress-indicator/progress-indicator-actions';
@@ -28,6 +31,8 @@ import {
     ResourceObjectType
 } from "models/resource";
 import { resourcesActions } from "store/resources/resources-actions";
+import { getPublicGroupUuid } from "store/workflow-panel/workflow-panel-actions";
+import { getSharingPublicAccessFormData } from './sharing-dialog-types';
 
 export const openSharingDialog = (resourceUuid: string, refresh?: () => void) =>
     (dispatch: Dispatch) => {
@@ -44,6 +49,7 @@ export const connectSharingDialogProgress = withProgress(SHARING_DIALOG_NAME);
 
 export const saveSharingDialogChanges = async (dispatch: Dispatch, getState: () => RootState) => {
     dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME));
+    await dispatch<any>(savePublicPermissionChanges);
     await dispatch<any>(saveManagementChanges);
     await dispatch<any>(sendInvitations);
     dispatch(reset(SHARING_INVITATION_FORM_NAME));
@@ -109,7 +115,7 @@ export const deleteSharingToken = (uuid: string) => async (dispatch: Dispatch, g
     }
 };
 
-const loadSharingDialog = async (dispatch: Dispatch, getState: () => RootState, { permissionService, apiClientAuthorizationService }: ServiceRepository) => {
+const loadSharingDialog = async (dispatch: Dispatch, getState: () => RootState, { apiClientAuthorizationService }: ServiceRepository) => {
 
     const dialog = getDialog<SharingDialogData>(getState().dialog, SHARING_DIALOG_NAME);
     if (dialog) {
@@ -143,6 +149,7 @@ export const initializeManagementForm = async (dispatch: Dispatch, getState: ()
         dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME));
         const resourceUuid = dialog?.data.resourceUuid;
         const { items: permissionLinks } = await permissionService.listResourcePermissions(resourceUuid);
+        dispatch<any>(initializePublicAccessForm(permissionLinks));
         const filters = new FilterBuilder()
             .addIn('uuid', permissionLinks.map(({ tailUuid }) => tailUuid))
             .getFilters();
@@ -161,6 +168,8 @@ export const initializeManagementForm = async (dispatch: Dispatch, getState: ()
         };
 
         const managementPermissions = permissionLinks
+            .filter(item =>
+                item.tailUuid !== getPublicGroupUuid(getState()))
             .map(({ tailUuid, name, uuid }) => ({
                 email: getEmail(tailUuid),
                 permissions: name as PermissionLevel,
@@ -176,17 +185,63 @@ export const initializeManagementForm = async (dispatch: Dispatch, getState: ()
         dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME));
     };
 
+const initializePublicAccessForm = (permissionLinks: PermissionResource[]) =>
+    (dispatch: Dispatch, getState: () => RootState, ) => {
+        const [publicPermission] = permissionLinks
+            .filter(item => item.tailUuid === getPublicGroupUuid(getState()));
+        const publicAccessFormData: SharingPublicAccessFormData = publicPermission
+            ? {
+                visibility: VisibilityLevel.PUBLIC,
+                permissionUuid: publicPermission.uuid,
+            }
+            : {
+                visibility: permissionLinks.length > 0
+                    ? VisibilityLevel.SHARED
+                    : VisibilityLevel.PRIVATE,
+                permissionUuid: '',
+            };
+        dispatch(initialize(SHARING_PUBLIC_ACCESS_FORM_NAME, publicAccessFormData));
+    };
+
+const savePublicPermissionChanges = async (_: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => {
+    const state = getState();
+    const { user } = state.auth;
+    const dialog = getDialog<SharingDialogData>(state.dialog, SHARING_DIALOG_NAME);
+    if (dialog && user) {
+        const { permissionUuid, visibility } = getSharingPublicAccessFormData(state);
+        if (permissionUuid) {
+            if (visibility === VisibilityLevel.PUBLIC) {
+                await permissionService.update(permissionUuid, {
+                    name: PermissionLevel.CAN_READ
+                });
+            } else {
+                await permissionService.delete(permissionUuid);
+            }
+        } else if (visibility === VisibilityLevel.PUBLIC) {
+            await permissionService.create({
+                ownerUuid: user.uuid,
+                headUuid: dialog.data.resourceUuid,
+                tailUuid: getPublicGroupUuid(state),
+                name: PermissionLevel.CAN_READ,
+            });
+        }
+    }
+};
+
 const saveManagementChanges = async (_: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => {
     const state = getState();
     const { user } = state.auth;
     const dialog = getDialog<string>(state.dialog, SHARING_DIALOG_NAME);
     if (dialog && user) {
         const { initialPermissions, permissions } = getSharingMangementFormData(state);
-        const cancelledPermissions = differenceWith(
-            initialPermissions,
-            permissions,
-            (a, b) => a.permissionUuid === b.permissionUuid
-        );
+        const { visibility } = getSharingPublicAccessFormData(state);
+        const cancelledPermissions = visibility === VisibilityLevel.PRIVATE
+            ? initialPermissions
+            : differenceWith(
+                initialPermissions,
+                permissions,
+                (a, b) => a.permissionUuid === b.permissionUuid
+            );
 
         const deletions = cancelledPermissions.map(({ permissionUuid }) =>
             permissionService.delete(permissionUuid));
diff --git a/src/store/sharing-dialog/sharing-dialog-types.ts b/src/store/sharing-dialog/sharing-dialog-types.ts
index 7ca8b5c5..a05224e2 100644
--- a/src/store/sharing-dialog/sharing-dialog-types.ts
+++ b/src/store/sharing-dialog/sharing-dialog-types.ts
@@ -7,9 +7,21 @@ import { getFormValues, isDirty } from 'redux-form';
 import { RootState } from 'store/store';
 
 export const SHARING_DIALOG_NAME = 'SHARING_DIALOG_NAME';
+export const SHARING_PUBLIC_ACCESS_FORM_NAME = 'SHARING_PUBLIC_ACCESS_FORM_NAME';
 export const SHARING_MANAGEMENT_FORM_NAME = 'SHARING_MANAGEMENT_FORM_NAME';
 export const SHARING_INVITATION_FORM_NAME = 'SHARING_INVITATION_FORM_NAME';
 
+export enum VisibilityLevel {
+    PRIVATE = 'Private',
+    SHARED = 'Shared',
+    PUBLIC = 'Public',
+}
+
+export interface SharingPublicAccessFormData {
+    visibility: VisibilityLevel;
+    permissionUuid: string;
+}
+
 export interface SharingManagementFormData {
     permissions: SharingManagementFormDataRow[];
     initialPermissions: SharingManagementFormDataRow[];
@@ -35,6 +47,10 @@ export interface SharingInvitationFormPersonData {
 export const getSharingMangementFormData = (state: any) =>
     getFormValues(SHARING_MANAGEMENT_FORM_NAME)(state) as SharingManagementFormData;
 
+export const getSharingPublicAccessFormData = (state: any) =>
+    getFormValues(SHARING_PUBLIC_ACCESS_FORM_NAME)(state) as SharingPublicAccessFormData;
+
 export const hasChanges = (state: RootState) =>
+    isDirty(SHARING_PUBLIC_ACCESS_FORM_NAME)(state) ||
     isDirty(SHARING_MANAGEMENT_FORM_NAME)(state) ||
     isDirty(SHARING_INVITATION_FORM_NAME)(state);
diff --git a/src/views-components/sharing-dialog/sharing-dialog-component.tsx b/src/views-components/sharing-dialog/sharing-dialog-component.tsx
index 0fa0056c..cd7ea9bb 100644
--- a/src/views-components/sharing-dialog/sharing-dialog-component.tsx
+++ b/src/views-components/sharing-dialog/sharing-dialog-component.tsx
@@ -38,6 +38,7 @@ import {
 } from 'material-ui-pickers';
 import DateFnsUtils from "@date-io/date-fns";
 import moment from 'moment';
+import { SharingPublicAccessForm } from './sharing-public-access-form';
 
 export interface SharingDialogDataProps {
     open: boolean;
@@ -101,9 +102,12 @@ export default (props: SharingDialogDataProps & SharingDialogActionProps) => {
         <DialogContent>
             { tabNr === SharingDialogTab.PERMISSIONS &&
             <Grid container direction='column' spacing={24}>
-              <Grid item>
-                  <SharingManagementForm />
-              </Grid>
+                <Grid item>
+                    <SharingPublicAccessForm />
+                </Grid>
+                <Grid item>
+                    <SharingManagementForm />
+                </Grid>
             </Grid>
             }
             { tabNr === SharingDialogTab.URLS &&
diff --git a/src/views-components/sharing-dialog/sharing-management-form-component.tsx b/src/views-components/sharing-dialog/sharing-management-form-component.tsx
index 2ebf8c2d..d4d10952 100644
--- a/src/views-components/sharing-dialog/sharing-management-form-component.tsx
+++ b/src/views-components/sharing-dialog/sharing-management-form-component.tsx
@@ -21,13 +21,8 @@ export default () =>
     <FieldArray name='permissions' component={SharingManagementFieldArray as any} />;
 
 const SharingManagementFieldArray = ({ fields }: WrappedFieldArrayProps<{ email: string }>) =>
-    <div>
-        {
-        fields.length > 0
-        ? fields.map((field, index, fields) =>
-                <PermissionManagementRow key={field} {...{ field, index, fields }} />)
-        : <Typography>No permissions set</Typography>
-        }
+    <div>{ fields.map((field, index, fields) =>
+        <PermissionManagementRow key={field} {...{ field, index, fields }} />) }
         <Divider />
     </div>;
 
diff --git a/src/views-components/sharing-dialog/sharing-public-access-form-component.tsx b/src/views-components/sharing-dialog/sharing-public-access-form-component.tsx
new file mode 100644
index 00000000..7ec71161
--- /dev/null
+++ b/src/views-components/sharing-dialog/sharing-public-access-form-component.tsx
@@ -0,0 +1,54 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from 'react';
+import { Grid, StyleRulesCallback, Divider, Typography } from '@material-ui/core';
+import { Field, WrappedFieldProps } from 'redux-form';
+import { WithStyles } from '@material-ui/core/styles';
+import withStyles from '@material-ui/core/styles/withStyles';
+import { VisibilityLevelSelect } from './visibility-level-select';
+import { VisibilityLevel } from 'store/sharing-dialog/sharing-dialog-types';
+
+const sharingPublicAccessStyles: StyleRulesCallback<'root'> = theme => ({
+    root: {
+        padding: `${theme.spacing.unit * 2}px 0`,
+    }
+});
+
+const SharingPublicAccessForm = withStyles(sharingPublicAccessStyles)(
+    ({ classes, visibility }: WithStyles<'root'> & { visibility: VisibilityLevel }) =>
+        <>
+            <Divider />
+            <Grid container alignItems='center' spacing={8} className={classes.root}>
+                <Grid item xs={8}>
+                    <Typography variant='subtitle1'>
+                        {renderVisibilityInfo(visibility)}
+                    </Typography>
+                </Grid>
+                <Grid item xs={4} container wrap='nowrap'>
+                    <Field name='visibility' component={VisibilityLevelSelectComponent} />
+                </Grid>
+            </Grid>
+        </>
+);
+
+const renderVisibilityInfo = (visibility: VisibilityLevel) => {
+    switch (visibility) {
+        case VisibilityLevel.PUBLIC:
+            return 'Anyone can access';
+        case VisibilityLevel.SHARED:
+            return 'Specific people can access';
+        case VisibilityLevel.PRIVATE:
+            return 'Only you can access';
+        default:
+            return '';
+    }
+};
+
+export default ({ visibility }: { visibility: VisibilityLevel }) =>
+    <SharingPublicAccessForm {...{ visibility }} />;
+
+const VisibilityLevelSelectComponent = ({ input }: WrappedFieldProps) =>
+    <VisibilityLevelSelect fullWidth disableUnderline {...input} />;
+
diff --git a/src/views-components/sharing-dialog/sharing-public-access-form.tsx b/src/views-components/sharing-dialog/sharing-public-access-form.tsx
new file mode 100644
index 00000000..8ee1d94d
--- /dev/null
+++ b/src/views-components/sharing-dialog/sharing-public-access-form.tsx
@@ -0,0 +1,24 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { reduxForm } from 'redux-form';
+import { compose } from 'redux';
+import { connect } from 'react-redux';
+import SharingPublicAccessFormComponent from './sharing-public-access-form-component';
+import { SHARING_PUBLIC_ACCESS_FORM_NAME, VisibilityLevel } from 'store/sharing-dialog/sharing-dialog-types';
+import { RootState } from 'store/store';
+import { getSharingPublicAccessFormData } from '../../store/sharing-dialog/sharing-dialog-types';
+
+export const SharingPublicAccessForm = compose(
+    reduxForm(
+        { form: SHARING_PUBLIC_ACCESS_FORM_NAME }
+    ),
+    connect(
+        (state: RootState) => {
+            const { visibility } = getSharingPublicAccessFormData(state) || { visibility: VisibilityLevel.PRIVATE };
+            return { visibility };
+        }
+    )
+)(SharingPublicAccessFormComponent);
+
diff --git a/src/views-components/sharing-dialog/visibility-level-select.tsx b/src/views-components/sharing-dialog/visibility-level-select.tsx
new file mode 100644
index 00000000..434b8f51
--- /dev/null
+++ b/src/views-components/sharing-dialog/visibility-level-select.tsx
@@ -0,0 +1,55 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from 'react';
+import { MenuItem, Select, withStyles, StyleRulesCallback } from '@material-ui/core';
+import Lock from '@material-ui/icons/Lock';
+import People from '@material-ui/icons/People';
+import Public from '@material-ui/icons/Public';
+import { WithStyles } from '@material-ui/core/styles';
+import { SelectProps } from '@material-ui/core/Select';
+import { SelectItem } from './select-item';
+import { VisibilityLevel } from 'store/sharing-dialog/sharing-dialog-types';
+
+
+type VisibilityLevelSelectClasses = 'value';
+
+const VisibilityLevelSelectStyles: StyleRulesCallback<VisibilityLevelSelectClasses> = theme => ({
+    value: {
+        marginLeft: theme.spacing.unit,
+    }
+});
+export const VisibilityLevelSelect = withStyles(VisibilityLevelSelectStyles)(
+    ({ classes, ...props }: SelectProps & WithStyles<VisibilityLevelSelectClasses>) =>
+        <Select
+            {...props}
+            renderValue={renderPermissionItem}
+            inputProps={{ classes }}>
+            <MenuItem value={VisibilityLevel.PUBLIC}>
+                {renderPermissionItem(VisibilityLevel.PUBLIC)}
+            </MenuItem>
+            <MenuItem value={VisibilityLevel.SHARED}>
+                {renderPermissionItem(VisibilityLevel.SHARED)}
+            </MenuItem>
+            <MenuItem value={VisibilityLevel.PRIVATE}>
+                {renderPermissionItem(VisibilityLevel.PRIVATE)}
+            </MenuItem>
+        </Select>);
+
+const renderPermissionItem = (value: string) =>
+    <SelectItem {...{ value, icon: getIcon(value) }} />;
+
+const getIcon = (value: string) => {
+    switch (value) {
+        case VisibilityLevel.PUBLIC:
+            return Public;
+        case VisibilityLevel.SHARED:
+            return People;
+        case VisibilityLevel.PRIVATE:
+            return Lock;
+        default:
+            return Lock;
+    }
+};
+

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list