diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php index 4f4a848..acb4825 100644 --- a/core/modules/file/src/Element/ManagedFile.php +++ b/core/modules/file/src/Element/ManagedFile.php @@ -287,14 +287,29 @@ public static function processManagedFile(&$element, FormStateInterface $form_st '#error_no_message' => TRUE, ]; + // We have to remove any excess files here because field cardinality + // validation runs before submit handlers which prevents removal of these + // files in a submit handler. if (!empty($fids) && $element['#files']) { - // We have to remove any excess files here because field cardinality - // validation runs before submit handlers and prevents handling these - // files in a submit handler. + // Persist handled fields to prevent removing too many elements. + static $handled_fields; + + // Existing files only have a remove button, determine if it was clicked. + $remove_clicked = FALSE; + if (!isset($handled_fields[$element['#field_name']])) { + $input = $form_state->getUserInput(); + // Handle non ajax requests. + $remove_clicked = isset($input[$element['remove_button']['#name']]); + // Handle ajax requests. + if (!$remove_clicked && isset($input['_triggering_element_name'])){ + $remove_clicked = $input['_triggering_element_name'] === $element['remove_button']['#name']; + } + } + $values = NestedArray::getValue($form_state->getValues(), $element['#parents']); if (isset($values['fids'])) { foreach ($values['fids'] as $key => $fid) { - if (isset($values['file_' . $fid]) && $values['file_' . $fid]['selected']) { + if ($remove_clicked || (isset($values['file_' . $fid]) && $values['file_' . $fid]['selected'])) { // If it's a temporary file we can safely remove it immediately, otherwise // it's up to the implementing module to remove usages of files to have them // removed. @@ -303,6 +318,10 @@ public static function processManagedFile(&$element, FormStateInterface $form_st } unset($element['#files'][$fid]); unset($values['fids'][$key]); + if ($remove_clicked) { + $handled_fields[$element['#field_name']] = true; + unset($complete_form[$element['#field_name']]['widget'][$element['#delta']]); + } } } NestedArray::setValue($form_state->getValues(), $element['#parents'], $values); diff --git a/core/modules/file/src/Tests/FileFieldWidgetTest.php b/core/modules/file/src/Tests/FileFieldWidgetTest.php index 294994c..70c2147 100644 --- a/core/modules/file/src/Tests/FileFieldWidgetTest.php +++ b/core/modules/file/src/Tests/FileFieldWidgetTest.php @@ -448,7 +448,7 @@ public function testWidgetElement() { // If the field has at least a item, the table should be visible. $this->assertIdentical(count($elements), 1); } - + /** * Tests removal of excess files when too many files were uploaded. */ @@ -462,18 +462,18 @@ function testExcessFileRemoval() { $test_file = $this->getTestFile('text'); $upload_files = [$test_file, $test_file]; - foreach (array('nojs', 'js') as $type) { + foreach (array('nojs', 'js', 'nojs-existing', 'js-existing') as $type) { // Try to upload multiple files, but fewer than the maximum. $nid = $this->uploadNodeFiles($upload_files, $field_name, $type_name); $node_storage->resetCache([$nid]); $node = $node_storage->load($nid); - $this->assertEqual(count($node->{$field_name}), count($upload_files), 'Node was successfully saved with mulitple files.'); + $this->assertEqual(count($node->{$field_name}), count($upload_files), 'Node was successfully saved with multiple files.'); // Try to upload more files than allowed on revision. $this->uploadNodeFiles($upload_files, $field_name, $nid, 1); $args = ['%field' => $field_name, '@count' => $cardinality]; - $this->assertRaw(t('%field: this field cannot hold more than @count values.', $args), 'Node with file displays too many files error.'); - $this->assertFieldByXpath('//input[@type="submit"]', t('Remove selected'), 'Node with file displays the "Remove selected" button.'); + $this->assertRaw(t('%field: this field cannot hold more than @count values.', $args), 'Too many files error displayed as expected.'); + $this->assertFieldByXpath('//input[@type="submit"]', t('Remove selected'), '"Remove selected" button displayed as expected.'); // Locate the last checkbox $last_fid = $this->getLastFileId(); @@ -483,12 +483,23 @@ function testExcessFileRemoval() { case 'nojs': $this->drupalPostForm(NULL, $edit, t('Remove selected')); break; + case 'js': $button = $this->xpath('//input[@type="submit" and @value="' . t('Remove selected') . '"]'); - $this->drupalPostAjaxForm(NULL, $edit, array((string) $button[0]['name'] => (string) $button[0]['value'])); + $this->drupalPostAjaxForm(NULL, $edit, [(string) $button[0]['name'] => (string) $button[0]['value']]); + break; + + case 'nojs-existing': + $this->drupalPostForm(NULL, [], t('Remove')); break; + + case 'js-existing': + $button = $this->xpath('//input[@type="submit" and @value="' . t('Remove') . '"]'); + $this->drupalPostAjaxForm(NULL, [], [(string) $button[0]['name'] => (string) $button[0]['value']]); + break; + } - $this->assertNoRaw(t('%field: this field cannot hold more than @count values.', $args), 'Excess files were succesfully removed'); + $this->assertNoRaw(t('%field: this field cannot hold more than @count values.', $args), 'Excess files were successfully removed'); } }