diff --git a/README.md b/README.md index 9257b33..8db045d 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ site value when it changes. separate action, and restores inheritance. - Only listed config objects are exposed, and within them only the keys their schema declares. Internal keys are always dropped, and an exclusion - list removes the rest. The site email address is excluded by default. + list removes the rest. The site email address and the notification + address are excluded by default. - Logo and favicon come from core's theme settings resolution, as usable URLs with core's fallbacks. - Responses carry the config cache tags of every object read, and a cache diff --git a/config/install/decoupled_settings.settings.yml b/config/install/decoupled_settings.settings.yml index 22284f7..a45fc1b 100644 --- a/config/install/decoupled_settings.settings.yml +++ b/config/install/decoupled_settings.settings.yml @@ -8,3 +8,4 @@ expose_theme_settings: true # Schema bounds the shape of an object, not the sensitivity of its values. excluded_keys: - 'system.site:mail' + - 'system.site:mail_notification' diff --git a/decoupled_settings.install b/decoupled_settings.install index 0dd003b..a489291 100644 --- a/decoupled_settings.install +++ b/decoupled_settings.install @@ -28,3 +28,21 @@ function decoupled_settings_install(): void { } } } + +/** + * Exclude system.site:mail_notification from the exposed settings. + * + * The shipped exclude list held system.site:mail but not the notification + * address next to it. Append it for existing installs. A site that wants + * the key exposed can remove it again: this hook runs once. + */ +function decoupled_settings_update_10001(): string { + $config = \Drupal::configFactory()->getEditable('decoupled_settings.settings'); + $excluded = $config->get('excluded_keys') ?? []; + if (!in_array('system.site:mail_notification', $excluded, TRUE)) { + $excluded[] = 'system.site:mail_notification'; + $config->set('excluded_keys', $excluded)->save(); + return 'system.site:mail_notification is now excluded.'; + } + return 'system.site:mail_notification was already excluded.'; +} diff --git a/tests/src/Kernel/FormLogicTest.php b/tests/src/Kernel/FormLogicTest.php index ea5547c..6b9f3d7 100644 --- a/tests/src/Kernel/FormLogicTest.php +++ b/tests/src/Kernel/FormLogicTest.php @@ -147,6 +147,27 @@ public function testOverrideFormListsInheritedValues(): void { $this->assertFalse((bool) $form['settings']['system.site:name']['enabled']['#default_value']); } + /** + * A setting with no value is listed with an empty inherited cell. + * + * The notification address is NULL on a fresh site. It is excluded by + * default, so this exposes it on purpose to render the NULL path. + */ + public function testOverrideFormListsNullValueAsEmpty(): void { + $this->config('decoupled_settings.settings') + ->set('excluded_keys', []) + ->save(); + $consumer = $this->createConsumer(); + + $form_state = new FormState(); + $form_state->addBuildInfo('args', [$consumer]); + $form = $this->container->get('form_builder') + ->buildForm(ConsumerOverridesForm::class, $form_state); + + $this->assertArrayHasKey('system.site:mail_notification', $form['settings']); + $this->assertSame('', $form['settings']['system.site:mail_notification']['inherited']['#markup']); + } + /** * An already overridden setting is shown as ticked, with its own value. */ diff --git a/tests/src/Kernel/SettingsResolverTest.php b/tests/src/Kernel/SettingsResolverTest.php index 3c218d0..4045078 100644 --- a/tests/src/Kernel/SettingsResolverTest.php +++ b/tests/src/Kernel/SettingsResolverTest.php @@ -56,6 +56,7 @@ protected function setUp(): void { $this->config('system.site') ->set('name', 'Global Site') ->set('slogan', 'Global slogan') + ->set('mail_notification', 'webmaster@example.com') ->save(); // Theme settings are off by default here, so each test opts in. @@ -141,6 +142,31 @@ public function testExcludedKeyIsNotExposed(): void { $resolved = $this->resolver->resolve(NULL, new CacheableMetadata()); $this->assertArrayNotHasKey('mail', $resolved['system.site']); + $this->assertArrayNotHasKey('mail_notification', $resolved['system.site']); + } + + /** + * The update hook appends the notification address exclusion once. + */ + public function testUpdateHookExcludesMailNotification(): void { + \Drupal::moduleHandler()->loadInclude('decoupled_settings', 'install'); + + // An install from before the exclusion shipped. + $this->config('decoupled_settings.settings') + ->set('excluded_keys', ['system.site:mail']) + ->save(); + $resolved = $this->resolver->resolve(NULL, new CacheableMetadata()); + $this->assertArrayHasKey('mail_notification', $resolved['system.site']); + + decoupled_settings_update_10001(); + + $resolved = $this->resolver->resolve(NULL, new CacheableMetadata()); + $this->assertArrayNotHasKey('mail_notification', $resolved['system.site']); + + // Running again does not duplicate the entry. + decoupled_settings_update_10001(); + $excluded = $this->config('decoupled_settings.settings')->get('excluded_keys'); + $this->assertCount(1, array_keys($excluded, 'system.site:mail_notification', TRUE)); } /**