mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
NEW Maximum email attachment size can be configured
Previously hard coded size of 1MB meant any file larger was excluded from recipient emails - often confusing for CMS admins configuring an advanced use case for a userform, expecting to recieve files to begin a business process external to the website. The configuration has been made to support PHP 'shorthand byte values' https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes in the configuration option. This give flexibility around oddly specific limits, but can also be used to set as 0, disabling attachments Tests are updated to set up a known state before testing, as to be able to accurately assert the results. One should test the class they intend, not an Injector service of some unknown element.
This commit is contained in:
parent
25e64441b8
commit
0ff72734dd
@ -56,6 +56,16 @@ class UserDefinedFormController extends PageController
|
|||||||
|
|
||||||
private static string $file_upload_stage = Versioned::DRAFT;
|
private static string $file_upload_stage = Versioned::DRAFT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size that an uploaded file must not excede for it to be attached to an email
|
||||||
|
* Follows PHP "shorthand bytes" definition rules.
|
||||||
|
* @see self::parseByteSizeString()
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
* @config
|
||||||
|
*/
|
||||||
|
private static $maximum_email_attachment_size = '1M';
|
||||||
|
|
||||||
protected function init()
|
protected function init()
|
||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
@ -215,6 +225,42 @@ JS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum size uploaded files can be before they're excluded from CMS configured recipient emails
|
||||||
|
*
|
||||||
|
* @return int size in megabytes
|
||||||
|
*/
|
||||||
|
public function getMaximumAllowedEmailAttachmentSize()
|
||||||
|
{
|
||||||
|
return $this->parseByteSizeString($this->config()->get('maximum_email_attachment_size'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert file sizes with a single character for unit size to true byte count.
|
||||||
|
* Just as with php.ini and e.g. 128M -> 1024 * 1024 * 128 bytes.
|
||||||
|
* @see https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
|
||||||
|
*
|
||||||
|
* @param string $byteSize
|
||||||
|
* @return int bytes
|
||||||
|
*/
|
||||||
|
protected function parseByteSizeString($byteSize)
|
||||||
|
{
|
||||||
|
// kilo, mega, giga
|
||||||
|
$validUnits = 'kmg';
|
||||||
|
$valid = preg_match("/^(?<number>\d+)((?<unit>[$validUnits])b?)?$/i", $byteSize, $matches);
|
||||||
|
if (!$valid) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"Expected a positive integer followed optionally by K, M, or G. Found '$byteSize' instead"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$power = 0;
|
||||||
|
// prepend b for bytes to $validUnits to give correct mapping of ordinal position to exponent
|
||||||
|
if (isset($matches['unit'])) {
|
||||||
|
$power = stripos("b$validUnits", $matches['unit']);
|
||||||
|
}
|
||||||
|
return intval($matches['number']) * pow(1024, $power);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the form that is submitted through the site
|
* Process the form that is submitted through the site
|
||||||
*
|
*
|
||||||
@ -268,7 +314,7 @@ JS
|
|||||||
if (!$field->getFolderExists()) {
|
if (!$field->getFolderExists()) {
|
||||||
$field->createProtectedFolder();
|
$field->createProtectedFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = Versioned::withVersionedMode(function () use ($field, $form) {
|
$file = Versioned::withVersionedMode(function () use ($field, $form) {
|
||||||
$stage = Injector::inst()->get(self::class)->config()->get('file_upload_stage');
|
$stage = Injector::inst()->get(self::class)->config()->get('file_upload_stage');
|
||||||
Versioned::set_stage($stage);
|
Versioned::set_stage($stage);
|
||||||
@ -308,8 +354,8 @@ JS
|
|||||||
// write file to form field
|
// write file to form field
|
||||||
$submittedField->UploadedFileID = $file->ID;
|
$submittedField->UploadedFileID = $file->ID;
|
||||||
|
|
||||||
// attach a file only if lower than 1MB
|
// attach a file to recipient email only if lower than configured size
|
||||||
if ($file->getAbsoluteSize() < 1024 * 1024 * 1) {
|
if ($file->getAbsoluteSize() <= $this->getMaximumAllowedEmailAttachmentSize()) {
|
||||||
$attachments[] = $file;
|
$attachments[] = $file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ use SilverStripe\UserForms\Model\EditableFormField\EditableTextField;
|
|||||||
use SilverStripe\UserForms\Model\Recipient\EmailRecipient;
|
use SilverStripe\UserForms\Model\Recipient\EmailRecipient;
|
||||||
use SilverStripe\UserForms\Model\Submission\SubmittedFormField;
|
use SilverStripe\UserForms\Model\Submission\SubmittedFormField;
|
||||||
use SilverStripe\UserForms\Model\UserDefinedForm;
|
use SilverStripe\UserForms\Model\UserDefinedForm;
|
||||||
|
use SilverStripe\UserForms\Tests\Control\fixtures\SizeStringTestableController;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\View\ArrayData;
|
use SilverStripe\View\ArrayData;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
@ -47,7 +48,9 @@ class UserDefinedFormControllerTest extends FunctionalTest
|
|||||||
// Set backend and base url
|
// Set backend and base url
|
||||||
TestAssetStore::activate('AssetStoreTest');
|
TestAssetStore::activate('AssetStoreTest');
|
||||||
|
|
||||||
Config::modify()->merge(SSViewer::class, 'themes', ['simple', '$default']);
|
$config = Config::modify();
|
||||||
|
$config->set(UserDefinedFormController::class, 'maximum_email_attachment_size', "1M");
|
||||||
|
$config->merge(SSViewer::class, 'themes', ['simple', '$default']);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
@ -273,7 +276,8 @@ class UserDefinedFormControllerTest extends FunctionalTest
|
|||||||
$actions = $controller->Form()->getFormActions();
|
$actions = $controller->Form()->getFormActions();
|
||||||
|
|
||||||
$expected = new FieldList(new FormAction('process', 'Custom Button'));
|
$expected = new FieldList(new FormAction('process', 'Custom Button'));
|
||||||
$expected->push(FormAction::create('clearForm', 'Clear')->setAttribute('type', 'reset'));
|
$clearAction = new FormAction('clearForm', 'Clear');
|
||||||
|
$expected->push($clearAction->setAttribute('type', 'reset'));
|
||||||
$expected->setForm($controller->Form());
|
$expected->setForm($controller->Form());
|
||||||
|
|
||||||
$this->assertEquals($actions, $expected);
|
$this->assertEquals($actions, $expected);
|
||||||
@ -404,7 +408,7 @@ class UserDefinedFormControllerTest extends FunctionalTest
|
|||||||
Config::modify()->set(Upload_Validator::class, 'use_is_uploaded_file', false);
|
Config::modify()->set(Upload_Validator::class, 'use_is_uploaded_file', false);
|
||||||
|
|
||||||
$userForm = $this->setupFormFrontend('upload-form');
|
$userForm = $this->setupFormFrontend('upload-form');
|
||||||
$controller = UserDefinedFormController::create($userForm);
|
$controller = new UserDefinedFormController($userForm);
|
||||||
$field = $this->objFromFixture(EditableFileField::class, 'file-field-1');
|
$field = $this->objFromFixture(EditableFileField::class, 'file-field-1');
|
||||||
|
|
||||||
$path = realpath(__DIR__ . '/fixtures/testfile.jpg');
|
$path = realpath(__DIR__ . '/fixtures/testfile.jpg');
|
||||||
@ -440,7 +444,7 @@ class UserDefinedFormControllerTest extends FunctionalTest
|
|||||||
Config::modify()->set(Upload_Validator::class, 'use_is_uploaded_file', false);
|
Config::modify()->set(Upload_Validator::class, 'use_is_uploaded_file', false);
|
||||||
|
|
||||||
$userForm = $this->setupFormFrontend('upload-form');
|
$userForm = $this->setupFormFrontend('upload-form');
|
||||||
$controller = UserDefinedFormController::create($userForm);
|
$controller = new UserDefinedFormController($userForm);
|
||||||
$field = $this->objFromFixture(EditableFileField::class, 'file-field-1');
|
$field = $this->objFromFixture(EditableFileField::class, 'file-field-1');
|
||||||
|
|
||||||
$path = realpath(__DIR__ . '/fixtures/testfile.jpg');
|
$path = realpath(__DIR__ . '/fixtures/testfile.jpg');
|
||||||
@ -511,4 +515,71 @@ class UserDefinedFormControllerTest extends FunctionalTest
|
|||||||
$this->assertEquals(1, $fileDraftCount);
|
$this->assertEquals(1, $fileDraftCount);
|
||||||
$this->assertEquals(0, $fileLiveCount);
|
$this->assertEquals(0, $fileLiveCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEmailAttachmentMaximumSizeCanBeConfigured()
|
||||||
|
{
|
||||||
|
$udfController = new UserDefinedFormController();
|
||||||
|
$config = Config::modify();
|
||||||
|
$config->set(UserDefinedFormController::class, 'maximum_email_attachment_size', '1M');
|
||||||
|
$this->assertSame(1 * 1024 * 1024, $udfController->getMaximumAllowedEmailAttachmentSize());
|
||||||
|
$config->set(UserDefinedFormController::class, 'maximum_email_attachment_size', '5M');
|
||||||
|
$this->assertSame(5 * 1024 * 1024, $udfController->getMaximumAllowedEmailAttachmentSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParseByteSizeStringTestValues()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['9846', 9846],
|
||||||
|
['1048576', 1048576],
|
||||||
|
['1k', 1024],
|
||||||
|
['1K', 1024],
|
||||||
|
['4k', 4096],
|
||||||
|
['4K', 4096],
|
||||||
|
['1kb', 1024],
|
||||||
|
['1KB', 1024],
|
||||||
|
['4kB', 4096],
|
||||||
|
['4Kb', 4096],
|
||||||
|
['1m', 1048576],
|
||||||
|
['1M', 1048576],
|
||||||
|
['4mb', 4194304],
|
||||||
|
['4MB', 4194304],
|
||||||
|
['25mB', 26214400],
|
||||||
|
['25Mb', 26214400],
|
||||||
|
['1g', 1073741824],
|
||||||
|
['2GB', 2147483648],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getParseByteSizeStringTestValues
|
||||||
|
*/
|
||||||
|
public function testParseByteSizeString($input, $expectedOutput)
|
||||||
|
{
|
||||||
|
$controller = new SizeStringTestableController(); // extends UserDefinedFormController
|
||||||
|
$this->assertSame($expectedOutput, $controller->convertSizeStringToBytes($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParseByteSizeStringTestBadValues()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['1234b'],
|
||||||
|
['9846B'],
|
||||||
|
['1kilobyte'],
|
||||||
|
['1 K'],
|
||||||
|
['Four kilobytes'],
|
||||||
|
['4Mbs'],
|
||||||
|
['12Gigs'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getParseByteSizeStringTestBadValues
|
||||||
|
* @expectedException \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function testParseByteSizeStringBadValuesThrowException($input)
|
||||||
|
{
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$controller = new SizeStringTestableController(); // extends UserDefinedFormController
|
||||||
|
$controller->convertSizeStringToBytes($input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
14
tests/php/Control/fixtures/SizeStringTestableController.php
Normal file
14
tests/php/Control/fixtures/SizeStringTestableController.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\UserForms\Tests\Control\fixtures;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\UserForms\Control\UserDefinedFormController;
|
||||||
|
|
||||||
|
class SizeStringTestableController extends UserDefinedFormController implements TestOnly
|
||||||
|
{
|
||||||
|
public function convertSizeStringToBytes($sizeString)
|
||||||
|
{
|
||||||
|
return $this->parseByteSizeString($sizeString);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user