Feature: #105708 - Multiple file upload for EXT:form elements
See forge#105708
Description
The TYPO3 form framework now supports multiple file uploads in the
File and
Image form elements. This allows users
to select and upload multiple files using a single form field.
The implementation follows the same security patterns as Extbase file upload handling. It uses HMAC-signed deletion requests to ensure secure file removal.
Configuration
To enable multiple file uploads for a form element, set the
multiple
property to
true in your form definition:
type: Form
identifier: contact-form
label: 'Contact Form'
prototypeName: standard
renderables:
- type: Page
identifier: page-1
label: 'Page 1'
renderables:
- type: FileUpload
identifier: attachments
label: 'Attachments'
properties:
multiple: true
allowRemoval: true
saveToFileMount: '1:/user_upload/'
allowedMimeTypes:
- application/pdf
- image/jpeg
- type: ImageUpload
identifier: images
label: 'Images'
properties:
multiple: true
allowRemoval: true
saveToFileMount: '1:/user_upload/'
allowedMimeTypes:
- image/jpeg
- image/png
The
multiple option is also available in the Form Editor backend
module as a checkbox in the element's inspector panel.
The
allow property enables users to remove previously uploaded
files before submitting the form. When enabled, a Remove checkbox is
displayed next to each uploaded file.
File count validation
The existing
Count validator can now be used with
File and
Image elements to limit the number of
uploaded files:
- type: FileUpload
identifier: attachments
label: 'Attachments'
properties:
multiple: true
validators:
- identifier: Count
options:
minimum: 1
maximum: 5
Frontend rendering
When
multiple is enabled:
- The file input field renders with the HTML5
multipleattribute - Previously uploaded files are displayed in a list with individual remove checkboxes
- Users can select multiple files in the browser's file picker dialog
- On the summary page, multiple files are displayed as a list
File deletion
The implementation uses HMAC-signed deletion requests similar to Extbase file handling. Each uploaded file displays a checkbox that, when checked, marks the file for removal on form submission. The deletion data is signed with an HMAC to prevent manipulation.
A new ViewHelper,
<formvh:, is available
for custom templates:
<formvh:form.uploadDeleteCheckbox
property="{element.identifier}"
fileReference="{file}"
fileIndex="{iterator.index}"
/>
Adapting custom finishers for multiple file uploads
When
multiple is enabled on a
File element, the value
returned by
$form is an
Object instead of a single
File. Custom finishers that process file uploads need to be
adapted to handle both cases, single and multiple uploads.
The following example shows the pattern used in the core
Email and
Delete:
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;
use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload;
class MyFinisher extends AbstractFinisher
{
protected function executeInternal(): void
{
$formRuntime = $this->finisherContext->getFormRuntime();
foreach (
$formRuntime->getFormDefinition()->getRenderablesRecursively()
as $element
) {
if (!$element instanceof FileUpload) {
continue;
}
$file = $formRuntime[$element->getIdentifier()];
// Single file upload: value is a FileReference
if ($file instanceof FileReference) {
$this->processFile($file->getOriginalResource());
}
// Multiple file upload: value is an ObjectStorage of FileReferences
if ($file instanceof ObjectStorage) {
foreach ($file as $singleFile) {
if ($singleFile instanceof FileReference) {
$this->processFile(
$singleFile->getOriginalResource()
);
}
}
}
}
}
private function processFile(FileInterface $file): void
{
// Your custom logic, e.g. move, copy, attach, etc.
}
}
Per-element validation with ObjectStorageElementValidatorInterface
When a form field value is an
Object, for example, a
multiple-file upload, the
Processing must decide how to call each
registered validator:
- Collection-level validators (default) receive the entire
Object. Use this for validators that check the collection as a whole, such asStorage Countfor the minimum or maximum number of items.Validator - Element-level validators receive each item individually. Use this for
validators that inspect a single item, such as
MimeorType Validator File.Size Validator
To mark a validator as element-level, implement the marker interface
Object:
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
use TYPO3\CMS\Form\Mvc\Validation\ObjectStorageElementValidatorInterface;
final class MyFileValidator extends AbstractValidator implements
ObjectStorageElementValidatorInterface
{
public function isValid(mixed $value): void
{
// $value is a single element from the ObjectStorage,
// e.g. a FileReference - not the whole collection.
}
}
For single-value fields, that is, non-
Object values, the
interface has no effect. Validators are always called with the field value
directly.
Impact
- Form integrators can now create forms that accept multiple file uploads without custom extensions
- The
FileandUpload Imageelements support the newUpload multipleproperty - All existing finishers,
Email,Finisher Save, andTo Database Finisher Delete, automatically support multiple file uploadsUploads Finisher - Email templates display multiple files as a list of filenames
- The summary page displays multiple images as a gallery and multiple files as a list of filenames