[FEATURE] EXT:form - add file size validator 35/54135/17
authorRalf Zimmermann <ralf.zimmermann@tritum.de>
Wed, 13 Sep 2017 14:01:50 +0000 (16:01 +0200)
committerSusanne Moog <susanne.moog@typo3.org>
Thu, 7 Dec 2017 19:34:56 +0000 (20:34 +0100)
A new ExtbaseValidator called "FileSizeValidator" has been added which
is able to validate a file resource regarding its file size. The
validator is also available within the form editor.

Resolves: #82177
Releases: master
Change-Id: I04ae755b8632c473769fc7ae859c97d88c60b390
Reviewed-on: https://review.typo3.org/54135
Reviewed-by: Bjoern Jacob <bjoern.jacob@tritum.de>
Tested-by: Bjoern Jacob <bjoern.jacob@tritum.de>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Carlos Meyer <cm@davitec.de>
Tested-by: Carlos Meyer <cm@davitec.de>
Reviewed-by: Daniel Lorenz <daniel.lorenz@extco.de>
Tested-by: Daniel Lorenz <daniel.lorenz@extco.de>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
typo3/sysext/core/Documentation/Changelog/master/Feature-82177-ExtFormAddFileSizeValidator.rst [new file with mode: 0644]
typo3/sysext/form/Classes/Mvc/ProcessingRule.php
typo3/sysext/form/Classes/Mvc/Property/PropertyMappingConfiguration.php
typo3/sysext/form/Classes/Mvc/Property/TypeConverter/UploadedFileReferenceConverter.php
typo3/sysext/form/Classes/Mvc/Validation/FileSizeValidator.php [new file with mode: 0644]
typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml
typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml
typo3/sysext/form/Resources/Private/Language/Database.xlf
typo3/sysext/form/Resources/Private/Language/locallang.xlf
typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ViewModel.js
typo3/sysext/form/Tests/Unit/Mvc/Validation/FileSizeValidatorTest.php [new file with mode: 0644]

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-82177-ExtFormAddFileSizeValidator.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-82177-ExtFormAddFileSizeValidator.rst
new file mode 100644 (file)
index 0000000..5445f77
--- /dev/null
@@ -0,0 +1,45 @@
+.. include:: ../../Includes.txt
+
+=========================================
+Feature: #82177 - add file size validator
+=========================================
+
+See :issue:`82177`
+
+Description
+===========
+
+A new ExtbaseValidator called "FileSizeValidator" has been added which is able to validate a file
+resource regarding its file size. This validator has 2 options:
+
+- minimum
+
+The minimum file size to accept. Use the format <size>B|K|M|G. For exmaple: 10M means 10 megabytes.
+
+- maximum
+
+The maximum file size to accept. Use the format <size>B|K|M|G. For exmaple: 10M means 10 megabytes.
+
+Please keep in mind that the maximum file size also depends on you php.ini settings.
+
+Example configuration:
+
+.. code-block:: yaml
+
+    validators:
+      -
+        identifier: FileSize
+        options:
+          minimum: 1M
+          maximum: 10M
+
+This validator can also be used within the form editor for file and image upload elements.
+
+Impact
+======
+
+A file upload element can be validated regarding its file size. It is possible to add, remove and
+edit the FileSizeValidator for file upload elements like "ImageUpload" and "FileUpload" within the
+form editor.
+
+.. index:: Backend, Frontend, ext:form
\ No newline at end of file
index bd41f14..81756cd 100644 (file)
@@ -143,6 +143,18 @@ class ProcessingRule
     }
 
     /**
+     * Removes the specified validator.
+     *
+     * @param ValidatorInterface $validator The validator to remove
+     * @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
+     * @internal
+     */
+    public function removeValidator(ValidatorInterface $validator)
+    {
+        $this->validator->removeValidator($validator);
+    }
+
+    /**
      * @param mixed $value
      * @return mixed
      * @internal
index bde3904..753eac7 100644 (file)
@@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Resource\ResourceFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
+use TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator;
 use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload;
 use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
 use TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter;
@@ -55,8 +56,18 @@ class PropertyMappingConfiguration
 
             $mimeTypeValidator = GeneralUtility::makeInstance(ObjectManager::class)
                 ->get(MimeTypeValidator::class, ['allowedMimeTypes' => $renderable->getProperties()['allowedMimeTypes']]);
+
+            $processingRule = $renderable->getRootForm()->getProcessingRule($renderable->getIdentifier());
+            $validators = [$mimeTypeValidator];
+            foreach ($processingRule->getValidators() as $validator) {
+                if (get_class($validator) !== NotEmptyValidator::class) {
+                    $validators[] = $validator;
+                    $processingRule->removeValidator($validator);
+                }
+            }
+
             $uploadConfiguration = [
-                UploadedFileReferenceConverter::CONFIGURATION_FILE_VALIDATORS => [$mimeTypeValidator],
+                UploadedFileReferenceConverter::CONFIGURATION_FILE_VALIDATORS => $validators,
                 UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_CONFLICT_MODE => 'rename',
             ];
 
index 1533957..f86c03b 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Form\Mvc\Property\TypeConverter;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Log\LogManager;
 use TYPO3\CMS\Core\Resource\File as File;
 use TYPO3\CMS\Core\Resource\FileReference as CoreFileReference;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -25,6 +26,7 @@ use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException;
 use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
 use TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter;
 use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
+use TYPO3\CMS\Form\Service\TranslationService;
 
 /**
  * Class UploadedFileReferenceConverter
@@ -265,23 +267,32 @@ class UploadedFileReferenceConverter extends AbstractTypeConverter
      */
     protected function getUploadErrorMessage(int $errorCode): string
     {
+        $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
         switch ($errorCode) {
             case \UPLOAD_ERR_INI_SIZE:
-                return 'The uploaded file exceeds the upload_max_filesize directive in php.ini';
+                $logger->error('The uploaded file exceeds the upload_max_filesize directive in php.ini.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
             case \UPLOAD_ERR_FORM_SIZE:
-                return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
+                $logger->error('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
             case \UPLOAD_ERR_PARTIAL:
-                return 'The uploaded file was only partially uploaded';
+                $logger->error('The uploaded file was only partially uploaded.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530346', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
             case \UPLOAD_ERR_NO_FILE:
-                return 'No file was uploaded';
+                $logger->error('No file was uploaded.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530347', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
             case \UPLOAD_ERR_NO_TMP_DIR:
-                return 'Missing a temporary folder';
+                $logger->error('Missing a temporary folder.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
             case \UPLOAD_ERR_CANT_WRITE:
-                return 'Failed to write file to disk';
+                $logger->error('Failed to write file to disk.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
             case \UPLOAD_ERR_EXTENSION:
-                return 'File upload stopped by extension';
+                $logger->error('File upload stopped by extension.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
             default:
-                return 'Unknown upload error';
+                $logger->error('Unknown upload error.', []);
+                return TranslationService::getInstance()->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
         }
     }
 }
diff --git a/typo3/sysext/form/Classes/Mvc/Validation/FileSizeValidator.php b/typo3/sysext/form/Classes/Mvc/Validation/FileSizeValidator.php
new file mode 100644 (file)
index 0000000..2afc5bc
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Form\Mvc\Validation;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Domain\Model\FileReference;
+use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
+use TYPO3\CMS\Form\Mvc\Validation\Exception\InvalidValidationOptionsException;
+
+/**
+ * Validator for countable types
+ *
+ * Scope: frontend
+ * @internal
+ */
+class FileSizeValidator extends AbstractValidator
+{
+    /**
+     * @var array
+     */
+    protected $supportedOptions = [
+        'minimum' => ['0B', 'The minimum file size to accept', 'string'],
+        'maximum' => [PHP_INT_MAX . 'B', 'The maximum file size to accept', 'string']
+    ];
+
+    /**
+     * The given value is valid
+     *
+     * @param FileReference|File $resource
+     * @api
+     */
+    public function isValid($resource)
+    {
+        $this->validateOptions();
+        if ($resource instanceof FileReference) {
+            $resource = $resource->getOriginalResource();
+        } elseif (!$resource instanceof File) {
+            $this->addError(
+                $this->translateErrorMessage(
+                    'validation.error.1505303626',
+                    'form'
+                ),
+                1505303626
+            );
+            return;
+        }
+
+        $fileSize = $resource->getSize();
+        $minFileSize = GeneralUtility::getBytesFromSizeMeasurement($this->options['minimum']);
+        $maxFileSize = GeneralUtility::getBytesFromSizeMeasurement($this->options['maximum']);
+
+        $labels = ' Bytes| Kilobyte| Megabyte| Gigabyte';
+        if ($fileSize < $minFileSize) {
+            $this->addError(
+                $this->translateErrorMessage(
+                    'validation.error.1505305752',
+                    'form',
+                    [GeneralUtility::formatSize($minFileSize, $labels)]
+                ),
+                1505305752,
+                [GeneralUtility::formatSize($minFileSize, $labels)]
+            );
+        }
+        if ($fileSize > $maxFileSize) {
+            $this->addError(
+                $this->translateErrorMessage(
+                    'validation.error.1505305753',
+                    'form',
+                    [GeneralUtility::formatSize($maxFileSize, $labels)]
+                ),
+                1505305753,
+                [GeneralUtility::formatSize($maxFileSize, $labels)]
+            );
+        }
+    }
+
+    /**
+     * Checks if this validator is correctly configured
+     *
+     * @throws InvalidValidationOptionsException if the configured validation options are incorrect
+     */
+    protected function validateOptions()
+    {
+        if (!preg_match('/^(\d*\.?\d+)(B|K|M|G)$/i', $this->options['minimum'])) {
+            throw new InvalidValidationOptionsException('The option "minimum" has an invalid format. Valid formats are something like this: "10B|K|M|G".', 1505304205);
+        }
+        if (!preg_match('/^(\d*\.?\d+)(B|K|M|G)$/i', $this->options['maximum'])) {
+            throw new InvalidValidationOptionsException('The option "maximum" has an invalid format. Valid formats are something like this: "10B|K|M|G".', 1505304206);
+        }
+    }
+}
index 7fc3d8b..bb445df 100644 (file)
@@ -362,6 +362,11 @@ TYPO3:
               #options:
                 #minimum: 0
                 #maximum: 0
+            FileSize:
+              implementationClassName: 'TYPO3\CMS\Form\Mvc\Validation\FileSizeValidator'
+              #options:
+                #minimum: '0B'
+                #maximum: '10M'
 
       ########### MIXINS ###########
       mixins:
index 61d1dd1..f9ce552 100644 (file)
@@ -113,6 +113,8 @@ TYPO3:
                 errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label'
               FormElementIdentifierWithinCurlyBracesExclusive:
                 errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label'
+              FileSize:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FileSize.label'
 
             formElementGroups:
               input:
@@ -970,6 +972,14 @@ TYPO3:
                   options:
                     minimum: ''
                     maximum: ''
+            FileSize:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.FileUploadMixin.validators.FileSize.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    minimum: '0B'
+                    maximum: '10M'
 
       ########### MIXINS ###########
       mixins:
@@ -1267,6 +1277,34 @@ TYPO3:
                     10:
                       value: '1:/user_upload/'
                       label: '1:/user_upload/'
+                900:
+                  identifier: 'validators'
+                  templateName: 'Inspector-ValidatorsEditor'
+                  label: 'formEditor.elements.FileUploadMixin.editor.validators.label'
+                  selectOptions:
+                    10:
+                      value: ''
+                      label: 'formEditor.elements.FileUploadMixin.editor.validators.EmptyValue.label'
+                    20:
+                      value: 'FileSize'
+                      label: 'formEditor.elements.FileUploadMixin.editor.validators.FileSize.label'
+
+              propertyCollections:
+                validators:
+                  10:
+                    identifier: 'FileSize'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.FileUploadMixin.validators.FileSize.editor.header.label'
+                      200:
+                        propertyValidators:
+                          10: 'FileSize'
+                      300:
+                        propertyValidators:
+                          10: 'FileSize'
 
           formEmailFinisherMixin:
             editors:
index 11d7588..3d12b67 100644 (file)
             <trans-unit id="formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label" xml:space="preserve">
                 <source>Invalid form element</source>
             </trans-unit>
+            <trans-unit id="formEditor.formElementPropertyValidatorsDefinition.FileSize.label" xml:space="preserve">
+                <source>Invalid file size format, valid e.g. "10B|K|M|G"</source>
+            </trans-unit>
 
             <trans-unit id="formEditor.formElementGroups.input.label" xml:space="preserve">
                 <source>Basic elements</source>
             <trans-unit id="formEditor.elements.FileUploadMixin.editor.saveToFileMount.label" xml:space="preserve">
                 <source>Uploads save path</source>
             </trans-unit>
+            <trans-unit id="formEditor.elements.FileUploadMixin.editor.validators.label" xml:space="preserve">
+                <source>Validators</source>
+            </trans-unit>
+            <trans-unit id="formEditor.elements.FileUploadMixin.editor.validators.EmptyValue.label" xml:space="preserve">
+                <source>Add validator</source>
+            </trans-unit>
+            <trans-unit id="formEditor.elements.FileUploadMixin.editor.validators.FileSize.label" xml:space="preserve">
+                <source>File size</source>
+            </trans-unit>
+            <trans-unit id="formEditor.elements.FileUploadMixin.validators.FileSize.editor.header.label" xml:space="preserve">
+                <source>File size</source>
+            </trans-unit>
 
             <trans-unit id="formEditor.elements.Form.editor.submitButtonLabel.label" xml:space="preserve">
                 <source>Submit label</source>
index e8db63f..845d152 100644 (file)
             </trans-unit>
 
             <trans-unit id="validation.error.1221560910" xml:space="preserve">
-                <source>This field is mandatory</source>
+                <source>This field is mandatory.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221560718" xml:space="preserve">
-                <source>This field is mandatory</source>
+                <source>This field is mandatory.</source>
             </trans-unit>
             <trans-unit id="validation.error.1347992400" xml:space="preserve">
-                <source>This field is mandatory</source>
+                <source>This field is mandatory.</source>
             </trans-unit>
             <trans-unit id="validation.error.1347992453" xml:space="preserve">
-                <source>This field is mandatory</source>
+                <source>This field is mandatory.</source>
             </trans-unit>
             <trans-unit id="validation.error.1238087674" xml:space="preserve">
-                <source>Please enter a valid Date</source>
+                <source>Please enter a valid Date.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221551320" xml:space="preserve">
-                <source>Please enter letters or digits</source>
+                <source>Please enter letters or digits.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221565786" xml:space="preserve">
-                <source>Please enter a valid text (e.g. without XML tags)</source>
+                <source>Please enter a valid text (e.g. without XML tags).</source>
             </trans-unit>
             <trans-unit id="validation.error.1238110957" xml:space="preserve">
-                <source>Please enter a valid text</source>
+                <source>Please enter a valid text.</source>
             </trans-unit>
             <trans-unit id="validation.error.1269883975" xml:space="preserve">
-                <source>Please enter a valid text</source>
+                <source>Please enter a valid text.</source>
             </trans-unit>
             <trans-unit id="validation.error.1428504122" xml:space="preserve">
-                <source>Please enter a text between %s and %s characters</source>
+                <source>Please enter a text between %s and %s characters.</source>
             </trans-unit>
             <trans-unit id="validation.error.1238108068" xml:space="preserve">
-                <source>Please enter a text which is longer than %s characters</source>
+                <source>Please enter a text which is longer than %s characters.</source>
             </trans-unit>
             <trans-unit id="validation.error.1238108069" xml:space="preserve">
-                <source>Please enter a text which is not longer than %s characters</source>
+                <source>Please enter a text which is not longer than %s characters.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221559976" xml:space="preserve">
-                <source>Please enter a valid email address</source>
+                <source>Please enter a valid email address.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221560494" xml:space="preserve">
-                <source>Please enter a valid number</source>
+                <source>Please enter a valid number.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221560288" xml:space="preserve">
-                <source>Please enter a valid floating point number</source>
+                <source>Please enter a valid floating point number.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221563685" xml:space="preserve">
-                <source>Please enter a valid number</source>
+                <source>Please enter a valid number.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221561046" xml:space="preserve">
-                <source>Please enter a valid number between %s and %s</source>
+                <source>Please enter a valid number between %s and %s.</source>
             </trans-unit>
             <trans-unit id="validation.error.1221565130" xml:space="preserve">
-                <source>Please enter a valid value</source>
+                <source>Please enter a valid value.</source>
             </trans-unit>
             <trans-unit id="validation.error.1475002976" xml:space="preserve">
                 <source>The given subject is not countable.</source>
                 <source>The media type "%s" is not allowed for this file.</source>
             </trans-unit>
             <trans-unit id="validation.error.1476396435" xml:space="preserve">
-                <source>Do not fill this field</source>
+                <source>Do not fill this field.</source>
+            </trans-unit>
+            <trans-unit id="validation.error.1505303626" xml:space="preserve">
+                <source>The given value was not an instance of \TYPO3\CMS\Extbase\Domain\Model\FileReference or \TYPO3\CMS\Core\Resource\File.</source>
+            </trans-unit>
+            <trans-unit id="validation.error.1505305752" xml:space="preserve">
+                <source>The file must be at least %s in size.</source>
+            </trans-unit>
+            <trans-unit id="validation.error.1505305753" xml:space="preserve">
+                <source>The file size can not exceed %s in size.</source>
             </trans-unit>
             <trans-unit id="form_new_wizard_title" xml:space="preserve">
                 <source>Form</source>
             <trans-unit id="form_new_wizard_description" xml:space="preserve">
                 <source>A form allowing website users to submit messages.</source>
             </trans-unit>
+            <trans-unit id="upload.error.150530345" xml:space="preserve">
+                <source>Maximum file size exceeded.</source>
+            </trans-unit>
+            <trans-unit id="upload.error.150530346" xml:space="preserve">
+                <source>The file is partially uploaded, please try again.</source>
+            </trans-unit>
+            <trans-unit id="upload.error.150530347" xml:space="preserve">
+                <source>No file was uploaded.</source>
+            </trans-unit>
+            <trans-unit id="upload.error.150530348" xml:space="preserve">
+                <source>Upload not possible.</source>
+            </trans-unit>
         </body>
     </file>
 </xliff>
index 761b606..17f63f1 100644 (file)
@@ -244,6 +244,12 @@ define(['jquery',
                     return getFormEditorApp().getFormElementPropertyValidatorDefinition('FormElementIdentifierWithinCurlyBracesInclusive')['errorMessage'] || 'invalid value';
                 }
             });
+
+            getFormEditorApp().addPropertyValidationValidator('FileSize', function(formElement, propertyPath) {
+                if (!formElement.get(propertyPath).match(/^(\d*\.?\d+)(B|K|M|G)$/i)) {
+                    return getFormEditorApp().getFormElementPropertyValidatorDefinition('FileSize')['errorMessage'] || 'invalid value';
+                }
+            });
         };
 
         /**
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Validation/FileSizeValidatorTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Validation/FileSizeValidatorTest.php
new file mode 100644 (file)
index 0000000..2ce79cf
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Validation;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\ResourceStorage;
+use TYPO3\CMS\Form\Mvc\Validation\Exception\InvalidValidationOptionsException;
+use TYPO3\CMS\Form\Mvc\Validation\FileSizeValidator;
+
+/**
+ * Test case
+ */
+class FileSizeValidatorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
+{
+
+    /**
+     * @test
+     */
+    public function FileSizeValidatorThrowsExceptionIfMinimumOptionIsInvalid()
+    {
+        $this->expectException(InvalidValidationOptionsException::class);
+        $this->expectExceptionCode(1505304205);
+
+        $options = ['minimum' => '0', 'maximum' => '1B'];
+        $validator = $this->getMockBuilder(FileSizeValidator::class)
+            ->setMethods(['translateErrorMessage'])
+            ->setConstructorArgs(['options' => $options])
+            ->getMock();
+
+        $validator->validate(true);
+    }
+
+    /**
+     * @test
+     */
+    public function FileSizeValidatorThrowsExceptionIfMaximumOptionIsInvalid()
+    {
+        $this->expectException(InvalidValidationOptionsException::class);
+        $this->expectExceptionCode(1505304206);
+
+        $options = ['minimum' => '0B', 'maximum' => '1'];
+        $validator = $this->getMockBuilder(FileSizeValidator::class)
+            ->setMethods(['translateErrorMessage'])
+            ->setConstructorArgs(['options' => $options])
+            ->getMock();
+
+        $validator->validate(true);
+    }
+
+    /**
+     * @test
+     */
+    public function FileSizeValidatorHasErrosIfFileResourceSizeIsToSmall()
+    {
+        $options = ['minimum' => '1M', 'maximum' => '10M'];
+        $validator = $this->getMockBuilder(FileSizeValidator::class)
+            ->setMethods(['translateErrorMessage'])
+            ->setConstructorArgs(['options' => $options])
+            ->getMock();
+
+        $mockedStorage = $this->getMockBuilder(ResourceStorage::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $file = new File(['identifier' => '/foo', 'size' => '1'], $mockedStorage);
+        $this->assertTrue($validator->validate($file)->hasErrors());
+    }
+
+    /**
+     * @test
+     */
+    public function FileSizeValidatorHasErrosIfFileResourceSizeIsToBig()
+    {
+        $options = ['minimum' => '1M', 'maximum' => '1M'];
+        $validator = $this->getMockBuilder(FileSizeValidator::class)
+            ->setMethods(['translateErrorMessage'])
+            ->setConstructorArgs(['options' => $options])
+            ->getMock();
+
+        $mockedStorage = $this->getMockBuilder(ResourceStorage::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $file = new File(['identifier' => '/foo', 'size' => '1048577'], $mockedStorage);
+        $this->assertTrue($validator->validate($file)->hasErrors());
+    }
+
+    /**
+     * @test
+     */
+    public function FileSizeValidatorHasNoErrorsIfInputIsEmptyString()
+    {
+        $options = ['minimum' => '0B', 'maximum' => '1M'];
+        $validator = $this->getMockBuilder(FileSizeValidator::class)
+            ->setMethods(['translateErrorMessage'])
+            ->setConstructorArgs(['options' => $options])
+            ->getMock();
+
+        $this->assertFalse($validator->validate('')->hasErrors());
+    }
+
+    /**
+     * @test
+     */
+    public function FileSizeValidatorHasErrorsIfInputIsNoFileResource()
+    {
+        $options = ['minimum' => '0B', 'maximum' => '1M'];
+        $validator = $this->getMockBuilder(FileSizeValidator::class)
+            ->setMethods(['translateErrorMessage'])
+            ->setConstructorArgs(['options' => $options])
+            ->getMock();
+
+        $this->assertTrue($validator->validate('string')->hasErrors());
+    }
+}