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/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_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..8492a97 100644 --- a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php +++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php @@ -7,24 +7,36 @@ namespace Drupal\language\Config; -use Drupal\Core\Config\Config; +use Drupal\Core\Config\ConfigCollectionNamesEvent; +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 +70,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 +79,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 +139,48 @@ 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)); + } + + /** + * 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; + } + + /** + * Reacts to the ConfigEvents::COLLECTION_NAMES event. + * + * @param \Drupal\Core\Config\ConfigCollectionNamesEvent $event + * The configuration collection names event. + */ + public function addCollectionNames(ConfigCollectionNamesEvent $event) { + $collections = array(); + foreach (\Drupal::languageManager()->getLanguages() as $language) { + $collections[] = $this->createConfigCollectionName($language->getId()); + } + $event->addCollectionNames($collections); + } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + $events[ConfigEvents::COLLECTION_NAMES][] = array('addCollectionNames'); + 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..a3ea635 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigOverrideImportTest.php @@ -0,0 +1,70 @@ + 'Language config override synchronize', + 'description' => 'Ensures the language config overrides can be synchronized.', + 'group' => 'Language', + ); + } + + /** + * + */ + 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'); + } +} 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