Skip to content

Commit

Permalink
feat: add support for uploading multiple files to the file upload field
Browse files Browse the repository at this point in the history
  • Loading branch information
rasstislav committed Feb 7, 2025
1 parent 184dad7 commit dcf4be3
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 108 deletions.
121 changes: 74 additions & 47 deletions code/Control/UserDefinedFormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -313,49 +313,74 @@ public function process($data, $form)
$field->createProtectedFolder();
}

$file = Versioned::withVersionedMode(function () use ($field, $form) {
$stage = Injector::inst()->get(UserDefinedFormController::class)->config()->get('file_upload_stage');
Versioned::set_stage($stage);

$foldername = $field->getFormField()->getFolderName();
// create the file from post data
$upload = Upload::create();
try {
$upload->loadIntoFile($_FILES[$field->Name], null, $foldername);
} catch (ValidationException $e) {
$validationResult = $e->getResult();
foreach ($validationResult->getMessages() as $message) {
$form->sessionMessage($message['message'], ValidationResult::TYPE_ERROR);
$processFile = function ($fileData) use ($field, $form, &$attachments) {
$file = Versioned::withVersionedMode(function () use ($fileData, $field, $form, &$attachments) {
$stage = Injector::inst()->get(UserDefinedFormController::class)->config()->get('file_upload_stage');
Versioned::set_stage($stage);

$foldername = $field->getFormField()->getFolderName();
// create the file from post data
$upload = Upload::create();
try {
$upload->loadIntoFile($fileData, null, $foldername);
} catch (ValidationException $e) {
$validationResult = $e->getResult();
foreach ($validationResult->getMessages() as $message) {
$form->sessionMessage($message['message'], ValidationResult::TYPE_ERROR);
}
Controller::curr()->redirectBack();
return null;
}
Controller::curr()->redirectBack();
/** @var AssetContainer|File $file */
$file = $upload->getFile();
$file->ShowInSearch = 0;
$file->UserFormUpload = UserFormFileExtension::USER_FORM_UPLOAD_TRUE;
$file->write();

return $file;
});

if (is_null($file)) {
return null;
}
/** @var AssetContainer|File $file */
$file = $upload->getFile();
$file->ShowInSearch = 0;
$file->UserFormUpload = UserFormFileExtension::USER_FORM_UPLOAD_TRUE;
$file->write();

// generate image thumbnail to show in asset-admin
// you can run userforms without asset-admin, so need to ensure asset-admin is installed
if (class_exists(AssetAdmin::class)) {
AssetAdmin::singleton()->generateThumbnails($file);
}

// attach a file to recipient email only if lower than configured size
if ($file->getAbsoluteSize() <= $this->getMaximumAllowedEmailAttachmentSize()) {
$attachments[$field->Name][] = $file;
}

return $file;
});
};

if (is_null($file)) {
return;
}
$files = $_FILES[$field->Name];

// generate image thumbnail to show in asset-admin
// you can run userforms without asset-admin, so need to ensure asset-admin is installed
if (class_exists(AssetAdmin::class)) {
AssetAdmin::singleton()->generateThumbnails($file);
}
if (is_array($files['name'])) {
foreach (array_keys((array) $files['name']) as $key) {
$fileData = [];
foreach ($files as $column => $data) {
$fileData[$column] = $data[$key];
}

// write file to form field
$submittedField->UploadedFileID = $file->ID;
if (!$file = $processFile($fileData)) {
return;
}

// write file to form field
$submittedField->UploadedFiles()->add($file);
}
} else {
if (!$file = $processFile($files)) {
return;
}

// attach a file to recipient email only if lower than configured size
if ($file->getAbsoluteSize() <= $this->getMaximumAllowedEmailAttachmentSize()) {
// using the field name as array index is fine as file upload field only allows one file
$attachments[$field->Name] = $file;
// write file to form field
$submittedField->UploadedFileID = $file->ID;
}
}
}
Expand Down Expand Up @@ -393,21 +418,23 @@ public function process($data, $form)
$mergeFields = $this->getMergeFieldsMap($emailData['Fields']);

if ($attachments && (bool) $recipient->HideFormData === false) {
foreach ($attachments as $uploadFieldName => $file) {
/** @var File $file */
if ((int) $file->ID === 0) {
continue;
}
foreach ($attachments as $uploadFieldName => $files) {
foreach ($files as $file) {
/** @var File $file */
if ((int) $file->ID === 0) {
continue;
}

$canAttachFileForRecipient = true;
$this->extend('updateCanAttachFileForRecipient', $canAttachFileForRecipient, $recipient, $uploadFieldName, $file);
$canAttachFileForRecipient = true;
$this->extend('updateCanAttachFileForRecipient', $canAttachFileForRecipient, $recipient, $uploadFieldName, $file);

if ($canAttachFileForRecipient) {
$email->addAttachmentFromData(
$file->getString(),
$file->getFilename(),
$file->getMimeType()
);
if ($canAttachFileForRecipient) {
$email->addAttachmentFromData(
$file->getString(),
$file->getFilename(),
$file->getMimeType()
);
}
}
}
}
Expand Down
18 changes: 15 additions & 3 deletions code/Form/UserForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use SilverStripe\Forms\FormAction;
use SilverStripe\UserForms\FormField\UserFormsStepField;
use SilverStripe\UserForms\FormField\UserFormsFieldList;
use SilverStripe\UserForms\Model\EditableFormField\EditableFileField;

/**
* @package userforms
Expand Down Expand Up @@ -165,12 +166,23 @@ public function getFormActions()
public function getRequiredFields()
{
// Generate required field validator
$requiredNames = $this
$requiredFields = $this
->getController()
->data()
->Fields()
->filter('Required', true)
->column('Name');
->filter('Required', true);

$requiredNames = [];
foreach ($requiredFields as $requiredField) {
$requiredName = $requiredField->Name;

if ($requiredField instanceof EditableFileField && $requiredField->Multiple) {
$requiredName .= '[]';
}

$requiredNames[] = $requiredName;
}

$requiredNames = array_merge($requiredNames, $this->getEmailRecipientRequiredFields());
$required = UserFormsRequiredFields::create($requiredNames);
$this->extend('updateRequiredFields', $required);
Expand Down
11 changes: 9 additions & 2 deletions code/Form/UserFormsRequiredFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,19 @@ public function php($data)
continue;
}

$originalFieldName = $fieldName;

// get form field
if ($fieldName instanceof FormField) {
$formField = $fieldName;
$fieldName = $fieldName->getName();
$originalFieldName = $fieldName;
} else {
$formField = $fields->dataFieldByName($fieldName);

if ($formField instanceof FileField && strpos($fieldName ?? '', '[') !== false) {
$fieldName = preg_replace('#\[(.*?)\]$#', '', $fieldName ?? '');
}
}

// get editable form field - owns display rules for field
Expand All @@ -75,7 +82,7 @@ public function php($data)

// handle error case
if ($formField && $error) {
$this->handleError($formField, $fieldName);
$this->handleError($formField, $originalFieldName);
$valid = false;
}
}
Expand Down Expand Up @@ -122,7 +129,7 @@ private function validateRequired(FormField $field, array $data)
if ($field instanceof FileField && isset($value['error']) && $value['error']) {
$error = true;
} else {
$error = (count($value ?? [])) ? false : true;
$error = (count(array_filter($value ?? []))) ? false : true;
}
} else {
// assume a string or integer
Expand Down
20 changes: 19 additions & 1 deletion code/Model/EditableFormField/EditableFileField.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use SilverStripe\Core\Convert;
use SilverStripe\Forms\FieldList;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\FileField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\NumericField;
Expand All @@ -18,6 +19,7 @@
use SilverStripe\Security\InheritedPermissions;
use SilverStripe\UserForms\Control\UserDefinedFormAdmin;
use SilverStripe\UserForms\Control\UserDefinedFormController;
use SilverStripe\UserForms\Model\EditableCustomRule;
use SilverStripe\UserForms\Model\EditableFormField;
use SilverStripe\UserForms\Model\Submission\SubmittedFileField;

Expand All @@ -40,6 +42,7 @@ class EditableFileField extends EditableFormField
private static $db = [
'MaxFileSizeMB' => 'Float',
'FolderConfirmed' => 'Boolean',
'Multiple' => 'Boolean',
];

private static $has_one = [
Expand Down Expand Up @@ -173,6 +176,12 @@ public function getCMSFields()
->setDescription("Note: Maximum php allowed size is {$this->getPHPMaxFileSizeMB()} MB")
);

$fields->addFieldToTab(
'Root.Main',
CheckboxField::create('Multiple')
->setTitle('Allow multiple files')
);

$fields->removeByName('Default');
});

Expand Down Expand Up @@ -209,7 +218,7 @@ public function createProtectedFolder(): void

public function getFormField()
{
$field = FileField::create($this->Name, $this->Title ?: false)
$field = FileField::create($this->Name . ($this->Multiple ? '[]' : ''), $this->Title ?: false)
->setFieldHolderTemplate(EditableFormField::class . '_holder')
->setTemplate(__CLASS__)
->setValidator(Injector::inst()->get(Upload_Validator::class . '.userforms', false));
Expand All @@ -231,6 +240,10 @@ public function getFormField()
$field->getValidator()->setAllowedMaxFileSize(static::get_php_max_file_size());
}

if ($this->Multiple) {
$field->setAttribute('multiple', 'multiple');
}

$folder = $this->Folder();
if ($folder && $folder->exists()) {
$field->setFolderName(
Expand Down Expand Up @@ -300,4 +313,9 @@ public function onBeforeWrite()
$this->FolderConfirmed = true;
}
}

public function getSelectorFieldOnly()
{
return $this->Multiple ? "[name='{$this->Name}[]']" : parent::getSelectorFieldOnly();
}
}
Loading

0 comments on commit dcf4be3

Please sign in to comment.