diff --git a/catalog/app/components/Dialog/Confirm.tsx b/catalog/app/components/Dialog/Confirm.tsx index 5ccf067de4e..15d1749d709 100644 --- a/catalog/app/components/Dialog/Confirm.tsx +++ b/catalog/app/components/Dialog/Confirm.tsx @@ -3,13 +3,22 @@ import * as React from 'react' import * as M from '@material-ui/core' interface DialogProps { + cancelTitle?: string children: React.ReactNode onSubmit: (value: boolean) => void open: boolean + submitTitle?: string title: string } -function Dialog({ children, onSubmit, open, title }: DialogProps) { +function Dialog({ + children, + onSubmit, + open, + cancelTitle = 'Cancel', + submitTitle = 'Submit', + title, +}: DialogProps) { const handleCancel = React.useCallback(() => onSubmit(false), [onSubmit]) const handleSubmit = React.useCallback(() => onSubmit(true), [onSubmit]) return ( @@ -18,10 +27,10 @@ function Dialog({ children, onSubmit, open, title }: DialogProps) { {children} - Cancel + {cancelTitle} - Submit + {submitTitle} @@ -29,11 +38,13 @@ function Dialog({ children, onSubmit, open, title }: DialogProps) { } interface PromptProps { + cancelTitle?: string onSubmit: (value: boolean) => void + submitTitle?: string title: string } -export function useConfirm({ title, onSubmit }: PromptProps) { +export function useConfirm({ cancelTitle, title, onSubmit, submitTitle }: PromptProps) { const [key, setKey] = React.useState(0) const [opened, setOpened] = React.useState(false) const open = React.useCallback(() => { @@ -52,15 +63,17 @@ export function useConfirm({ title, onSubmit }: PromptProps) { (children: React.ReactNode) => ( ), - [key, handleSubmit, opened, title], + [cancelTitle, key, handleSubmit, opened, title, submitTitle], ) return React.useMemo( () => ({ diff --git a/catalog/app/containers/Admin/Buckets/Buckets.tsx b/catalog/app/containers/Admin/Buckets/Buckets.tsx index d1bc9322765..32efc35c696 100644 --- a/catalog/app/containers/Admin/Buckets/Buckets.tsx +++ b/catalog/app/containers/Admin/Buckets/Buckets.tsx @@ -11,6 +11,7 @@ import * as M from '@material-ui/core' import * as Lab from '@material-ui/lab' import BucketIcon from 'components/BucketIcon' +import * as Dialog from 'components/Dialog' import * as Pagination from 'components/Pagination' import Skeleton from 'components/Skeleton' import * as Notifications from 'containers/Notifications' @@ -81,6 +82,66 @@ const integerInRange = (min: number, max: number) => (v: string | null | undefin return undefined } +const usePFSCheckboxStyles = M.makeStyles({ + root: { + marginBottom: -9, + marginTop: -9, + }, +}) +function PFSCheckbox({ input, meta }: Form.CheckboxProps & M.CheckboxProps) { + const classes = usePFSCheckboxStyles() + const confirm = React.useCallback((checked) => input?.onChange(checked), [input]) + const dialog = Dialog.useConfirm({ + submitTitle: 'I agree', + title: + 'You are about to enable JavaScript execution and data access in iframe previews of HTML files', + onSubmit: confirm, + }) + const handleCheckbox = React.useCallback( + (event, checked: boolean) => { + if (checked) { + dialog.open() + } else { + input?.onChange(checked) + } + }, + [dialog, input], + ) + return ( + <> + {dialog.render( + + Warning: you must only enable this feature for buckets with trusted contents. + Failure to heed this warning may result in breach of sensitive data. + , + )} + + } + label={ + <> + Enable permissive HTML rendering + + This allows execution of any linked JavaScript code and fetching network + resources relative to the package. Be aware that the iframe with rendered + HTML (and package resources) can be shared publicly during the session + lifespan. The session is active while the page with rendered HTML is open. +
+ Enable only on trusted AWS S3 buckets. +
+ + } + /> + + ) +} + const editFormSpec: FormSpec = { title: R.pipe( R.prop('title'), @@ -648,24 +709,7 @@ function BucketFields({ bucket, reindex }: BucketFieldsProps) { File preview options - - Enable permissive HTML rendering - - This allows execution of any JavaScript code and fetching network - resources relative to package. But beware, the iframe with rendered HTML - (and package resources) can be shared publicly during session lifespan. - Session is active while the page with rendered HTML is opened. -
- Enable only on trusted buckets. -
- - } - /> +
diff --git a/catalog/app/containers/Admin/Form.tsx b/catalog/app/containers/Admin/Form.tsx index 1a6da17bcaf..2d9e8ccb66a 100644 --- a/catalog/app/containers/Admin/Form.tsx +++ b/catalog/app/containers/Admin/Form.tsx @@ -36,11 +36,11 @@ const useCheckboxStyles = M.makeStyles({ }, }) -interface CheckboxProps { +export interface CheckboxProps { errors?: Record input?: RF.FieldInputProps meta: RF.FieldMetaState - label?: string + label?: React.ReactNode FormControlLabelProps?: M.FormControlLabelProps } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index eb5639227a6..f0f8157b8bc 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -22,6 +22,7 @@ Entries inside each section should be ordered by type: * [Added] Add basic support for tasklist in Markdown ([#3339](https://github.com/quiltdata/quilt/pull/3339)) * [Added] Object-level validation, frontend ([#3336](https://github.com/quiltdata/quilt/pull/3336)) * [Added] Frontend for permissive HTML rendering ([#3198](https://github.com/quiltdata/quilt/pull/3198)) +* [Added] Confirmation to enable Package Files Server ([#3388](https://github.com/quiltdata/quilt/pull/3388)) * [Fixed] Fixed mobile layout for collaborators badges ([#3307](https://github.com/quiltdata/quilt/pull/3307)) * [Fixed] Fixed metadata handling for entries without hash or size in pkgpush lambda ([#3314](https://github.com/quiltdata/quilt/pull/3314)) * [Fixed] Fixed adding metadata for S3 entries ([#3367]https://github.com/quiltdata/quilt/pull/3367)