From ae8dbe309b797a20a91088351d70486cf3dfea79 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Tue, 31 Mar 2015 21:57:32 +1030 Subject: [PATCH] FEATURE - Added maximum upload file size by type This support is on both an instance level and a global default level. --- .../03_Forms/Field_types/05_UploadField.md | 23 +++++ filesystem/Upload.php | 59 +++++++++++- tests/filesystem/UploadTest.php | 95 ++++++++++++++++++- 3 files changed, 167 insertions(+), 10 deletions(-) diff --git a/docs/en/02_Developer_Guides/03_Forms/Field_types/05_UploadField.md b/docs/en/02_Developer_Guides/03_Forms/Field_types/05_UploadField.md index 31eef3c27..c0fde52df 100644 --- a/docs/en/02_Developer_Guides/03_Forms/Field_types/05_UploadField.md +++ b/docs/en/02_Developer_Guides/03_Forms/Field_types/05_UploadField.md @@ -148,6 +148,16 @@ NOTE: this only sets the configuration for your UploadField, this does NOT chang $this->getValidator()->setAllowedMaxFileSize($size); ``` +You can also specify a default global maximum file size setting in your config for different file types. This is overridden when specifying the max allowed file size on the UploadField instance. + +```yaml + Upload_Validator: + default_max_file_size: + '[image]': '1m' + '[doc]': '5m' + 'jpeg': 2000 +``` + ### Preview dimensions Set the dimensions of the image preview. By default the max width is set to 80 and the max height is set to 60. @@ -297,6 +307,19 @@ Certain default values for the above can be configured using the YAML config sys The above settings can also be set on a per-instance basis by using `setConfig` with the appropriate key. +The `Upload_Validator` class has configuration options for setting the `default_max_file_size`. + +```yaml + Upload_Validator: + default_max_file_size: + '[image]': '1m' + '[doc]': '5m' + 'jpeg': 2000 +``` + +You can specify the file extension or the app category (as specified in the `File` class) in square brackets. It supports setting the file size in bytes or using the syntax supported by `File::ini2bytes()`. + + You can also configure the underlying `[api:Upload]` class, by using the YAML config system. ```yaml diff --git a/filesystem/Upload.php b/filesystem/Upload.php index e69301855..92b22025c 100644 --- a/filesystem/Upload.php +++ b/filesystem/Upload.php @@ -305,6 +305,16 @@ class Upload extends Controller { */ class Upload_Validator { + /** + * Contains a list of the max file sizes shared by + * all upload fields. This is then duplicated into the + * "allowedMaxFileSize" instance property on construct. + * + * @config + * @var array + */ + private static $default_max_file_size = array(); + /** * Information about the temporary file produced * by the PHP-runtime. @@ -360,22 +370,46 @@ class Upload_Validator { * @return int Filesize in bytes */ public function getAllowedMaxFileSize($ext = null) { + + // Check if there is any defined instance max file sizes + if (empty($this->allowedMaxFileSize)) { + // Set default max file sizes if there isn't + $fileSize = Config::inst()->get('Upload_Validator', 'default_max_file_size'); + if (isset($fileSize)) { + $this->setAllowedMaxFileSize($fileSize); + } else { + // When no default is present, use maximum set by PHP + $maxUpload = File::ini2bytes(ini_get('upload_max_filesize')); + $maxPost = File::ini2bytes(ini_get('post_max_size')); + $this->setAllowedMaxFileSize(min($maxUpload, $maxPost)); + } + } + $ext = strtolower($ext); - if(isset($ext) && isset($this->allowedMaxFileSize[$ext])) { - return $this->allowedMaxFileSize[$ext]; + if ($ext) { + if (isset($this->allowedMaxFileSize[$ext])) { + return $this->allowedMaxFileSize[$ext]; + } + + $category = File::get_app_category($ext); + if ($category && isset($this->allowedMaxFileSize['[' . $category . ']'])) { + return $this->allowedMaxFileSize['[' . $category . ']']; + } + + return false; } else { return (isset($this->allowedMaxFileSize['*'])) ? $this->allowedMaxFileSize['*'] : false; } } /** - * Set filesize maximums (in bytes). + * Set filesize maximums (in bytes or INI format). * Automatically converts extensions to lowercase * for easier matching. * * Example: * - * array('*' => 200, 'jpg' => 1000) + * array('*' => 200, 'jpg' => 1000, '[doc]' => '5m') * * * @param array|int $rules @@ -384,7 +418,22 @@ class Upload_Validator { if(is_array($rules) && count($rules)) { // make sure all extensions are lowercase $rules = array_change_key_case($rules, CASE_LOWER); - $this->allowedMaxFileSize = $rules; + $finalRules = array(); + $tmpSize = 0; + + foreach ($rules as $rule => $value) { + if (is_numeric($value)) { + $tmpSize = $value; + } else { + $tmpSize = File::ini2bytes($value); + } + + $finalRules[$rule] = (int)$tmpSize; + } + + $this->allowedMaxFileSize = $finalRules; + } elseif(is_string($rules)) { + $this->allowedMaxFileSize['*'] = File::ini2bytes($rules); } elseif((int) $rules > 0) { $this->allowedMaxFileSize['*'] = (int)$rules; } diff --git a/tests/filesystem/UploadTest.php b/tests/filesystem/UploadTest.php index 77e4edb15..79467ed74 100644 --- a/tests/filesystem/UploadTest.php +++ b/tests/filesystem/UploadTest.php @@ -83,16 +83,101 @@ class UploadTest extends SapphireTest { 'extension' => 'txt', 'error' => UPLOAD_ERR_OK, ); - - $v = new UploadTest_Validator(); - $v->setAllowedMaxFileSize(array('txt' => 10)); - + // test upload into default folder $u1 = new Upload(); + $v = new UploadTest_Validator(); + + $v->setAllowedMaxFileSize(array('txt' => 10)); $u1->setValidator($v); $result = $u1->load($tmpFile); - $this->assertFalse($result, 'Load failed because size was too big'); + + $v->setAllowedMaxFileSize(array('[doc]' => 10)); + $u1->setValidator($v); + $result = $u1->load($tmpFile); + $this->assertFalse($result, 'Load failed because size was too big'); + + $v->setAllowedMaxFileSize(array('txt' => 200000)); + $u1->setValidator($v); + $result = $u1->load($tmpFile); + $this->assertTrue($result, 'Load failed with setting max file size'); + + // check max file size set by app category + $tmpFileName = 'UploadTest-testUpload.jpg'; + $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + file_put_contents($tmpFilePath, $tmpFileContent . $tmpFileContent); + + $tmpFile = array( + 'name' => $tmpFileName, + 'type' => 'image/jpeg', + 'size' => filesize($tmpFilePath), + 'tmp_name' => $tmpFilePath, + 'extension' => 'jpg', + 'error' => UPLOAD_ERR_OK, + ); + + $v->setAllowedMaxFileSize(array('[image]' => '40k')); + $u1->setValidator($v); + $result = $u1->load($tmpFile); + $this->assertTrue($result, 'Load failed with setting max file size'); + + $v->setAllowedMaxFileSize(array('[image]' => '1k')); + $u1->setValidator($v); + $result = $u1->load($tmpFile); + $this->assertFalse($result, 'Load failed because size was too big'); + + $v->setAllowedMaxFileSize(array('[image]' => 1000)); + $u1->setValidator($v); + $result = $u1->load($tmpFile); + $this->assertFalse($result, 'Load failed because size was too big'); + } + + public function testGetAllowedMaxFileSize() { + Config::nest(); + + // Check the max file size uses the config values + $configMaxFileSizes = array( + '[image]' => '1k', + 'txt' => 1000 + ); + Config::inst()->update('Upload_Validator', 'default_max_file_size', $configMaxFileSizes); + $v = new UploadTest_Validator(); + + $retrievedSize = $v->getAllowedMaxFileSize('[image]'); + $this->assertEquals(1024, $retrievedSize, 'Max file size check on default values failed (config category set check)'); + + $retrievedSize = $v->getAllowedMaxFileSize('txt'); + $this->assertEquals(1000, $retrievedSize, 'Max file size check on default values failed (config extension set check)'); + + // Check instance values for max file size + $maxFileSizes = array( + '[doc]' => 2000, + 'txt' => '4k' + ); + $v = new UploadTest_Validator(); + $v->setAllowedMaxFileSize($maxFileSizes); + + $retrievedSize = $v->getAllowedMaxFileSize('[doc]'); + $this->assertEquals(2000, $retrievedSize, 'Max file size check on instance values failed (instance category set check)'); + + // Check that the instance values overwrote the default values + // ie. The max file size will not exist for [image] + $retrievedSize = $v->getAllowedMaxFileSize('[image]'); + $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (config overridden check)'); + + // Check a category that has not been set before + $retrievedSize = $v->getAllowedMaxFileSize('[zip]'); + $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (category not set check)'); + + // Check a file extension that has not been set before + $retrievedSize = $v->getAllowedMaxFileSize('mp3'); + $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (extension not set check)'); + + $retrievedSize = $v->getAllowedMaxFileSize('txt'); + $this->assertEquals(4096, $retrievedSize, 'Max file size check on instance values failed (instance extension set check)'); + + Config::unnest(); } public function testAllowedSizeOnFileWithNoExtension() {