fix(ui): restrict file picking via upload config mimetypes (#8710)
Fixes #8673 This PR restricts inputs with `type="file"` to only those mimetypes specified in collection upload configs. This also works for the input in `bulkUpload` and drag-and-drop capabilities by omitting dropped files if they do not conform to the upload config mimetypes. This PR also assumes that an upload config with an empty mimetype array should accept all files since the negation of that statement makes an upload collection redundant.
This commit is contained in:
@@ -11,10 +11,11 @@ import './index.scss'
|
|||||||
const baseClass = 'bulk-upload--add-files'
|
const baseClass = 'bulk-upload--add-files'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
readonly acceptMimeTypes?: string
|
||||||
readonly onCancel: () => void
|
readonly onCancel: () => void
|
||||||
readonly onDrop: (acceptedFiles: FileList) => void
|
readonly onDrop: (acceptedFiles: FileList) => void
|
||||||
}
|
}
|
||||||
export function AddFilesView({ onCancel, onDrop }: Props) {
|
export function AddFilesView({ acceptMimeTypes, onCancel, onDrop }: Props) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const inputRef = React.useRef(null)
|
const inputRef = React.useRef(null)
|
||||||
@@ -37,6 +38,7 @@ export function AddFilesView({ onCancel, onDrop }: Props) {
|
|||||||
{t('upload:selectFile')}
|
{t('upload:selectFile')}
|
||||||
</Button>
|
</Button>
|
||||||
<input
|
<input
|
||||||
|
accept={acceptMimeTypes}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={`${baseClass}__hidden-input`}
|
className={`${baseClass}__hidden-input`}
|
||||||
hidden
|
hidden
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { JsonObject } from 'payload'
|
|||||||
import { useModal } from '@faceless-ui/modal'
|
import { useModal } from '@faceless-ui/modal'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import { useConfig } from '../../providers/Config/index.js'
|
||||||
import { EditDepthProvider, useEditDepth } from '../../providers/EditDepth/index.js'
|
import { EditDepthProvider, useEditDepth } from '../../providers/EditDepth/index.js'
|
||||||
import { Drawer } from '../Drawer/index.js'
|
import { Drawer } from '../Drawer/index.js'
|
||||||
import { AddFilesView } from './AddFilesView/index.js'
|
import { AddFilesView } from './AddFilesView/index.js'
|
||||||
@@ -17,12 +18,27 @@ function DrawerContent() {
|
|||||||
const { addFiles, forms, isInitializing } = useFormsManager()
|
const { addFiles, forms, isInitializing } = useFormsManager()
|
||||||
const { closeModal } = useModal()
|
const { closeModal } = useModal()
|
||||||
const { collectionSlug, drawerSlug } = useBulkUpload()
|
const { collectionSlug, drawerSlug } = useBulkUpload()
|
||||||
|
const { config } = useConfig()
|
||||||
|
|
||||||
|
const uploadCollection = config.collections.find((col) => col.slug === collectionSlug)
|
||||||
|
const uploadConfig = uploadCollection.upload
|
||||||
|
const uploadMimeTypes = uploadConfig.mimeTypes
|
||||||
|
|
||||||
const onDrop = React.useCallback(
|
const onDrop = React.useCallback(
|
||||||
(acceptedFiles: FileList) => {
|
(acceptedFiles: FileList) => {
|
||||||
void addFiles(acceptedFiles)
|
const fileTransfer = new DataTransfer()
|
||||||
|
for (const candidateFile of acceptedFiles) {
|
||||||
|
if (
|
||||||
|
uploadMimeTypes === undefined ||
|
||||||
|
uploadMimeTypes.length === 0 ||
|
||||||
|
uploadMimeTypes?.includes(candidateFile.type)
|
||||||
|
) {
|
||||||
|
fileTransfer.items.add(candidateFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void addFiles(fileTransfer.files)
|
||||||
},
|
},
|
||||||
[addFiles],
|
[addFiles, uploadMimeTypes],
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!collectionSlug) {
|
if (!collectionSlug) {
|
||||||
@@ -30,7 +46,13 @@ function DrawerContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!forms.length && !isInitializing) {
|
if (!forms.length && !isInitializing) {
|
||||||
return <AddFilesView onCancel={() => closeModal(drawerSlug)} onDrop={onDrop} />
|
return (
|
||||||
|
<AddFilesView
|
||||||
|
acceptMimeTypes={uploadMimeTypes?.join(', ')}
|
||||||
|
onCancel={() => closeModal(drawerSlug)}
|
||||||
|
onDrop={onDrop}
|
||||||
|
/>
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return <AddingFilesView />
|
return <AddingFilesView />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ export const Upload: React.FC<UploadProps> = (props) => {
|
|||||||
|
|
||||||
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions || focalPointEnabled)
|
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions || focalPointEnabled)
|
||||||
|
|
||||||
|
const acceptMimeTypes = uploadConfig.mimeTypes?.join(', ')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[fieldBaseClass, baseClass].filter(Boolean).join(' ')}>
|
<div className={[fieldBaseClass, baseClass].filter(Boolean).join(' ')}>
|
||||||
<FieldError field={null} message={errorMessage} showError={showError} />
|
<FieldError field={null} message={errorMessage} showError={showError} />
|
||||||
@@ -251,6 +253,7 @@ export const Upload: React.FC<UploadProps> = (props) => {
|
|||||||
{t('upload:selectFile')}
|
{t('upload:selectFile')}
|
||||||
</Button>
|
</Button>
|
||||||
<input
|
<input
|
||||||
|
accept={acceptMimeTypes}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={`${baseClass}__hidden-input`}
|
className={`${baseClass}__hidden-input`}
|
||||||
hidden
|
hidden
|
||||||
|
|||||||
Reference in New Issue
Block a user