diff -u b/core/includes/config.inc b/core/includes/config.inc --- b/core/includes/config.inc +++ b/core/includes/config.inc @@ -24,13 +24,15 @@ * The name of the module or theme to install default configuration for. */ function config_install_default_config($type, $name) { - // Use the admin context for config importing so that any overrides do not - // change the data on import. - $free_context = drupal_container()->get('config.context.free'); + // Use the override free context for config importing so that any overrides do + // not change the data on import. + $config_factory = drupal_container()->get('config.factory'); + $config_factory->enterContext(drupal_container()->get('config.context.free')); + // If this module defines any ConfigEntity types then create an empty // manifest file for each of them. foreach (config_get_module_config_entities($name) as $entity_info) { - config('manifest.' . $entity_info['config_prefix'], $free_context)->save(); + config('manifest.' . $entity_info['config_prefix'])->save(); } $config_dir = drupal_get_path($type, $name) . '/config'; @@ -50,6 +52,11 @@ $remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage); config_sync_changes($remaining_changes, $source_storage, $target_storage); } + // Exit the override free context and ensure that any new manifest data is + // available. + $config_factory + ->leaveContext() + ->reset('manifest'); } /** @@ -95,14 +102,11 @@ * a configuration file. For @code config('book.admin') @endcode, the config * object returned will contain the contents of book.admin configuration file. * - * @param \Drupal\Core\Config\Context\ContextInterface $context - * (Optional) The configuration context to use. - * * @return Drupal\Core\Config\Config * A configuration object. */ -function config($name, ContextInterface $context = NULL) { - return drupal_container()->get('config.factory')->get($name, $context); +function config($name) { + return drupal_container()->get('config.factory')->get($name); } /** @@ -225,8 +229,19 @@ $success = TRUE; try { + // Use the override free context for config importing so that any overrides do + // not change the data on import. + $config_factory = drupal_container()->get('config.factory'); + $config_factory->enterContext(drupal_container()->get('config.context.free')); + $remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage); config_sync_changes($remaining_changes, $source_storage, $target_storage); + + // Exit the override free context and ensure that any new manifest data is + // available. + $config_factory + ->leaveContext() + ->reset('manifest'); } catch (ConfigException $e) { watchdog_exception('config_import', $e); @@ -250,6 +265,7 @@ * @todo Add support for other extension types; e.g., themes etc. */ function config_import_invoke_owner(array $config_changes, StorageInterface $source_storage, StorageInterface $target_storage) { + $factory = drupal_container()->get('config.factory'); // Use the admin context for config importing so that any overrides do not // change the data on import. $free_context = drupal_container()->get('config.context.free'); @@ -279,6 +295,7 @@ $handled_by_module = $manager->getStorageController($entity_type)->$method($name, $new_config, $old_config); } if (!empty($handled_by_module)) { + $factory->reset($name); unset($config_changes[$op][$key]); } } diff -u b/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php --- b/core/lib/Drupal/Core/Config/ConfigFactory.php +++ b/core/lib/Drupal/Core/Config/ConfigFactory.php @@ -38,11 +38,11 @@ protected $storage; /** - * The default configuration context associated with this factory. + * A stack of configuration contexts the last being the context in use. * - * @var \Drupal\Core\Config\Context\ContextInterface + * @var array */ - protected $defaultContext; + protected $contextStack = array(); /** * Cached configuration objects. @@ -61,7 +61,7 @@ */ public function __construct(StorageInterface $storage, ContextInterface $context) { $this->storage = $storage; - $this->defaultContext = $context; + $this->enterContext($context); } /** @@ -69,13 +69,9 @@ * * @param string $name * The name of the configuration object to construct. - * @param \Drupal\Core\Config\Context\ContextInterface $context - * (Optional) The configuration context to use. */ - public function get($name, ContextInterface $context = NULL) { - if (!$context) { - $context = $this->defaultContext; - } + public function get($name) { + $context = $this->getContext(); $cache_key = $this->getCacheKey($name, $context); if (isset($this->cache[$cache_key])) { return $this->cache[$cache_key]; @@ -91,6 +87,9 @@ * @param string $name * (optional) The name of the configuration object to reset. If omitted, all * configuration objects are reset. + * + * @return \Drupal\Core\Config\ConfigFactory + * The config factory object. */ public function reset($name = NULL) { if ($name) { @@ -104,6 +103,7 @@ $config->init(); } } + return $this; } /** @@ -117,8 +117,8 @@ * @todo D8: Remove after http://drupal.org/node/1865206. */ public function rename($old_name, $new_name) { - $old_cache_key = $this->getCacheKey($old_name, $this->defaultContext); - $new_cache_key = $this->getCacheKey($new_name, $this->defaultContext); + $old_cache_key = $this->getCacheKey($old_name, $this->getContext()); + $new_cache_key = $this->getCacheKey($new_name, $this->getContext()); if (isset($this->cache[$old_cache_key])) { $config = $this->cache[$old_cache_key]; // Clone the object into the existing slot. @@ -130,6 +130,43 @@ } } + /** + * Sets the config context by adding it to the context stack. + * + * @param \Drupal\Core\Config\Context\ContextInterface $context + * The configuration context to add. + * + * @return \Drupal\Core\Config\ConfigFactory + * The config factory object. + */ + public function enterContext(ContextInterface $context) { + $this->contextStack[] = $context; + return $this; + } + + /** + * Gets the current config context. + * + * @return \Drupal\Core\Config\Context\ContextInterface $context + * The current configuration context. + */ + public function getContext() { + return end($this->contextStack); + } + + /** + * Leaves the current context by removing it from the context stack. + * + * @return \Drupal\Core\Config\ConfigFactory + * The config factory object. + */ + public function leaveContext() { + if (count($this->contextStack) > 1) { + array_pop($this->contextStack); + } + return $this; + } + /* * Gets the cache key for a given config name in a particular context. * @@ -140,6 +177,12 @@ return $name . '.' . $context->getUuid(); } + /** + * Gets all the cache keys that match the provided config name. + * + * @return array + * An array of cache keys that match the provided config name. + */ public function getCacheKeys($name) { $cache_keys = array_keys($this->cache); return array_filter($cache_keys, function($key) use ($name) { @@ -150 +192,0 @@ - diff -u b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php --- b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php @@ -40,9 +40,13 @@ */ function testConfigLocaleOverride() { $name = 'config_test.system'; - // Verify the default configuration values exist. + // The default language is en so the config key should be localised. $config = config($name); $this->assertIdentical($config->get('foo'), 'en bar'); + + // Ensure that we get the expected value when we use system_config. + $config = system_config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'bar'); } /* @@ -59,6 +63,10 @@ 'name' => 'English', 'langcode' => 'en', ))); + language_save(new Language(array( + 'name' => 'German', + 'langcode' => 'de', + ))); $this->installSchema('user', 'users'); $account = entity_create('user', array( @@ -69,25 +77,61 @@ 'preferred_langcode' => 'fr', )); - $config_context = drupal_container()->get('config.context.user')->setAccount($account); - $config = config('config_test.system', $config_context); + $user_config_context = drupal_container()->get('config.context.user'); + $config_factory = drupal_container()->get('config.factory'); + + $config_factory->enterContext($user_config_context->setAccount($account)); + $config = config('config_test.system'); $this->assertIdentical($config->get('foo'), 'fr bar'); + // Ensure that we get the expected value when we leave the user context. + $config_factory->leaveContext(); + $config = config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'en bar'); + $account = entity_create('user', array( - 'name' => 'English user', + 'name' => 'German user', 'mail' => 'test@example.com', 'created' => REQUEST_TIME, 'status' => 1, - 'preferred_langcode' => 'en', + 'preferred_langcode' => 'de', )); - $config_context = drupal_container()->get('config.context.user')->setAccount($account); + + $config_factory->enterContext($user_config_context->setAccount($account)); // Should not have to re-initialise config object to get new overrides as // the new context will have a different uuid. - $config = config('config_test.system', $config_context); + $config = config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'de bar'); + + // Enter an english context on top of the german context. + $account = entity_create('user', array( + 'name' => 'English user', + 'mail' => 'test@example.com', + 'created' => REQUEST_TIME, + 'status' => 1, + 'preferred_langcode' => 'en', + )); + $en_user_config_context = clone $user_config_context; + $config_factory->enterContext($en_user_config_context->setAccount($account)); + $config = config('config_test.system'); $this->assertIdentical($config->get('foo'), 'en bar'); - $config = system_config('config_test.system'); - $this->assertIdentical($config->get('foo'), 'bar'); + // Ensure that we get the expected value when we leave the english user + // context. + $config_factory->leaveContext(); + $config = config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'de bar'); + + // Ensure that we get the expected value when we leave the german user + // context. + $config_factory->leaveContext(); + $config = config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'en bar'); + + // Ensure that we cannot leave the default context. + $config_factory->leaveContext(); + $config = config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'en bar'); } } diff -u b/core/modules/system/system.module b/core/modules/system/system.module --- b/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -3462,7 +3462,9 @@ * @see config() */ function system_config($name) { - return config($name, drupal_container()->get('config.context.free')); + $config_factory = drupal_container()->get('config.factory'); + $config_factory->enterContext(drupal_container()->get('config.context.free')); + return config($name); } /** diff -u b/core/modules/user/user.module b/core/modules/user/user.module --- b/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1759,14 +1759,17 @@ // Get configuration objects customized for this user, that may be localized // for the user's language if the locale module is enabled. - $config_context = drupal_container()->get('config.context.user')->setAccount($params['account']); - $mail_config = config('user.mail', $config_context); + $config_factory = drupal_container()->get('config.factory'); + $user_config_context = drupal_container()->get('config.context.user'); + $config_factory->enterContext($user_config_context->setAccount($params['account'])); + $mail_config = config('user.mail'); // We do not sanitize the token replacement, since the output of this // replacement is intended for an e-mail message, not a web browser. $token_options = array('langcode' => $langcode, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE); $message['subject'] .= token_replace($mail_config->get($key . '.subject'), $variables, $token_options); $message['body'][] = token_replace($mail_config->get($key . '.body'), $variables, $token_options); + $config_factory->leaveContext(); } /** only in patch2: unchanged: --- /dev/null +++ b/core/modules/config/tests/config_test/config/locale.config.de.config_test.system.yml @@ -0,0 +1 @@ +foo: de bar