From b701b250a3d9b903a59913e1c0a563dfa59d7e29 Mon Sep 17 00:00:00 2001
From: muskie9 <nhorstmeier@gmail.com>
Date: Thu, 11 Aug 2016 17:51:47 -0500
Subject: [PATCH] ENHANCEMENT add customisable file upload size limit

---
 .../editableformfields/EditableFileField.php  | 57 ++++++++++++++--
 docs/en/installation.md                       |  2 +-
 tests/EditableFileFieldTest.php               | 67 +++++++++++++++++++
 3 files changed, 120 insertions(+), 6 deletions(-)
 create mode 100644 tests/EditableFileFieldTest.php

diff --git a/code/model/editableformfields/EditableFileField.php b/code/model/editableformfields/EditableFileField.php
index a66064e..10ddef6 100755
--- a/code/model/editableformfields/EditableFileField.php
+++ b/code/model/editableformfields/EditableFileField.php
@@ -5,7 +5,6 @@
  *
  * @package userforms
  */
-
 class EditableFileField extends EditableFormField
 {
 
@@ -13,6 +12,10 @@ class EditableFileField extends EditableFormField
 
     private static $plural_names = 'File Fields';
 
+    private static $db = array(
+        'MaxFileSizeMB' => 'Float',
+    );
+
     private static $has_one = array(
         'Folder' => 'Folder' // From CustomFields
     );
@@ -43,13 +46,35 @@ class EditableFileField extends EditableFormField
         );
 
         $fields->addFieldToTab("Root.Main", new LiteralField("FileUploadWarning",
-                "<p class=\"message notice\">" . _t("UserDefinedForm.FileUploadWarning",
+            "<p class=\"message notice\">" . _t("UserDefinedForm.FileUploadWarning",
                 "Files uploaded through this field could be publicly accessible if the exact URL is known")
-                . "</p>"), "Type");
+            . "</p>"), "Type");
+
+        $fields->addFieldToTab(
+            'Root.Main',
+            NumericField::create('MaxFileSizeMB')
+                ->setTitle('Max File Size MB')
+                ->setDescription("Note: Maximum php allowed size is {$this->getPHPMaxFileSize()} MB")
+        );
 
         return $fields;
     }
 
+    /**
+     * @return ValidationResult
+     */
+    public function validate()
+    {
+        $result = parent::validate();
+
+        $max = static::get_php_max_file_size();
+        if ($this->MaxFileSizeMB * 1024 > $max) {
+            $result->error("Your max file size limit can't be larger than the server's limit of {$this->getPHPMaxFileSizeMB()}.");
+        }
+
+        return $result;
+    }
+
     public function getFormField()
     {
         $field = FileField::create($this->Name, $this->EscapedTitle)
@@ -57,16 +82,22 @@ class EditableFileField extends EditableFormField
             ->setTemplate('UserFormsFileField');
 
         $field->setFieldHolderTemplate('UserFormsField_holder')
-                ->setTemplate('UserFormsFileField');
+            ->setTemplate('UserFormsFileField');
 
         $field->getValidator()->setAllowedExtensions(
             array_diff(
-                // filter out '' since this would be a regex problem on JS end
+            // filter out '' since this would be a regex problem on JS end
                 array_filter(Config::inst()->get('File', 'allowed_extensions')),
                 $this->config()->allowed_extensions_blacklist
             )
         );
 
+        if ($this->MaxFileSizeMB > 0) {
+            $field->getValidator()->setAllowedMaxFileSize($this->MaxFileSizeMB * 1024);
+        } else {
+            $field->getValidator()->setAllowedMaxFileSize(static::get_php_max_file_size());
+        }
+
         $folder = $this->Folder();
         if ($folder && $folder->exists()) {
             $field->setFolderName(
@@ -107,4 +138,20 @@ class EditableFileField extends EditableFormField
 
         parent::migrateSettings($data);
     }
+
+    /**
+     * @return float
+     */
+    public static function get_php_max_file_size()
+    {
+        $maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
+        $maxPost = File::ini2bytes(ini_get('post_max_size'));
+        return min($maxUpload, $maxPost);
+    }
+
+    public function getPHPMaxFileSizeMB()
+    {
+        return round(static::get_php_max_file_size() / 1024.0, 1);
+    }
+
 }
diff --git a/docs/en/installation.md b/docs/en/installation.md
index d3a5600..8f87897 100644
--- a/docs/en/installation.md
+++ b/docs/en/installation.md
@@ -33,7 +33,7 @@ and default to a safe set of files (e.g. disallowing `*.php` uploads).
 You can define further exclusions through the `EditableFileField.allowed_extensions_blacklist`
 configuration setting.
 
-The allowed upload size is determined by PHP configuration
+The allowed upload size can be set in the cms as long as it doesn't exceed the PHP configuration
 for this website (the smaller value of `upload_max_filesize` or `post_max_size`).
 
 The field is disabled by default since implementors need to determine how files are secured.
diff --git a/tests/EditableFileFieldTest.php b/tests/EditableFileFieldTest.php
new file mode 100644
index 0000000..46537fb
--- /dev/null
+++ b/tests/EditableFileFieldTest.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @package userforms
+ */
+class EditableFileFieldTest extends SapphireTest
+{
+
+    /**
+     * @var string
+     */
+    public static $fixture_file = 'userforms/tests/EditableFormFieldTest.yml';
+
+    /**
+     * @var
+     */
+    private $php_max_file_size;
+
+    /**
+     * Hold the server default max file size upload limit for later
+     */
+    public function setUp()
+    {
+        parent::setUp();
+
+        $editableFileField = singleton('EditableFileField');
+        $this->php_max_file_size = $editableFileField::get_php_max_file_size();
+
+    }
+
+    /**
+     * Test that the field validator has the server default as the max file size upload
+     */
+    public function testDefaultMaxFileSize()
+    {
+        $fileField = $this->objFromFixture('EditableFileField', 'file-field');
+        $formField = $fileField->getFormField();
+
+        $this->assertEquals($this->php_max_file_size, $formField->getValidator()->getAllowedMaxFileSize());
+    }
+
+    /**
+     * Test that validation prevents the provided upload size limit to be less than or equal to the max php size
+     */
+    public function testValidateFileSizeFieldValue()
+    {
+
+        $fileField = $this->objFromFixture('EditableFileField', 'file-field');
+        $this->setExpectedException('ValidationException');
+        $fileField->MaxFileSizeMB = $this->php_max_file_size * 2;
+        $fileField->write();
+    }
+
+    /**
+     * Test the field validator has the updated allowed max file size
+     */
+    public function testUpdatedMaxFileSize()
+    {
+        $fileField = $this->objFromFixture('EditableFileField', 'file-field');
+        $fileField->MaxFileSizeMB = .25;
+        $fileField->write();
+
+        $formField = $fileField->getFormField();
+        $this->assertEquals($formField->getValidator()->getAllowedMaxFileSize(), 256);
+    }
+
+}
\ No newline at end of file