From b83554bac17f0fa7e57b2b914dd17d4b1e7d9b19 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Fri, 14 Mar 2025 10:56:30 +0100 Subject: [PATCH 1/8] Apply patch from comment 32 --- core/lib/Drupal/Core/Form/FormBuilder.php | 65 +++++++++++++++++++ .../Drupal/Core/Form/FormBuilderInterface.php | 15 +++++ 2 files changed, 80 insertions(+) diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index ee3e4893feca..3b7114270394 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -18,6 +18,7 @@ use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Theme\ThemeManagerInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Symfony\Component\HttpFoundation\FileBag; use Symfony\Component\HttpFoundation\InputBag; use Symfony\Component\HttpFoundation\RequestStack; @@ -277,6 +278,7 @@ public function buildForm($form_arg, FormStateInterface &$form_state) { } $form = $this->retrieveForm($form_id, $form_state); + $this->addAsteriskExplanation($form_id, $form); $this->prepareForm($form_id, $form, $form_state); // self::setCache() removes uncacheable $form_state keys (see properties @@ -638,6 +640,69 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) { } } + /** + * {@inheritdoc} + */ + public function addAsteriskExplanation($form_id, array &$form) { + + $form_note_identifier = $form_id . '_required_fields_note'; + + foreach ($form as $form_key => $form_item) { + if (strpos($form_key, '#') === 0) { + // We'll skip over the special fields. + continue; + } + else { + // Check if type is primitive. + if (isset($form_item['#type']) && $this->isComplexElement($form_item['#type'])) { + $this->addAsteriskExplanation($form_id, $form_item); + if (isset($form_item[$form_note_identifier])) { + $form[$form_note_identifier] = $form_item[$form_note_identifier]; + unset($form_item[$form_note_identifier]); + return; + } + } + else { + if (isset($form_item['#required']) && (bool) $form_item['#required'] === TRUE) { + $form[$form_note_identifier] = [ + '#type' => 'markup', + '#markup' => new TranslatableMarkup('<strong>@strong-markup: </strong><label>@label-markup *.</label>', [ + '@strong-markup' => new TranslatableMarkup('Note'), + '@label-markup' => new TranslatableMarkup('Required fields are marked with an asterisk'), + ]), + '#weight' => INF, + ]; + + return; + } + } + } + } + return []; + } + + /** + * Checks to see if a specified type is used for grouping elements. + * + * @param string $type + * String representation of the type. + * + * @return bool + * Is the type in the array? + */ + public function isComplexElement($type) { + return in_array($type, [ + 'actions', + 'container', + 'details', + 'dropbutton', + 'fieldgroup', + 'fieldset', + 'form', + 'operations', + ]); + } + /** * Renders a form action URL. It's a #lazy_builder callback. * diff --git a/core/lib/Drupal/Core/Form/FormBuilderInterface.php b/core/lib/Drupal/Core/Form/FormBuilderInterface.php index 1a1b22403677..c875c03103d1 100644 --- a/core/lib/Drupal/Core/Form/FormBuilderInterface.php +++ b/core/lib/Drupal/Core/Form/FormBuilderInterface.php @@ -91,6 +91,21 @@ public function getForm($form_arg, mixed ...$args); */ public function buildForm($form_arg, FormStateInterface &$form_state); + /** + * Checks the form to see if any of the fields are required. + * + * If there is a required field it adds a text explaining what the asterisk means. + * + * @param string $form_id + * The unique string identifying the desired form. + * @param array $form + * An associative array containing the structure of the form. + * + * @return array + * The rendered form. + */ + public function addAsteriskExplanation($form_id, array &$form); + /** * Constructs a new $form from the information in $form_state. * -- GitLab From 4bdc18e777cad63ecb0bd2142899f3d56c9c72a4 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Fri, 14 Mar 2025 11:01:03 +0100 Subject: [PATCH 2/8] Move explanation to the beginning of the form --- core/lib/Drupal/Core/Form/FormBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index 3b7114270394..d2385c79e490 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -670,7 +670,7 @@ public function addAsteriskExplanation($form_id, array &$form) { '@strong-markup' => new TranslatableMarkup('Note'), '@label-markup' => new TranslatableMarkup('Required fields are marked with an asterisk'), ]), - '#weight' => INF, + '#weight' => -INF, ]; return; -- GitLab From b2212f4299eaad678e6ef7b7c65443acbcfd259c Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Fri, 14 Mar 2025 11:02:49 +0100 Subject: [PATCH 3/8] Fix return type The return value is never used --- core/lib/Drupal/Core/Form/FormBuilder.php | 5 ++--- core/lib/Drupal/Core/Form/FormBuilderInterface.php | 5 +---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index d2385c79e490..f73e62e95902 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -643,12 +643,12 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) { /** * {@inheritdoc} */ - public function addAsteriskExplanation($form_id, array &$form) { + public function addAsteriskExplanation(string $form_id, array &$form): void { $form_note_identifier = $form_id . '_required_fields_note'; foreach ($form as $form_key => $form_item) { - if (strpos($form_key, '#') === 0) { + if (str_starts_with($form_key, '#')) { // We'll skip over the special fields. continue; } @@ -678,7 +678,6 @@ public function addAsteriskExplanation($form_id, array &$form) { } } } - return []; } /** diff --git a/core/lib/Drupal/Core/Form/FormBuilderInterface.php b/core/lib/Drupal/Core/Form/FormBuilderInterface.php index c875c03103d1..52e44600fb65 100644 --- a/core/lib/Drupal/Core/Form/FormBuilderInterface.php +++ b/core/lib/Drupal/Core/Form/FormBuilderInterface.php @@ -100,11 +100,8 @@ public function buildForm($form_arg, FormStateInterface &$form_state); * The unique string identifying the desired form. * @param array $form * An associative array containing the structure of the form. - * - * @return array - * The rendered form. */ - public function addAsteriskExplanation($form_id, array &$form); + public function addAsteriskExplanation(string $form_id, array &$form): void; /** * Constructs a new $form from the information in $form_state. -- GitLab From 9e55f2a6b07af6981b4bc0d2bf568d935a0de233 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Fri, 14 Mar 2025 11:11:38 +0100 Subject: [PATCH 4/8] Add test --- .../Tests/Core/Form/FormBuilderTest.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index d7a364170f04..3ef88d25a2d9 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -19,6 +19,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountProxyInterface; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -1002,6 +1003,26 @@ public static function providerTestFormTokenCacheability() { ]; } + /** + * @covers ::addAsteriskExplanation + */ + function testAddAsteriskExplanation() { + $form_id = 'test_form_id'; + + // Tests without a required field. + $form = $form_id(); + $this->formBuilder->addAsteriskExplanation($form_id, $form); + $this->assertArrayNotHasKey('test_form_id_required_fields_note', $form); + + // Tests with a required field. + $form = $form_id(); + $form['test']['#required'] = TRUE; + $this->formBuilder->addAsteriskExplanation($form_id, $form); + $this->assertEquals('markup', $form['test_form_id_required_fields_note']['#type']); + $this->assertEquals(-INF, $form['test_form_id_required_fields_note']['#weight']); + $this->assertInstanceOf(TranslatableMarkup::class, $form['test_form_id_required_fields_note']['#markup']); + } + } /** -- GitLab From 6cd80ab3265ebeebb33ffb9135b738397607b28e Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Fri, 14 Mar 2025 11:19:09 +0100 Subject: [PATCH 5/8] Lint --- core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 3ef88d25a2d9..268a8f9becd4 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -1006,7 +1006,7 @@ public static function providerTestFormTokenCacheability() { /** * @covers ::addAsteriskExplanation */ - function testAddAsteriskExplanation() { + public function testAddAsteriskExplanation(): void { $form_id = 'test_form_id'; // Tests without a required field. -- GitLab From 39590754bf605a9f7ee345eed3a6e519ffafdb26 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Fri, 14 Mar 2025 11:44:58 +0100 Subject: [PATCH 6/8] Weight can't be a float --- core/lib/Drupal/Core/Form/FormBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index f73e62e95902..1ec6510d0951 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -670,7 +670,7 @@ public function addAsteriskExplanation(string $form_id, array &$form): void { '@strong-markup' => new TranslatableMarkup('Note'), '@label-markup' => new TranslatableMarkup('Required fields are marked with an asterisk'), ]), - '#weight' => -INF, + '#weight' => -1000, ]; return; -- GitLab From f9df8a5d31302a3b7caefe7269280b4304d88017 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Fri, 14 Mar 2025 11:50:38 +0100 Subject: [PATCH 7/8] Fix related test --- core/lib/Drupal/Core/Form/FormBuilder.php | 2 +- .../config/tests/src/Functional/ConfigFormOverrideTest.php | 2 +- core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index 1ec6510d0951..0f38132a975f 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -665,7 +665,7 @@ public function addAsteriskExplanation(string $form_id, array &$form): void { else { if (isset($form_item['#required']) && (bool) $form_item['#required'] === TRUE) { $form[$form_note_identifier] = [ - '#type' => 'markup', + '#type' => 'container', '#markup' => new TranslatableMarkup('<strong>@strong-markup: </strong><label>@label-markup *.</label>', [ '@strong-markup' => new TranslatableMarkup('Note'), '@label-markup' => new TranslatableMarkup('Required fields are marked with an asterisk'), diff --git a/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php b/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php index a693d3072383..1eb45e3d1e6e 100644 --- a/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php +++ b/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php @@ -70,7 +70,7 @@ public function testFormsWithOverrides(): void { $this->assertSession()->titleEquals('Basic site settings | ' . $overridden_name); $this->assertSession()->elementTextContains('css', 'div[data-drupal-messages]', self::OVERRIDE_TEXT); // Ensure the configuration overrides message is at the top of the form. - $this->assertSession()->elementExists('css', 'div[data-drupal-messages] + details#edit-site-information'); + $this->assertSession()->elementExists('css', 'div[data-drupal-messages] + div#edit-system-site-information-settings-required-fields-note'); $this->assertSession()->elementContains('css', 'div[data-drupal-messages]', '<a href="#edit-site-name" title="\'Site name\' form element">Site name</a>'); $this->assertSession()->fieldValueEquals("site_name", 'Drupal'); $this->submitForm([ diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 268a8f9becd4..a3502bc839b4 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -1018,8 +1018,8 @@ public function testAddAsteriskExplanation(): void { $form = $form_id(); $form['test']['#required'] = TRUE; $this->formBuilder->addAsteriskExplanation($form_id, $form); - $this->assertEquals('markup', $form['test_form_id_required_fields_note']['#type']); - $this->assertEquals(-INF, $form['test_form_id_required_fields_note']['#weight']); + $this->assertEquals('container', $form['test_form_id_required_fields_note']['#type']); + $this->assertEquals(-1000, $form['test_form_id_required_fields_note']['#weight']); $this->assertInstanceOf(TranslatableMarkup::class, $form['test_form_id_required_fields_note']['#markup']); } -- GitLab From 33b821e03091d579eba9707effca50d17489a1f7 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff <contact@rudloff.pro> Date: Mon, 24 Mar 2025 20:55:25 +0100 Subject: [PATCH 8/8] Comment is too long --- core/lib/Drupal/Core/Form/FormBuilderInterface.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Form/FormBuilderInterface.php b/core/lib/Drupal/Core/Form/FormBuilderInterface.php index 52e44600fb65..d7a6fb3fe13f 100644 --- a/core/lib/Drupal/Core/Form/FormBuilderInterface.php +++ b/core/lib/Drupal/Core/Form/FormBuilderInterface.php @@ -94,7 +94,8 @@ public function buildForm($form_arg, FormStateInterface &$form_state); /** * Checks the form to see if any of the fields are required. * - * If there is a required field it adds a text explaining what the asterisk means. + * If there is a required field + * it adds a text explaining what the asterisk means. * * @param string $form_id * The unique string identifying the desired form. -- GitLab