diff --git a/core/core.services.yml b/core/core.services.yml index e15618f..b8118ec 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -77,7 +77,7 @@ services: arguments: [discovery] config.manager: class: Drupal\Core\Config\ConfigManager - arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage'] + arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage', '@event_dispatcher'] config.factory: class: Drupal\Core\Config\ConfigFactory tags: diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 5360edd..b53d139 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -237,7 +237,6 @@ public function save() { * The configuration object. */ public function delete() { - // @todo Consider to remove the pruning of data for Config::delete(). $this->data = array(); $this->storage->delete($this->name); $this->isNew = TRUE; diff --git a/core/lib/Drupal/Core/Config/ConfigCollectionInfoEvent.php b/core/lib/Drupal/Core/Config/ConfigCollectionInfoEvent.php new file mode 100644 index 0000000..3b0de03 --- /dev/null +++ b/core/lib/Drupal/Core/Config/ConfigCollectionInfoEvent.php @@ -0,0 +1,82 @@ + 0) { + $collections_to_add = array_combine($collections, array_fill(0, count($collections), $override_service)); + $this->collections = array_merge($this->collections, $collections_to_add); + } + } + + /** + * Adds a collection to the list of possible collections. + * + * @param string $collection + * Collection name to add. + * @param \Drupal\Core\Config\ConfigFactoryOverrideInterface + * (optional) The configuration factory override service responsible for the + * collection. + */ + public function addCollection($collection, ConfigFactoryOverrideInterface $override_service = NULL) { + $this->addCollections(array($collection), $override_service); + } + + /** + * Gets the list of possible collection names. + * + * @return array + * The list of possible collection names. + */ + public function getCollectionNames($include_default = TRUE) { + $collection_names = array_keys($this->collections); + sort($collection_names); + if ($include_default) { + array_unshift($collection_names, StorageInterface::DEFAULT_COLLECTION); + } + return $collection_names; + } + + /** + * Gets the config factory override service responsible for the collection. + * + * @param string $collection + * The configuration collection. + * + * @return \Drupal\Core\Config\ConfigFactoryOverrideInterface|NULL + * The override service responsible for the collection if one exists. NULL + * if not. + */ + public function getOverrideService($collection) { + return isset($this->collections[$collection]) ? $this->collections[$collection] : NULL; + } + +} diff --git a/core/lib/Drupal/Core/Config/ConfigCollectionNamesEvent.php b/core/lib/Drupal/Core/Config/ConfigCollectionNamesEvent.php deleted file mode 100644 index 3633d43..0000000 --- a/core/lib/Drupal/Core/Config/ConfigCollectionNamesEvent.php +++ /dev/null @@ -1,59 +0,0 @@ -collections = array_merge($this->collections, $collections); - } - - /** - * Adds a name to the list of possible collections. - * - * @param string $collection - * Collection name to add. - */ - public function addCollectionName($collection) { - $this->addCollectionNames(array($collection)); - } - - /** - * Gets the list of possible collection names. - * - * @return array - * The list of possible collection names. - */ - public function getCollectionNames($include_default = TRUE) { - sort($this->collections); - $collections = array_unique($this->collections); - if ($include_default) { - array_unshift($collections, StorageInterface::DEFAULT_COLLECTION); - } - return $collections; - } - -} diff --git a/core/lib/Drupal/Core/Config/ConfigEvents.php b/core/lib/Drupal/Core/Config/ConfigEvents.php index 2d12a50..fff2755 100644 --- a/core/lib/Drupal/Core/Config/ConfigEvents.php +++ b/core/lib/Drupal/Core/Config/ConfigEvents.php @@ -55,6 +55,6 @@ * * @see \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() */ - const COLLECTION_NAMES = 'config.collection_names'; + const COLLECTION_INFO = 'config.collection_info'; } diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php index 06e1f3f..a861d89 100644 --- a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php @@ -32,4 +32,17 @@ public function loadOverrides($names); */ public function getCacheSuffix(); + /** + * Gets a configuration object for the override. + * + * @param string $name + * The configuration object name. + * @param string $collection + * The configuration collection. + * + * @return \Drupal\Core\Config\StorableConfigBase + * The configuration object for the provided name and collection. + */ + public function getConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION); + } diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index 716a6ae..1f8b519 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -896,7 +896,13 @@ protected function checkOp($collection, $op, $name) { * The name of the configuration to process. */ protected function importConfig($collection, $op, $name) { - $config = new Config($name, $this->storageComparer->getTargetStorage($collection), $this->eventDispatcher, $this->typedConfigManager); + $overrider = $this->configManager->getConfigCollectionInfo()->getOverrideService($collection); + if ($overrider) { + $config = $overrider->getConfigObject($name, $collection); + } + else { + $config = new Config($name, $this->storageComparer->getTargetStorage($collection), $this->eventDispatcher, $this->typedConfigManager); + } if ($op == 'delete') { $config->delete(); } diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php index cea8b53..d535655 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -96,10 +96,8 @@ public function installDefaultConfig($type, $name) { $this->typedConfig->clearCachedDefinitions(); } - // Gather all the supported collection names. - $event = new ConfigCollectionNamesEvent(); - $this->eventDispatcher->dispatch(ConfigEvents::COLLECTION_NAMES, $event); - $collections = $event->getCollectionNames(); + // Gather information about all the supported collections. + $collection_info = $this->configManager->getConfigCollectionInfo(); $old_state = $this->configFactory->getOverrideState(); $this->configFactory->setOverrideState(FALSE); @@ -110,10 +108,10 @@ public function installDefaultConfig($type, $name) { $enabled_extensions = array_keys((array) $extension_config->get('module')); $enabled_extensions += array_keys((array) $extension_config->get('theme')); - foreach ($collections as $collection) { + foreach ($collection_info->getCollectionNames(TRUE) as $collection) { $config_to_install = $this->listDefaultConfigCollection($collection, $type, $name, $enabled_extensions); if (!empty($config_to_install)) { - $this->createConfiguration($collection, $config_to_install); + $this->createConfiguration($collection, $config_to_install, $collection_info); } } $this->configFactory->setOverrideState($old_state); @@ -169,8 +167,10 @@ protected function listDefaultConfigCollection($collection, $type, $name, array * The configuration collection. * @param array $config_to_install * A list of configuration object names to create. + * @param \Drupal\Core\Config\ConfigCollectionInfoEvent $collection_info + * The configuration collection information. */ - protected function createConfiguration($collection, array $config_to_install) { + protected function createConfiguration($collection, array $config_to_install, ConfigCollectionInfoEvent $collection_info) { // Order the configuration to install in the order of dependencies. $data = $this->getSourceStorage($collection)->readMultiple($config_to_install); $config_entity_support = $this->configManager->supportsConfigurationEntities($collection); @@ -185,7 +185,13 @@ protected function createConfiguration($collection, array $config_to_install) { $config_to_install = array_diff($config_to_install, $this->getActiveStorage($collection)->listAll()); foreach ($config_to_install as $name) { - $new_config = new Config($name, $this->getActiveStorage($collection), $this->eventDispatcher, $this->typedConfig); + $overrider = $collection_info->getOverrideService($collection); + if ($overrider) { + $new_config = $overrider->getConfigObject($name, $collection); + } + else { + $new_config = new Config($name, $this->getActiveStorage($collection), $this->eventDispatcher, $this->typedConfig); + } if ($data[$name] !== FALSE) { $new_config->setData($data[$name]); } @@ -238,9 +244,11 @@ public function installCollectionDefaultConfig($collection) { return in_array($provider, $enabled_extensions); }); if (!empty($config_to_install)) { + // Gather information about all the configuration collections. + $collection_info = $this->configManager->getConfigCollectionInfo(); $old_state = $this->configFactory->getOverrideState(); $this->configFactory->setOverrideState(FALSE); - $this->createConfiguration($collection, $config_to_install); + $this->createConfiguration($collection, $config_to_install, $collection_info); $this->configFactory->setOverrideState($old_state); // Reset all the static caches and list caches. $this->configFactory->reset(); diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php index 7d33de6..8284ca7 100644 --- a/core/lib/Drupal/Core/Config/ConfigManager.php +++ b/core/lib/Drupal/Core/Config/ConfigManager.php @@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\StringTranslation\TranslationManager; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * The ConfigManager provides helper functions for the configuration system. @@ -54,6 +55,20 @@ class ConfigManager implements ConfigManagerInterface { protected $activeStorage; /** + * The event dispatcher. + * + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + protected $eventDispatcher; + + /** + * The configuration collection info. + * + * @var \Drupal\Core\Config\ConfigCollectionInfoEvent + */ + protected $configCollectionInfo; + + /** * Creates ConfigManager objects. * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager @@ -64,13 +79,15 @@ class ConfigManager implements ConfigManagerInterface { * The typed config manager. * @param \Drupal\Core\StringTranslation\TranslationManager $string_translation * The string translation service. + * * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */ - public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, TypedConfigManager $typed_config_manager, TranslationManager $string_translation, StorageInterface $active_storage) { + public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, TypedConfigManager $typed_config_manager, TranslationManager $string_translation, StorageInterface $active_storage, EventDispatcherInterface $event_dispatcher) { $this->entityManager = $entity_manager; $this->configFactory = $config_factory; $this->typedConfigManager = $typed_config_manager; $this->stringTranslation = $string_translation; $this->activeStorage = $active_storage; + $this->eventDispatcher = $event_dispatcher; } /** @@ -246,4 +263,16 @@ public function findConfigEntityDependentsAsEntities($type, array $names) { public function supportsConfigurationEntities($collection) { return $collection == StorageInterface::DEFAULT_COLLECTION; } + + /** + * {@inheritdoc} + */ + public function getConfigCollectionInfo() { + if (!isset($this->configCollectionInfo)) { + $this->configCollectionInfo = new ConfigCollectionInfoEvent(); + $this->eventDispatcher->dispatch(ConfigEvents::COLLECTION_INFO, $this->configCollectionInfo); + } + return $this->configCollectionInfo; + } + } diff --git a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php index 3be67c8..8abf838 100644 --- a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php @@ -123,4 +123,12 @@ public function findConfigEntityDependentsAsEntities($type, array $names); */ public function supportsConfigurationEntities($collection); + /** + * Gets available collection information using the event. + * + * @return \Drupal\Core\Config\ConfigCollectionInfoEvent + * The event which collects information about all the available collections. + */ + public function getConfigCollectionInfo(); + } diff --git a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php index df1b335..e90a058 100644 --- a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php @@ -51,9 +51,9 @@ public function testDefaultConfig() { $default_config_storage = new TestInstallStorage(); foreach ($default_config_storage->listAll() as $config_name) { - // @todo: remove once migration (https://drupal.org/node/2183957) and - // translation (https://drupal.org/node/2168609) schemas are in. - if (strpos($config_name, 'migrate.migration') === 0 || strpos($config_name, 'language.config') === 0) { + // @todo: remove once migration (https://drupal.org/node/2183957) schemas + // are in. + if (strpos($config_name, 'migrate.migration') === 0) { continue; } diff --git a/core/modules/config/tests/config_collection_install_test/lib/Drupal/config_collection_install_test/EventSubscriber.php b/core/modules/config/tests/config_collection_install_test/lib/Drupal/config_collection_install_test/EventSubscriber.php index bb481e0..288b46e 100644 --- a/core/modules/config/tests/config_collection_install_test/lib/Drupal/config_collection_install_test/EventSubscriber.php +++ b/core/modules/config/tests/config_collection_install_test/lib/Drupal/config_collection_install_test/EventSubscriber.php @@ -7,7 +7,7 @@ namespace Drupal\config_collection_install_test; -use Drupal\Core\Config\ConfigCollectionNamesEvent; +use Drupal\Core\Config\ConfigCollectionInfoEvent; use Drupal\Core\Config\ConfigEvents; use Drupal\Core\State\StateInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -34,18 +34,18 @@ public function __construct(StateInterface $state) { /** * Reacts to the ConfigEvents::COLLECTION_NAMES event. * - * @param \Drupal\Core\Config\ConfigCollectionNamesEvent $event + * @param \Drupal\Core\Config\ConfigCollectionInfoEvent $event * The configuration collection names event. */ - public function addCollectionNames(ConfigCollectionNamesEvent $event) { - $event->addCollectionNames($this->state->get('config_collection_install_test.collection_names', array())); + public function addCollections(ConfigCollectionInfoEvent $event) { + $event->addCollections($this->state->get('config_collection_install_test.collection_names', array())); } /** * {@inheritdoc} */ static function getSubscribedEvents() { - $events[ConfigEvents::COLLECTION_NAMES][] = array('addCollectionNames'); + $events[ConfigEvents::COLLECTION_INFO][] = array('addCollections'); return $events; } diff --git a/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverrider.php b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverrider.php index 106df3d..1bdcc7e 100644 --- a/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverrider.php +++ b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverrider.php @@ -10,6 +10,7 @@ use Drupal\Core\Config\ConfigEvents; use Drupal\Core\Config\ConfigFactoryOverrideInterface; use Drupal\Core\Config\ConfigModuleOverridesEvent; +use Drupal\Core\Config\StorageInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -40,5 +41,12 @@ public function getCacheSuffix() { return 'ConfigOverrider'; } + /** + * {@inheritdoc} + */ + public function getConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) { + return NULL; + } + } diff --git a/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverriderLowPriority.php b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverriderLowPriority.php index a9a435e..70069eb 100644 --- a/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverriderLowPriority.php +++ b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverriderLowPriority.php @@ -41,5 +41,12 @@ public function getCacheSuffix() { return 'ConfigOverriderLowPriority'; } + /** + * {@inheritdoc} + */ + public function getConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) { + return NULL; + } + } diff --git a/core/modules/config/tests/config_test/config/install/language.config.de.config_test.system.yml b/core/modules/config/tests/config_test/config/install/language/de/config_test.system.yml similarity index 100% rename from core/modules/config/tests/config_test/config/install/language.config.de.config_test.system.yml rename to core/modules/config/tests/config_test/config/install/language/de/config_test.system.yml diff --git a/core/modules/config/tests/config_test/config/install/language.config.en.config_test.system.yml b/core/modules/config/tests/config_test/config/install/language/en/config_test.system.yml similarity index 100% rename from core/modules/config/tests/config_test/config/install/language.config.en.config_test.system.yml rename to core/modules/config/tests/config_test/config/install/language/en/config_test.system.yml diff --git a/core/modules/config/tests/config_test/config/install/language.config.fr.config_test.system.yml b/core/modules/config/tests/config_test/config/install/language/fr/config_test.system.yml similarity index 100% rename from core/modules/config/tests/config_test/config/install/language.config.fr.config_test.system.yml rename to core/modules/config/tests/config_test/config/install/language/fr/config_test.system.yml diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php index fb3ba88..13311d9 100644 --- a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php +++ b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php @@ -15,6 +15,7 @@ use Drupal\Core\Form\BaseFormIdInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Language\Language; +use Drupal\language\Config\LanguageConfigOverride; use Drupal\language\ConfigurableLanguageManagerInterface; use Drupal\locale\StringStorageInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -352,7 +353,7 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d * Set the configuration in this language. * @param \Drupal\Core\Config\Config $base_config * Base configuration values, in the source language. - * @param \Drupal\Core\Config\Config $config_translation + * @param \Drupal\language\Config\LanguageConfigOverride $config_translation * Translation configuration override data. * @param array $config_values * A simple one dimensional or recursive array: @@ -372,7 +373,7 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d * @return array * Translation configuration override data. */ - protected function setConfig(Language $language, Config $base_config, Config $config_translation, array $config_values, $shipped_config = FALSE) { + protected function setConfig(Language $language, Config $base_config, LanguageConfigOverride $config_translation, array $config_values, $shipped_config = FALSE) { foreach ($config_values as $key => $value) { if (is_array($value) && !isset($value['translation'])) { // Traverse into this level in the configuration. diff --git a/core/modules/language/language.module b/core/modules/language/language.module index f35da4d..6a71fc7 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -441,6 +441,8 @@ function language_save($language) { $language_entity->save(); $t_args = array('%language' => $language->name, '%langcode' => $language->id); if ($language->is_new) { + // Install any available language configuration overrides for the language. + \Drupal::service('language.config_factory_override')->installLanguageOverrides($language->getId()); watchdog('language', 'The %language (%langcode) language has been created.', $t_args); } else { diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml index 7396d57..e183e6d 100644 --- a/core/modules/language/language.services.yml +++ b/core/modules/language/language.services.yml @@ -16,3 +16,4 @@ services: arguments: ['@config.storage', '@event_dispatcher', '@config.typed'] tags: - { name: config.factory.override, priority: -254 } + - { name: event_subscriber } diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php index bfadec0..0780087 100644 --- a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php +++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php @@ -7,24 +7,37 @@ namespace Drupal\language\Config; -use Drupal\Core\Config\Config; +use Drupal\Component\Utility\String; +use Drupal\Core\Config\ConfigCollectionInfoEvent; +use Drupal\Core\Config\ConfigEvents; use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageDefault; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Provides language overrides for the configuration factory. */ -class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInterface { +class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInterface, EventSubscriberInterface { /** * The configuration storage. * + * Do not access this directly. Should be accessed through self::getStorage() + * so that the cache of storages per langcode is used. + * * @var \Drupal\Core\Config\StorageInterface */ - protected $storage; + protected $baseStorage; + + /** + * An array of configuration storages keyed by langcode. + * + * @var \Drupal\Core\Config\StorageInterface[] + */ + protected $storages; /** * The typed config manager. @@ -58,7 +71,7 @@ class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInte * The typed configuration manager. */ public function __construct(StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config) { - $this->storage = $storage; + $this->baseStorage = $storage; $this->eventDispatcher = $event_dispatcher; $this->typedConfigManager = $typed_config; } @@ -67,72 +80,34 @@ public function __construct(StorageInterface $storage, EventDispatcherInterface * {@inheritdoc} */ public function loadOverrides($names) { - $data = array(); - $language_names = $this->getLanguageConfigNames($names); - if ($language_names) { - $data = $this->storage->readMultiple(array_values($language_names)); - // Re-key the data array to use configuration names rather than override - // names. - $prefix_length = strlen(static::LANGUAGE_CONFIG_PREFIX . '.' . $this->language->id) + 1; - foreach ($data as $key => $value) { - unset($data[$key]); - $key = substr($key, $prefix_length); - $data[$key] = $value; - } + if ($this->language) { + $storage = $this->getStorage($this->language->getId()); + return $storage->readMultiple($names); } - return $data; + return array(); } /** * {@inheritdoc} */ public function getOverride($langcode, $name) { - $override_name = $this->getLanguageConfigName($langcode, $name); - $overrides = $this->storage->read($override_name); - $config = new Config($override_name, $this->storage, $this->eventDispatcher, $this->typedConfigManager); - if (!empty($overrides)) { - $config->initWithData($overrides); + $storage = $this->getStorage($langcode); + $data = $storage->read($name); + $override = new LanguageConfigOverride($name, $storage, $this->typedConfigManager); + if (!empty($data)) { + $override->initWithData($data); } - return $config; + return $override; } /** - * Generate a list of configuration names based on base names. - * - * @param array $names - * List of configuration names. - * - * @return array - * List of configuration names for language override files if applicable. - */ - protected function getLanguageConfigNames(array $names) { - $language_names = array(); - if (isset($this->language)) { - foreach ($names as $name) { - if ($language_name = $this->getLanguageConfigName($this->language->id, $name)) { - $language_names[$name] = $language_name; - } - } - } - return $language_names; - } - - /** - * Get language override name for given language and configuration name. - * - * @param string $langcode - * Language code. - * @param string $name - * Configuration name. - * - * @return bool|string - * Configuration name or FALSE if not applicable. + * {@inheritdoc} */ - protected function getLanguageConfigName($langcode, $name) { - if (strpos($name, static::LANGUAGE_CONFIG_PREFIX) === 0) { - return FALSE; + public function getStorage($langcode) { + if (!isset($this->storages[$langcode])) { + $this->storages[$langcode] = $this->baseStorage->createCollection($this->createConfigCollectionName($langcode)); } - return static::LANGUAGE_CONFIG_PREFIX . '.' . $langcode . '.' . $name; + return $this->storages[$langcode]; } /** @@ -165,4 +140,79 @@ public function setLanguageFromDefault(LanguageDefault $language_default = NULL) return $this; } + /** + * {@inheritdoc} + */ + public function installLanguageOverrides($langcode) { + /** @var \Drupal\Core\Config\ConfigInstallerInterface $config_installer */ + $config_installer = \Drupal::service('config.installer'); + $config_installer->installCollectionDefaultConfig($this->createConfigCollectionName($langcode)); + } + + /** + * {@inheritdoc} + */ + public function getConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) { + $langcode = $this->getLangcodeFromCollectionName($collection); + return $this->getOverride($langcode, $name); + } + + /** + * Creates a configuration collection name based on a langcode. + * + * @param string $langcode + * The langcode. + * + * @return string + * The configuration collection name for a langcode. + */ + protected function createConfigCollectionName($langcode) { + return 'language.' . $langcode; + } + + /** + * Converts a configuration collection name to a langcode. + * + * @param $collection + * The configuration collection name. + * + * @return string + * The langcode of the collection. + * + * @throws \InvalidArgumentException + * Exception thrown if the provided collection name is not in the format + * "language.LANGCODE". + * + * @see self::createConfigCollectionName() + */ + protected function getLangcodeFromCollectionName($collection) { + preg_match('/^language\.(.*)$/', $collection, $matches); + if (!isset($matches[1])) { + throw new \InvalidArgumentException(String::format('!collection is not a valid language override collection', array('!collection' => $collection))); + } + return $matches[1]; + } + + /** + * Reacts to the ConfigEvents::COLLECTION_INFO event. + * + * @param \Drupal\Core\Config\ConfigCollectionInfoEvent $event + * The configuration collection names event. + */ + public function addCollections(ConfigCollectionInfoEvent $event) { + $collections = array(); + foreach (\Drupal::languageManager()->getLanguages() as $language) { + $collections[] = $this->createConfigCollectionName($language->getId()); + } + $event->addCollections($collections, $this); + } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + $events[ConfigEvents::COLLECTION_INFO][] = array('addCollections'); + return $events; + } + } diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php index 1c3de55..1d3a43b 100644 --- a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php +++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php @@ -17,11 +17,6 @@ interface LanguageConfigFactoryOverrideInterface extends ConfigFactoryOverrideInterface { /** - * Prefix for all language configuration files. - */ - const LANGUAGE_CONFIG_PREFIX = 'language.config'; - - /** * Gets the language object used to override configuration data. * * @return \Drupal\Core\Language\Language @@ -62,4 +57,23 @@ public function setLanguageFromDefault(LanguageDefault $language_default = NULL) */ public function getOverride($langcode, $name); + /** + * Returns the storage instance for a particular langcode. + * + * @param string $langcode + * Language code. + * + * @return \Drupal\language\Config\LanguageOverrideStorageInterface + * The language override storage object. + */ + public function getStorage($langcode); + + /** + * Installs available language configuration overrides for a given langcode. + * + * @param string $langcode + * Language code. + */ + public function installLanguageOverrides($langcode); + } diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigOverride.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigOverride.php new file mode 100644 index 0000000..19fb549 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigOverride.php @@ -0,0 +1,63 @@ +name = $name; + $this->storage = $storage; + $this->typedConfigManager = $typed_config; + } + + /** + * {@inheritdoc} + */ + public function save() { + // @todo Use configuration schema to validate. + // https://drupal.org/node/2270399 + // Perform basic data validation. + foreach ($this->data as $key => $value) { + $this->validateValue($key, $value); + } + $this->storage->write($this->name, $this->data); + $this->isNew = FALSE; + $this->originalData = $this->data; + return $this; + } + + /** + * {@inheritdoc} + */ + public function delete() { + $this->data = array(); + $this->storage->delete($this->name); + $this->isNew = TRUE; + $this->originalData = $this->data; + return $this; + } + +} diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php index 101449c..b28e02b 100644 --- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php +++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php @@ -410,6 +410,13 @@ public function getLanguageConfigOverride($langcode, $name) { /** * {@inheritdoc} */ + public function getLanguageConfigOverrideStorage($langcode) { + return $this->configFactoryOverride->getStorage($langcode); + } + + /** + * {@inheritdoc} + */ public function getStandardLanguageListWithoutConfigured() { $languages = $this->getLanguages(); $predefined = $this->getStandardLanguageList(); diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php index 9bbf2ce..f02e0ef 100644 --- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php +++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php @@ -91,12 +91,24 @@ public function updateLockedLanguageWeights(); * @param string $name * The language configuration object name. * - * @return \Drupal\Core\Config\Config + * @return \Drupal\language\Config\LanguageConfigOverride * The language config override object. */ public function getLanguageConfigOverride($langcode, $name); /** + * Gets a language configuration override storage object. + * + * @param string $langcode + * The language code for the override. + * + * @param \Drupal\Core\Config\StorageInterface $storage + * A storage object to use for reading and writing the + * configuration override. + */ + public function getLanguageConfigOverrideStorage($langcode); + + /** * Returns the standard language list excluding already configured languages. * * @return array diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigOverrideImportTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigOverrideImportTest.php new file mode 100644 index 0000000..ef44c84 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigOverrideImportTest.php @@ -0,0 +1,106 @@ + 'Language config override synchronize', + 'description' => 'Ensures the language config overrides can be synchronized.', + 'group' => 'Language', + ); + } + + /** + * Tests that language can be enabled and overrides created during a sync. + */ + public function testConfigOverrideImport() { + language_save(new Language(array( + 'name' => 'French', + 'id' => 'fr', + ))); + /* @var \Drupal\Core\Config\StorageInterface $staging */ + $staging = \Drupal::service('config.storage.staging'); + $this->copyConfig(\Drupal::service('config.storage'), $staging); + + // Uninstall the language module and its dependencies so we can test + // enabling the language module and creating overrides at the same time + // during a configuration synchronisation. + \Drupal::moduleHandler()->uninstall(array('language')); + // Ensure that the current site has no overrides registered to the + // ConfigFactory. + $this->rebuildContainer(); + + /* @var \Drupal\Core\Config\StorageInterface $override_staging */ + $override_staging = $staging->createCollection('language.fr'); + // Create some overrides in staging. + $override_staging->write('system.site', array('name' => 'FR default site name')); + $override_staging->write('system.maintenance', array('message' => 'FR message: @site is currently under maintenance. We should be back shortly. Thank you for your patience')); + + $this->configImporter()->import(); + $this->rebuildContainer(); + \Drupal::service('router.builder')->rebuild(); + + $override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site'); + $this->assertEqual('FR default site name', $override->get('name')); + $this->drupalGet('fr'); + $this->assertText('FR default site name'); + + $this->drupalLogin($this->root_user); + $this->drupalGet('admin/config/development/maintenance/translate/fr/edit'); + $this->assertText('FR message: @site is currently under maintenance. We should be back shortly. Thank you for your patience'); + } + + /** + * Tests that configuration events are not fired during a sync of overrides. + */ + public function testConfigOverrideImportEvents() { + // Enable the config_events_test module so we can record events occurring. + \Drupal::moduleHandler()->install(array('config_events_test')); + $this->rebuildContainer(); + + language_save(new Language(array( + 'name' => 'French', + 'id' => 'fr', + ))); + /* @var \Drupal\Core\Config\StorageInterface $staging */ + $staging = \Drupal::service('config.storage.staging'); + $this->copyConfig(\Drupal::service('config.storage'), $staging); + + /* @var \Drupal\Core\Config\StorageInterface $override_staging */ + $override_staging = $staging->createCollection('language.fr'); + // Create some overrides in staging. + $override_staging->write('system.site', array('name' => 'FR default site name')); + \Drupal::state()->set('config_events_test.event', FALSE); + + $this->configImporter()->import(); + $this->rebuildContainer(); + \Drupal::service('router.builder')->rebuild(); + + // Test that no config save event has been fired during the import becuase + // language configuration overrides do not fire events. + $event_recorder = \Drupal::state()->get('config_events_test.event', FALSE); + $this->assertFalse($event_recorder); + + $this->drupalGet('fr'); + $this->assertText('FR default site name'); + } + +} diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigOverrideInstallTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigOverrideInstallTest.php new file mode 100644 index 0000000..740e24f --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigOverrideInstallTest.php @@ -0,0 +1,45 @@ + 'Language config override synchronize', + 'description' => 'Ensures the language config overrides can be synchronized.', + 'group' => 'Language', + ); + } + + /** + * Tests the configuration events are not fired during install of overrides. + */ + public function testLanguageConfigOverrideInstall() { + language_save(new Language(array('id' => 'de'))); + // Need to enable test module after creating the language otherwise saving + // the language will install the configuration. + $this->enableModules(array('language_config_override_test')); + \Drupal::state()->set('config_events_test.event', FALSE); + $this->installConfig(array('language_config_override_test')); + $event_recorder = \Drupal::state()->get('config_events_test.event', FALSE); + $this->assertFalse($event_recorder); + $config = \Drupal::service('language.config_factory_override')->getOverride('de', 'language_config_override_test.settings'); + $this->assertEqual($config->get('name'), 'Deutsch'); + } + +} diff --git a/core/modules/language/tests/language_config_override_test/config/install/language/de/language_config_override_test.settings.yml b/core/modules/language/tests/language_config_override_test/config/install/language/de/language_config_override_test.settings.yml new file mode 100644 index 0000000..c362980 --- /dev/null +++ b/core/modules/language/tests/language_config_override_test/config/install/language/de/language_config_override_test.settings.yml @@ -0,0 +1 @@ +name: 'Deutsch' diff --git a/core/modules/language/tests/language_config_override_test/language_config_override_test.info.yml b/core/modules/language/tests/language_config_override_test/language_config_override_test.info.yml new file mode 100644 index 0000000..4345db5 --- /dev/null +++ b/core/modules/language/tests/language_config_override_test/language_config_override_test.info.yml @@ -0,0 +1,6 @@ +name: 'Language config overridetest' +type: module +description: 'Support module for the language config override test.' +core: 8.x +package: Testing +version: VERSION diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php index 3c0dc57..f731b13 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php @@ -230,9 +230,9 @@ public function getStringNames(array $lids) { * Language code to delete. */ public function deleteLanguageTranslations($langcode) { - $locale_name = LanguageConfigFactoryOverrideInterface::LANGUAGE_CONFIG_PREFIX . '.' . $langcode . '.'; - foreach ($this->configStorage->listAll($locale_name) as $name) { - $this->configStorage->delete($name); + $storage = $this->languageManager->getLanguageConfigOverrideStorage($langcode); + foreach ($storage->listAll() as $name) { + $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete(); } } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php index bdde87f..50cbc7d 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php @@ -42,7 +42,7 @@ public function testHasTranslation() { $this->installConfig(array('locale_test')); $locale_config_manager = \Drupal::service('locale.config.typed'); - $language = new Language(array('id' => 'de')); + $language = language_save(new Language(array('id' => 'de'))); $result = $locale_config_manager->hasTranslation('locale_test.no_translation', $language); $this->assertFalse($result, 'There is no translation for locale_test.no_translation configuration.'); diff --git a/core/modules/locale/tests/modules/locale_test/config/install/language.config.de.locale_test.translation.yml b/core/modules/locale/tests/modules/locale_test/config/install/language/de/locale_test.translation.yml similarity index 100% rename from core/modules/locale/tests/modules/locale_test/config/install/language.config.de.locale_test.translation.yml rename to core/modules/locale/tests/modules/locale_test/config/install/language/de/locale_test.translation.yml diff --git a/core/modules/system/tests/modules/menu_test/config/install/language.config.nl.menu_test.menu_item.yml b/core/modules/system/tests/modules/menu_test/config/install/language/nl/menu_test.menu_item.yml similarity index 100% rename from core/modules/system/tests/modules/menu_test/config/install/language.config.nl.menu_test.menu_item.yml rename to core/modules/system/tests/modules/menu_test/config/install/language/nl/menu_test.menu_item.yml diff --git a/core/modules/tour/tests/tour_test/config/install/language.config.it.tour.tour.tour-test.yml b/core/modules/tour/tests/tour_test/config/install/language/it/tour.tour.tour-test.yml similarity index 100% rename from core/modules/tour/tests/tour_test/config/install/language.config.it.tour.tour.tour-test.yml rename to core/modules/tour/tests/tour_test/config/install/language/it/tour.tour.tour-test.yml