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

Issue #2408549 by alexpott, narendraR, yash.rode, kunal.sachdev, lauriii, Liam...

Issue #2408549 by alexpott, narendraR, yash.rode, kunal.sachdev, lauriii, Liam Morland, Wim Leers, Hardik_Patel_12, jofitz, DamienMcKenna, eiriksm, andypost, jenlampton, Gábor Hojtsy, swentel, borisson_, jhedstrom, snehi, Elijah Lynn, narendra.rajwar27, Shubham Chandra, smustgrave, sime, AaronMcHale, Chi, karolus, rkoller, joshua.boltz, anavarre, colan, frob, Berdir, bircher, minnur, effulgentsia, quietone, catch, xjm, hanoii, benjifisher, worldlinemine, larowlan, longwave, simohell, shaal, worldlinemine: Display status message on configuration forms when there are overridden values

(cherry picked from commit 46895166)
parent de3e9ac6
Branches
Tags
20 merge requests!10663Issue #3495778: Update phpdoc in FileSaveHtaccessLoggingTest,!10451Issue #3472458 by watergate, smustgrave: CKEditor 5 show blocks label is not translated,!103032838547 Fix punctuation rules for inline label suffix colon with CSS only,!10150Issue #3467294 by quietone, nod_, smustgrave, catch, longwave: Change string...,!10130Resolve #3480321 "Second level menu",!9936Issue #3483087: Check the module:// prefix in the translation server path and replace it with the actual module path,!9933Issue #3394728 by ankondrat4: Undefined array key "#prefix" and deprecated function: explode() in Drupal\file\Element\ManagedFile::uploadAjaxCallback(),!9914Issue #3451136 by quietone, gapple, ghost of drupal past: Improve...,!9882Draft: Issue #3481777 In bulk_form ensure the triggering element is the bulk_form button,!9839Issue #3445469 by pooja_sharma, smustgrave: Add additional test coverage for...,!9815Issue #3480025: There is no way to remove entity cache items,!9757Issue #3478869 Add "All" or overview links to parent links,!9752Issue #3439910 by pooja_sharma, vensires: Fix Toolbar tests that rely on UID1's super user behavior,!9749Issue #3439910 by pooja_sharma, vensires: Fix Toolbar tests that rely on UID1's super user behavior,!9678Issue #3465132 by catch, Spokje, nod_: Show test run time by class in run-tests.sh output,!9578Issue #3304746 by scott_euser, casey, smustgrave: BigPipe cannot handle (GET)...,!9449Issue #3344041: Allow textarea widgets to be used for text (formatted) fields,!8945🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥...,!8893Resolve #3444391 "Navigation center sm logo",!8772Issue #3445909 by seanB, smustgrave, alexpott, catch: Add static caching to...
Pipeline #220900 passed with warnings
Pipeline: drupal

#220916

    Pipeline: drupal

    #220912

      Pipeline: drupal

      #220904

        Showing with 189 additions and 31 deletions
        ......@@ -2,11 +2,13 @@
        namespace Drupal\Core\Form;
        use Drupal\Component\Utility\NestedArray;
        use Drupal\Core\Config\Config;
        use Drupal\Core\Config\ConfigFactoryInterface;
        use Drupal\Core\Config\TypedConfigManagerInterface;
        use Drupal\Core\Render\Element;
        use Drupal\Core\StringTranslation\TranslatableMarkup;
        use Drupal\Core\Url;
        use Symfony\Component\DependencyInjection\ContainerInterface;
        /**
        ......@@ -86,7 +88,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
        // property.
        $form['#process'][] = '::loadDefaultValuesFromConfig';
        $form['#after_build'][] = '::storeConfigKeyToFormElementMap';
        $form['#after_build'][] = '::checkConfigOverrides';
        return $form;
        }
        ......@@ -333,4 +335,58 @@ private static function copyFormValuesToConfig(Config $config, FormStateInterfac
        }
        }
        /**
        * Form #after_build callback: Adds message if overrides exist.
        */
        public function checkConfigOverrides(array $form, FormStateInterface $form_state): array {
        // Determine which of those editable config keys have overrides.
        $override_links = [];
        $map = $form_state->get(static::CONFIG_KEY_TO_FORM_ELEMENT_MAP) ?? [];
        foreach ($map as $config_name => $config_keys) {
        $stored_config = $this->configFactory->get($config_name);
        if (!$stored_config->hasOverrides()) {
        // The config has no overrides at all. Can be skipped.
        continue;
        }
        foreach ($config_keys as $key => $array_parents) {
        if ($stored_config->hasOverrides($key)) {
        $element = NestedArray::getValue($form, $array_parents);
        $override_links[] = [
        'attributes' => ['title' => $this->t("'@title' form element", ['@title' => $element['#title']])],
        'url' => Url::fromUri("internal:#{$element['#id']}"),
        'title' => $element['#title'],
        ];
        }
        }
        }
        if (!empty($override_links)) {
        $override_output = [
        '#theme' => 'links__config_overrides',
        '#heading' => [
        'text' => $this->t('These values are overridden. Changes on this form will be saved, but overrides will take precedence. See <a href="https://www.drupal.org/docs/drupal-apis/configuration-api/configuration-override-system">configuration overrides documentation</a> for more information.'),
        'level' => 'div',
        ],
        '#links' => $override_links,
        ];
        $form['config_override_status_messages'] = [
        'message' => [
        '#theme' => 'status_messages',
        '#message_list' => ['status' => [$override_output]],
        '#status_headings' => [
        'status' => $this->t('Status message'),
        ],
        ],
        // Ensure that the status message is at the top of the form.
        '#weight' => array_reduce(
        Element::children($form),
        fn (int $carry, string $key) => min(($form[$key]['#weight'] ?? 0), $carry),
        0
        ) - 1,
        ];
        }
        return $form;
        }
        }
        name: 'Configuration override message test'
        type: module
        package: Testing
        version: VERSION
        <?php
        /**
        * @file
        * Tests configuration override message functionality.
        */
        use Drupal\Core\Form\FormStateInterface;
        /**
        * Implements hook_form_FORM_ID_alter().
        */
        function config_override_message_test_form_system_site_information_settings_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
        // Set a weight to a negative amount to ensure the config overrides message
        // is above it.
        $form['site_information']['#weight'] = -5;
        }
        ......@@ -14,6 +14,18 @@
        */
        class ConfigFormOverrideTest extends BrowserTestBase {
        /**
        * Message text that appears when forms have values for overridden config.
        */
        private const OVERRIDE_TEXT = 'These values are overridden. Changes on this form will be saved, but overrides will take precedence. See configuration overrides documentation for more information.';
        /**
        * Modules to enable.
        *
        * @var array
        */
        protected static $modules = ['update', 'config_override_message_test'];
        /**
        * {@inheritdoc}
        */
        ......@@ -26,31 +38,61 @@ public function testFormsWithOverrides(): void {
        $this->drupalLogin($this->drupalCreateUser([
        'access administration pages',
        'administer site configuration',
        'link to any page',
        ]));
        $overridden_name = 'Site name global conf override';
        // Set up an overrides for configuration that is present in the form.
        $settings['config']['system.site']['weight_select_max'] = (object) [
        'value' => 200,
        'required' => TRUE,
        ];
        $this->writeSettings($settings);
        // Set up an override.
        // Test that although system.site has an overridden key no override
        // information is displayed because there is no corresponding form field.
        $this->drupalGet('admin/config/system/site-information');
        $this->assertSession()->fieldValueEquals("site_name", 'Drupal');
        $this->assertSession()->pageTextNotContains(self::OVERRIDE_TEXT);
        // Set up an overrides for configuration that is present in the form.
        $overridden_name = 'Site name global conf override';
        $settings['config']['system.site']['name'] = (object) [
        'value' => $overridden_name,
        'required' => TRUE,
        ];
        $settings['config']['update.settings']['notification']['emails'] = (object) [
        'value' => [
        0 => 'a@abc.com',
        1 => 'admin@example.com',
        ],
        'required' => TRUE,
        ];
        $this->writeSettings($settings);
        // Test that everything on the form is the same, but that the override
        // worked for the actual site name.
        $this->drupalGet('admin/config/system/site-information');
        $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()->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');
        // Submit the form and ensure the site name is not changed.
        $edit = [
        $this->submitForm([
        'site_name' => 'Custom site name',
        ];
        $this->drupalGet('admin/config/system/site-information');
        $this->submitForm($edit, 'Save configuration');
        ], 'Save configuration');
        $this->assertSession()->titleEquals('Basic site settings | ' . $overridden_name);
        $this->assertSession()->fieldValueEquals("site_name", $edit['site_name']);
        $this->assertSession()->fieldValueEquals("site_name", 'Custom site name');
        // Ensure it works for sequence.
        $this->drupalGet('admin/reports/updates/settings');
        $this->submitForm([], 'Save configuration');
        $this->assertSession()->pageTextContainsOnce(self::OVERRIDE_TEXT);
        // There are two status messages on the page due to the save.
        $messages = $this->getSession()->getPage()->findAll('css', 'div[data-drupal-messages]');
        $this->assertCount(2, $messages);
        $this->assertStringContainsString('The configuration options have been saved.', $messages[0]->getText());
        $this->assertTrue(
        $messages[1]->hasLink('Email addresses to notify when updates are available'),
        "Link to 'Email addresses to notify when updates are available' exists"
        );
        }
        }
        ......@@ -5,6 +5,7 @@
        use Drupal\Core\Config\ConfigFactoryInterface;
        use Drupal\Core\Config\TypedConfigManagerInterface;
        use Drupal\Core\Form\ConfigFormBase;
        use Drupal\Core\Form\ConfigTarget;
        use Drupal\Core\Form\FormStateInterface;
        use Drupal\Core\Path\PathValidatorInterface;
        use Drupal\Core\Routing\RequestContext;
        ......@@ -92,10 +93,6 @@ protected function getEditableConfigNames() {
        */
        public function buildForm(array $form, FormStateInterface $form_state) {
        $site_config = $this->config('system.site');
        $site_mail = $site_config->get('mail');
        if (empty($site_mail)) {
        $site_mail = ini_get('sendmail_from');
        }
        $form['site_information'] = [
        '#type' => 'details',
        ......@@ -105,20 +102,24 @@ public function buildForm(array $form, FormStateInterface $form_state) {
        $form['site_information']['site_name'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Site name'),
        '#default_value' => $site_config->get('name'),
        '#config_target' => 'system.site:name',
        '#required' => TRUE,
        ];
        $form['site_information']['site_slogan'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Slogan'),
        '#default_value' => $site_config->get('slogan'),
        '#config_target' => 'system.site:slogan',
        '#description' => $this->t("How this is used depends on your site's theme."),
        '#maxlength' => 255,
        ];
        $form['site_information']['site_mail'] = [
        '#type' => 'email',
        '#title' => $this->t('Email address'),
        '#default_value' => $site_mail,
        '#config_target' => new ConfigTarget(
        'system.site',
        'mail',
        fromConfig: fn($value) => $value ?: ini_get('sendmail_from'),
        ),
        '#description' => $this->t("The <em>From</em> address in automated emails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this email being flagged as spam.)"),
        '#required' => TRUE,
        ];
        ......@@ -144,14 +145,14 @@ public function buildForm(array $form, FormStateInterface $form_state) {
        $form['error_page']['site_403'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Default 403 (access denied) page'),
        '#default_value' => $site_config->get('page.403'),
        '#config_target' => 'system.site:page.403',
        '#size' => 40,
        '#description' => $this->t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'),
        ];
        $form['error_page']['site_404'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Default 404 (not found) page'),
        '#default_value' => $site_config->get('page.404'),
        '#config_target' => 'system.site:page.404',
        '#size' => 40,
        '#description' => $this->t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'),
        ];
        ......@@ -203,12 +204,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
        */
        public function submitForm(array &$form, FormStateInterface $form_state) {
        $this->config('system.site')
        ->set('name', $form_state->getValue('site_name'))
        ->set('mail', $form_state->getValue('site_mail'))
        ->set('slogan', $form_state->getValue('site_slogan'))
        ->set('page.front', $form_state->getValue('site_frontpage'))
        ->set('page.403', $form_state->getValue('site_403'))
        ->set('page.404', $form_state->getValue('site_404'))
        ->save();
        parent::submitForm($form, $form_state);
        ......
        ......@@ -165,7 +165,7 @@ router_test.25:
        router_test.26:
        path: '/router_test/test26'
        defaults:
        _form: '\Drupal\system\Form\LoggingForm'
        _form: '\Drupal\router_test\Form'
        _title: 'Cron'
        requirements:
        _access: 'TRUE'
        ......
        <?php
        declare(strict_types=1);
        namespace Drupal\router_test;
        use Drupal\Core\Form\FormBase;
        use Drupal\Core\Form\FormStateInterface;
        /**
        * Form to test _form routing.
        *
        * @internal
        */
        class Form extends FormBase {
        /**
        * {@inheritdoc}
        */
        public function getFormId() {
        return 'router_test_form';
        }
        /**
        * {@inheritdoc}
        */
        public function buildForm(array $form, FormStateInterface $form_state): array {
        $form['submit'] = [
        '#type' => 'submit',
        '#value' => 'Save',
        ];
        return $form;
        }
        /**
        * {@inheritdoc}
        */
        public function submitForm(array &$form, FormStateInterface $form_state): void {
        $this->messenger()->addStatus('The router_test_form form has been submitted successfully.');
        }
        }
        ......@@ -8,9 +8,9 @@
        use Drupal\Core\Form\FormState;
        use Drupal\Core\Session\AnonymousUserSession;
        use Drupal\entity_test\Entity\EntityTestMulRevPub;
        use Drupal\form_test\Form\FormTestAlterForm;
        use Drupal\KernelTests\KernelTestBase;
        use Drupal\language\Entity\ConfigurableLanguage;
        use Drupal\system\Form\SiteInformationForm;
        use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
        use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
        use Drupal\Tests\node\Traits\NodeCreationTrait;
        ......@@ -71,6 +71,7 @@ class WorkspaceIntegrationTest extends KernelTestBase {
        'language',
        'content_translation',
        'path_alias',
        'form_test',
        ];
        /**
        ......@@ -1073,7 +1074,7 @@ public function testFormCacheForRegularForms(): void {
        $form_builder = $this->container->get('form_builder');
        $form_state = new FormState();
        $built_form = $form_builder->getForm(SiteInformationForm::class, $form_state);
        $built_form = $form_builder->getForm(FormTestAlterForm::class, $form_state);
        $form_builder->setCache($built_form['#build_id'], $built_form, $form_state);
        }
        ......
        ......@@ -132,7 +132,7 @@ public function testExceptionResponseGeneratedForOriginalRequest(): void {
        // Test with 404 path pointing to a route that uses '_form'.
        $response = $this->doTest404Route('/router_test/test26');
        $this->assertStringContainsString('<form class="system-logging-settings"', $response->getContent());
        $this->assertStringContainsString('<form class="router-test-form"', $response->getContent());
        // Test with 404 path pointing to a route that uses '_entity_form'.
        $response = $this->doTest404Route('/router_test/test27');
        ......
        0% Loading or .
        You are about to add 0 people to the discussion. Proceed with caution.
        Please register or to comment