diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index bbfc2b2..b7aaf91 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -50,7 +50,6 @@ function template_preprocess_file_widget_multiple(&$variables) {
// Save the uploading row for last.
if (empty($widget['#files'])) {
$widget['#title'] = $element['#file_upload_title'];
- $widget['#description'] = \Drupal::service('renderer')->renderPlain($element['#file_upload_description']);
continue;
}
@@ -134,8 +133,8 @@ function template_preprocess_file_widget_multiple(&$variables) {
*
* @param array $variables
* An associative array containing:
- * - description: The normal description for this field, specified by the
- * user.
+ * - description: An optional description of files that can be uploaded, such
+ * as 'A Gettext Portable Object file'.
* - upload_validators: An array of upload validators as used in
* $element['#upload_validators'].
*/
diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php
index 1d9a742..227a6e1 100644
--- a/core/modules/file/src/Element/ManagedFile.php
+++ b/core/modules/file/src/Element/ManagedFile.php
@@ -287,6 +287,15 @@ public static function processManagedFile(&$element, FormStateInterface $form_st
'#error_no_message' => TRUE,
];
+ // Add a description of the validators if any are present.
+ if (!empty($element['#upload_validators'])) {
+ $element['upload_validator_description'] = [
+ '#theme' => 'file_upload_help',
+ '#upload_validators' => $element['#upload_validators'],
+ '#cardinality' => isset($element['#cardinality']) ? $element['#cardinality'] : NULL,
+ ];
+ }
+
if (!empty($fids) && $element['#files']) {
foreach ($element['#files'] as $delta => $file) {
$file_link = [
@@ -348,6 +357,7 @@ public static function preRenderManagedFile($element) {
if (!$element['#multiple']) {
$element['upload']['#access'] = FALSE;
$element['upload_button']['#access'] = FALSE;
+ $element['upload_validator_description']['#access'] = FALSE;
}
}
// If we don't already have a file, there is nothing to remove.
diff --git a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
index 944e102..8c8bfb7 100644
--- a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
+++ b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
@@ -161,10 +161,12 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
if ($empty_single_allowed || $empty_multiple_allowed) {
// Create a new empty item.
$items->appendItem();
- $element = array(
- '#title' => $title,
- '#description' => $description,
- );
+ $element = ['#title' => $title];
+ // Single-value fields display the description on the individual widget
+ // itself; multi-value fields are handled further down.
+ if (!$is_multiple) {
+ $element['#description'] = $description;
+ }
$element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
if ($element) {
$element['#required'] = ($element['#required'] && $delta == 0);
@@ -197,12 +199,6 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
// field. These are added here so that they may be referenced easily
// through a hook_form_alter().
$elements['#file_upload_title'] = t('Add a new file');
- $elements['#file_upload_description'] = array(
- '#theme' => 'file_upload_help',
- '#description' => '',
- '#upload_validators' => $elements[0]['#upload_validators'],
- '#cardinality' => $cardinality,
- );
}
return $elements;
@@ -262,13 +258,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
$default_fids = $element['#extended'] ? $element['#default_value']['fids'] : $element['#default_value'];
if (empty($default_fids)) {
- $file_upload_help = array(
- '#theme' => 'file_upload_help',
- '#description' => $element['#description'],
- '#upload_validators' => $element['#upload_validators'],
- '#cardinality' => $cardinality,
- );
- $element['#description'] = \Drupal::service('renderer')->renderPlain($file_upload_help);
$element['#multiple'] = $cardinality != 1 ? TRUE : FALSE;
if ($cardinality != 1 && $cardinality != -1) {
$element['#element_validate'] = array(array(get_class($this), 'validateMultipleCount'));
diff --git a/core/modules/file/src/Tests/FileManagedFileElementTest.php b/core/modules/file/src/Tests/FileManagedFileElementTest.php
index e1528bc..971c1ce 100644
--- a/core/modules/file/src/Tests/FileManagedFileElementTest.php
+++ b/core/modules/file/src/Tests/FileManagedFileElementTest.php
@@ -26,100 +26,98 @@ function testManagedFile() {
// Perform the tests with all permutations of $form['#tree'],
// $element['#extended'], and $element['#multiple'].
$test_file = $this->getTestFile('text');
- foreach (array(0, 1) as $tree) {
- foreach (array(0, 1) as $extended) {
- foreach (array(0, 1) as $multiple) {
- $path = 'file/test/' . $tree . '/' . $extended . '/' . $multiple;
- $input_base_name = $tree ? 'nested_file' : 'file';
- $file_field_name = $multiple ? 'files[' . $input_base_name . '][]' : 'files[' . $input_base_name . ']';
-
- // Submit without a file.
- $this->drupalPostForm($path, array(), t('Save'));
- $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), 'Submitted without a file.');
-
- // Submit with a file, but with an invalid form token. Ensure the file
- // was not saved.
- $last_fid_prior = $this->getLastFileId();
- $edit = [
- $file_field_name => drupal_realpath($test_file->getFileUri()),
- 'form_token' => 'invalid token',
- ];
- $this->drupalPostForm($path, $edit, t('Save'));
- $this->assertText('The form has become outdated. Copy any unsaved work in the form below');
- $last_fid = $this->getLastFileId();
- $this->assertEqual($last_fid_prior, $last_fid, 'File was not saved when uploaded with an invalid form token.');
-
- // Submit a new file, without using the Upload button.
- $last_fid_prior = $this->getLastFileId();
- $edit = array($file_field_name => drupal_realpath($test_file->getFileUri()));
- $this->drupalPostForm($path, $edit, t('Save'));
- $last_fid = $this->getLastFileId();
- $this->assertTrue($last_fid > $last_fid_prior, 'New file got saved.');
- $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), 'Submit handler has correct file info.');
-
- // Submit no new input, but with a default file.
- $this->drupalPostForm($path . '/' . $last_fid, array(), t('Save'));
- $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), 'Empty submission did not change an existing file.');
-
- // Now, test the Upload and Remove buttons, with and without Ajax.
- foreach (array(FALSE, TRUE) as $ajax) {
- // Upload, then Submit.
- $last_fid_prior = $this->getLastFileId();
- $this->drupalGet($path);
- $edit = array($file_field_name => drupal_realpath($test_file->getFileUri()));
- if ($ajax) {
- $this->drupalPostAjaxForm(NULL, $edit, $input_base_name . '_upload_button');
- }
- else {
- $this->drupalPostForm(NULL, $edit, t('Upload'));
- }
- $last_fid = $this->getLastFileId();
- $this->assertTrue($last_fid > $last_fid_prior, 'New file got uploaded.');
- $this->drupalPostForm(NULL, array(), t('Save'));
- $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), 'Submit handler has correct file info.');
-
- // Remove, then Submit.
- $remove_button_title = $multiple ? t('Remove selected') : t('Remove');
- $remove_edit = array();
- if ($multiple) {
- $selected_checkbox = ($tree ? 'nested[file]' : 'file') . '[file_' . $last_fid . '][selected]';
- $remove_edit = array($selected_checkbox => '1');
- }
- $this->drupalGet($path . '/' . $last_fid);
- if ($ajax) {
- $this->drupalPostAjaxForm(NULL, $remove_edit, $input_base_name . '_remove_button');
- }
- else {
- $this->drupalPostForm(NULL, $remove_edit, $remove_button_title);
- }
- $this->drupalPostForm(NULL, array(), t('Save'));
- $this->assertRaw(t('The file ids are %fids.', array('%fids' => '')), 'Submission after file removal was successful.');
-
- // Upload, then Remove, then Submit.
- $this->drupalGet($path);
- $edit = array($file_field_name => drupal_realpath($test_file->getFileUri()));
- if ($ajax) {
- $this->drupalPostAjaxForm(NULL, $edit, $input_base_name . '_upload_button');
- }
- else {
- $this->drupalPostForm(NULL, $edit, t('Upload'));
- }
- $remove_edit = array();
- if ($multiple) {
- $selected_checkbox = ($tree ? 'nested[file]' : 'file') . '[file_' . $this->getLastFileId() . '][selected]';
- $remove_edit = array($selected_checkbox => '1');
- }
- if ($ajax) {
- $this->drupalPostAjaxForm(NULL, $remove_edit, $input_base_name . '_remove_button');
- }
- else {
- $this->drupalPostForm(NULL, $remove_edit, $remove_button_title);
- }
-
- $this->drupalPostForm(NULL, array(), t('Save'));
- $this->assertRaw(t('The file ids are %fids.', array('%fids' => '')), 'Submission after file upload and removal was successful.');
- }
+ foreach ($this->generateContext() as $context) {
+ $path = $context['path'];
+ $input_base_name = $context['input_base_name'];
+ $file_field_name = $context['file_field_name'];
+ $tree = $context['tree'];
+ $multiple = $context['multiple'];
+
+ // Submit without a file.
+ $this->drupalPostForm($path, array(), t('Save'));
+ $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), 'Submitted without a file.');
+
+ // Submit with a file, but with an invalid form token. Ensure the file
+ // was not saved.
+ $last_fid_prior = $this->getLastFileId();
+ $edit = [
+ $file_field_name => drupal_realpath($test_file->getFileUri()),
+ 'form_token' => 'invalid token',
+ ];
+ $this->drupalPostForm($path, $edit, t('Save'));
+ $this->assertText('The form has become outdated. Copy any unsaved work in the form below');
+ $last_fid = $this->getLastFileId();
+ $this->assertEqual($last_fid_prior, $last_fid, 'File was not saved when uploaded with an invalid form token.');
+
+ // Submit a new file, without using the Upload button.
+ $last_fid_prior = $this->getLastFileId();
+ $edit = array($file_field_name => drupal_realpath($test_file->getFileUri()));
+ $this->drupalPostForm($path, $edit, t('Save'));
+ $last_fid = $this->getLastFileId();
+ $this->assertTrue($last_fid > $last_fid_prior, 'New file got saved.');
+ $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), 'Submit handler has correct file info.');
+
+ // Submit no new input, but with a default file.
+ $this->drupalPostForm($path . '/' . $last_fid, array(), t('Save'));
+ $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), 'Empty submission did not change an existing file.');
+
+ // Now, test the Upload and Remove buttons, with and without Ajax.
+ foreach (array(FALSE, TRUE) as $ajax) {
+ // Upload, then Submit.
+ $last_fid_prior = $this->getLastFileId();
+ $this->drupalGet($path);
+ $edit = array($file_field_name => drupal_realpath($test_file->getFileUri()));
+ if ($ajax) {
+ $this->drupalPostAjaxForm(NULL, $edit, $input_base_name . '_upload_button');
+ }
+ else {
+ $this->drupalPostForm(NULL, $edit, t('Upload'));
+ }
+ $last_fid = $this->getLastFileId();
+ $this->assertTrue($last_fid > $last_fid_prior, 'New file got uploaded.');
+ $this->drupalPostForm(NULL, array(), t('Save'));
+ $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), 'Submit handler has correct file info.');
+
+ // Remove, then Submit.
+ $remove_button_title = $multiple ? t('Remove selected') : t('Remove');
+ $remove_edit = array();
+ if ($multiple) {
+ $selected_checkbox = ($tree ? 'nested[file]' : 'file') . '[file_' . $last_fid . '][selected]';
+ $remove_edit = array($selected_checkbox => '1');
+ }
+ $this->drupalGet($path . '/' . $last_fid);
+ if ($ajax) {
+ $this->drupalPostAjaxForm(NULL, $remove_edit, $input_base_name . '_remove_button');
+ }
+ else {
+ $this->drupalPostForm(NULL, $remove_edit, $remove_button_title);
+ }
+ $this->drupalPostForm(NULL, array(), t('Save'));
+ $this->assertRaw(t('The file ids are %fids.', array('%fids' => '')), 'Submission after file removal was successful.');
+
+ // Upload, then Remove, then Submit.
+ $this->drupalGet($path);
+ $edit = array($file_field_name => drupal_realpath($test_file->getFileUri()));
+ if ($ajax) {
+ $this->drupalPostAjaxForm(NULL, $edit, $input_base_name . '_upload_button');
+ }
+ else {
+ $this->drupalPostForm(NULL, $edit, t('Upload'));
}
+ $remove_edit = array();
+ if ($multiple) {
+ $selected_checkbox = ($tree ? 'nested[file]' : 'file') . '[file_' . $this->getLastFileId() . '][selected]';
+ $remove_edit = array($selected_checkbox => '1');
+ }
+ if ($ajax) {
+ $this->drupalPostAjaxForm(NULL, $remove_edit, $input_base_name . '_remove_button');
+ }
+ else {
+ $this->drupalPostForm(NULL, $remove_edit, $remove_button_title);
+ }
+
+ $this->drupalPostForm(NULL, array(), t('Save'));
+ $this->assertRaw(t('The file ids are %fids.', array('%fids' => '')), 'Submission after file upload and removal was successful.');
}
}
@@ -176,4 +174,61 @@ public function testManagedFileRemoved() {
$this->assertRaw('The file referenced by the Managed file & butter field does not exist.');
}
+ /**
+ * Ensure that a validator description is shown when there's a file upload
+ * element with core validators attached.
+ */
+ public function testManagedFileValidatorDescription() {
+ $extensions = 'png gif jpg jpeg';
+ $description = t('Allowed types: @extensions', array('@extensions' => $extensions));
+ foreach ($this->generateContext() as $context) {
+ // Check that there's no validator description for an empty file element
+ // without validators.
+ $this->drupalGet($context['path']);
+ $this->assertNoRaw($description, "An empty file element without validators doesn't show a validator description.");
+
+ // Check that a validator description shows up for an empty file element
+ // with validators.
+ $this->drupalGet($context['path'] . "/empty/$extensions");
+ $this->assertRaw($description, "An empty file element with validators shows a validator description");
+
+ $test_file = $this->getTestFile('image');
+ $edit = [$context['file_field_name'] => drupal_realpath($test_file->getFileUri())];
+ $this->drupalPostForm(NULL, $edit, t('Upload'));
+ if ($context['multiple']) {
+ // Check that there's a validator description for a populated multi-
+ // value file element.
+ $this->assertRaw($description, "A populated multi-value file element still displays the validator description.");
+ }
+ else {
+ // Check that there's no validator description for a populated single
+ // value file element.
+ $this->assertNoRaw($description, "A populated single valued file element doesn't display the validator description");
+ }
+ }
+ }
+
+ /**
+ * Generates all combinations of the context for testing a managed file
+ * element.
+ *
+ * @return \Generator
+ */
+ private function generateContext() {
+ foreach ([0, 1] as $tree) {
+ $input_base_name = $tree ? 'nested_file' : 'file';
+ foreach ([0, 1] as $extended) {
+ foreach ([0, 1] as $multiple) {
+ yield [
+ 'tree' => $tree,
+ 'extended' => $extended,
+ 'multiple' => $multiple,
+ 'path' => 'file/test/' . $tree . '/' . $extended . '/' . $multiple,
+ 'input_base_name' => $input_base_name,
+ 'file_field_name' => $multiple ? 'files[' . $input_base_name . '][]' : 'files[' . $input_base_name . ']',
+ ];
+ }
+ }
+ }
+ }
}
diff --git a/core/modules/file/templates/file-upload-help.html.twig b/core/modules/file/templates/file-upload-help.html.twig
index 8fa6b3e..8c94ca4 100644
--- a/core/modules/file/templates/file-upload-help.html.twig
+++ b/core/modules/file/templates/file-upload-help.html.twig
@@ -11,4 +11,6 @@
* @ingroup themeable
*/
#}
-{{ descriptions|safe_join('
') }}
+