Issue #1763640 by beejeebus, alexpott, Jose Reyero, c31ck, das-peter, YesCT, heyrocker, Gábor Hojtsy: Introduce config context to make original config and different overrides accessible.
parent
3714bed1e9
commit
0612c664da
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigException;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\NullStorage;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,10 @@ const CONFIG_IMPORT_LOCK = 'config_import';
|
|||
* The name of the module or theme to install default configuration for.
|
||||
*/
|
||||
function config_install_default_config($type, $name) {
|
||||
// Use the override free context for config importing so that any overrides do
|
||||
// not change the data on import.
|
||||
config_context_enter('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) {
|
||||
|
@ -47,6 +51,8 @@ function config_install_default_config($type, $name) {
|
|||
$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.
|
||||
config_context_leave();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +93,7 @@ function config_get_storage_names_with_prefix($prefix = '') {
|
|||
* @code config('book.admin') @endcode will return a configuration object in
|
||||
* which the book module can store its administrative settings.
|
||||
*
|
||||
* @param $name
|
||||
* @param string $name
|
||||
* The name of the configuration object to retrieve. The name corresponds to
|
||||
* a configuration file. For @code config('book.admin') @endcode, the config
|
||||
* object returned will contain the contents of book.admin configuration file.
|
||||
|
@ -99,6 +105,54 @@ function config($name) {
|
|||
return drupal_container()->get('config.factory')->get($name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the config context on the config factory.
|
||||
*
|
||||
* This allows configuration objects to be created using special configuration
|
||||
* contexts eg. global override free or locale using a user preferred language.
|
||||
* Calling this function affects all subsequent calls to config() until
|
||||
* config_context_leave() is called.
|
||||
*
|
||||
* @see config_context_leave()
|
||||
* @see \Drupal\Core\Config\ConfigFactory
|
||||
*
|
||||
* @param string $context_name
|
||||
* The name of the config context service on the container or a fully
|
||||
* qualified class implementing \Drupal\Core\Config\Context\ContextInterface.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Context\ContextInterface
|
||||
* The configuration context object.
|
||||
*/
|
||||
function config_context_enter($context_name) {
|
||||
if (drupal_container()->has($context_name)) {
|
||||
$context = drupal_container()->get($context_name);
|
||||
}
|
||||
elseif (class_exists($context_name) && in_array("Drupal\\Core\\Config\\Context\\ContextInterface", class_implements($context_name))) {
|
||||
$context = drupal_container()
|
||||
->get('config.context.factory')
|
||||
->get($context_name);
|
||||
}
|
||||
else {
|
||||
throw new ConfigException(sprintf('Unknown config context service or class: %s', $context_name));
|
||||
}
|
||||
drupal_container()
|
||||
->get('config.factory')
|
||||
->enterContext($context);
|
||||
return $context;
|
||||
}
|
||||
|
||||
/*
|
||||
* Leaves the current config context returning to the previous context.
|
||||
*
|
||||
* @see config_context_enter()
|
||||
* @see \Drupal\Core\Config\ConfigFactory
|
||||
*/
|
||||
function config_context_leave() {
|
||||
drupal_container()
|
||||
->get('config.factory')
|
||||
->leaveContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of differences between configuration storages.
|
||||
*
|
||||
|
@ -184,10 +238,11 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf
|
|||
* The storage to synchronize configuration to.
|
||||
*/
|
||||
function config_sync_changes(array $config_changes, StorageInterface $source_storage, StorageInterface $target_storage) {
|
||||
$target_context = drupal_container()->get('config.context.free');
|
||||
$factory = drupal_container()->get('config.factory');
|
||||
foreach (array('delete', 'create', 'change') as $op) {
|
||||
foreach ($config_changes[$op] as $name) {
|
||||
$config = new Config($name, $target_storage);
|
||||
$config = new Config($name, $target_storage, $target_context);
|
||||
if ($op == 'delete') {
|
||||
$config->delete();
|
||||
}
|
||||
|
@ -230,9 +285,16 @@ function config_import() {
|
|||
|
||||
$success = TRUE;
|
||||
try {
|
||||
// Use the override free context for config importing so that any overrides do
|
||||
// not change the data on import.
|
||||
config_context_enter('config.context.free');
|
||||
|
||||
$remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage);
|
||||
config_sync_changes($remaining_changes, $source_storage, $target_storage);
|
||||
config_import_create_snapshot($target_storage, $snapshot_storage);
|
||||
|
||||
// Exit the override free context.
|
||||
config_context_leave();
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
watchdog_exception('config_import', $e);
|
||||
|
@ -271,6 +333,10 @@ function config_import_create_snapshot(StorageInterface $source_storage, Storage
|
|||
* @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');
|
||||
// Allow modules to take over configuration change operations for
|
||||
// higher-level configuration data.
|
||||
// First pass deleted, then new, and lastly changed configuration, in order to
|
||||
|
@ -284,11 +350,11 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou
|
|||
// Validate the configuration object name before importing it.
|
||||
Config::validateName($name);
|
||||
if ($entity_type = config_get_entity_type_by_name($name)) {
|
||||
$old_config = new Config($name, $target_storage);
|
||||
$old_config = new Config($name, $target_storage, $free_context);
|
||||
$old_config->load();
|
||||
|
||||
$data = $source_storage->read($name);
|
||||
$new_config = new Config($name, $target_storage);
|
||||
$new_config = new Config($name, $source_storage, $free_context);
|
||||
if ($data !== FALSE) {
|
||||
$new_config->setData($data);
|
||||
}
|
||||
|
@ -297,6 +363,10 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou
|
|||
$handled_by_module = $manager->getStorageController($entity_type)->$method($name, $new_config, $old_config);
|
||||
}
|
||||
if (!empty($handled_by_module)) {
|
||||
$factory->reset($name);
|
||||
// Reset the manifest config object for the config entity.
|
||||
$entity_info = drupal_container()->get('plugin.manager.entity')->getDefinition($entity_type);
|
||||
$factory->reset('manifest.' . $entity_info['config_prefix']);
|
||||
unset($config_changes[$op][$key]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -337,9 +337,16 @@ function install_begin_request(&$install_state) {
|
|||
$container->register('event_dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher');
|
||||
|
||||
$container->register('config.storage', 'Drupal\Core\Config\InstallStorage');
|
||||
$container->register('config.context.factory', 'Drupal\Core\Config\Context\ConfigContextFactory')
|
||||
->addArgument(new Reference('event_dispatcher'));
|
||||
|
||||
$container->register('config.context', 'Drupal\Core\Config\Context\ContextInterface')
|
||||
->setFactoryService(new Reference('config.context.factory'))
|
||||
->setFactoryMethod('get');
|
||||
|
||||
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
|
||||
->addArgument(new Reference('config.storage'))
|
||||
->addArgument(new Reference('event_dispatcher'));
|
||||
->addArgument(new Reference('config.context'));
|
||||
|
||||
// Register the 'language_manager' service.
|
||||
$container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Drupal\Core\Config;
|
|||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Config\ConfigNameException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Drupal\Core\Config\Context\ContextInterface;
|
||||
|
||||
/**
|
||||
* Defines the default configuration object.
|
||||
|
@ -71,11 +71,11 @@ class Config {
|
|||
protected $storage;
|
||||
|
||||
/**
|
||||
* The event dispatcher used to notify subscribers.
|
||||
* The configuration context used for this configuration object.
|
||||
*
|
||||
* @var Symfony\Component\EventDispatcher\EventDispatcher
|
||||
* @var \Drupal\Core\Config\Context\ContextInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Whether the config object has already been loaded.
|
||||
|
@ -89,16 +89,16 @@ class Config {
|
|||
*
|
||||
* @param string $name
|
||||
* The name of the configuration object being constructed.
|
||||
* @param Drupal\Core\Config\StorageInterface $storage
|
||||
* @param \Drupal\Core\Config\StorageInterface $storage
|
||||
* A storage controller object to use for reading and writing the
|
||||
* configuration data.
|
||||
* @param Symfony\Component\EventDispatcher\EventDispatcher $event_dispatcher
|
||||
* The event dispatcher used to notify subscribers.
|
||||
* @param \Drupal\Core\Config\Context\ContextInterface $context
|
||||
* The configuration context used for this configuration object.
|
||||
*/
|
||||
public function __construct($name, StorageInterface $storage, EventDispatcher $event_dispatcher = NULL) {
|
||||
public function __construct($name, StorageInterface $storage, ContextInterface $context) {
|
||||
$this->name = $name;
|
||||
$this->storage = $storage;
|
||||
$this->eventDispatcher = $event_dispatcher ? $event_dispatcher : drupal_container()->get('event_dispatcher');
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -491,7 +491,7 @@ class Config {
|
|||
* Dispatch a config event.
|
||||
*/
|
||||
protected function notify($config_event_name) {
|
||||
$this->eventDispatcher->dispatch('config.' . $config_event_name, new ConfigEvent($this));
|
||||
$this->context->notify($config_event_name, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\Context\ContextInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Drupal\Core\Config\Config;
|
||||
|
||||
class ConfigEvent extends Event {
|
||||
|
||||
/**
|
||||
* Configuration object.
|
||||
*
|
||||
|
@ -14,10 +15,23 @@ class ConfigEvent extends Event {
|
|||
protected $config;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Configuration context object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Context\ContextInterface
|
||||
*/
|
||||
public function __construct(Config $config) {
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Constructs a configuration event object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Context\ContextInterface
|
||||
* Configuration context object.
|
||||
* @param \Drupal\Core\Config\Config
|
||||
* (optional) Configuration object.
|
||||
*/
|
||||
public function __construct(ContextInterface $context, Config $config = NULL) {
|
||||
$this->config = $config;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,4 +40,14 @@ class ConfigEvent extends Event {
|
|||
public function getConfig() {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration context object.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Context\ContextInterface
|
||||
* Configuration context.
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->context;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Drupal\Core\Config\Context\ContextInterface;
|
||||
|
||||
/**
|
||||
* Defines the configuration object factory.
|
||||
|
@ -21,22 +21,28 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
|
|||
* is used for reading and writing the configuration data.
|
||||
*
|
||||
* @see Drupal\Core\Config\StorageInterface
|
||||
*
|
||||
* A configuration context is an object containing parameters that will be
|
||||
* available to the configuration plug-ins for them to customize the
|
||||
* configuration data in different ways.
|
||||
*
|
||||
* @see Drupal\Core\Config\Context\ContextInterface
|
||||
*/
|
||||
class ConfigFactory {
|
||||
|
||||
/**
|
||||
* A storage controller instance for reading and writing configuration data.
|
||||
*
|
||||
* @var Drupal\Core\Config\StorageInterface
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
* A stack of configuration contexts the last being the context in use.
|
||||
*
|
||||
* @var Symfony\Component\EventDispatcher\EventDispatcher
|
||||
* @var array
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
protected $contextStack = array();
|
||||
|
||||
/**
|
||||
* Cached configuration objects.
|
||||
|
@ -48,33 +54,31 @@ class ConfigFactory {
|
|||
/**
|
||||
* Constructs the Config factory.
|
||||
*
|
||||
* @param Drupal\Core\Config\StorageInterface $storage
|
||||
* The storage controller object to use for reading and writing
|
||||
* configuration data.
|
||||
* @param Symfony\Component\EventDispatcher\EventDispatcher
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
* @param \Drupal\Core\Config\StorageInterface
|
||||
* The configuration storage engine.
|
||||
* @param \Drupal\Core\Config\Context\ContextInterface
|
||||
* Configuration context object.
|
||||
*/
|
||||
public function __construct(StorageInterface $storage, EventDispatcher $event_dispatcher) {
|
||||
public function __construct(StorageInterface $storage, ContextInterface $context) {
|
||||
$this->storage = $storage;
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
$this->enterContext($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a configuration object for a given name.
|
||||
* Returns a configuration object for a given name and context.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the configuration object to construct.
|
||||
*
|
||||
* @return Drupal\Core\Config\Config
|
||||
* A configuration object with the given $name.
|
||||
*/
|
||||
public function get($name) {
|
||||
if (isset($this->cache[$name])) {
|
||||
return $this->cache[$name];
|
||||
$context = $this->getContext();
|
||||
$cache_key = $this->getCacheKey($name, $context);
|
||||
if (isset($this->cache[$cache_key])) {
|
||||
return $this->cache[$cache_key];
|
||||
}
|
||||
|
||||
$this->cache[$name] = new Config($name, $this->storage, $this->eventDispatcher);
|
||||
return $this->cache[$name]->init();
|
||||
$this->cache[$cache_key] = new Config($name, $this->storage, $context);
|
||||
return $this->cache[$cache_key]->init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,11 +87,15 @@ class ConfigFactory {
|
|||
* @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) {
|
||||
if (isset($this->cache[$name])) {
|
||||
$this->cache[$name]->init();
|
||||
// Reinitialise the configuration object in all contexts.
|
||||
foreach ($this->getCacheKeys($name) as $cache_key) {
|
||||
$this->cache[$cache_key]->init();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -95,6 +103,7 @@ class ConfigFactory {
|
|||
$config->init();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,14 +117,84 @@ class ConfigFactory {
|
|||
* @todo D8: Remove after http://drupal.org/node/1865206.
|
||||
*/
|
||||
public function rename($old_name, $new_name) {
|
||||
if (isset($this->cache[$old_name])) {
|
||||
$config = $this->cache[$old_name];
|
||||
$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.
|
||||
$this->cache[$old_name] = clone $config;
|
||||
$this->cache[$old_cache_key] = clone $config;
|
||||
|
||||
// Change the object's name and re-initialize it.
|
||||
$config->setName($new_name)->init();
|
||||
$this->cache[$new_name] = $config;
|
||||
$this->cache[$new_cache_key] = $config;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the configuration object.
|
||||
* @param \Drupal\Core\Config\Context\ContextInterface $context
|
||||
* The configuration context.
|
||||
*
|
||||
* @return string
|
||||
* The cache key.
|
||||
*/
|
||||
public function getCacheKey($name, ContextInterface $context) {
|
||||
return $name . ':' . $context->getUuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the cache keys that match the provided config name.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the configuration object.
|
||||
*
|
||||
* @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) {
|
||||
return ( strpos($key, $name) !== false );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Config\Context\ConfigContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config\Context;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigEvent;
|
||||
use Drupal\Component\Uuid\Uuid;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Defines the base configuration context object.
|
||||
*
|
||||
* A configuration context object provides a data array that can be used:
|
||||
* - as a parameter to get customized configuration objects.
|
||||
* - as a store of config data used to override values.
|
||||
*/
|
||||
class ConfigContext implements ContextInterface {
|
||||
|
||||
/**
|
||||
* Predefined key, values to override specific configuration objects.
|
||||
*/
|
||||
const OVERRIDE = 'config.override';
|
||||
|
||||
/**
|
||||
* The actual storage of key-value pairs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcher
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* A unique identifier for the context.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uuid;
|
||||
|
||||
/**
|
||||
* Constructs the configuration context.
|
||||
*
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcher $event_dispatcher
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
*/
|
||||
public function __construct(EventDispatcher $event_dispatcher) {
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\Context\ContextInterface::init().
|
||||
*/
|
||||
public function init($context_key, $data) {
|
||||
if ($data) {
|
||||
$this->set($context_key, $data);
|
||||
}
|
||||
$this->setUuid();
|
||||
// Notify event listeners that a configuration context has been created.
|
||||
$this->notify('context', NULL);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\Context\ContextInterface::get().
|
||||
*/
|
||||
public function get($key) {
|
||||
return array_key_exists($key, $this->data) ? $this->data[$key] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\Context\ContextInterface::set().
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets override data.
|
||||
*
|
||||
* @param mixed $data
|
||||
* Override data to store.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Context\ConfigContext
|
||||
* The config context object.
|
||||
*/
|
||||
public function setOverride($data) {
|
||||
$this->init(self::OVERRIDE, $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\Context\ContextInterface::setUuid().
|
||||
*/
|
||||
public function setUuid() {
|
||||
$uuid = new Uuid();
|
||||
$this->uuid = $uuid->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\Context\ContextInterface::getUuid().
|
||||
*/
|
||||
public function getUuid() {
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\Context\ContextInterface::notify().
|
||||
*/
|
||||
public function notify($config_event_name, Config $config = NULL) {
|
||||
$this->eventDispatcher->dispatch('config.' . $config_event_name, new ConfigEvent($this, $config));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Config\Context\ConfigContextFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config\Context;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Defines configuration context factory.
|
||||
*
|
||||
* The configuration context factory creates configuration context objects.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Context\ContextInterface
|
||||
*/
|
||||
class ConfigContextFactory {
|
||||
|
||||
/**
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcher
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* Constructs the configuration context.
|
||||
*
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcher $event_dispatcher
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
*/
|
||||
public function __construct(EventDispatcher $event_dispatcher) {
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a configuration context object.
|
||||
*
|
||||
* @param string $class
|
||||
* (Optional) The name of the configuration class to use. Defaults to
|
||||
* Drupal\Core\Config\Context\ConfigContext
|
||||
*
|
||||
* @return \Drupal\Core\Config\Context\ContextInterface $context
|
||||
* (Optional) The configuration context to use.
|
||||
*/
|
||||
public function get($class = NULL) {
|
||||
if (!$class) {
|
||||
$class = "Drupal\\Core\\Config\\Context\\ConfigContext";
|
||||
}
|
||||
if (class_exists($class)) {
|
||||
$context = new $class($this->eventDispatcher);
|
||||
}
|
||||
else {
|
||||
throw new ConfigException(sprintf('Unknown config context class: %s', $class));
|
||||
}
|
||||
return $context;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Config\Context\ContextInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config\Context;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
|
||||
/**
|
||||
* Defines the configuration context interface.
|
||||
*
|
||||
* The configuration context object will contain predefined parameters used
|
||||
* by the configuration object for storage operations and notifications
|
||||
* and contextual data to be used by configuration event listeners.
|
||||
*
|
||||
* @see Drupal\Core\Config\Config
|
||||
* @see Drupal\Core\Config\ConfigFactory
|
||||
* @see config()
|
||||
*/
|
||||
interface ContextInterface {
|
||||
|
||||
/*
|
||||
* Initialises a config context for use.
|
||||
*
|
||||
* Creates a unique context identifier, adds data and notifies system about
|
||||
* the new context.
|
||||
*
|
||||
* @param string $context_key
|
||||
* The key that is used to set context data.
|
||||
* @param mixed $data
|
||||
* The context config data.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Context\ConfigContext
|
||||
* The config context object.
|
||||
*/
|
||||
public function init($context_key, $data);
|
||||
|
||||
/**
|
||||
* Returns the stored value for a given key.
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the data to retrieve.
|
||||
*
|
||||
* @return mixed
|
||||
* The stored value, or NULL if no value exists.
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Saves a value for a given key.
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the data to store.
|
||||
* @param mixed $value
|
||||
* The data to store.
|
||||
*/
|
||||
public function set($key, $value);
|
||||
|
||||
/**
|
||||
* Sets the uuid for the context.
|
||||
*
|
||||
* @return string
|
||||
* The context's uuid.
|
||||
*/
|
||||
public function setUuid();
|
||||
|
||||
/**
|
||||
* Gets the uuid for the context.
|
||||
*
|
||||
* @return string
|
||||
* The context's uuid.
|
||||
*/
|
||||
public function getUuid();
|
||||
|
||||
/**
|
||||
* Dispatches a config event.
|
||||
*
|
||||
* @param string $config_event_name
|
||||
* Event name.
|
||||
* @param \Drupal\Core\Config\Config $config
|
||||
* (optional) Configuration object.
|
||||
*/
|
||||
public function notify($config_event_name, Config $config = NULL);
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Config\Context\GlobalConfigContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config\Context;
|
||||
|
||||
/**
|
||||
* Defines the global configuration context object.
|
||||
*
|
||||
* The global configuration context allows config object data to be overridden
|
||||
* with values from the $conf global.
|
||||
*/
|
||||
class GlobalConfigContext extends ConfigContext {
|
||||
|
||||
/**
|
||||
* Sets global override data.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Context\ConfigContext
|
||||
* The config context object.
|
||||
*/
|
||||
public function setGlobalOverride() {
|
||||
global $conf;
|
||||
$this->init(self::OVERRIDE, $conf);
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -52,9 +52,25 @@ class CoreBundle extends Bundle {
|
|||
->addArgument(new Reference('config.cachedstorage.storage'))
|
||||
->addArgument(new Reference('cache.config'));
|
||||
|
||||
$container->register('config.context.factory', 'Drupal\Core\Config\Context\ConfigContextFactory')
|
||||
->addArgument(new Reference('event_dispatcher'));
|
||||
|
||||
$container->register('config.context', 'Drupal\Core\Config\Context\ContextInterface')
|
||||
->setFactoryService(new Reference('config.context.factory'))
|
||||
->setFactoryMethod('get')
|
||||
->addArgument('Drupal\Core\Config\Context\GlobalConfigContext')
|
||||
->addTag('persist')
|
||||
->addMethodCall('setGlobalOverride');
|
||||
|
||||
// Register a config context with no overrides for use in administration
|
||||
// forms, enabling modules and importing configuration.
|
||||
$container->register('config.context.free', 'Drupal\Core\Config\Context\ContextInterface')
|
||||
->setFactoryService(new Reference('config.context.factory'))
|
||||
->setFactoryMethod('get');
|
||||
|
||||
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
|
||||
->addArgument(new Reference('config.storage'))
|
||||
->addArgument(new Reference('event_dispatcher'))
|
||||
->addArgument(new Reference('config.context'))
|
||||
->addTag('persist');
|
||||
|
||||
// Register staging configuration storage.
|
||||
|
@ -262,7 +278,7 @@ class CoreBundle extends Bundle {
|
|||
$container->register('request_close_subscriber', 'Drupal\Core\EventSubscriber\RequestCloseSubscriber')
|
||||
->addArgument(new Reference('module_handler'))
|
||||
->addTag('event_subscriber');
|
||||
$container->register('config_global_override_subscriber', 'Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber')
|
||||
$container->register('config_global_override_subscriber', 'Drupal\Core\EventSubscriber\ConfigOverrideSubscriber')
|
||||
->addTag('event_subscriber');
|
||||
$container->register('language_request_subscriber', 'Drupal\Core\EventSubscriber\LanguageRequestSubscriber')
|
||||
->addArgument(new Reference('language_manager'))
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Override configuration values with values in global $conf variable.
|
||||
*/
|
||||
class ConfigGlobalOverrideSubscriber implements EventSubscriberInterface {
|
||||
/**
|
||||
* Override configuration values with global $conf.
|
||||
*
|
||||
* @param Drupal\Core\Config\ConfigEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function configInit(ConfigEvent $event) {
|
||||
global $conf;
|
||||
|
||||
$config = $event->getConfig();
|
||||
if (isset($conf[$config->getName()])) {
|
||||
$config->setOverride($conf[$config->getName()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EventSubscriberInterface::getSubscribedEvents().
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events['config.init'][] = array('configInit', 30);
|
||||
return $events;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\EventSubscriber\ConfigOverrideSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigEvent;
|
||||
use Drupal\Core\Config\Context\ConfigContext;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Override configuration values with predefined values in context.
|
||||
*/
|
||||
class ConfigOverrideSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* Overrides configuration values.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function configInit(ConfigEvent $event) {
|
||||
if ($override = $event->getContext()->get(ConfigContext::OVERRIDE)) {
|
||||
$config = $event->getConfig();
|
||||
if (isset($override[$config->getName()])) {
|
||||
$config->setOverride($override[$config->getName()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EventSubscriberInterface::getSubscribedEvents().
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events['config.init'][] = array('configInit', 30);
|
||||
return $events;
|
||||
}
|
||||
}
|
|
@ -172,21 +172,13 @@ class ConfigCRUDTest extends DrupalUnitTestBase {
|
|||
|
||||
// Write configuration with an invalid name (missing a namespace) to
|
||||
// staging.
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$manifest_data = config('manifest.invalid_object_name')->get();
|
||||
$manifest_data['new']['name'] = 'invalid';
|
||||
$staging->write('manifest.invalid_object_name', $manifest_data);
|
||||
|
||||
// Verify that an exception is thrown when synchronizing.
|
||||
$message = 'Expected ConfigNameException was thrown when attempting to sync invalid configuration.';
|
||||
try {
|
||||
config_import();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
// Assert that config_import returns false indicating a failure.
|
||||
$this->assertFalse(config_import(), "Config import failed when trying to importing an object with an invalid name");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of \Drupal\config\Tests\ConfigLocaleOverride.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\ConfigException;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests locale config override.
|
||||
*/
|
||||
class ConfigLocaleOverride extends DrupalUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('locale', 'config_test', 'user', 'language', 'system');
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Locale override',
|
||||
'description' => 'Confirm that locale overrides work',
|
||||
'group' => 'Configuration',
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
config_install_default_config('module', 'config_test');
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests basic locale override.
|
||||
*/
|
||||
function testConfigLocaleOverride() {
|
||||
$name = 'config_test.system';
|
||||
// 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_context_enter('config.context.free');
|
||||
$config_admin = config('config_test.system');
|
||||
$this->assertIdentical($config_admin->get('foo'), 'bar');
|
||||
|
||||
// Leave the non override context.
|
||||
config_context_leave();
|
||||
$config = config($name);
|
||||
$this->assertIdentical($config->get('foo'), 'en bar');
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests locale override based on user's preferred language.
|
||||
*/
|
||||
function testConfigLocaleUserOverride() {
|
||||
$this->installSchema('system', 'variable');
|
||||
$this->installSchema('language', 'language');
|
||||
language_save(new Language(array(
|
||||
'name' => 'French',
|
||||
'langcode' => 'fr',
|
||||
)));
|
||||
language_save(new Language(array(
|
||||
'name' => 'English',
|
||||
'langcode' => 'en',
|
||||
)));
|
||||
language_save(new Language(array(
|
||||
'name' => 'German',
|
||||
'langcode' => 'de',
|
||||
)));
|
||||
|
||||
$this->installSchema('user', 'users');
|
||||
$account = entity_create('user', array(
|
||||
'name' => 'French user',
|
||||
'mail' => 'test@example.com',
|
||||
'created' => REQUEST_TIME,
|
||||
'status' => 1,
|
||||
'preferred_langcode' => 'fr',
|
||||
));
|
||||
$config_factory = drupal_container()->get('config.factory');
|
||||
|
||||
$user_config_context = config_context_enter("Drupal\\user\\UserConfigContext");
|
||||
$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' => 'German user',
|
||||
'mail' => 'test@example.com',
|
||||
'created' => REQUEST_TIME,
|
||||
'status' => 1,
|
||||
'preferred_langcode' => 'de',
|
||||
));
|
||||
|
||||
$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');
|
||||
$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',
|
||||
));
|
||||
// Create a new user config context to stack on top of the existign one.
|
||||
$en_user_config_context = config_context_enter("Drupal\\user\\UserConfigContext");
|
||||
$en_user_config_context->setAccount($account);
|
||||
$config = config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), 'en 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');
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests config_context_enter() invalid context name handling.
|
||||
*/
|
||||
function testInvalidContextName() {
|
||||
$message = 'Expected ConfigException was thrown for an invalid context_name argument.';
|
||||
try {
|
||||
config_context_enter('invalid.config.context');
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
use Drupal\Core\Config\Context\ConfigContext;
|
||||
|
||||
/**
|
||||
* Tests configuration overrides via $conf in settings.php.
|
||||
|
@ -19,7 +20,7 @@ class ConfigOverrideTest extends DrupalUnitTestBase {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
public static $modules = array('system', 'config_test');
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
|
@ -29,10 +30,9 @@ class ConfigOverrideTest extends DrupalUnitTestBase {
|
|||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
config_install_default_config('module', 'config_test');
|
||||
$this->installSchema('system', 'config_snapshot');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,16 +46,39 @@ class ConfigOverrideTest extends DrupalUnitTestBase {
|
|||
'404' => 'herp',
|
||||
);
|
||||
|
||||
// Set globals before installing to prove that the installed file does not
|
||||
// contain these values.
|
||||
$conf['config_test.system']['foo'] = 'overridden';
|
||||
$conf['config_test.system']['baz'] = 'injected';
|
||||
$conf['config_test.system']['404'] = 'derp';
|
||||
drupal_container()->get('config.context')->setGlobalOverride();
|
||||
|
||||
config_install_default_config('module', 'config_test');
|
||||
|
||||
// Verify that the original configuration data exists. Have to read storage
|
||||
// directly otherwise overrides will apply.
|
||||
$active = $this->container->get('config.storage');
|
||||
$data = $active->read('config_test.system');
|
||||
$this->assertIdentical($data['foo'], $expected_original_data['foo']);
|
||||
$this->assertFalse(isset($data['baz']));
|
||||
$this->assertIdentical($data['404'], $expected_original_data['404']);
|
||||
|
||||
// Remove the $conf overrides and reset value in config.context service.
|
||||
unset($conf['config_test.system']);
|
||||
drupal_container()->get('config.context')->setGlobalOverride();
|
||||
|
||||
// Verify that the original configuration data exists.
|
||||
$config = config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $expected_original_data['baz']);
|
||||
$this->assertIdentical($config->get('404'), $expected_original_data['404']);
|
||||
|
||||
// Apply the overridden data.
|
||||
// Apply the overridden data, that needs to be set into the config.context
|
||||
// service.
|
||||
$conf['config_test.system']['foo'] = 'overridden';
|
||||
$conf['config_test.system']['baz'] = 'injected';
|
||||
$conf['config_test.system']['404'] = 'derp';
|
||||
drupal_container()->get('config.context')->setGlobalOverride();
|
||||
|
||||
// Verify that the in-memory configuration object still contains the
|
||||
// original data.
|
||||
|
@ -93,14 +116,45 @@ class ConfigOverrideTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($config->get('baz'), $conf['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $conf['config_test.system']['404']);
|
||||
|
||||
// Remove the $conf overrides.
|
||||
// Remove the $conf overrides and reset value in config.context service.
|
||||
unset($conf['config_test.system']);
|
||||
drupal_container()->get('config.context')->setGlobalOverride();
|
||||
|
||||
// Reload it and verify that it still contains the original data.
|
||||
$config->init();
|
||||
$this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $expected_original_data['baz']);
|
||||
$this->assertIdentical($config->get('404'), $expected_original_data['404']);
|
||||
|
||||
// Set globals before importing to prove that the imported file does not
|
||||
// contain these values.
|
||||
$conf['config_test.system']['foo'] = 'overridden';
|
||||
$conf['config_test.system']['baz'] = 'injected';
|
||||
$conf['config_test.system']['404'] = 'derp';
|
||||
|
||||
// Write file to staging
|
||||
drupal_container()->get('config.context')->setGlobalOverride();
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$expected_new_data = array(
|
||||
'foo' => 'barbar',
|
||||
'404' => 'herpderp',
|
||||
);
|
||||
$staging->write('config_test.system', $expected_new_data);
|
||||
|
||||
// Import changed data from staging to active.
|
||||
config_import();
|
||||
$data = $active->read('config_test.system');
|
||||
|
||||
// Verify that the new configuration data exists. Have to read storage
|
||||
// directly otherwise overrides will apply.
|
||||
$this->assertIdentical($data['foo'], $expected_new_data['foo']);
|
||||
$this->assertFalse(isset($data['baz']));
|
||||
$this->assertIdentical($data['404'], $expected_new_data['404']);
|
||||
|
||||
// Verifiy the overrides are still working.
|
||||
$this->assertIdentical($config->get('foo'), $conf['config_test.system']['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $conf['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $conf['config_test.system']['404']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\config\Tests\LocaleConfigOverride.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests locale config override.
|
||||
*/
|
||||
class LocaleConfigOverride extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('locale', 'config_test');
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Locale override',
|
||||
'description' => 'Confirm that locale overrides work',
|
||||
'group' => 'Configuration',
|
||||
);
|
||||
}
|
||||
|
||||
function testLocaleConfigOverride() {
|
||||
$name = 'config_test.system';
|
||||
// Verify the default configuration values exist.
|
||||
$config = config($name);
|
||||
$this->assertIdentical($config->get('foo'), 'en bar');
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
foo: de bar
|
|
@ -0,0 +1 @@
|
|||
foo: fr bar
|
|
@ -22,6 +22,7 @@ class LocaleBundle extends Bundle {
|
|||
public function build(ContainerBuilder $container) {
|
||||
$container->register('locale_config_subscriber', 'Drupal\locale\LocaleConfigSubscriber')
|
||||
->addArgument(new Reference('language_manager'))
|
||||
->addArgument(new Reference('config.context'))
|
||||
->addTag('event_subscriber');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\locale\LocaleConfigSubscriber.
|
||||
* Definition of \Drupal\locale\LocaleConfigSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\locale;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\Context\ConfigContext;
|
||||
use Drupal\Core\Config\Context\ContextInterface;
|
||||
use Drupal\Core\Config\ConfigEvent;
|
||||
use Drupal\Core\Config\StorageDispatcher;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
|
||||
|
@ -19,36 +25,68 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|||
* $config is always a DrupalConfig object.
|
||||
*/
|
||||
class LocaleConfigSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The language manager for retrieving the interface language.
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManager
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
protected $defaultConfigContext;
|
||||
|
||||
/**
|
||||
* Constructs a LocaleConfigSubscriber object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Context\ConfigContext $config_context
|
||||
* The config context service.
|
||||
* @param \Drupal\Core\Language\LanguageManager $language_manager
|
||||
* The language manager service.
|
||||
*/
|
||||
public function __construct(LanguageManager $language_manager) {
|
||||
public function __construct(LanguageManager $language_manager, ContextInterface $config_context) {
|
||||
$this->languageManager = $language_manager;
|
||||
$this->defaultConfigContext = $config_context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize configuration context with language.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function configContext(ConfigEvent $event) {
|
||||
$context = $event->getContext();
|
||||
|
||||
// Add user's language for user context.
|
||||
if ($account = $context->get('user.account')) {
|
||||
$context->set('locale.language', language_load(user_preferred_langcode($account)));
|
||||
}
|
||||
elseif ($language = $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE)) {
|
||||
$context->set('locale.language', $language);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override configuration values with localized data.
|
||||
*
|
||||
* @param Drupal\Core\Config\ConfigEvent $event
|
||||
* @param \Drupal\Core\Config\ConfigEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function configLoad(ConfigEvent $event) {
|
||||
$config = $event->getConfig();
|
||||
$language = $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE);
|
||||
$locale_name = $this->getLocaleConfigName($config->getName(), $language);
|
||||
if ($override = $config->getStorage()->read($locale_name)) {
|
||||
$config->setOverride($override);
|
||||
$context = $event->getContext();
|
||||
if ($language = $context->get('locale.language')) {
|
||||
$config = $event->getConfig();
|
||||
$locale_name = $this->getLocaleConfigName($config->getName(), $language);
|
||||
// Check to see if the config storage has an appropriately named file
|
||||
// containing override data.
|
||||
if ($override = $event->getConfig()->getStorage()->read($locale_name)) {
|
||||
$config->setOverride($override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onKernelRequestSetDefaultConfigContextLocale(GetResponseEvent $event) {
|
||||
if ($language = $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE)) {
|
||||
$this->defaultConfigContext->set('locale.language', $language);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,8 +95,16 @@ class LocaleConfigSubscriber implements EventSubscriberInterface {
|
|||
*
|
||||
* It will be the same name with a prefix depending on language code:
|
||||
* locale.config.LANGCODE.NAME
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the config object.
|
||||
* @param \Drupal\Core\Language\Language $language
|
||||
* The language object.
|
||||
*
|
||||
* @return string
|
||||
* The localised config name.
|
||||
*/
|
||||
public function getLocaleConfigName($name, $language) {
|
||||
public function getLocaleConfigName($name, Language $language) {
|
||||
return 'locale.config.' . $language->langcode . '.' . $name;
|
||||
}
|
||||
|
||||
|
@ -66,7 +112,9 @@ class LocaleConfigSubscriber implements EventSubscriberInterface {
|
|||
* Implements EventSubscriberInterface::getSubscribedEvents().
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events['config.context'][] = array('configContext', 20);
|
||||
$events['config.load'][] = array('configLoad', 20);
|
||||
$events[KernelEvents::REQUEST][] = array('onKernelRequestSetDefaultConfigContextLocale', 20);
|
||||
return $events;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1403,6 +1403,7 @@ function system_modules_uninstall_submit($form, &$form_state) {
|
|||
* @see system_settings_form()
|
||||
*/
|
||||
function system_site_information_settings($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
$site_config = config('system.site');
|
||||
$site_mail = $site_config->get('mail');
|
||||
if (empty($site_mail)) {
|
||||
|
@ -1509,6 +1510,7 @@ function system_site_information_settings_validate($form, &$form_state) {
|
|||
* Form submission handler for system_site_information_settings().
|
||||
*/
|
||||
function system_site_information_settings_submit($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
config('system.site')
|
||||
->set('name', $form_state['values']['site_name'])
|
||||
->set('mail', $form_state['values']['site_mail'])
|
||||
|
@ -1525,6 +1527,7 @@ function system_site_information_settings_submit($form, &$form_state) {
|
|||
* @ingroup forms
|
||||
*/
|
||||
function system_cron_settings($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
$form['description'] = array(
|
||||
'#markup' => '<p>' . t('Cron takes care of running periodic tasks like checking for updates and indexing content for search.') . '</p>',
|
||||
);
|
||||
|
@ -1562,6 +1565,7 @@ function system_cron_settings($form, &$form_state) {
|
|||
* @ingroup forms
|
||||
*/
|
||||
function system_cron_settings_submit($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
config('system.cron')
|
||||
->set('threshold.autorun', $form_state['values']['cron_safe_threshold'])
|
||||
->save();
|
||||
|
@ -1591,6 +1595,7 @@ function system_run_cron_submit($form, &$form_state) {
|
|||
* @see system_logging_settings_submit()
|
||||
*/
|
||||
function system_logging_settings($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
$form['error_level'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Error messages to display'),
|
||||
|
@ -1613,6 +1618,7 @@ function system_logging_settings($form, &$form_state) {
|
|||
* @ingroup forms
|
||||
*/
|
||||
function system_logging_settings_submit($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
config('system.logging')
|
||||
->set('error_level', $form_state['values']['error_level'])
|
||||
->save();
|
||||
|
@ -1626,6 +1632,7 @@ function system_logging_settings_submit($form, &$form_state) {
|
|||
*/
|
||||
function system_performance_settings($form, &$form_state) {
|
||||
drupal_add_library('system', 'drupal.system');
|
||||
config_context_enter('config.context.free');
|
||||
$config = config('system.performance');
|
||||
|
||||
$form['clear_cache'] = array(
|
||||
|
@ -1715,6 +1722,7 @@ function system_performance_settings($form, &$form_state) {
|
|||
* @ingroup forms
|
||||
*/
|
||||
function system_performance_settings_submit($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
$config = config('system.performance');
|
||||
$config->set('cache.page.enabled', $form_state['values']['cache']);
|
||||
$config->set('cache.page.max_age', $form_state['values']['page_cache_maximum_age']);
|
||||
|
@ -1860,6 +1868,7 @@ function system_image_toolkit_settings() {
|
|||
* @ingroup forms
|
||||
*/
|
||||
function system_rss_feeds_settings($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
$rss_config = config('system.rss');
|
||||
$form['feed_description'] = array(
|
||||
'#type' => 'textarea',
|
||||
|
@ -1895,6 +1904,7 @@ function system_rss_feeds_settings($form, &$form_state) {
|
|||
* @ingroup forms
|
||||
*/
|
||||
function system_rss_feeds_settings_submit($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
config('system.rss')
|
||||
->set('channel.description', $form_state['values']['feed_description'])
|
||||
->set('items.limit', $form_state['values']['feed_default_items'])
|
||||
|
@ -2015,6 +2025,7 @@ function system_regional_settings_submit($form, &$form_state) {
|
|||
* @see system_site_maintenance_mode_submit()
|
||||
*/
|
||||
function system_site_maintenance_mode($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
$config = config('system.maintenance');
|
||||
$form['maintenance_mode'] = array(
|
||||
'#type' => 'checkbox',
|
||||
|
@ -2037,6 +2048,7 @@ function system_site_maintenance_mode($form, &$form_state) {
|
|||
* @ingroup forms
|
||||
*/
|
||||
function system_site_maintenance_mode_submit($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
config('system.maintenance')
|
||||
->set('enabled', $form_state['values']['maintenance_mode'])
|
||||
->set('message', $form_state['values']['maintenance_mode_message'])
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\UserConfigContext
|
||||
*/
|
||||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Config\Context\ConfigContext;
|
||||
use Drupal\user\Plugin\Core\Entity\User;
|
||||
|
||||
|
||||
/**
|
||||
* Defines a configuration context object for a user account.
|
||||
*
|
||||
* This should be used when configuration objects need a context for a user
|
||||
* other than the current user.
|
||||
*
|
||||
* @see user_mail()
|
||||
*/
|
||||
class UserConfigContext extends ConfigContext {
|
||||
|
||||
/**
|
||||
* Predefined key for account object.
|
||||
*/
|
||||
const USER_KEY = 'user.account';
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Config\Context\ContextInterface::setUuid().
|
||||
*/
|
||||
public function setUuid() {
|
||||
// Use the user's uuid to identify the config context.
|
||||
$this->uuid = $this->get(self::USER_KEY)->uuid();
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to create config context for user accounts.
|
||||
*
|
||||
* @param \Drupal\user\Plugin\Core\Entity\User $account
|
||||
* The account to add to the config context.
|
||||
*
|
||||
* @return \Drupal\user\UserConfigContext
|
||||
* The user config context object.
|
||||
*/
|
||||
public function setAccount(User $account) {
|
||||
$this->init(self::USER_KEY, $account);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -292,6 +292,7 @@ function user_admin_account_validate($form, &$form_state) {
|
|||
* @see user_admin_settings_submit()
|
||||
*/
|
||||
function user_admin_settings($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
$config = config('user.settings');
|
||||
$mail_config = config('user.mail');
|
||||
|
||||
|
@ -641,6 +642,7 @@ function user_admin_settings($form, &$form_state) {
|
|||
* Form submission handler for user_admin_settings().
|
||||
*/
|
||||
function user_admin_settings_submit($form, &$form_state) {
|
||||
config_context_enter('config.context.free');
|
||||
config('user.settings')
|
||||
->set('anonymous', $form_state['values']['anonymous'])
|
||||
->set('admin_role', $form_state['values']['user_admin_role'])
|
||||
|
|
|
@ -1756,35 +1756,30 @@ function user_view_multiple($accounts, $view_mode = 'full', $langcode = NULL) {
|
|||
function user_mail($key, &$message, $params) {
|
||||
$langcode = $message['langcode'];
|
||||
$variables = array('user' => $params['account']);
|
||||
$message['subject'] .= _user_mail_text($key . '.subject', $langcode, $variables);
|
||||
$message['body'][] = _user_mail_text($key . '.body', $langcode, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mail string for a variable name.
|
||||
*
|
||||
* @param string $key
|
||||
* The config key that provides the mail text.
|
||||
* @param string $langcode
|
||||
* (optional) A language code to use to generate the e-mail text.
|
||||
* @param array $variables
|
||||
* (optional) An array of token keys and values.
|
||||
*
|
||||
* @return
|
||||
* A string value containing the text for the user.mail config key.
|
||||
*/
|
||||
function _user_mail_text($key, $langcode = NULL, $variables = array()) {
|
||||
// We do not sanitize the token replacement, since the output of this
|
||||
// replacement is intended for an e-mail message, not a web browser.
|
||||
return token_replace(config('user.mail')->get($key), $variables, array('langcode' => $langcode, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
|
||||
// Get configuration objects customized for the user specified in $params as
|
||||
// this user is not necessarily the same as the one triggering the mail. This
|
||||
// allows the configuration objects to be localized for the user's language if
|
||||
// the locale module is enabled.
|
||||
$user_config_context = config_context_enter("Drupal\\user\\UserConfigContext");
|
||||
$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);
|
||||
|
||||
// Return the previous config context.
|
||||
config_context_leave();
|
||||
}
|
||||
|
||||
/**
|
||||
* Token callback to add unsafe tokens for user mails.
|
||||
*
|
||||
* This function is used by the token_replace() call at the end of
|
||||
* _user_mail_text() to set up some additional tokens that can be
|
||||
* used in email messages generated by user_mail().
|
||||
* This function is used by the token_replace() to set up some additional
|
||||
* tokens that can be used in email messages generated by user_mail().
|
||||
*
|
||||
* @param $replacements
|
||||
* An associative array variable containing mappings from token names to
|
||||
|
|
Loading…
Reference in New Issue