Skip to content
Snippets Groups Projects
Commit a44a23e6 authored by catch's avatar catch
Browse files

Issue #1489692 by Liam Morland, pfrenssen, YesCT, geekinpink, sudishth,...

Issue #1489692 by Liam Morland, pfrenssen, YesCT, geekinpink, sudishth, josmera01, David_Rothstein: Incorrect handling of file upload limit exceeded - file widget disappears
parent 522ed005
Branches
Tags
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
......@@ -3,7 +3,7 @@
namespace Drupal\Core\Form\EventSubscriber;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Form\Exception\BrokenPostRequestException;
use Drupal\Core\Form\FormAjaxException;
......@@ -78,7 +78,7 @@ public function onException(GetResponseForExceptionEvent $event) {
$this->drupalSetMessage($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', ['@size' => $this->formatSize($exception->getSize())]), 'error');
$response = new AjaxResponse();
$status_messages = ['#type' => 'status_messages'];
$response->addCommand(new ReplaceCommand(NULL, $status_messages));
$response->addCommand(new PrependCommand(NULL, $status_messages));
$response->headers->set('X-Status-Code', 200);
$event->setResponse($response);
return;
......
<?php
namespace Drupal\Tests\file\Functional;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
/**
* Provides methods for creating file fields.
*/
trait FileFieldCreationTrait {
/**
* Creates a new file field.
*
* @param string $name
* The name of the new field (all lowercase), exclude the "field_" prefix.
* @param string $entity_type
* The entity type.
* @param string $bundle
* The bundle that this field will be added to.
* @param array $storage_settings
* A list of field storage settings that will be added to the defaults.
* @param array $field_settings
* A list of instance settings that will be added to the instance defaults.
* @param array $widget_settings
* A list of widget settings that will be added to the widget defaults.
*
* @return \Drupal\field\FieldStorageConfigInterface
* The file field.
*/
public function createFileField($name, $entity_type, $bundle, $storage_settings = [], $field_settings = [], $widget_settings = []) {
$field_storage = FieldStorageConfig::create([
'entity_type' => $entity_type,
'field_name' => $name,
'type' => 'file',
'settings' => $storage_settings,
'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1,
]);
$field_storage->save();
$this->attachFileField($name, $entity_type, $bundle, $field_settings, $widget_settings);
return $field_storage;
}
/**
* Attaches a file field to an entity.
*
* @param string $name
* The name of the new field (all lowercase), exclude the "field_" prefix.
* @param string $entity_type
* The entity type this field will be added to.
* @param string $bundle
* The bundle this field will be added to.
* @param array $field_settings
* A list of field settings that will be added to the defaults.
* @param array $widget_settings
* A list of widget settings that will be added to the widget defaults.
*/
public function attachFileField($name, $entity_type, $bundle, $field_settings = [], $widget_settings = []) {
$field = [
'field_name' => $name,
'label' => $name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'required' => !empty($field_settings['required']),
'settings' => $field_settings,
];
FieldConfig::create($field)->save();
entity_get_form_display($entity_type, $bundle, 'default')
->setComponent($name, [
'type' => 'file_generic',
'settings' => $widget_settings,
])
->save();
// Assign display settings.
entity_get_display($entity_type, $bundle, 'default')
->setComponent($name, [
'label' => 'hidden',
'type' => 'file_default',
])
->save();
}
}
......@@ -13,6 +13,8 @@
*/
abstract class FileFieldTestBase extends BrowserTestBase {
use FileFieldCreationTrait;
/**
* Modules to enable.
*
......@@ -57,76 +59,6 @@ public function getLastFileId() {
return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField();
}
/**
* Creates a new file field.
*
* @param string $name
* The name of the new field (all lowercase), exclude the "field_" prefix.
* @param string $entity_type
* The entity type.
* @param string $bundle
* The bundle that this field will be added to.
* @param array $storage_settings
* A list of field storage settings that will be added to the defaults.
* @param array $field_settings
* A list of instance settings that will be added to the instance defaults.
* @param array $widget_settings
* A list of widget settings that will be added to the widget defaults.
*/
public function createFileField($name, $entity_type, $bundle, $storage_settings = [], $field_settings = [], $widget_settings = []) {
$field_storage = FieldStorageConfig::create([
'entity_type' => $entity_type,
'field_name' => $name,
'type' => 'file',
'settings' => $storage_settings,
'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1,
]);
$field_storage->save();
$this->attachFileField($name, $entity_type, $bundle, $field_settings, $widget_settings);
return $field_storage;
}
/**
* Attaches a file field to an entity.
*
* @param string $name
* The name of the new field (all lowercase), exclude the "field_" prefix.
* @param string $entity_type
* The entity type this field will be added to.
* @param string $bundle
* The bundle this field will be added to.
* @param array $field_settings
* A list of field settings that will be added to the defaults.
* @param array $widget_settings
* A list of widget settings that will be added to the widget defaults.
*/
public function attachFileField($name, $entity_type, $bundle, $field_settings = [], $widget_settings = []) {
$field = [
'field_name' => $name,
'label' => $name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'required' => !empty($field_settings['required']),
'settings' => $field_settings,
];
FieldConfig::create($field)->save();
entity_get_form_display($entity_type, $bundle, 'default')
->setComponent($name, [
'type' => 'file_generic',
'settings' => $widget_settings,
])
->save();
// Assign display settings.
entity_get_display($entity_type, $bundle, 'default')
->setComponent($name, [
'label' => 'hidden',
'type' => 'file_default',
])
->save();
}
/**
* Updates an existing file field with new settings.
*/
......
<?php
namespace Drupal\Tests\file\FunctionalJavascript;
use Drupal\Component\Utility\Bytes;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\Tests\file\Functional\FileFieldCreationTrait;
/**
* Tests uploading a file that exceeds the maximum file size.
*
* @group file
*/
class MaximumFileSizeExceededUploadTest extends JavascriptTestBase {
use FileFieldCreationTrait;
use TestFileCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'file'];
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* A test user.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* The original value of the 'display_errors' PHP configuration option.
*
* @todo Remove this when issue #2905597 is fixed.
* @see https://www.drupal.org/node/2905597
*
* @var string
*/
protected $originalDisplayErrorsValue;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->fileSystem = $this->container->get('file_system');
// Create the Article node type.
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
// Attach a file field to the node type.
$field_settings = ['file_extensions' => 'txt'];
$this->createFileField('field_file', 'node', 'article', [], $field_settings);
// Log in as a content author who can create Articles.
$this->user = $this->drupalCreateUser([
'access content',
'create article content',
]);
$this->drupalLogin($this->user);
// Disable the displaying of errors, so that the AJAX responses are not
// contaminated with error messages about exceeding the maximum POST size.
// @todo Remove this when issue #2905597 is fixed.
// @see https://www.drupal.org/node/2905597
$this->originalDisplayErrorsValue = ini_set('display_errors', '0');
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
// Restore the displaying of errors to the original value.
// @todo Remove this when issue #2905597 is fixed.
// @see https://www.drupal.org/node/2905597
ini_set('display_errors', $this->originalDisplayErrorsValue);
parent::tearDown();
}
/**
* Tests that uploading files exceeding maximum size are handled correctly.
*/
public function testUploadFileExceedingMaximumFileSize() {
$session = $this->getSession();
// Create a test file that exceeds the maximum POST size with 1 kilobyte.
$post_max_size = Bytes::toInt(ini_get('post_max_size'));
$invalid_file = $this->generateFile('exceeding_post_max_size', ceil(($post_max_size + 1024) / 1024), 1024);
// Go to the node creation form and try to upload the test file.
$this->drupalGet('node/add/article');
$page = $session->getPage();
$page->attachFileToField("files[field_file_0]", $this->fileSystem->realpath($invalid_file));
// An error message should appear informing the user that the file exceeded
// the maximum file size.
$this->assertSession()->waitForElement('css', '.messages--error');
// The error message includes the actual file size limit which depends on
// the current environment, so we check for a part of the message.
$this->assertSession()->pageTextContains('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size');
// Now upload a valid file and check that the error message disappears.
$valid_file = $this->generateFile('not_exceeding_post_max_size', 8, 8);
$page->attachFileToField("files[field_file_0]", $this->fileSystem->realpath($valid_file));
$this->assertSession()->waitForElement('named', ['id_or_name', 'field_file_0_remove_button']);
$this->assertSession()->elementNotExists('css', '.messages--error');
}
}
......@@ -181,7 +181,7 @@ public function testOnExceptionBrokenPostRequest() {
$this->assertSame(200, $actual_response->headers->get('X-Status-Code'));
$expected_commands[] = [
'command' => 'insert',
'method' => 'replaceWith',
'method' => 'prepend',
'selector' => NULL,
'data' => $rendered_output,
'settings' => NULL,
......
......@@ -164,7 +164,8 @@ public static function generateFile($filename, $width, $lines, $type = 'binary-t
}
// Create filename.
file_put_contents('public://' . $filename . '.txt', $text);
$filename = 'public://' . $filename . '.txt';
file_put_contents($filename, $text);
return $filename;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment