diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php index 18d2ee8..09123e8 100644 --- a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php +++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php @@ -67,12 +67,12 @@ static function getSubscribedEvents() { */ protected function filterOverride(Config $config, StorableConfigBase $override) { $override_data = $override->get(); - $this->filterNestedArray($config->get(), $override_data); + $changed = $this->filterNestedArray($config->get(), $override_data); if (empty($override_data)) { // If no override values are left that would apply, remove the override. $override->delete(); } - else { + elseif ($changed) { // Otherwise set the filtered override values back. $override->setData($override_data)->save(); } @@ -87,27 +87,32 @@ protected function filterOverride(Config $config, StorableConfigBase $override) * Override data to filter. */ protected function filterNestedArray(array $original_data, array &$override_data) { + $changed = FALSE; foreach ($override_data as $key => $value) { if (!isset($original_data[$key])) { // The original data is not there anymore, remove the override. unset($override_data[$key]); + $changed = TRUE; } elseif (is_array($override_data[$key])) { if (is_array($original_data[$key])) { // Do the filtering one level deeper. - $this->filterNestedArray($original_data[$key], $override_data[$key]); + $changed = $this->filterNestedArray($original_data[$key], $override_data[$key]); // If no overrides are left under this level, remove the level. if (empty($override_data[$key])) { unset($override_data[$key]); + $changed = TRUE; } } else { // The override is an array but the value is not, this will not go // well, remove the override. unset($override_data[$key]); + $changed = TRUE; } } } + return $changed; } } diff --git a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php index d95458c..b358835 100644 --- a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php +++ b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php @@ -22,7 +22,7 @@ class LocaleConfigSubscriberTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['language', 'locale', 'locale_test']; + public static $modules = ['language', 'locale']; /** * The configurable language manager used in this test. @@ -53,31 +53,52 @@ class LocaleConfigSubscriberTest extends KernelTestBase { protected $localeConfigManager; /** - * The language code used in this test. - * - * @var string - */ - protected $langcode = 'de'; - - /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); - $this->languageManager = $this->container->get('language_manager'); + $this->setUpDefaultLanguage(); + + $this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']); + + $this->setupLanguages(); + + $this->enableModules(['locale_test']); + $this->installConfig(['locale_test']); + // Simulate this hook invoked which would happen if in a non-kernel test + // or normal environment. + locale_modules_installed(array('locale_test')); + $this->configFactory = $this->container->get('config.factory'); $this->stringStorage = $this->container->get('locale.storage'); $this->localeConfigManager = $this->container->get('locale.config_manager'); + $this->languageManager = $this->container->get('language_manager'); - $this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']); + $this->setUpLocale(); + } - $this->installConfig(['locale_test']); - ConfigurableLanguage::createFromLangcode($this->langcode)->save(); + /** + * Sets up default language for this test. + */ + protected function setUpDefaultLanguage() { + // Keep the default English. + } + + /** + * Sets up languages needed for this test. + */ + protected function setUpLanguages() { + ConfigurableLanguage::createFromLangcode('de')->save(); + } + /** + * Sets up the locale storage strings to be in line with configuration. + */ + protected function setUpLocale() { // Set up the locale database the same way we have in the config samples. - $this->setUpNoTranslation('locale_test.no_translation', 'test', 'Test'); - $this->setUpTranslation('locale_test.translation', 'test', 'English test', 'German test'); + $this->setUpNoTranslation('locale_test.no_translation', 'test', 'Test', 'de'); + $this->setUpTranslation('locale_test.translation', 'test', 'English test', 'German test', 'de'); } /** @@ -86,8 +107,8 @@ protected function setUp() { public function testCreateTranslation() { $config_name = 'locale_test.no_translation'; - $this->saveLanguageOverride($config_name, 'test', 'Test (German)'); - $this->assertTranslation($config_name, 'Test (German)'); + $this->saveLanguageOverride($config_name, 'test', 'Test (German)', 'de'); + $this->assertTranslation($config_name, 'Test (German)', 'de'); } /** @@ -96,8 +117,8 @@ public function testCreateTranslation() { public function testLocaleCreateTranslation() { $config_name = 'locale_test.no_translation'; - $this->saveLocaleTranslationData($config_name, 'test', 'Test', 'Test (German)'); - $this->assertTranslation($config_name, 'Test (German)', FALSE); + $this->saveLocaleTranslationData($config_name, 'test', 'Test', 'Test (German)', 'de'); + $this->assertTranslation($config_name, 'Test (German)', 'de', FALSE); } /** @@ -106,8 +127,8 @@ public function testLocaleCreateTranslation() { public function testUpdateTranslation() { $config_name = 'locale_test.translation'; - $this->saveLanguageOverride($config_name, 'test', 'Updated German test'); - $this->assertTranslation($config_name, 'Updated German test'); + $this->saveLanguageOverride($config_name, 'test', 'Updated German test', 'de'); + $this->assertTranslation($config_name, 'Updated German test', 'de'); } /** @@ -116,8 +137,8 @@ public function testUpdateTranslation() { public function testLocaleUpdateTranslation() { $config_name = 'locale_test.translation'; - $this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated German test'); - $this->assertTranslation($config_name, 'Updated German test', FALSE); + $this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated German test', 'de'); + $this->assertTranslation($config_name, 'Updated German test', 'de', FALSE); } /** @@ -126,11 +147,11 @@ public function testLocaleUpdateTranslation() { public function testDeleteTranslation() { $config_name = 'locale_test.translation'; - $this->deleteLanguageOverride($config_name, 'test', 'English test'); + $this->deleteLanguageOverride($config_name, 'test', 'English test', 'de'); // Instead of deleting the translation, we need to keep a translation with // the source value and mark it as customized to prevent the deletion being // reverted by importing community translations. - $this->assertTranslation($config_name, 'English test'); + $this->assertTranslation($config_name, 'English test', 'de'); } /** @@ -139,8 +160,8 @@ public function testDeleteTranslation() { public function testLocaleDeleteTranslation() { $config_name = 'locale_test.translation'; - $this->deleteLocaleTranslationData($config_name, 'test', 'English test'); - $this->assertNoTranslation($config_name); + $this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'de'); + $this->assertNoTranslation($config_name, 'de'); } /** @@ -157,14 +178,13 @@ public function testLocaleDeleteTranslation() { * The configuration key. * @param string $source * The source string. + * @param string $langcode + * The language code. */ - protected function setUpNoTranslation($config_name, $key, $source) { - $this->localeConfigManager->updateConfigTranslations(array($config_name), array($this->langcode)); - $this->languageManager - ->setConfigOverrideLanguage(ConfigurableLanguage::load($this->langcode)); - - $this->assertConfigValue($config_name, $key, $source); - $this->assertNoTranslation($config_name); + protected function setUpNoTranslation($config_name, $key, $source, $langcode) { + $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode)); + $this->assertNoConfigOverride($config_name, $key, $source, $langcode); + $this->assertNoTranslation($config_name, $langcode); } @@ -184,23 +204,33 @@ protected function setUpNoTranslation($config_name, $key, $source) { * The source string. * @param string $translation * The translation string. + * @param string $langcode + * The language code. + * @param bool $is_active + * Whether the update will affect the active configuration. */ - protected function setUpTranslation($config_name, $key, $source, $translation) { + protected function setUpTranslation($config_name, $key, $source, $translation, $langcode, $is_active = FALSE) { // Create source and translation strings for the configuration value and add // the configuration name as a location. This would be performed by // locale_translate_batch_import() invoking // LocaleConfigManager::updateConfigTranslations() normally. $this->localeConfigManager->reset(); $this->localeConfigManager - ->getStringTranslation($config_name, $this->langcode, $source, '') + ->getStringTranslation($config_name, $langcode, $source, '') ->setString($translation) ->setCustomized(FALSE) ->save(); - $this->languageManager - ->setConfigOverrideLanguage(ConfigurableLanguage::load($this->langcode)); + $this->configFactory->reset($config_name); + $this->localeConfigManager->reset(); + $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode)); - $this->assertConfigValue($config_name, $key, $translation); - $this->assertTranslation($config_name, $translation, FALSE); + if ($is_active) { + $this->assertActiveConfig($config_name, $key, $translation, $langcode); + } + else { + $this->assertConfigOverride($config_name, $key, $translation, $langcode); + } + $this->assertTranslation($config_name, $translation, $langcode, FALSE); } /** @@ -218,16 +248,18 @@ protected function setUpTranslation($config_name, $key, $source, $translation) { * The configuration key. * @param string $value * The configuration value to save. + * @param string $langcode + * The language code. */ - protected function saveLanguageOverride($config_name, $key, $value) { + protected function saveLanguageOverride($config_name, $key, $value, $langcode) { $translation_override = $this->languageManager - ->getLanguageConfigOverride($this->langcode, $config_name); + ->getLanguageConfigOverride($langcode, $config_name); $translation_override ->set($key, $value) ->save(); $this->configFactory->reset($config_name); - $this->assertConfigValue($config_name, $key, $value); + $this->assertConfigOverride($config_name, $key, $value, $langcode); } /** @@ -245,17 +277,27 @@ protected function saveLanguageOverride($config_name, $key, $value) { * The configuration key. * @param string $value * The configuration value to save. + * @param string $langcode + * The language code. + * @param bool $is_active + * Whether the update will affect the active configuration. */ - protected function saveLocaleTranslationData($config_name, $key, $source, $translation) { + protected function saveLocaleTranslationData($config_name, $key, $source, $translation, $langcode, $is_active = FALSE) { + $this->localeConfigManager->reset(); $this->localeConfigManager - ->getStringTranslation($config_name, $this->langcode, $source, '') + ->getStringTranslation($config_name, $langcode, $source, '') ->setString($translation) ->save(); $this->localeConfigManager->reset(); - $this->localeConfigManager->updateConfigTranslations(array($config_name), array($this->langcode)); + $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode)); $this->configFactory->reset($config_name); - $this->assertConfigValue($config_name, $key, $translation); + if ($is_active) { + $this->assertActiveConfig($config_name, $key, $translation, $langcode); + } + else { + $this->assertConfigOverride($config_name, $key, $translation, $langcode); + } } /** @@ -274,16 +316,18 @@ protected function saveLocaleTranslationData($config_name, $key, $source, $trans * @param string $source_value * The source configuration value to verify the correct value is returned * from the configuration factory after the deletion. + * @param string $langcode + * The language code. */ - protected function deleteLanguageOverride($config_name, $key, $source_value) { + protected function deleteLanguageOverride($config_name, $key, $source_value, $langcode) { $translation_override = $this->languageManager - ->getLanguageConfigOverride($this->langcode, $config_name); + ->getLanguageConfigOverride($langcode, $config_name); $translation_override ->clear($key) ->save(); $this->configFactory->reset($config_name); - $this->assertConfigValue($config_name, $key, $source_value); + $this->assertNoConfigOverride($config_name, $key, $source_value, $langcode); } /** @@ -302,35 +346,78 @@ protected function deleteLanguageOverride($config_name, $key, $source_value) { * @param string $source_value * The source configuration value to verify the correct value is returned * from the configuration factory after the deletion. + * @param string $langcode + * The language code. */ - protected function deleteLocaleTranslationData($config_name, $key, $source_value) { + protected function deleteLocaleTranslationData($config_name, $key, $source_value, $langcode) { $this->localeConfigManager - ->getStringTranslation($config_name, $this->langcode, $source_value, '') + ->getStringTranslation($config_name, $langcode, $source_value, '') ->delete(); $this->localeConfigManager->reset(); - $this->localeConfigManager->updateConfigTranslations(array($config_name), array($this->langcode)); + $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode)); $this->configFactory->reset($config_name); - $this->assertConfigValue($config_name, $key, $source_value); + $this->assertNoConfigOverride($config_name, $key, $source_value, $langcode); + } + + /** + * Ensures configuration override is not present anymore. + * + * @param string $config_name + * The configuration name. + * @param string $langcode + * The language code. + * + * @return bool + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertNoConfigOverride($config_name, $langcode) { + $config_langcode = $this->configFactory->getEditable($config_name)->get('langcode'); + $override = $this->languageManager->getLanguageConfigOverride($langcode, $config_name); + return $this->assertNotEqual($config_langcode, $langcode) && $this->assertEqual($override->isNew(), TRUE); + } + + /** + * Ensures configuration was saved correctly. + * + * @param string $config_name + * The configuration name. + * @param string $key + * The configuration key. + * @param string $value + * The configuration value. + * @param string $langcode + * The language code. + * + * @return bool + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertConfigOverride($config_name, $key, $value, $langcode) { + $config_langcode = $this->configFactory->getEditable($config_name)->get('langcode'); + $override = $this->languageManager->getLanguageConfigOverride($langcode, $config_name); + return $this->assertNotEqual($config_langcode, $langcode) && $this->assertEqual($override->get($key), $value); } /** * Ensures configuration was saved correctly. * - * @param $config_name + * @param string $config_name * The configuration name. - * @param $key + * @param string $key * The configuration key. - * @param $value + * @param string $value * The configuration value. + * @param string $langcode + * The language code. * * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ - protected function assertConfigValue($config_name, $key, $value) { - // Make sure the configuration was translated correctly. - $translation_config = $this->configFactory->get($config_name); - return $this->assertIdentical($value, $translation_config->get($key)); + protected function assertActiveConfig($config_name, $key, $value, $langcode) { + $config = $this->configFactory->getEditable($config_name); + return + $this->assertEqual($config->get('langcode'), $langcode) && + $this->assertIdentical($config->get($key), $value); } /** @@ -338,15 +425,17 @@ protected function assertConfigValue($config_name, $key, $value) { * * @param string $config_name * The configuration name. + * @param string $langcode + * The language code. * * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ - protected function assertNoTranslation($config_name) { + protected function assertNoTranslation($config_name, $langcode) { $strings = $this->stringStorage->getTranslations([ 'type' => 'configuration', 'name' => $config_name, - 'language' => $this->langcode, + 'language' => $langcode, 'translated' => TRUE, ]); return $this->assertIdentical([], $strings); @@ -359,6 +448,8 @@ protected function assertNoTranslation($config_name) { * The configuration name. * @param string $translation * The translation. + * @param string $langcode + * The language code. * @param bool $customized * Whether or not the string should be asserted to be customized or not * customized. @@ -366,12 +457,12 @@ protected function assertNoTranslation($config_name) { * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ - protected function assertTranslation($config_name, $translation, $customized = TRUE) { + protected function assertTranslation($config_name, $translation, $langcode, $customized = TRUE) { // Make sure a string exists. $strings = $this->stringStorage->getTranslations([ 'type' => 'configuration', 'name' => $config_name, - 'language' => $this->langcode, + 'language' => $langcode, 'translated' => TRUE, ]); $pass = $this->assertIdentical(1, count($strings)); diff --git a/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml b/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml index 5e7e056..daf3391 100644 --- a/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml +++ b/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml @@ -9,6 +9,8 @@ locale_test.no_translation: label: 'Test' # See \Drupal\locale\Tests\LocaleConfigSubscriberTest translatable: true + langcode: + type: string locale_test.translation: type: mapping @@ -19,3 +21,5 @@ locale_test.translation: label: 'Test' # See \Drupal\locale\Tests\LocaleConfigSubscriberTest translatable: true + langcode: + type: string diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 89930ad..f6c23b6 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -290,7 +290,7 @@ public function containerBuild(ContainerBuilder $container) { $this->container = $container; // Set the default language on the minimal container. - $this->container->setParameter('language.default_values', Language::$defaultValues); + $this->container->setParameter('language.default_values', $this->defaultLanguageData()); $container->register('lock', 'Drupal\Core\Lock\NullLockBackend'); $container->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory'); @@ -359,6 +359,16 @@ public function containerBuild(ContainerBuilder $container) { } /** + * Provides the data for setting the default language on the container. + * + * @return array + * The data array for the default language. + */ + protected function defaultLanguageData() { + return Language::$defaultValues; + } + + /** * Installs default configuration for a given list of modules. * * @param array $modules diff --git a/core/modules/locale/src/Tests/LocaleConfigSubscriberForeignTest.php b/core/modules/locale/src/Tests/LocaleConfigSubscriberForeignTest.php new file mode 100644 index 0000000..289333e --- /dev/null +++ b/core/modules/locale/src/Tests/LocaleConfigSubscriberForeignTest.php @@ -0,0 +1,170 @@ +save(); + } + + /** + * {@inheritdoc} + */ + protected function setUpLocale() { + parent::setUpLocale(); + $this->setUpTranslation('locale_test.translation', 'test', 'English test', 'Hungarian test', 'hu', TRUE); + } + + /** + * Tests that the language of default configuration was updated. + */ + public function testDefaultConfigLanguage() { + $this->assertEqual('hu', $this->configFactory->getEditable('locale_test.no_translation')->get('langcode')); + $this->assertEqual('hu', $this->configFactory->getEditable('locale_test.translation')->get('langcode')); + $this->assertEqual($this->configFactory->getEditable('locale_test.translation')->get('test'), 'Hungarian test'); + } + + /** + * Tests creating translations of shipped configuration. + */ + public function testCreateActiveTranslation() { + $config_name = 'locale_test.no_translation'; + $this->saveLanguageActive($config_name, 'test', 'Test (Hungarian)', 'hu'); + $this->assertTranslation($config_name, 'Test (Hungarian)', 'hu'); + } + + /** + * Tests importing community translations of shipped configuration. + */ + public function testLocaleCreateActiveTranslation() { + $config_name = 'locale_test.no_translation'; + $this->saveLocaleTranslationData($config_name, 'test', 'Test', 'Test (Hungarian)', 'hu', TRUE); + $this->assertTranslation($config_name, 'Test (Hungarian)', 'hu', FALSE); + } + + /** + * Tests updating translations of shipped configuration. + */ + public function testUpdateActiveTranslation() { + $config_name = 'locale_test.translation'; + $this->saveLanguageActive($config_name, 'test', 'Updated Hungarian test', 'hu'); + $this->assertTranslation($config_name, 'Updated Hungarian test', 'hu'); + } + + /** + * Tests updating community translations of shipped configuration. + */ + public function testLocaleUpdateActiveTranslation() { + $config_name = 'locale_test.translation'; + $this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated Hungarian test', 'hu', TRUE); + $this->assertTranslation($config_name, 'Updated Hungarian test', 'hu', FALSE); + } + + /** + * @inheritdoc + */ + public function testDeleteTranslation() { + $config_name = 'locale_test.translation'; + $this->deleteLanguageOverride($config_name, 'test', 'English test', 'de'); + // The German translation in this case will be forced to the Hungarian + // source so its not overwritten with locale data later. + $this->assertTranslation($config_name, 'Hungarian test', 'de'); + } + + /** + * Tests deleting translations of shipped configuration. + */ + public function testDeleteActiveTranslation() { + $config_name = 'locale_test.translation'; + $this->configFactory->getEditable($config_name)->delete(); + // Deleting active configuration should not change the locale translation. + $this->assertTranslation($config_name, 'Hungarian test', 'hu', FALSE); + } + + /** + * Tests deleting community translations of shipped configuration. + */ + public function testLocaleDeleteActiveTranslation() { + $config_name = 'locale_test.translation'; + $this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'hu'); + // Deleting the locale translation should not change active config. + $this->assertEqual($this->configFactory->getEditable($config_name)->get('test'), 'Hungarian test'); + } + + /** + * Tests that adding English creates a translation override. + */ + public function testEnglish() { + $config_name = 'locale_test.translation'; + ConfigurableLanguage::createFromLangcode('en')->save(); + // Adding a language on the UI would normally call updateConfigTranslations. + $this->localeConfigManager->updateConfigTranslations(array($config_name), array('en')); + $this->assertConfigOverride($config_name, 'test', 'English test', 'en'); + + $this->configFactory->getEditable('locale.settings')->set('translate_english', TRUE)->save(); + $this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated English test', 'en'); + $this->assertTranslation($config_name, 'Updated English test', 'en', FALSE); + + $this->saveLanguageOverride($config_name, 'test', 'Updated English', 'en'); + $this->assertTranslation($config_name, 'Updated English', 'en'); + + $this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'en'); + $this->assertNoConfigOverride($config_name, 'en'); + } + + /** + * Saves a language override. + * + * This will invoke LocaleConfigSubscriber through the event dispatcher. To + * make sure the configuration was persisted correctly, the configuration + * value is checked. Because LocaleConfigSubscriber temporarily disables the + * override state of the configuration factory we check that the correct value + * is restored afterwards. + * + * @param string $config_name + * The configuration name. + * @param string $key + * The configuration key. + * @param string $value + * The configuration value to save. + * @param string $langcode + * The language code. + */ + protected function saveLanguageActive($config_name, $key, $value, $langcode) { + $this + ->configFactory + ->getEditable($config_name) + ->set($key, $value) + ->save(); + $this->assertActiveConfig($config_name, $key, $value, $langcode); + } + +}