Commit f82a2e2f authored by bircher's avatar bircher

Issue #2896753 by bircher: Add possibility to ignore only certain keys and not...

Issue #2896753 by bircher: Add possibility to ignore only certain keys and not the whole coniguration entity
parent d631476b
......@@ -33,13 +33,24 @@ class Settings extends ConfigFormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL) {
$description = $this->t('One configuration name per line.<br />
Examples: <ul>
<li>user.settings</li>
<li>views.settings</li>
<li>contact.settings</li>
<li>webform.webform.* (will ignore all config entities that starts with <em>webform.webform</em>)</li>
<li>*.contact_message.custom_contact_form.* (will ignore all config entities that starts with <em>.contact_message.custom_contact_form.</em> like fields attached to a custom contact form)</li>
<li>* (will ignore everything)</li>
<li>~webform.webform.contact (will force import for this configuration, even if ignored by a wildcard)</li>
<li>user.mail:register_no_approval_required.body (will ignore the body of the no approval required email setting, but will not ignore other user.mail configuration.)</li>
</ul>');
$config_ignore_settings = $this->config('config_ignore.settings');
$form['ignored_config_entities'] = [
'#type' => 'textarea',
'#rows' => 25,
'#title' => $this->t('Configuration entity names to ignore'),
'#description' => $this->t('One configuration name per line.<br />Examples: <ul><li>user.settings</li><li>views.settings</li><li>contact.settings</li><li>webform.webform.* (will ignore all config entities that starts with <em>webform.webform</em>)</li><li>*.contact_message.custom_contact_form.* (will ignore all config entities that starts with <em>.contact_message.custom_contact_form.</em> like fields attached to a custom contact form)</li><li>* (will ignore everything)</li><li>~webform.webform.contact (will force import for this configuration, even if ignored by a wildcard)</li></ul>'),
'#description' => $description,
'#default_value' => implode(PHP_EOL, $config_ignore_settings->get('ignored_config_entities')),
'#size' => 60,
];
......
......@@ -2,6 +2,7 @@
namespace Drupal\config_ignore\Plugin\ConfigFilter;
use Drupal\Component\Utility\NestedArray;
use Drupal\config_filter\Plugin\ConfigFilterBase;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
......@@ -87,7 +88,9 @@ class IgnoreFilter extends ConfigFilterBase implements ContainerFactoryPluginInt
}
foreach ($this->configuration['ignored'] as $config_ignore_setting) {
if (fnmatch($config_ignore_setting, $config_name)) {
// Split the ignore settings so that we can ignore individual keys.
$ignore = explode(':', $config_ignore_setting);
if (fnmatch($ignore[0], $config_name)) {
return TRUE;
}
}
......@@ -95,13 +98,89 @@ class IgnoreFilter extends ConfigFilterBase implements ContainerFactoryPluginInt
return FALSE;
}
/**
* Read from the active configuration.
*
* This method will read the configuration from the active config store.
* But rather than just straight up returning the value it will check if
* a nested config key is set to be ignored and set only that value on the
* data to be filtered.
*
* @param string $name
* The name of the configuration to read.
* @param mixed $data
* The data to be filtered.
*
* @return mixed
* The data filtered or read from the active storage.
*/
protected function activeRead($name, $data) {
$keys = [];
foreach ($this->configuration['ignored'] as $ignored) {
// Split the ignore settings so that we can ignore individual keys.
$ignored = explode(':', $ignored);
if (fnmatch($ignored[0], $name)) {
if (count($ignored) == 1) {
// If one of the definitions does not have keys ignore the
// whole config.
return $this->active->read($name);
}
else {
// Add the sub parts to ignore to the keys.
$keys[] = $ignored[1];
}
}
}
$active = $this->active->read($name);
foreach ($keys as $key) {
$parts = explode('.', $key);
if (count($parts) == 1) {
if (isset($active[$key])) {
$data[$key] = $active[$key];
}
}
else {
$value = NestedArray::getValue($active, $parts, $key_exists);
if ($key_exists) {
// Enforce the value if it existed in the active config.
NestedArray::setValue($data, $parts, $value, TRUE);
}
}
}
return $data;
}
/**
* Read multiple from the active storage.
*
* @param array $names
* The names of the configuration to read.
* @param array $data
* The data to filter.
*
* @return array
* The new data.
*/
protected function activeReadMultiple(array $names, array $data) {
$filtered_data = [];
foreach ($names as $name) {
$filtered_data[$name] = $this->activeRead($name, $data[$name]);
}
return $filtered_data;
}
/**
* {@inheritdoc}
*/
public function filterRead($name, $data) {
// Read from the active storage when the name is in the ignored list.
if ($this->matchConfigName($name)) {
return $this->active->read($name);
return $this->activeRead($name, $data);
}
return $data;
......@@ -121,7 +200,7 @@ class IgnoreFilter extends ConfigFilterBase implements ContainerFactoryPluginInt
public function filterReadMultiple(array $names, array $data) {
// Limit the names which are read from the active storage.
$names = array_filter($names, [$this, 'matchConfigName']);
$active_data = $this->active->readMultiple($names);
$active_data = $this->activeReadMultiple($names, $data);
// Return the data with merged in active data.
return array_merge($data, $active_data);
......@@ -154,4 +233,4 @@ class IgnoreFilter extends ConfigFilterBase implements ContainerFactoryPluginInt
return array_merge($collections, $this->active->getAllCollectionNames());
}
}
\ No newline at end of file
}
......@@ -158,4 +158,39 @@ class ConfigIgnoreTest extends ConfigIgnoreBrowserTestBase {
}
/**
* Verify ignoring only some config keys.
*
* This test covers the scenario when not the whole config is to be ignored
* but only a certain subset of it.
*/
public function testValidateImportingWithIgnoredSubKeys() {
// Set the site name to a known value that we later will try and overwrite.
$this->config('system.site')
->set('name', 'Test name')
->set('slogan', 'Test slogan')
->set('page.front', '/ignore')
->save();
// Set the system.site:name to be (force-) imported upon config import.
$settings = ['system.site:name', 'system.site:page.front'];
$this->config('config_ignore.settings')->set('ignored_config_entities', $settings)->save();
$this->doExport();
// Change the site name, perform an import and see if the site name remains
// the same, as it should.
$this->config('system.site')
->set('name', 'Changed title')
->set('slogan', 'Changed slogan')
->set('page.front', '/new-ignore')
->save();
$this->doImport();
$this->assertEquals('Changed title', $this->config('system.site')->get('name'));
$this->assertEquals('Test slogan', $this->config('system.site')->get('slogan'));
$this->assertEquals('/new-ignore', $this->config('system.site')->get('page.front'));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment