diff --git a/config/install/config_ignore.settings.yml b/config/install/config_ignore.settings.yml index 629b342..c89e103 100644 --- a/config/install/config_ignore.settings.yml +++ b/config/install/config_ignore.settings.yml @@ -1 +1,2 @@ ignored_config_entities: { } +ignored_config_collections: { } diff --git a/config/schema/config_ignore.schema.yml b/config/schema/config_ignore.schema.yml index 9930f0d..889c34c 100644 --- a/config/schema/config_ignore.schema.yml +++ b/config/schema/config_ignore.schema.yml @@ -7,3 +7,8 @@ config_ignore.settings: label: 'List of ignored configurations' sequence: type: string + ignored_config_collections: + type: sequence + label: 'List of ignored configuration storage collections' + sequence: + type: string diff --git a/config_ignore.api.php b/config_ignore.api.php index 89e9f91..1054e84 100644 --- a/config_ignore.api.php +++ b/config_ignore.api.php @@ -18,6 +18,13 @@ function hook_config_ignore_settings_alter(array &$settings) { $settings[] = 'field.*'; } +/** + * Alter the list of config entities that should be ignored. + */ +function hook_config_ignore_collections_alter(array &$collections) { + $collections[] = 'language.*'; +} + /** * @} End of "addtogroup hooks". */ diff --git a/config_ignore.install b/config_ignore.install index cf8f3a4..9381d7a 100644 --- a/config_ignore.install +++ b/config_ignore.install @@ -29,3 +29,11 @@ function config_ignore_update_8301() { \Drupal::cache('discovery')->delete('config_filter_plugins'); } +/** + * Update schema with storage collection ignore value. + */ +function config_ignore_update_8302() { + $config = \Drupal::configFactory()->getEditable('config_ignore.settings'); + $ignored_config_collections = $config->get('ignored_config_collections') ?: []; + $config->set('ignored_config_collections', $ignored_config_collections)->save(); +} diff --git a/src/EventSubscriber/ConfigIgnoreEventSubscriber.php b/src/EventSubscriber/ConfigIgnoreEventSubscriber.php index 866c611..509e816 100644 --- a/src/EventSubscriber/ConfigIgnoreEventSubscriber.php +++ b/src/EventSubscriber/ConfigIgnoreEventSubscriber.php @@ -56,6 +56,16 @@ class ConfigIgnoreEventSubscriber implements EventSubscriberInterface, CacheTags */ protected $ignoredConfig = NULL; + /** + * Statically cached ignored collections. + * + * - 0: (string[]) Array of ignored collections patterns + * - 1: (string[]) Exceptions to ignored collections patterns. + * + * @var array|null + */ + protected $ignoredCollections = NULL; + /** * Constructs a new event subscriber instance. * @@ -92,6 +102,7 @@ class ConfigIgnoreEventSubscriber implements EventSubscriberInterface, CacheTags // Invalidate static cache if config changes. if (in_array('config:config_ignore.settings', $tags, TRUE)) { $this->ignoredConfig = NULL; + $this->ignoredCollections = NULL; } } @@ -131,10 +142,26 @@ class ConfigIgnoreEventSubscriber implements EventSubscriberInterface, CacheTags $collection_names = array_unique(array_merge($transformation_storage->getAllCollectionNames(), $destination_storage->getAllCollectionNames())); array_unshift($collection_names, StorageInterface::DEFAULT_COLLECTION); + $ignored_collections = array_keys($this->getIgnoredCollections($collection_names)); + foreach ($collection_names as $collection_name) { $destination_storage = $destination_storage->createCollection($collection_name); $transformation_storage = $transformation_storage->createCollection($collection_name); + if (in_array($collection_name, $ignored_collections)) { + foreach ($this->configFactory->listAll() as $config_name) { + $collection_source_data = $destination_storage->read($config_name); + + if (is_array($collection_source_data) && count(array_filter($collection_source_data)) != 0) { + $transformation_storage->write($config_name, $destination_storage->read($config_name)); + } + else { + $transformation_storage->delete($config_name); + } + } + continue; + } + // Loop over the ignored config in the destination. // We need to do this inside of the collection loop because some config // to be ignored may only be present in some collections. @@ -247,6 +274,52 @@ class ConfigIgnoreEventSubscriber implements EventSubscriberInterface, CacheTags return array_diff_key($ignored_configs, array_flip($exceptions)); } + /** + * Returns the list of all ignored collections by expanding the wildcards. + * + * @param array $collection_names + * An array of existing collection names. + * + * @return array + * An associative array of collection names. + */ + protected function getIgnoredCollections(array $collection_names) { + [$patterns, $exceptions] = $this->getCollectionRules(); + + $ignored_collections = []; + foreach ($collection_names as $collection_name) { + foreach ($patterns as $ignored_collection_pattern) { + if (strpos($ignored_collection_pattern, ':') !== FALSE) { + // Some patterns are defining also a key. + [$collection_name_pattern, $key] = explode(':', $ignored_collection_pattern, 2); + $key = trim($key); + if (strpos($key, '*') !== FALSE) { + throw new \LogicException("The key part of the config ignore pattern cannot contain the wildcard character '*'."); + } + } + else { + $collection_name_pattern = $ignored_collection_pattern; + $key = NULL; + } + if ($this->wildcardMatch($collection_name_pattern, $collection_name)) { + if ($key) { + $ignored_collections[$collection_name][] = explode('.', $key); + } + else { + $ignored_collections[$collection_name] = NULL; + // As this pattern has no key we continue with the next config. Any + // subsequent pattern with the same config but with key is covered + // by this ignore pattern. + break; + } + } + } + } + + // Extract the exceptions from the ignored collections. + return array_diff_key($ignored_collections, array_flip($exceptions)); + } + /** * Checks if a string matches a given wildcard pattern. * @@ -296,4 +369,37 @@ class ConfigIgnoreEventSubscriber implements EventSubscriberInterface, CacheTags return $this->ignoredConfig; } + /** + * Get ignored collection rules. + * + * @return array + * A keyed array containing: + * - 0: (string[]) Array of ignored collection patterns + * - 1: (string[]) Exceptions to ignored collection patterns. + */ + protected function getCollectionRules() { + if (isset($this->ignoredCollections)) { + return $this->ignoredCollections; + } + + /** @var string[] $ignored_collections_patterns */ + $ignored_collections_patterns = $this->configFactory->get('config_ignore.settings')->get('ignored_config_collections'); + $this->moduleHandler->invokeAll('config_ignore_collections_alter', [&$ignored_collections_patterns]); + + // Builds ignored collections exceptions and remove them from the pattern list. + $exceptions = []; + foreach ($ignored_collections_patterns as $delta => $ignored_collection_pattern) { + if (strpos($ignored_collection_pattern, '~') === 0) { + if (strpos($ignored_collection_pattern, '*') !== FALSE) { + throw new \LogicException("A collection ignore pattern entry cannot contain both, '~' and '*'."); + } + $exceptions[] = substr($ignored_collection_pattern, 1); + unset($ignored_collections_patterns[$delta]); + } + } + $this->ignoredCollections = [$ignored_collections_patterns, $exceptions]; + + return $this->ignoredCollections; + } + } diff --git a/src/Form/Settings.php b/src/Form/Settings.php index d5b6baa..d1bf521 100644 --- a/src/Form/Settings.php +++ b/src/Form/Settings.php @@ -54,6 +54,16 @@ Examples: