Issue #2166065 by alexpott, mtift: Replace config_install_default_config with ConfigInstaller service.

8.0.x
Nathaniel Catchpole 2014-01-10 11:48:37 +00:00
parent 5d24ec7cc0
commit 68ce258d63
21 changed files with 251 additions and 128 deletions

View File

@ -81,6 +81,9 @@ services:
- { name: persist }
- { name: event_subscriber }
arguments: ['@config.storage', '@event_dispatcher', '@config.typed']
config.installer:
class: Drupal\Core\Config\ConfigInstaller
arguments: ['@config.factory', '@config.storage', '@config.typed', '@entity.manager', '@event_dispatcher']
config.storage.staging:
class: Drupal\Core\Config\FileStorage
factory_class: Drupal\Core\Config\FileStorageFactory
@ -177,7 +180,7 @@ services:
arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
theme_handler:
class: Drupal\Core\Extension\ThemeHandler
arguments: ['@config.factory', '@module_handler', '@cache.cache', '@info_parser', '@router.builder']
arguments: ['@config.factory', '@module_handler', '@cache.cache', '@info_parser', '@config.installer', '@router.builder']
entity.manager:
class: Drupal\Core\Entity\EntityManager
arguments: ['@container.namespaces', '@service_container', '@module_handler', '@cache.cache', '@language_manager', '@string_translation']

View File

@ -16,87 +16,6 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
* This is the API for configuration storage.
*/
/**
* Installs the default configuration of a given extension.
*
* When an extension is installed, it searches all the default configuration
* directories for all other extensions to locate any configuration with its
* name prefix. For example, the Node module provides the frontpage view as a
* default configuration file:
* core/modules/node/config/views.view.frontpage.yml
* When the Views module is installed after the Node module is already enabled,
* the frontpage view will be installed.
*
* Additionally, the default configuration directory for the extension being
* installed is searched to discover if it contains default configuration
* that is owned by other enabled extensions. So, the frontpage view will also
* be installed when the Node module is installed after Views.
*
* @param string $type
* The extension type; e.g., 'module' or 'theme'.
* @param string $name
* The name of the module or theme to install default configuration for.
*
* @see \Drupal\Core\Config\ExtensionInstallStorage
*/
function config_install_default_config($type, $name) {
// Get all default configuration owned by this extension.
$source_storage = new ExtensionInstallStorage();
$config_to_install = $source_storage->listAll($name . '.');
// Work out if this extension provides default configuration for any other
// enabled extensions.
$config_dir = drupal_get_path($type, $name) . '/config';
if (is_dir($config_dir)) {
$default_storage = new FileStorage($config_dir);
$other_module_config = array_filter($default_storage->listAll(),
function ($value) use ($name) {
return !preg_match('/^' . $name . '\./', $value);
}
);
$enabled_extensions = array_keys(\Drupal::moduleHandler()->getModuleList());
$enabled_extensions += array_keys(array_filter(list_themes(), function ($theme) {return $theme->status;}));
$other_module_config = array_filter($other_module_config, function ($config_name) use ($enabled_extensions) {
$provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
return in_array($provider, $enabled_extensions);
});
$config_to_install = array_merge($config_to_install, $other_module_config);
}
if (!empty($config_to_install)) {
$entity_manager = Drupal::service('entity.manager');
$config_factory = Drupal::service('config.factory');
$target_storage = Drupal::service('config.storage');
$typed_config = Drupal::service('config.typed');
$event_dispatcher = Drupal::service('event_dispatcher');
$config_factory->disableOverrides();
foreach ($config_to_install as $name) {
// Only import new config.
if ($target_storage->exists($name)) {
continue;
}
$new_config = new Config($name, $target_storage, $event_dispatcher, $typed_config);
$data = $source_storage->read($name);
if ($data !== FALSE) {
$new_config->setData($data);
}
if ($entity_type = config_get_entity_type_by_name($name)) {
$entity_manager
->getStorageController($entity_type)
->create($new_config->get())
->save();
}
else {
$new_config->save();
}
$config_factory->enableOverrides();
}
}
}
/**
* Uninstalls the default configuration of a given extension.
*

View File

@ -639,8 +639,8 @@ function drupal_install_system() {
\Drupal::keyValue('system.schema')->set('system', $system_version);
// System module needs to be enabled and the system/module lists need to be
// reset first in order to allow config_install_default_config() to invoke
// config import callbacks.
// reset first in order to allow installation of default configuration to
// invoke config import callbacks.
// @todo Installation profiles may override the system.module config object.
\Drupal::config('system.module')
->set('enabled.system', 0)
@ -649,7 +649,7 @@ function drupal_install_system() {
// Update the module list to include it.
\Drupal::moduleHandler()->setModuleList(array('system' => $system_path . '/system.module'));
config_install_default_config('module', 'system');
\Drupal::service('config.installer')->installDefaultConfig('module', 'system');
module_invoke('system', 'install');
}

View File

@ -0,0 +1,130 @@
<?php
/**
* @file
* Contains Drupal\Core\Config\ConfigInstaller.
*/
namespace Drupal\Core\Config;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class ConfigInstaller implements ConfigInstallerInterface {
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactory
*/
protected $configFactory;
/**
* The active configuration storage.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $activeStorage;
/**
* The typed configuration manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfig;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* Constructs the configuration installer.
*
* @param \Drupal\Core\Config\ConfigFactory $config_factory
* The configuration factory.
* @param \Drupal\Core\Config\StorageInterface $active_storage
* The active configuration storage.
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
* The typed configuration manager.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
*/
public function __construct(ConfigFactory $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, EntityManagerInterface $entity_manager, EventDispatcherInterface $event_dispatcher) {
$this->configFactory = $config_factory;
$this->activeStorage = $active_storage;
$this->typedConfig = $typed_config;
$this->entityManager = $entity_manager;
$this->eventDispatcher = $event_dispatcher;
}
/**
* {@inheritdoc}
*/
public function installDefaultConfig($type, $name) {
// Get all default configuration owned by this extension.
$source_storage = new ExtensionInstallStorage();
$config_to_install = $source_storage->listAll($name . '.');
// Work out if this extension provides default configuration for any other
// enabled extensions.
$config_dir = drupal_get_path($type, $name) . '/config';
if (is_dir($config_dir)) {
$default_storage = new FileStorage($config_dir);
$other_module_config = array_filter($default_storage->listAll(), function ($value) use ($name) {
return !preg_match('/^' . $name . '\./', $value);
});
// Read enabled extensions directly from configuration to avoid circular
// dependencies with ModuleHandler and ThemeHandler.
$enabled_extensions = array_keys((array) $this->configFactory->get('system.module')->get('enabled'));
$enabled_extensions += array_keys((array) $this->configFactory->get('system.theme')->get('enabled'));
$other_module_config = array_filter($other_module_config, function ($config_name) use ($enabled_extensions) {
$provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
return in_array($provider, $enabled_extensions);
});
$config_to_install = array_merge($config_to_install, $other_module_config);
}
if (!empty($config_to_install)) {
$this->configFactory->disableOverrides();
foreach ($config_to_install as $name) {
// Only import new config.
if ($this->activeStorage->exists($name)) {
continue;
}
$new_config = new Config($name, $this->activeStorage, $this->eventDispatcher, $this->typedConfig);
$data = $source_storage->read($name);
if ($data !== FALSE) {
$new_config->setData($data);
}
if ($entity_type = config_get_entity_type_by_name($name)) {
$this->entityManager
->getStorageController($entity_type)
->create($new_config->get())
->save();
}
else {
$new_config->save();
}
}
$this->configFactory->enableOverrides();
}
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains Drupal\Core\Config\ConfigInstallerInterface.
*/
namespace Drupal\Core\Config;
/**
* Interface for classes that install config.
*/
interface ConfigInstallerInterface {
/**
* Installs the default configuration of a given extension.
*
* When an extension is installed, it searches all the default configuration
* directories for all other extensions to locate any configuration with its
* name prefix. For example, the Node module provides the frontpage view as a
* default configuration file:
* core/modules/node/config/views.view.frontpage.yml
* When the Views module is installed after the Node module is already
* enabled, the frontpage view will be installed.
*
* Additionally, the default configuration directory for the extension being
* installed is searched to discover if it contains default configuration
* that is owned by other enabled extensions. So, the frontpage view will also
* be installed when the Node module is installed after Views.
*
* @param string $type
* The extension type; e.g., 'module' or 'theme'.
* @param string $name
* The name of the module or theme to install default configuration for.
*
* @see \Drupal\Core\Config\ExtensionInstallStorage
*/
public function installDefaultConfig($type, $name);
}

View File

@ -633,7 +633,7 @@ class ModuleHandler implements ModuleHandlerInterface {
$version = $versions ? max($versions) : SCHEMA_INSTALLED;
// Install default configuration of the module.
config_install_default_config('module', $module);
\Drupal::service('config.installer')->installDefaultConfig('module', $module);
// If the module has no current updates, but has some that were
// previously removed, set the version to the value of

View File

@ -10,6 +10,7 @@ namespace Drupal\Core\Extension;
use Drupal\Component\Utility\String;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Config\ConfigInstallerInterface;
use Drupal\Core\Routing\RouteBuilder;
use Drupal\Core\SystemListingInfo;
@ -63,6 +64,13 @@ class ThemeHandler implements ThemeHandlerInterface {
*/
protected $cacheBackend;
/**
* The config installer to install configuration.
*
* @var \Drupal\Core\Config\ConfigInstallerInterface
*/
protected $configInstaller;
/**
* The info parser to parse the theme.info.yml files.
*
@ -95,16 +103,21 @@ class ThemeHandler implements ThemeHandlerInterface {
* The cache backend to clear the local tasks cache.
* @param \Drupal\Core\Extension\InfoParserInterface $info_parser
* The info parser to parse the theme.info.yml files.
* @param \Drupal\Core\Config\ConfigInstallerInterface $config_installer
* (optional) The config installer to install configuration. This optional
* to allow the theme handler to work before Drupal is installed and has a
* database.
* @param \Drupal\Core\Routing\RouteBuilder $route_builder
* (optional) The route builder to rebuild the routes if a theme is enabled.
* @param \Drupal\Core\SystemListingInfo $system_list_info
* (optional) The system listing info.
*/
public function __construct(ConfigFactory $config_factory, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, InfoParserInterface $info_parser, RouteBuilder $route_builder = NULL, SystemListingInfo $system_list_info = NULL) {
public function __construct(ConfigFactory $config_factory, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, InfoParserInterface $info_parser, ConfigInstallerInterface $config_installer = NULL, RouteBuilder $route_builder = NULL, SystemListingInfo $system_list_info = NULL) {
$this->configFactory = $config_factory;
$this->moduleHandler = $module_handler;
$this->cacheBackend = $cache_backend;
$this->infoParser = $info_parser;
$this->configInstaller = $config_installer;
$this->routeBuilder = $route_builder;
$this->systemListingInfo = $system_list_info;
}
@ -129,11 +142,11 @@ class ThemeHandler implements ThemeHandlerInterface {
$theme_config->set("enabled.$key", 0)->save();
$disabled_themes->clear($key)->save();
// Refresh the theme list as config_install_default_config() needs an
// updated list to work.
// Refresh the theme list as installation of default configuration needs
// an updated list to work.
$this->reset();
// Install default configuration of the theme.
$this->configInstallDefaultConfig($key);
$this->configInstaller->installDefaultConfig('theme', $key);
}
$this->resetSystem();
@ -441,16 +454,6 @@ class ThemeHandler implements ThemeHandlerInterface {
return $this->systemListingInfo;
}
/**
* Installs the default theme config.
*
* @param string $theme
* The theme to install config for.
*/
protected function configInstallDefaultConfig($theme) {
config_install_default_config('theme', $theme);
}
/**
* Resets some other systems like rebuilding the route information or caches.
*/

View File

@ -317,7 +317,7 @@ class CKEditorTest extends DrupalUnitTestBase {
function testLanguages() {
// Get CKEditor supported language codes and spot-check.
$this->enableModules(array('language'));
config_install_default_config('module', 'language');
$this->installConfig(array('language'));
$langcodes = $this->ckeditor->getLangcodes();
// Language codes transformed with browser mappings.

View File

@ -47,7 +47,7 @@ class ConfigDiffTest extends DrupalUnitTestBase {
);
// Install the default config.
config_install_default_config('module', 'config_test');
$this->installConfig(array('config_test'));
// Change a configuration value in staging.
$staging_data = $original_data;

View File

@ -44,7 +44,7 @@ class ConfigImporterTest extends DrupalUnitTestBase {
$this->installSchema('system', 'config_snapshot');
config_install_default_config('module', 'config_test');
$this->installConfig(array('config_test'));
// Installing config_test's default configuration pollutes the global
// variable being used for recording hook invocations by this test already,
// so it has to be cleared out manually.

View File

@ -32,8 +32,7 @@ class ConfigLanguageOverride extends DrupalUnitTestBase {
public function setUp() {
parent::setUp();
config_install_default_config('module', 'config_test');
config_install_default_config('module', 'locale');
$this->installConfig(array('config_test'));
\Drupal::configFactory()->setLanguage(language_default());
}

View File

@ -51,7 +51,7 @@ class ConfigOverrideTest extends DrupalUnitTestBase {
$conf['config_test.system']['baz'] = 'injected';
$conf['config_test.system']['404'] = 'derp';
config_install_default_config('module', 'config_test');
$this->installConfig(array('config_test'));
// Verify that the original configuration data exists. Have to read storage
// directly otherwise overrides will apply.

View File

@ -33,9 +33,7 @@ class ConfigSchemaTest extends DrupalUnitTestBase {
public function setUp() {
parent::setUp();
config_install_default_config('module', 'system');
config_install_default_config('module', 'image');
config_install_default_config('module', 'config_test');
$this->installConfig(array('system', 'image', 'config_test'));
}
/**

View File

@ -33,6 +33,9 @@ class ConfigSnapshotTest extends DrupalUnitTestBase {
public function setUp() {
parent::setUp();
$this->installSchema('system', 'config_snapshot');
// Update the config snapshot. This allows the parent::setUp() to write
// configuration files.
config_import_create_snapshot(\Drupal::service('config.storage'), \Drupal::service('config.storage.snapshot'));
}
/**
@ -54,7 +57,7 @@ class ConfigSnapshotTest extends DrupalUnitTestBase {
$this->assertFalse($active_snapshot_comparer->createChangelist()->hasChanges());
// Install the default config.
config_install_default_config('module', 'config_test');
$this->installConfig(array('config_test'));
// Although we have imported config this has not affected the snapshot.
$this->assertTrue($active_snapshot_comparer->reset()->hasChanges());

View File

@ -173,6 +173,11 @@ class FieldInfoTest extends FieldUnitTestBase {
* Test that instances on disabled entity types are filtered out.
*/
function testInstanceDisabledEntityType() {
// Disabling the comment module invokes user_modules_uninstalled() and calls
// drupal_flush_all_caches(). Install the necessary schema to support this.
$this->installSchema('user', array('users_data'));
$this->installSchema('system', array('router'));
// For this test the field type and the entity type must be exposed by
// different modules.
$this->enableModules(array('node', 'comment'));
@ -187,9 +192,10 @@ class FieldInfoTest extends FieldUnitTestBase {
'entity_type' => 'comment',
'bundle' => 'comment_node_article',
);
entity_create('field_instance', $instance_definition);
entity_create('field_instance', $instance_definition)->save();
// Disable coment module. This clears field_info cache.
$this->assertNotNull(field_info_instance('comment', 'field', 'comment_node_article'), 'Instance is returned on enabled entity types.');
// Disable comment module. This clears field_info cache.
module_uninstall(array('comment'));
$this->assertNull(field_info_instance('comment', 'field', 'comment_node_article'), 'No instances are returned on disabled entity types.');
}

View File

@ -37,7 +37,7 @@ class FilterUnitTest extends DrupalUnitTestBase {
protected function setUp() {
parent::setUp();
config_install_default_config('module', 'system');
$this->installConfig(array('system'));
$manager = $this->container->get('plugin.manager.filter');
$bag = new FilterBag($manager, array());

View File

@ -102,6 +102,13 @@ abstract class DrupalUnitTestBase extends UnitTestBase {
$this->kernel = new DrupalKernel('unit_testing', drupal_classloader(), FALSE);
$this->kernel->boot();
// Create a minimal system.module configuration object so that the list of
// enabled modules can be maintained allowing
// \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work.
// Write directly to active storage to avoid early instantiation of
// the event dispatcher which can prevent modules from registering events.
\Drupal::service('config.storage')->write('system.module', array('enabled' => array()));
// Collect and set a fixed module list.
$class = get_class($this);
$modules = array();
@ -209,7 +216,7 @@ abstract class DrupalUnitTestBase extends UnitTestBase {
'@module' => $module,
)));
}
config_install_default_config('module', $module);
\Drupal::service('config.installer')->installDefaultConfig('module', $module);
}
$this->pass(format_string('Installed default config: %modules.', array(
'%modules' => implode(', ', $modules),
@ -268,9 +275,16 @@ abstract class DrupalUnitTestBase extends UnitTestBase {
// Set the list of modules in the extension handler.
$module_handler = $this->container->get('module_handler');
$module_filenames = $module_handler->getModuleList();
// Write directly to active storage to avoid early instantiation of
// the event dispatcher which can prevent modules from registering events.
$active_storage = \Drupal::service('config.storage');
$system_config = $active_storage->read('system.module');
foreach ($modules as $module) {
$module_filenames[$module] = drupal_get_filename('module', $module);
// Maintain the list of enabled modules in configuration.
$system_config['enabled'][$module] = 0;
}
$active_storage->write('system.module', $system_config);
$module_handler->setModuleList($module_filenames);
$module_handler->resetImplementations();
// Update the kernel to make their services available.
@ -299,9 +313,12 @@ abstract class DrupalUnitTestBase extends UnitTestBase {
// Unset the list of modules in the extension handler.
$module_handler = $this->container->get('module_handler');
$module_filenames = $module_handler->getModuleList();
$system_config = $this->container->get('config.factory')->get('system.module');
foreach ($modules as $module) {
unset($module_filenames[$module]);
$system_config->clear('enabled.' . $module);
}
$system_config->save();
$module_handler->setModuleList($module_filenames);
$module_handler->resetImplementations();
// Update the kernel to remove their services.

View File

@ -20,7 +20,7 @@ class XssUnitTest extends DrupalUnitTestBase {
*
* @var array
*/
public static $modules = array('filter');
public static $modules = array('filter', 'system');
public static function getInfo() {
return array(
@ -32,7 +32,7 @@ class XssUnitTest extends DrupalUnitTestBase {
protected function setUp() {
parent::setUp();
config_install_default_config('module', 'system');
$this->installConfig(array('system'));
}
/**

View File

@ -557,7 +557,7 @@ function system_requirements($phase) {
function system_install() {
// Enable and set the default theme. Can't use theme_enable() this early in
// installation.
config_install_default_config('theme', 'stark');
\Drupal::service('config.installer')->installDefaultConfig('theme', 'stark');
\Drupal::config('system.theme')
->set('default', 'stark')
->save();

View File

@ -39,7 +39,7 @@ class TourPluginTest extends DrupalUnitTestBase {
protected function setUp() {
parent::setUp();
config_install_default_config('module', 'tour');
$this->installConfig(array('tour'));
$this->pluginManager = $this->container->get('plugin.manager.tour.tip');
}

View File

@ -9,6 +9,7 @@ namespace Drupal\Tests\Core\Extension;
use Drupal\Core\Extension\InfoParser;
use Drupal\Core\Extension\ThemeHandler;
use Drupal\Core\Config\ConfigInstaller;
use Drupal\Tests\UnitTestCase;
/**
@ -56,6 +57,13 @@ class ThemeHandlerTest extends UnitTestCase {
*/
protected $moduleHandler;
/**
* The mocked config installer.
*
* @var \Drupal\Core\Config\ConfigInstaller|\PHPUnit_Framework_MockObject_MockObject
*/
protected $configInstaller;
/**
* The system listing info.
*
@ -89,14 +97,14 @@ class ThemeHandlerTest extends UnitTestCase {
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface');
$this->configInstaller = $this->getMock('Drupal\Core\Config\ConfigInstallerInterface');
$this->routeBuilder = $this->getMockBuilder('Drupal\Core\Routing\RouteBuilder')
->disableOriginalConstructor()
->getMock();
$this->systemListingInfo = $this->getMockBuilder('Drupal\Core\SystemListingInfo')
->disableOriginalConstructor()
->getMock();
$this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->cacheBackend, $this->infoParser, $this->routeBuilder, $this->systemListingInfo);
$this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->cacheBackend, $this->infoParser, $this->configInstaller, $this->routeBuilder, $this->systemListingInfo);
}
/**
@ -149,11 +157,15 @@ class ThemeHandlerTest extends UnitTestCase {
->method('invokeAll')
->with('themes_enabled', array($theme_list));
// Ensure the config installer will be called.
$this->configInstaller->expects($this->once())
->method('installDefaultConfig')
->with('theme', $theme_list[0]);
$this->themeHandler->enable($theme_list);
$this->assertTrue($this->themeHandler->clearedCssCache);
$this->assertTrue($this->themeHandler->registryRebuild);
$this->assertTrue($this->themeHandler->installedDefaultConfig['theme_test']);
}
/**
@ -393,13 +405,6 @@ class TestThemeHandler extends ThemeHandler {
$this->registryRebuild = TRUE;
}
/**
* {@inheritdoc}
*/
protected function configInstallDefaultConfig($theme) {
$this->installedDefaultConfig[$theme] = TRUE;
}
/**
* {@inheritdoc}
*/