diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php index c7d1cd2..1f1d620 100644 --- a/core/lib/Drupal/Core/Config/DatabaseStorage.php +++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php @@ -77,6 +77,9 @@ public function read($name) { if (isset($data[$name])) { return $data[$name]; } + // @todo change to return to be consistent with other parts of Drupal, for + // example, loading an entity returns NULL if it does not exist. + return FALSE; } /** diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php index ed769fd..c545174 100644 --- a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php +++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php @@ -23,9 +23,19 @@ class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInte /** * 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\language\Config\LanguageOverrideStorageInterface */ - protected $storage; + protected $baseStorage; + + /** + * An array of configuration storages keyed by langcode. + * + * @var \Drupal\language\Config\LanguageOverrideStorageInterface[] + */ + protected $storages; /** * The typed config manager. @@ -59,7 +69,7 @@ class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInte * The typed configuration manager. */ public function __construct(LanguageOverrideStorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config) { - $this->storage = $storage; + $this->baseStorage = $storage; $this->eventDispatcher = $event_dispatcher; $this->typedConfigManager = $typed_config; } @@ -69,7 +79,8 @@ public function __construct(LanguageOverrideStorageInterface $storage, EventDisp */ public function loadOverrides($names) { if ($this->language) { - return $this->storage->readMultiple($names); + $storage = $this->getStorage($this->language->getId()); + return $storage->readMultiple($names); } return array(); } @@ -78,8 +89,8 @@ public function loadOverrides($names) { * {@inheritdoc} */ public function getOverride($langcode, $name) { - $storage = clone $this->storage; - $data = $storage->setLangcode($langcode)->read($name); + $storage = $this->getStorage($langcode); + $data = $storage->read($name); $override = new LanguageConfigOverride($name, $storage, $this->typedConfigManager); if (!empty($data)) { $override->initWithData($data); @@ -90,8 +101,17 @@ public function getOverride($langcode, $name) { /** * {@inheritdoc} */ - public function getStorage() { - return $this->storage; + public function getStorage($langcode = NULL) { + if (!isset($langcode)) { + $langcode = $this->language ? $this->language->id : NULL; + } + if (!isset($this->storages[$langcode])) { + // Clone the language override storage so a process could compare language + // overrides if it wanted to. + $storage = clone $this->baseStorage; + $this->storages[$langcode] = $storage->setLangcode($langcode); + } + return $this->storages[$langcode]; } /** @@ -113,7 +133,6 @@ public function getLanguage() { */ public function setLanguage(Language $language = NULL) { $this->language = $language; - $this->storage->setLangcode($this->language ? $this->language->id : NULL); return $this; } @@ -122,14 +141,11 @@ public function setLanguage(Language $language = NULL) { */ public function setLanguageFromDefault(LanguageDefault $language_default = NULL) { $this->language = $language_default ? $language_default->get() : NULL; - $this->storage->setLangcode($this->language->id); return $this; } /** * {@inheritdoc} - * - * @todo maybe this should be done somewhere else? */ public function install($type, $name) { // Work out if this extension provides default language overrides. @@ -141,10 +157,10 @@ public function install($type, $name) { foreach ($it as $dir) { if (!$dir->isDot() && $dir->isDir() ) { $default_language_config = new FileStorage($dir->getPathname()); - $this->storage->setLangcode($dir->getFilename()); + $storage = $this->getStorage($dir->getFilename()); foreach ($default_language_config->listAll() as $config_name) { $data = $default_language_config->read($config_name); - $config = new LanguageConfigOverride($config_name, $this->storage, $this->typedConfigManager); + $config = new LanguageConfigOverride($config_name, $storage, $this->typedConfigManager); $config->setData($data)->save(); } } @@ -154,11 +170,9 @@ public function install($type, $name) { /** * {@inheritdoc} - * - * @todo maybe this should be done somewhere else? */ public function uninstall($type, $name) { - $this->storage->deleteAllLanguages($name . '.'); + $this->baseStorage->deleteAllOverrides($name . '.'); } } diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php index 43fcd41..6578c7c 100644 --- a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php +++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php @@ -63,8 +63,14 @@ public function setLanguageFromDefault(LanguageDefault $language_default = NULL) public function getOverride($langcode, $name); /** - * Returns the storage instance. + * 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(); + public function getStorage($langcode); } diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageOverrideDatabaseStorage.php b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideDatabaseStorage.php index ae8fcb6..d900309 100644 --- a/core/modules/language/lib/Drupal/language/Config/LanguageOverrideDatabaseStorage.php +++ b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideDatabaseStorage.php @@ -9,6 +9,7 @@ use Drupal\Core\Config\DatabaseStorage; use Drupal\Core\Database\Database; +use Drupal\language\Exception\LanguageOverrideStorageLockedException; /** * Defines cached storage for language configuration overrides. @@ -148,7 +149,7 @@ public function deleteAll($prefix = '') { /** * {@inheritdoc} */ - public function deleteAllLanguages($prefix) { + public function deleteAllOverrides($prefix) { try { $options = array('return' => Database::RETURN_AFFECTED) + $this->options; return (bool) $this->connection->delete($this->table, $options) @@ -164,6 +165,15 @@ public function deleteAllLanguages($prefix) { * {@inheritdoc} */ public function setLangcode($langcode) { + // Prevent side effects from switching changing a storage's langcode. The + // configuration override objects returned from + // \Drupal\language\Config\LanguageConfigFactoryOverride::getOverride() + // have a storage object injected so allowing the langcode to be changed + // once set could create a mismatch between the language of the data and the + // storage. + if (isset($this->langcode) && $langcode !== $this->langcode) { + throw new LanguageOverrideStorageLockedException('Cannot switch language override storage langcode once it has been set.'); + } $this->langcode = $langcode; return $this; } diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorageInterface.php b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorageInterface.php index f2a369a..93d27b6 100644 --- a/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorageInterface.php +++ b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorageInterface.php @@ -14,11 +14,16 @@ /** * Sets the langcode to determine the override configuration directory to use. * + * Implementations + * * @param string $langcode * The language langcode to get the directory for. * * @return $this * The language configuration storage. + * + * @throws \Drupal\language\Exception\LanguageOverrideStorageLockedException + * Exception thrown when trying to set the langcode on a storage when */ public function setLangcode($langcode); @@ -26,14 +31,14 @@ public function setLangcode($langcode); * Deletes all configuration in all languages for the given prefix. * * @param string $prefix - * (optional) The prefix to search for. If omitted, all configuration - * objects that exist will be deleted. + * The prefix to search for. * * @return boolean * TRUE on success, FALSE otherwise. * * @see \Drupal\Core\Config\StorageInterface::deleteAll() + * @see \Drupal\language\Config\LanguageConfigFactoryOverride::uninstall() */ - public function deleteAllLanguages($prefix); + public function deleteAllOverrides($prefix); } diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php index c2e0bb4..7c77a31 100644 --- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php +++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php @@ -410,11 +410,8 @@ public function getLanguageConfigOverride($langcode, $name) { /** * {@inheritdoc} */ - public function getLanguageOverrideStorage($langcode) { - // Clone the language override storage so a process could compare language - // overrides if it wanted to. - $storage = clone $this->configFactoryOverride->getStorage(); - return $storage->setLangcode($langcode); + public function getLanguageConfigOverrideStorage($langcode) { + return $this->configFactoryOverride->getStorage($langcode); } /** diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php index 3804cdd..fe6923e4 100644 --- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php +++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php @@ -97,6 +97,18 @@ public function updateLockedLanguageWeights(); public function getLanguageConfigOverride($langcode, $name); /** + * Gets a language config 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); + + /** * Prepare a language code list for unused predefined languages. * * @return array diff --git a/core/modules/language/lib/Drupal/language/Exception/LanguageOverrideStorageLockedException.php b/core/modules/language/lib/Drupal/language/Exception/LanguageOverrideStorageLockedException.php new file mode 100644 index 0000000..2dec626 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Exception/LanguageOverrideStorageLockedException.php @@ -0,0 +1,15 @@ + 'Language override database storage test', + 'description' => 'Tests the language override database storage', + 'group' => 'Language', + ); + } + + /** + * Sets necessary mock objects for testing LanguageOverrideDatabaseStorage. + */ + public function setUp() { + $this->connection = $this->getMockBuilder('Drupal\Core\Database\Connection') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Tests that the langcode can be set on the storage. + * + * This set exists to provide ensure that the exception is not being thrown + * on the first call to + * \Drupal\language\Config\LanguageOverrideDatabaseStorage::setLangcode(). + * + * @covers ::setLangcode + */ + public function testSetLangcode() { + $storage = new LanguageOverrideDatabaseStorage($this->connection, 'language_config'); + $storage->setLangcode('en'); + $this->assertTrue(TRUE, 'The langcode can be set.'); + } + + /** + * Tests that the langcode can only be set once on the storage. + * + * @expectedException \Drupal\language\Exception\LanguageOverrideStorageLockedException + * + * @covers ::setLangcode + */ + public function testSetLangcodeException() { + $storage = new LanguageOverrideDatabaseStorage($this->connection, 'language_config'); + $storage->setLangcode('en'); + $storage->setLangcode('fr'); + } +} diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php index 50e2f40..f731b13 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php @@ -230,7 +230,7 @@ public function getStringNames(array $lids) { * Language code to delete. */ public function deleteLanguageTranslations($langcode) { - $storage = $this->languageManager->getLanguageOverrideStorage($langcode); + $storage = $this->languageManager->getLanguageConfigOverrideStorage($langcode); foreach ($storage->listAll() as $name) { $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete(); }