Commit 4c304cb8 authored by catch's avatar catch
Browse files

Issue #2082117 by alexpott: Fixed Install default config only when the owner...

Issue #2082117 by alexpott: Fixed Install default config only when the owner and provider modules are both enabled.
parent 1a94d949
<?php
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigException;
use Drupal\Core\Config\ConfigInstaller;
use Drupal\Core\Config\ExtensionInstallStorage;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Config\StorageComparer;
use Drupal\Core\Config\ExtensionInstallStorageComparer;
use Symfony\Component\Yaml\Dumper;
/**
......@@ -16,16 +18,56 @@
/**
* 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
* @see \Drupal\Core\Config\ExtensionInstallStorageComparer
* @see \Drupal\Core\Config\ConfigInstaller
*/
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)) {
$source_storage = new FileStorage($config_dir);
$storage_comparer = new StorageComparer($source_storage, Drupal::service('config.storage'));
$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)) {
$storage_comparer = new ExtensionInstallStorageComparer($source_storage, Drupal::service('config.storage'));
$storage_comparer->setSourceNames($config_to_install);
// Only import new config. Changed config is from previous enables and
// should not be overwritten.
$storage_comparer->addChangelistCreate();
......
......@@ -1476,15 +1476,16 @@ function theme_enable($theme_list) {
}
// The value is not used; the weight is ignored for themes currently.
$theme_config->set("enabled.$key", 0);
$disabled_themes->clear($key);
$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.
list_themes(TRUE);
// Install default configuration of the theme.
config_install_default_config('theme', $key);
}
$theme_config->save();
$disabled_themes->save();
list_themes(TRUE);
Drupal::service('router.builder')->rebuild();
menu_router_rebuild();
drupal_theme_rebuild();
......
<?php
/**
* @file
* Contains \Drupal\Core\Config\ExtensionInstallStorage.
*/
namespace Drupal\Core\Config;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageException;
/**
* Defines the file storage controller for metadata files.
*/
class ExtensionInstallStorage extends InstallStorage {
/**
* Returns a map of all config object names and their folders.
*
* The list is based on enabled modules and themes.
*
* @return array
* An array mapping config object names with directories.
*/
protected function getAllFolders() {
if (!isset($this->folders)) {
$this->folders = $this->getComponentNames('module', array_keys(\Drupal::moduleHandler()->getModuleList()));
$this->folders += $this->getComponentNames('theme', array_keys(array_filter(list_themes(), function ($theme) {return $theme->status;})));
}
return $this->folders;
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\Config\StorageException
*/
public function write($name, array $data) {
throw new StorageException('Write operation is not allowed for config extension install storage.');
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\Config\StorageException
*/
public function delete($name) {
throw new StorageException('Delete operation is not allowed for config extension install storage.');
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\Config\StorageException
*/
public function rename($name, $new_name) {
throw new StorageException('Rename operation is not allowed for config extension install storage.');
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Config\ExtensionInstallStorageComparer.
*/
namespace Drupal\Core\Config;
/**
* Defines a config storage comparer.
*/
class ExtensionInstallStorageComparer extends StorageComparer {
/**
* Sets the configuration names in the source storage.
*
* @param array $source_names
* List of all the configuration names in the source storage.
*/
public function setSourceNames(array $source_names) {
$this->sourceNames = $source_names;
return $this;
}
/**
* Gets all the configuration names in the source storage.
*
* @return array
* List of all the configuration names in the source storage.
*
* @see self::setSourceNames()
*/
protected function getSourceNames() {
return $this->sourceNames;
}
}
......@@ -11,8 +11,8 @@
* Import breakpoints from all enabled themes.
*/
function breakpoint_enable() {
// Import breakpoints from themes.
$themes = list_themes();
// Import breakpoints from enabled themes.
$themes = array_filter(list_themes(), function ($theme) {return $theme->status;});
_breakpoint_theme_enabled(array_keys($themes));
// Import breakpoints from modules.
......
......@@ -182,17 +182,6 @@ function testNameValidation() {
$this->fail($message);
}
// Verify an exception is thrown when importing configuration with an
// invalid name (missing a namespace).
$message = 'Expected ConfigNameException was thrown when attempting to install invalid configuration.';
try {
$this->enableModules(array('config_test_invalid_name'));
$this->installConfig(array('config_test_invalid_name'));
$this->fail($message);
}
catch (ConfigNameException $e) {
$this->pass($message);
}
}
}
......@@ -34,6 +34,7 @@ public static function getInfo() {
public function setUp() {
parent::setUp();
config_install_default_config('module', 'config_test');
config_install_default_config('module', 'locale');
}
/**
......
<?php
/**
* @file
* Contains Drupal\config\Tests\ConfigOtherModuleTest.
*/
namespace Drupal\config\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests default configuration provided by a module that does not own it.
*/
class ConfigOtherModuleTest extends WebTestBase {
/**
* @var \Drupal\Core\Extension\ModuleHandler
*/
protected $moduleHandler;
public static function getInfo() {
return array(
'name' => 'Default configuration owner',
'description' => 'Tests default configuration provided by a module that does not own it.',
'group' => 'Configuration',
);
}
/**
* Sets up the module handler for enabling and disabling modules.
*/
public function setUp() {
parent::setUp();
$this->moduleHandler = $this->container->get('module_handler');
}
/**
* Tests enabling the provider of the default configuration first.
*/
public function testInstallOtherModuleFirst() {
$this->moduleHandler->enable(array('config_other_module_config'));
// Check that the config entity doesn't exist before the config_test module
// is enabled. We cannot use the entity system because the config_test
// entity type does not exist.
$config = $this->container->get('config.factory')->get('config_test.dynamic.other_module');
$this->assertTrue($config->isNew(), 'Default configuration for other modules is not installed if that module is not enabled.');
// Install the module that provides the entity type. This installs the
// default configuration.
$this->moduleHandler->enable(array('config_test'));
$this->assertTrue(entity_load('config_test', 'other_module', TRUE), 'Default configuration has been installed.');
// Uninstall the module that provides the entity type. This will remove the
// default configuration.
$this->moduleHandler->disable(array('config_test'));
$this->moduleHandler->uninstall(array('config_test'));
$config = $this->container->get('config.factory')->get('config_test.dynamic.other_module');
$this->assertTrue($config->isNew(), 'Default configuration for other modules is removed when the config entity provider is disabled.');
// Install the module that provides the entity type again. This installs the
// default configuration.
$this->moduleHandler->enable(array('config_test'));
$other_module_config_entity = entity_load('config_test', 'other_module', TRUE);
$this->assertTrue($other_module_config_entity, "Default configuration has been recreated.");
// Update the default configuration to test that the changes are preserved
// if the module that provides the default configuration is uninstalled.
$other_module_config_entity->set('style', "The piano ain't got no wrong notes.");
$other_module_config_entity->save();
// Uninstall the module that provides the default configuration.
$this->moduleHandler->disable(array('config_other_module_config'));
$this->moduleHandler->uninstall(array('config_other_module_config'));
$this->assertTrue(entity_load('config_test', 'other_module', TRUE), 'Default configuration for other modules is not removed when the module that provides it is uninstalled.');
// Default configuration provided by config_test should still exist.
$this->assertTrue(entity_load('config_test', 'dotted.default', TRUE), 'The configuration is not deleted.');
// Re-enable module to test that default config is unchanged.
$this->moduleHandler->enable(array('config_other_module_config'));
$config_entity = entity_load('config_test', 'other_module', TRUE);
$this->assertEqual($config_entity->get('style'), "The piano ain't got no wrong notes.", 'Re-enabling the module does not install default config over the existing config entity.');
}
/**
* Tests enabling the provider of the config entity type first.
*/
public function testInstallConfigEnityModuleFirst() {
$this->moduleHandler->enable(array('config_test'));
$this->assertFalse(entity_load('config_test', 'other_module', TRUE), 'Default configuration provided by config_other_module_config does not exist.');
$this->moduleHandler->enable(array('config_other_module_config'));
$this->assertTrue(entity_load('config_test', 'other_module', TRUE), 'Default configuration provided by config_other_module_config has been installed.');
}
}
......@@ -22,7 +22,7 @@ class ConfigSchemaTest extends DrupalUnitTestBase {
*
* @var array
*/
public static $modules = array('system', 'locale', 'image', 'config_test');
public static $modules = array('system', 'locale', 'field', 'image', 'config_test');
public static function getInfo() {
return array(
......
id: other_module
uuid: 486f9f5c-82ed-4add-a700-b0ee3af4d17d
label: 'Other module'
weight: '0'
style: ''
status: '1'
langcode: en
protected_property: Default
name: 'Config other module config'
type: module
package: Testing
version: VERSION
core: 8.x
hidden: true
<?php
/**
* @file
* Test module which supplies a configuration entity owned by another module.
*/
......@@ -205,15 +205,18 @@ function testTypedDataAPI() {
$user = drupal_anonymous_user();
$this->container->set('current_user', $user);
$available_values = $data->getPossibleValues();
$this->assertEqual($available_values, array('filtered_html', 'full_html', 'plain_text'));
$available_options = $data->getPossibleOptions();
$expected_available_options = array(
'filtered_html' => 'Filtered HTML',
'full_html' => 'Full HTML',
'filter_test' => 'Test format',
'plain_text' => 'Plain text',
);
$available_values = $data->getPossibleValues();
$this->assertEqual($available_values, array_keys($expected_available_options));
$available_options = $data->getPossibleOptions();
$this->assertEqual($available_options, $expected_available_options);
$allowed_values = $data->getSettableValues($user);
$this->assertEqual($allowed_values, array('plain_text'));
$allowed_options = $data->getSettableOptions($user);
......
......@@ -20,7 +20,7 @@ class FilterUnitTest extends DrupalUnitTestBase {
*
* @var array
*/
public static $modules = array('filter');
public static $modules = array('system', 'filter');
/**
* @var \Drupal\filter\Plugin\FilterInterface[]
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment