diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 8262e39..d331147 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -701,21 +701,19 @@ function file_cron() { * there was an error. Function returns NULL if no file was uploaded. */ function file_save_upload_from_form($element, FormStateInterface $form_state, $delta = NULL, $replace = FILE_EXISTS_RENAME) { - $destination = isset($element['#upload_location']) ? $element['#upload_location'] : FALSE; - $validators = isset($element['#upload_validators']) ? $element['#upload_validators'] : array(); + $errors_before = drupal_get_messages('error', TRUE); + $upload_location = isset($element['#upload_location']) ? $element['#upload_location'] : FALSE; $upload_name = implode('_', $element['#parents']); - $errors_before = drupal_get_messages('error'); + $upload_validators = isset($element['#upload_validators']) ? $element['#upload_validators'] : array(); - $files = file_save_upload($upload_name, $validators, $destination, $delta, $replace); + $result = file_save_upload($upload_name, $upload_validators, $upload_location, $delta, $replace); - // Get all possible new errors that are generated while trying to save the - // upload. - $new_errors = array_diff(drupal_get_messages('error'), $errors_before); + // Get new errors that are generated while trying to save the upload. + $errors_new = drupal_get_messages('error', TRUE); + if (!empty($errors_new['error'])) { + $errors_new = $errors_new['error']; - if (!empty($new_errors['error'])) { - $new_errors = $new_errors['error']; - - if (count($new_errors) > 1) { + if (count($errors_new) > 1) { // Render multiple errors into a single message. $render_array = array( 'error' => array( @@ -723,33 +721,31 @@ function file_save_upload_from_form($element, FormStateInterface $form_state, $d ), 'item_list' => array( '#theme' => 'item_list', - '#items' => $new_errors, + '#items' => $errors_new, ), ); $error_message = \Drupal::service('renderer')->renderPlain($render_array); } else { - $error_message = reset($new_errors); + $error_message = reset($errors_new); } $form_state->setError($element, $error_message); + } - // Empty the old queue and put the errors back as before. - drupal_get_messages('error', TRUE); - foreach ($errors_before as $error) { + // Put the errors back as before. + if (!empty($errors_before['error'])) { + foreach ($errors_before['error'] as $error) { drupal_set_message($error, 'error'); } } - return $files; + + return $result; } /** * Saves file uploads to a new location. * - * @deprecated in Drupal 8.3.0, intended to be removed in Drupal 9.0.0. - * This function should not be used during form validation, use - * file_save_upload_from_form() instead. - * * The files will be added to the {file_managed} table as temporary files. * Temporary files are periodically cleaned. Use the 'file.usage' service to * register the usage of the file which will automatically mark it as permanent. @@ -784,6 +780,12 @@ function file_save_upload_from_form($element, FormStateInterface $form_state, $d * An array of file entities or a single file entity if $delta != NULL. Each * array element contains the file entity if the upload succeeded or FALSE if * there was an error. Function returns NULL if no file was uploaded. + * + * @deprecated in Drupal 8.3.x, will be removed before Drupal 9.0.0. + * This function should not be used for form validation, use + * file_save_upload_from_form() instead. + * + * @see file_save_upload_from_form() */ function file_save_upload($form_field_name, $validators = array(), $destination = FALSE, $delta = NULL, $replace = FILE_EXISTS_RENAME) { $user = \Drupal::currentUser(); diff --git a/core/modules/file/src/Tests/SaveUploadFormTest.php b/core/modules/file/src/Tests/SaveUploadFormTest.php index 5d7f89f..90e8217 100644 --- a/core/modules/file/src/Tests/SaveUploadFormTest.php +++ b/core/modules/file/src/Tests/SaveUploadFormTest.php @@ -63,7 +63,7 @@ protected function setUp() { // Upload with replace to guarantee there's something there. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()), + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), ); $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); $this->assertResponse(200, 'Received a 200 response for posted test file.'); @@ -91,7 +91,7 @@ public function testNormal() { // Upload a second file. $image2 = current($this->drupalGetTestFiles('image')); - $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri)); + $edit = array('files[file_test_upload][]' => drupal_realpath($image2->uri)); $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); $this->assertResponse(200, 'Received a 200 response for posted test file.'); $this->assertRaw(t('You WIN!')); @@ -115,7 +115,7 @@ public function testNormal() { $image3_realpath = drupal_realpath($image3->uri); $dir = $this->randomMachineName(); $edit = array( - 'files[file_test_upload]' => $image3_realpath, + 'files[file_test_upload][]' => $image3_realpath, 'file_subdir' => $dir, ); $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); @@ -135,7 +135,7 @@ public function testHandleExtension() { $extensions = 'foo'; $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()), + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), 'extensions' => $extensions, ); @@ -155,7 +155,7 @@ public function testHandleExtension() { // Now tell file_save_upload() to allow the extension of our test image. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()), + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), 'extensions' => $extensions, ); @@ -173,7 +173,7 @@ public function testHandleExtension() { // Now tell file_save_upload() to allow any extension. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()), + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), 'allow_all_extensions' => TRUE, ); $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); @@ -194,7 +194,7 @@ public function testHandleDangerousFile() { // safety. Also check to make sure its MIME type was changed. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->phpfile->uri), + 'files[file_test_upload][]' => drupal_realpath($this->phpfile->uri), 'is_image_file' => FALSE, 'extensions' => 'php', ); @@ -241,7 +241,7 @@ public function testHandleFileMunge() { $extensions = $this->imageExtension; $edit = array( - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()), + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), 'extensions' => $extensions, ); @@ -263,7 +263,7 @@ public function testHandleFileMunge() { file_test_reset(); $edit = array( - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()), + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), 'allow_all_extensions' => TRUE, ); @@ -283,7 +283,7 @@ public function testHandleFileMunge() { public function testExistingRename() { $edit = array( 'file_test_replace' => FILE_EXISTS_RENAME, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()) + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()) ); $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); $this->assertResponse(200, 'Received a 200 response for posted test file.'); @@ -299,7 +299,7 @@ public function testExistingRename() { public function testExistingReplace() { $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()) + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()) ); $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); $this->assertResponse(200, 'Received a 200 response for posted test file.'); @@ -315,7 +315,7 @@ public function testExistingReplace() { public function testExistingError() { $edit = array( 'file_test_replace' => FILE_EXISTS_ERROR, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()) + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()) ); $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); $this->assertResponse(200, 'Received a 200 response for posted test file.'); @@ -344,7 +344,7 @@ public function testDrupalMovingUploadedFileError() { $edit = array( 'file_subdir' => $test_directory, - 'files[file_test_upload]' => drupal_realpath($this->image->getFileUri()) + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()) ); \Drupal::state()->set('file_test.disable_error_collection', TRUE); @@ -362,4 +362,65 @@ public function testDrupalMovingUploadedFileError() { )), 'Found upload error log entry.'); } + /** + * Tests form validation does not change error messages. + */ + public function testErrorMessagesAreNotChanged() { + $error = $this->randomMachineName(); + + $edit = [ + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), + 'error_message' => $error, + ]; + $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); + $this->assertResponse(200, 'Received a 200 response for posted test file.'); + $this->assertRaw(t('You WIN!'), 'Found the success message.'); + + $this->assertRaw(t('Error message @key before validation: @message', array('@key' => 0, '@message' => $error)), 'Custom error message is set before validation.'); + $this->assertRaw(t('Number of error messages before validation: @count', array('@count' => 1)), 'No extra error messages are set before validation.'); + $this->assertRaw(t('Error message @key after validation: @message', array('@key' => 0, '@message' => $error)), 'Custom error message is set after validation.'); + $this->assertRaw(t('Number of error messages after validation: @count', array('@count' => 1)), 'No extra error messages are set after validation.'); + } + + /** + * Test that multiple validation errors are combined in one message. + */ + public function testCombinedErrorMessages() { + $textfile = current($this->drupalGetTestFiles('text')); + $this->assertTrue(is_file($textfile->uri), 'The text file we are going to upload exists.'); + + $edit = [ + 'files[file_test_upload][]' => [ + drupal_realpath($this->phpfile->uri), + drupal_realpath($textfile->uri), + ], + 'allow_all_extensions' => FALSE, + 'is_image_file' => TRUE, + 'extensions' => 'jpeg', + ]; + + $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); + $this->assertResponse(200, 'Received a 200 response for posted test file.'); + $this->assertRaw(t('Epic upload FAIL!'), 'Found the failure message.'); + + // Search for combined error message followed by a formatted list of messages. + $this->assertRaw(t('One or more files could not be uploaded.') . '
', 'Error message contains combined list of validation errors.'); + } + + /** + * Tests that file upload field with highlighted when there is an error. + */ + public function testUploadFieldIsHighlighted() { + $this->assertEqual(0, count($this->cssSelect('input[name="files[file_test_upload][]"].error')), 'Successful file upload has no error.'); + + $edit = [ + 'files[file_test_upload][]' => drupal_realpath($this->image->getFileUri()), + 'extensions' => 'foo' + ]; + $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); + $this->assertResponse(200, 'Received a 200 response for posted test file.'); + $this->assertRaw(t('Epic upload FAIL!'), 'Found the failure message.'); + $this->assertEqual(1, count($this->cssSelect('input[name="files[file_test_upload][]"].error')), 'File upload field has error.'); + } + } diff --git a/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php b/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php index 8aa39b4..8500e39 100644 --- a/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php +++ b/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php @@ -23,6 +23,7 @@ public function getFormId() { public function buildForm(array $form, FormStateInterface $form_state) { $form['file_test_upload'] = array( '#type' => 'file', + '#multiple' => TRUE, '#title' => t('Upload a file'), ); $form['file_test_replace'] = array( @@ -59,6 +60,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => TRUE, ); + $form['error_message'] = array( + '#type' => 'textfield', + '#title' => t('Custom error message.'), + '#default_value' => '', + ); + $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), @@ -80,6 +87,11 @@ public function validateForm(array &$form, FormStateInterface $form_state) { $destination = FALSE; } + // Preset custom error message if requested. + if ($form_state->getValue('error_message')) { + drupal_set_message($form_state->getValue('error_message'), 'error'); + } + // Setup validators. $validators = array(); if ($form_state->getValue('is_image_file')) { @@ -103,7 +115,10 @@ public function validateForm(array &$form, FormStateInterface $form_state) { $form['file_test_upload']['#upload_validators'] = $validators; $form['file_test_upload']['#upload_location'] = $destination; + $messages_before = drupal_get_messages('error', FALSE) + array('error' => array()); $file = file_save_upload_from_form($form['file_test_upload'], $form_state, 0, $form_state->getValue('file_test_replace')); + $messages_after = drupal_get_messages('error', FALSE) + array('error' => array()); + if ($file) { $form_state->setValue('file_test_upload', $file); drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->getFileUri()))); @@ -114,6 +129,16 @@ public function validateForm(array &$form, FormStateInterface $form_state) { elseif ($file === FALSE) { drupal_set_message(t('Epic upload FAIL!'), 'error'); } + + drupal_set_message(t('Number of error messages before validation: @count', array('@count' => count($messages_before['error'])))); + foreach ($messages_before['error'] as $key => $message) { + drupal_set_message(t('Error message @key before validation: @message', array('@key' => $key, '@message' => $message))); + } + + drupal_set_message(t('Number of error messages after validation: @count', array('@count' => count($messages_after['error'])))); + foreach ($messages_after['error'] as $key => $message) { + drupal_set_message(t('Error message @key after validation: @message', array('@key' => $key, '@message' => $message))); + } } /**