Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH Add support for uploading multiple files to the file upload field #1367

Open
wants to merge 1 commit into
base: 6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 75 additions & 48 deletions code/Control/UserDefinedFormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -308,54 +308,79 @@ public function process($data, $form)

if (!empty($data[$field->Name])) {
if (in_array(EditableFileField::class, $field->getClassAncestry() ?? [])) {
if (!empty($_FILES[$field->Name]['name'])) {
if (($names = $_FILES[$field->Name]['name'] ?? '') && (!is_array($names) || array_filter($names))) {
if (!$field->getFolderExists()) {
$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($files['name']) as $index) {
$fileData = [];
foreach ($files as $key => $value) {
$fileData[$key] = $value[$index];
}

// 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
22 changes: 21 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(($originalName = $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 All @@ -240,6 +253,8 @@ public function getFormField()

$this->doUpdateFormField($field);

$field->setAttribute('htmlID', $originalName);

return $field;
}

Expand Down Expand Up @@ -300,4 +315,9 @@ public function onBeforeWrite()
$this->FolderConfirmed = true;
}
}

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