Issue #2090115 by alexpott, xjm: Don't install a module when its default configuration has unmet dependencies
parent
d6ea0767a3
commit
40cc6312d9
|
|
@ -741,6 +741,8 @@ function install_tasks($install_state) {
|
|||
),
|
||||
'install_profile_themes' => array(
|
||||
),
|
||||
'install_install_profile' => array(
|
||||
),
|
||||
'install_import_translations' => array(
|
||||
'display_name' => t('Set up translations'),
|
||||
'display' => $needs_translations,
|
||||
|
|
@ -1022,10 +1024,6 @@ function install_base_system(&$install_state) {
|
|||
// State can be set to the database now that system.module is installed.
|
||||
$modules = $install_state['profile_info']['dependencies'];
|
||||
|
||||
// The installation profile is also a module, which needs to be installed
|
||||
// after all the dependencies have been installed.
|
||||
$modules[] = drupal_get_profile();
|
||||
|
||||
\Drupal::state()->set('install_profile_modules', array_diff($modules, array('system')));
|
||||
$install_state['base_system_verified'] = TRUE;
|
||||
}
|
||||
|
|
@ -1571,10 +1569,7 @@ function install_profile_modules(&$install_state) {
|
|||
// the modules.
|
||||
$required = array();
|
||||
$non_required = array();
|
||||
// Although the profile module is marked as required, it needs to go after
|
||||
// every dependency, including non-required ones. So clear its required
|
||||
// flag for now to allow it to install late.
|
||||
$files[$install_state['parameters']['profile']]->info['required'] = FALSE;
|
||||
|
||||
// Add modules that other modules depend on.
|
||||
foreach ($modules as $module) {
|
||||
if ($files[$module]->requires) {
|
||||
|
|
@ -1633,6 +1628,24 @@ function install_profile_themes(&$install_state) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the install profile.
|
||||
*
|
||||
* @param $install_state
|
||||
* An array of information about the current installation state.
|
||||
*/
|
||||
function install_install_profile(&$install_state) {
|
||||
\Drupal::service('module_installer')->install(array(drupal_get_profile()), FALSE);
|
||||
// Install all available optional config. During installation the module order
|
||||
// is determined by dependencies. If there are no dependencies between modules
|
||||
// then the order in which they are installed is dependent on random factors
|
||||
// like PHP version. Optional configuration therefore might or might not be
|
||||
// created depending on this order. Ensuring that we have installed all of the
|
||||
// optional configuration whose dependencies can be met at this point removes
|
||||
// any disparities that this creates.
|
||||
\Drupal::service('config.installer')->installOptionalConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the system for import and downloads additional translations.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -786,8 +786,7 @@ class ConfigImporter {
|
|||
|
||||
$this->setProcessedExtension($type, $op, $name);
|
||||
\Drupal::service('config.installer')
|
||||
->setSyncing(FALSE)
|
||||
->resetSourceStorage();
|
||||
->setSyncing(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ namespace Drupal\Core\Config;
|
|||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\Entity\ConfigDependencyManager;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
|
|
@ -89,36 +90,66 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function installDefaultConfig($type, $name) {
|
||||
$extension_path = drupal_get_path($type, $name);
|
||||
$extension_path = $this->drupalGetPath($type, $name);
|
||||
// Refresh the schema cache if the extension provides configuration schema
|
||||
// or is a theme.
|
||||
if (is_dir($extension_path . '/' . InstallStorage::CONFIG_SCHEMA_DIRECTORY) || $type == 'theme') {
|
||||
$this->typedConfig->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
// Gather information about all the supported collections.
|
||||
$collection_info = $this->configManager->getConfigCollectionInfo();
|
||||
$default_install_path = $this->getDefaultConfigDirectory($type, $name);
|
||||
if (is_dir($default_install_path)) {
|
||||
if (!$this->isSyncing()) {
|
||||
$storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION);
|
||||
$prefix = '';
|
||||
}
|
||||
else {
|
||||
// The configuration importer sets the source storage on the config
|
||||
// installer. The configuration importer handles all of the
|
||||
// configuration entity imports. We only need to ensure that simple
|
||||
// configuration is created when the extension is installed.
|
||||
$storage = $this->getSourceStorage();
|
||||
$prefix = $name . '.';
|
||||
}
|
||||
|
||||
// Read enabled extensions directly from configuration to avoid circular
|
||||
// dependencies with ModuleHandler and ThemeHandler.
|
||||
$extension_config = $this->configFactory->get('core.extension');
|
||||
$modules = (array) $extension_config->get('module');
|
||||
// Unless we are installing the profile, remove it from the list.
|
||||
if ($install_profile = Settings::get('install_profile')) {
|
||||
if ($name !== $install_profile) {
|
||||
unset($modules[$install_profile]);
|
||||
// Gets a profile storage to search for overrides if necessary.
|
||||
$profile_storage = $this->getProfileStorage($name);
|
||||
|
||||
// Gather information about all the supported collections.
|
||||
$collection_info = $this->configManager->getConfigCollectionInfo();
|
||||
foreach ($collection_info->getCollectionNames() as $collection) {
|
||||
$config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storage);
|
||||
// If we're installing a profile ensure configuration that is overriding
|
||||
// is excluded.
|
||||
if ($name == $this->drupalGetProfile()) {
|
||||
$existing_configuration = $this->getActiveStorages($collection)->listAll();
|
||||
$config_to_create = array_diff_key($config_to_create, array_flip($existing_configuration));
|
||||
}
|
||||
if (!empty($config_to_create)) {
|
||||
$this->createConfiguration($collection, $config_to_create);
|
||||
}
|
||||
}
|
||||
}
|
||||
$enabled_extensions = array_keys($modules);
|
||||
$enabled_extensions += array_keys((array) $extension_config->get('theme'));
|
||||
|
||||
// Core can provide configuration.
|
||||
$enabled_extensions[] = 'core';
|
||||
|
||||
foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
|
||||
$config_to_install = $this->listDefaultConfigToInstall($type, $name, $collection, $enabled_extensions);
|
||||
if (!empty($config_to_install)) {
|
||||
$this->createConfiguration($collection, $config_to_install);
|
||||
// During a drupal installation optional configuration is installed at the
|
||||
// end of the installation process.
|
||||
// @see install_install_profile()
|
||||
if (!$this->isSyncing() && !$this->drupalInstallationAttempted()) {
|
||||
$optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
|
||||
if (is_dir($optional_install_path)) {
|
||||
// Install any optional config the module provides.
|
||||
$storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION);
|
||||
$this->installOptionalConfig($storage, '');
|
||||
}
|
||||
// Install any optional configuration entities whose type this extension
|
||||
// provides. This searches all the installed modules config/optional
|
||||
// directories.
|
||||
$provides_config_entity_type = array_reduce($this->configManager->getEntityManager()->getDefinitions(), function ($return, EntityTypeInterface $entity_type) use ($name) {
|
||||
return $return ?: $entity_type->getProvider() && $entity_type->getConfigPrefix();
|
||||
}, FALSE);
|
||||
if ($provides_config_entity_type) {
|
||||
$storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE);
|
||||
$this->installOptionalConfig($storage, $name . '.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,49 +158,76 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Lists default configuration for an extension that is available to install.
|
||||
*
|
||||
* This looks in the extension's config/install directory and all of the
|
||||
* currently enabled extensions config/install directories for configuration
|
||||
* that begins with the extension's name.
|
||||
*
|
||||
* @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.
|
||||
* @param string $collection
|
||||
* The configuration collection to install.
|
||||
* @param array $enabled_extensions
|
||||
* A list of all the currently enabled modules and themes.
|
||||
*
|
||||
* @return array
|
||||
* The list of configuration objects to create.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function listDefaultConfigToInstall($type, $name, $collection, array $enabled_extensions) {
|
||||
// Get all default configuration owned by this extension.
|
||||
$source_storage = $this->getSourceStorage($collection);
|
||||
$config_to_install = $source_storage->listAll($name . '.');
|
||||
|
||||
// If not installing the core base system default configuration, work out if
|
||||
// this extension provides default configuration for any other enabled
|
||||
// extensions.
|
||||
$extension_path = drupal_get_path($type, $name);
|
||||
if ($type !== 'core' && is_dir($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY)) {
|
||||
$default_storage = new FileStorage($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection);
|
||||
$extension_provided_config = array_filter($default_storage->listAll(), function ($config_name) use ($config_to_install, $enabled_extensions) {
|
||||
// Ensure that we have not already discovered the config to install.
|
||||
if (in_array($config_name, $config_to_install)) {
|
||||
return FALSE;
|
||||
}
|
||||
// Ensure the configuration is provided by an enabled module.
|
||||
$provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
|
||||
return in_array($provider, $enabled_extensions);
|
||||
});
|
||||
|
||||
$config_to_install = array_merge($config_to_install, $extension_provided_config);
|
||||
public function installOptionalConfig(StorageInterface $storage = NULL, $prefix = '') {
|
||||
if (!$storage) {
|
||||
// Search the install profile's optional configuration too.
|
||||
$storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE);
|
||||
// The extension install storage ensures that overrides are used.
|
||||
$profile_storage = NULL;
|
||||
}
|
||||
else {
|
||||
// Creates a profile storage to search for overrides.
|
||||
$profile_install_path = $this->drupalGetPath('module', $this->drupalGetProfile()) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
|
||||
$profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
|
||||
}
|
||||
|
||||
return $config_to_install;
|
||||
$collection_info = $this->configManager->getConfigCollectionInfo();
|
||||
$enabled_extensions = $this->getEnabledExtensions();
|
||||
|
||||
foreach ($collection_info->getCollectionNames() as $collection) {
|
||||
if (!$this->configManager->supportsConfigurationEntities($collection)) {
|
||||
continue;
|
||||
}
|
||||
$existing_config = $this->getActiveStorages($collection)->listAll($prefix);
|
||||
$config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storage);
|
||||
$all_config = array_merge($existing_config, array_keys($config_to_create));
|
||||
foreach ($config_to_create as $config_name => $data) {
|
||||
// Exclude configuration that:
|
||||
// - already exists
|
||||
// - is a not configuration entity
|
||||
// - or its dependencies cannot be met.
|
||||
if (in_array($config_name, $existing_config) ||
|
||||
!$this->configManager->getEntityTypeIdByName($config_name) ||
|
||||
!$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config)) {
|
||||
unset($config_to_create[$config_name]);
|
||||
}
|
||||
}
|
||||
if (!empty($config_to_create)) {
|
||||
$this->createConfiguration($collection, $config_to_create, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets configuration data from the provided storage to create.
|
||||
*
|
||||
* @param StorageInterface $storage
|
||||
* The configuration storage to read configuration from.
|
||||
* @param string $collection
|
||||
* The configuration collection to use.
|
||||
* @param string $prefix
|
||||
* (optional) Limit to configuration starting with the provided string.
|
||||
*
|
||||
* @return array
|
||||
* An array of configuration data read from the source storage keyed by the
|
||||
* configuration object name.
|
||||
*/
|
||||
protected function getConfigToCreate(StorageInterface $storage, $collection, $prefix = '', StorageInterface $profile_storage = NULL) {
|
||||
if ($storage->getCollectionName() != $collection) {
|
||||
$storage = $storage->createCollection($collection);
|
||||
}
|
||||
$data = $storage->readMultiple($storage->listAll($prefix));
|
||||
|
||||
// Check to see if the corresponding override storage has any overrides.
|
||||
if ($profile_storage) {
|
||||
if ($profile_storage->getCollectionName() != $collection) {
|
||||
$profile_storage = $profile_storage->createCollection($collection);
|
||||
}
|
||||
$data = $profile_storage->readMultiple(array_keys($data)) + $data;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -177,24 +235,23 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
*
|
||||
* @param string $collection
|
||||
* The configuration collection.
|
||||
* @param array $config_to_install
|
||||
* A list of configuration object names to create.
|
||||
* @param array $config_to_create
|
||||
* An array of configuration data to create, keyed by name.
|
||||
*/
|
||||
protected function createConfiguration($collection, array $config_to_install) {
|
||||
protected function createConfiguration($collection, array $config_to_create) {
|
||||
// Order the configuration to install in the order of dependencies.
|
||||
$data = $this->getSourceStorage($collection)->readMultiple($config_to_install);
|
||||
$config_entity_support = $this->configManager->supportsConfigurationEntities($collection);
|
||||
if ($config_entity_support) {
|
||||
$dependency_manager = new ConfigDependencyManager();
|
||||
$config_to_install = $dependency_manager
|
||||
->setData($data)
|
||||
$config_names = $dependency_manager
|
||||
->setData($config_to_create)
|
||||
->sortAll();
|
||||
}
|
||||
else {
|
||||
$config_names = array_keys($config_to_create);
|
||||
}
|
||||
|
||||
// Remove configuration that already exists in the active storage.
|
||||
$config_to_install = array_diff($config_to_install, $this->getActiveStorages($collection)->listAll());
|
||||
|
||||
foreach ($config_to_install as $name) {
|
||||
foreach ($config_names as $name) {
|
||||
// Allow config factory overriders to use a custom configuration object if
|
||||
// they are responsible for the collection.
|
||||
$overrider = $this->configManager->getConfigCollectionInfo()->getOverrideService($collection);
|
||||
|
|
@ -204,18 +261,17 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
else {
|
||||
$new_config = new Config($name, $this->getActiveStorages($collection), $this->eventDispatcher, $this->typedConfig);
|
||||
}
|
||||
if ($data[$name] !== FALSE) {
|
||||
$new_config->setData($data[$name]);
|
||||
if ($config_to_create[$name] !== FALSE) {
|
||||
$new_config->setData($config_to_create[$name]);
|
||||
}
|
||||
if ($config_entity_support && $entity_type = $this->configManager->getEntityTypeIdByName($name)) {
|
||||
|
||||
// If we are syncing do not create configuration entities. Pluggable
|
||||
// configuration entities can have dependencies on modules that are
|
||||
// not yet enabled. This approach means that any code that expects
|
||||
// default configuration entities to exist will be unstable after the
|
||||
// module has been enabled and before the config entity has been
|
||||
// imported.
|
||||
if ($this->isSyncing) {
|
||||
if ($this->isSyncing()) {
|
||||
continue;
|
||||
}
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage */
|
||||
|
|
@ -246,16 +302,15 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function installCollectionDefaultConfig($collection) {
|
||||
$config_to_install = $this->getSourceStorage($collection)->listAll();
|
||||
$extension_config = $this->configFactory->get('core.extension');
|
||||
$enabled_extensions = array_keys((array) $extension_config->get('module'));
|
||||
$enabled_extensions += array_keys((array) $extension_config->get('theme'));
|
||||
$config_to_install = array_filter($config_to_install, function ($config_name) use ($enabled_extensions) {
|
||||
$storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, $this->drupalInstallationAttempted());
|
||||
// Only install configuration for enabled extensions.
|
||||
$enabled_extensions = $this->getEnabledExtensions();
|
||||
$config_to_install = array_filter($storage->listAll(), function ($config_name) use ($enabled_extensions) {
|
||||
$provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
|
||||
return in_array($provider, $enabled_extensions);
|
||||
});
|
||||
if (!empty($config_to_install)) {
|
||||
$this->createConfiguration($collection, $config_to_install);
|
||||
$this->createConfiguration($collection, $storage->readMultiple($config_to_install));
|
||||
// Reset all the static caches and list caches.
|
||||
$this->configFactory->reset();
|
||||
}
|
||||
|
|
@ -269,34 +324,14 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetSourceStorage() {
|
||||
$this->sourceStorage = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration storage that provides the default configuration.
|
||||
*
|
||||
* @param string $collection
|
||||
* (optional) The configuration collection. Defaults to the default
|
||||
* collection.
|
||||
*
|
||||
* @return \Drupal\Core\Config\StorageInterface
|
||||
* @return \Drupal\Core\Config\StorageInterface|null
|
||||
* The configuration storage that provides the default configuration.
|
||||
* Returns null if the source storage has not been set.
|
||||
*/
|
||||
public function getSourceStorage($collection = StorageInterface::DEFAULT_COLLECTION) {
|
||||
if (!isset($this->sourceStorage)) {
|
||||
// Default to using the ExtensionInstallStorage which searches extension's
|
||||
// config directories for default configuration. Only include the profile
|
||||
// configuration during Drupal installation.
|
||||
$this->sourceStorage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, drupal_installation_attempted());
|
||||
}
|
||||
if ($this->sourceStorage->getCollectionName() != $collection) {
|
||||
$this->sourceStorage = $this->sourceStorage->createCollection($collection);
|
||||
}
|
||||
public function getSourceStorage() {
|
||||
return $this->sourceStorage;
|
||||
}
|
||||
|
||||
|
|
@ -321,6 +356,9 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSyncing($status) {
|
||||
if (!$status) {
|
||||
$this->sourceStorage = NULL;
|
||||
}
|
||||
$this->isSyncing = $status;
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -333,24 +371,30 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Finds pre-existing configuration objects for the provided extension.
|
||||
*
|
||||
* Extensions can not be installed if configuration objects exist in the
|
||||
* active storage with the same names. This can happen in a number of ways,
|
||||
* commonly:
|
||||
* - if a user has created configuration with the same name as that provided
|
||||
* by the extension.
|
||||
* - if the extension provides default configuration that does not depend on
|
||||
* it and the extension has been uninstalled and is about to the
|
||||
* reinstalled.
|
||||
*
|
||||
* @return array
|
||||
* Array of configuration object names that already exist keyed by
|
||||
* collection.
|
||||
*/
|
||||
public function findPreExistingConfiguration($type, $name) {
|
||||
protected function findPreExistingConfiguration(StorageInterface $storage) {
|
||||
$existing_configuration = array();
|
||||
// Gather information about all the supported collections.
|
||||
$collection_info = $this->configManager->getConfigCollectionInfo();
|
||||
|
||||
// Read enabled extensions directly from configuration to avoid circular
|
||||
// dependencies on ModuleHandler and ThemeHandler.
|
||||
$extension_config = $this->configFactory->get('core.extension');
|
||||
$enabled_extensions = array_keys((array) $extension_config->get('module'));
|
||||
$enabled_extensions += array_keys((array) $extension_config->get('theme'));
|
||||
// Add the extension that will be enabled to the list of enabled extensions.
|
||||
$enabled_extensions[] = $name;
|
||||
foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
|
||||
$config_to_install = $this->listDefaultConfigToInstall($type, $name, $collection, $enabled_extensions);
|
||||
foreach ($collection_info->getCollectionNames() as $collection) {
|
||||
$config_to_create = array_keys($this->getConfigToCreate($storage, $collection));
|
||||
$active_storage = $this->getActiveStorages($collection);
|
||||
foreach ($config_to_install as $config_name) {
|
||||
foreach ($config_to_create as $config_name) {
|
||||
if ($active_storage->exists($config_name)) {
|
||||
$existing_configuration[$collection][] = $config_name;
|
||||
}
|
||||
|
|
@ -358,4 +402,225 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
}
|
||||
return $existing_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkConfigurationToInstall($type, $name) {
|
||||
if ($this->isSyncing()) {
|
||||
// Configuration is assumed to already be checked by the config importer
|
||||
// validation events.
|
||||
return;
|
||||
}
|
||||
$config_install_path = $this->getDefaultConfigDirectory($type, $name);
|
||||
if (!is_dir($config_install_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$storage = new FileStorage($config_install_path, StorageInterface::DEFAULT_COLLECTION);
|
||||
|
||||
$enabled_extensions = $this->getEnabledExtensions();
|
||||
// Add the extension that will be enabled to the list of enabled extensions.
|
||||
$enabled_extensions[] = $name;
|
||||
// Gets a profile storage to search for overrides if necessary.
|
||||
$profile_storage = $this->getProfileStorage($name);
|
||||
|
||||
// Check the dependencies of configuration provided by the module.
|
||||
$invalid_default_config = $this->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storage);
|
||||
if (!empty($invalid_default_config)) {
|
||||
throw UnmetDependenciesException::create($name, $invalid_default_config);
|
||||
}
|
||||
|
||||
// Install profiles can not have config clashes. Configuration that
|
||||
// has the same name as a module's configuration will be used instead.
|
||||
if ($name != $this->drupalGetProfile()) {
|
||||
// Throw an exception if the module being installed contains configuration
|
||||
// that already exists. Additionally, can not continue installing more
|
||||
// modules because those may depend on the current module being installed.
|
||||
$existing_configuration = $this->findPreExistingConfiguration($storage);
|
||||
if (!empty($existing_configuration)) {
|
||||
throw PreExistingConfigException::create($name, $existing_configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds default configuration with unmet dependencies.
|
||||
*
|
||||
* @param array $enabled_extensions
|
||||
* A list of all the currently enabled modules and themes.
|
||||
*
|
||||
* @return array
|
||||
* List of configuration that has unmet dependencies
|
||||
*/
|
||||
protected function findDefaultConfigWithUnmetDependencies(StorageInterface $storage, array $enabled_extensions, StorageInterface $profile_storage = NULL) {
|
||||
$config_to_create = $this->getConfigToCreate($storage, StorageInterface::DEFAULT_COLLECTION, '', $profile_storage);
|
||||
$all_config = array_merge($this->configFactory->listAll(), array_keys($config_to_create));
|
||||
return array_filter(array_keys($config_to_create), function($config_name) use ($enabled_extensions, $all_config, $config_to_create) {
|
||||
return !$this->validateDependencies($config_name, $config_to_create[$config_name], $enabled_extensions, $all_config);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an array of config data that contains dependency information.
|
||||
*
|
||||
* @param string $config_name
|
||||
* The name of the configuration object that is being validated.
|
||||
* @param array $data
|
||||
* Configuration data.
|
||||
* @param array $enabled_extensions
|
||||
* A list of all the currently enabled modules and themes.
|
||||
* @param array $all_config
|
||||
* A list of all the active configuration names.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the dependencies are met, FALSE if not.
|
||||
*/
|
||||
protected function validateDependencies($config_name, array $data, array $enabled_extensions, array $all_config) {
|
||||
// All the migrate tests will fail if we check since they install the
|
||||
// migrate_drupal module but only set up the dependencies for the single
|
||||
// migration they are testing.
|
||||
if (strpos($config_name, 'migrate.migration.') === 0) {
|
||||
return TRUE;
|
||||
}
|
||||
if (isset($data['dependencies'])) {
|
||||
$all_dependencies = $data['dependencies'];
|
||||
|
||||
// Ensure enforced dependencies are included.
|
||||
if (isset($all_dependencies['enforced'])) {
|
||||
$all_dependencies = array_merge($all_dependencies, $data['dependencies']['enforced']);
|
||||
unset($all_dependencies['enforced']);
|
||||
}
|
||||
// Ensure the configuration entity type provider is in the list of
|
||||
// dependencies.
|
||||
list($provider) = explode('.', $config_name, 2);
|
||||
if (!isset($all_dependencies['module'])) {
|
||||
$all_dependencies['module'][] = $provider;
|
||||
}
|
||||
elseif (!in_array($provider, $all_dependencies['module'])) {
|
||||
$all_dependencies['module'][] = $provider;
|
||||
}
|
||||
|
||||
foreach ($all_dependencies as $type => $dependencies) {
|
||||
$list_to_check = [];
|
||||
switch ($type) {
|
||||
case 'module':
|
||||
case 'theme':
|
||||
$list_to_check = $enabled_extensions;
|
||||
break;
|
||||
case 'config':
|
||||
$list_to_check = $all_config;
|
||||
break;
|
||||
}
|
||||
if (!empty($list_to_check)) {
|
||||
$missing = array_diff($dependencies, $list_to_check);
|
||||
if (!empty($missing)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of enabled extensions including both modules and themes.
|
||||
*
|
||||
* @return array
|
||||
* A list of enabled extensions which includes both modules and themes.
|
||||
*/
|
||||
protected function getEnabledExtensions() {
|
||||
// Read enabled extensions directly from configuration to avoid circular
|
||||
// dependencies on ModuleHandler and ThemeHandler.
|
||||
$extension_config = $this->configFactory->get('core.extension');
|
||||
$enabled_extensions = (array) $extension_config->get('module');
|
||||
$enabled_extensions += (array) $extension_config->get('theme');
|
||||
// Core can provide configuration.
|
||||
$enabled_extensions['core'] = 'core';
|
||||
return array_keys($enabled_extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the profile storage to use to check for profile overrides.
|
||||
*
|
||||
* @param string $installing_name
|
||||
* (optional) The name of the extension currently being installed.
|
||||
*
|
||||
* @return \Drupal\Core\Config\StorageInterface|null
|
||||
* A storage to access configuration from the installation profile. If a
|
||||
* Drupal installation is not in progress or we're installing the profile
|
||||
* itself, then it will return NULL as the profile storage should not be
|
||||
* used.
|
||||
*/
|
||||
protected function getProfileStorage($installing_name = '') {
|
||||
$profile = $this->drupalGetProfile();
|
||||
if ($this->drupalInstallationAttempted() && $profile != $installing_name) {
|
||||
// Profiles should not contain optional configuration so always use the
|
||||
// install directory.
|
||||
$profile_install_path = $this->getDefaultConfigDirectory('module', $profile);
|
||||
$profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
|
||||
}
|
||||
else {
|
||||
$profile_storage = NULL;
|
||||
}
|
||||
return $profile_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an extension's default configuration directory.
|
||||
*
|
||||
* @param string $type
|
||||
* Type of extension to install.
|
||||
* @param string $name
|
||||
* Name of extension to install.
|
||||
*
|
||||
* @return string
|
||||
* The extension's default configuration directory.
|
||||
*/
|
||||
protected function getDefaultConfigDirectory($type, $name) {
|
||||
return $this->drupalGetPath($type, $name) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for drupal_get_path().
|
||||
*
|
||||
* @param $type
|
||||
* The type of the item; one of 'core', 'profile', 'module', 'theme', or
|
||||
* 'theme_engine'.
|
||||
* @param $name
|
||||
* The name of the item for which the path is requested. Ignored for
|
||||
* $type 'core'.
|
||||
*
|
||||
* @return string
|
||||
* The path to the requested item or an empty string if the item is not
|
||||
* found.
|
||||
*/
|
||||
protected function drupalGetPath($type, $name) {
|
||||
return drupal_get_path($type, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the install profile from settings.
|
||||
*
|
||||
* @return string|null $profile
|
||||
* The name of the installation profile or NULL if no installation profile
|
||||
* is currently active. This is the case for example during the first steps
|
||||
* of the installer or during unit tests.
|
||||
*/
|
||||
protected function drupalGetProfile() {
|
||||
// Settings is safe to use because settings.php is written before any module
|
||||
// is installed.
|
||||
return Settings::get('install_profile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for drupal_installation_attempted().
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if a Drupal installation is currently being attempted.
|
||||
*/
|
||||
protected function drupalInstallationAttempted() {
|
||||
return drupal_installation_attempted();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,24 @@ interface ConfigInstallerInterface {
|
|||
*/
|
||||
public function installDefaultConfig($type, $name);
|
||||
|
||||
/**
|
||||
* Installs optional configuration.
|
||||
*
|
||||
* Optional configuration is only installed if:
|
||||
* - the configuration does not exist already.
|
||||
* - it's a configuration entity.
|
||||
* - its dependencies can be met.
|
||||
*
|
||||
* @param \Drupal\Core\Config\StorageInterface
|
||||
* (optional) The configuration storage to search for optional
|
||||
* configuration. If not provided, all enabled extension's optional
|
||||
* configuration directories will be searched.
|
||||
* @param string $prefix
|
||||
* (optional) If set, limits the installed configuration to only
|
||||
* configuration beginning with the provided value.
|
||||
*/
|
||||
public function installOptionalConfig(StorageInterface $storage = NULL, $prefix = '');
|
||||
|
||||
/**
|
||||
* Installs all default configuration in the specified collection.
|
||||
*
|
||||
|
|
@ -59,13 +77,6 @@ interface ConfigInstallerInterface {
|
|||
*/
|
||||
public function setSourceStorage(StorageInterface $storage);
|
||||
|
||||
/**
|
||||
* Resets the configuration storage that provides the default configuration.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function resetSourceStorage();
|
||||
|
||||
/**
|
||||
* Sets the status of the isSyncing flag.
|
||||
*
|
||||
|
|
@ -85,25 +96,16 @@ interface ConfigInstallerInterface {
|
|||
public function isSyncing();
|
||||
|
||||
/**
|
||||
* Finds pre-existing configuration objects for the provided extension.
|
||||
*
|
||||
* Extensions can not be installed if configuration objects exist in the
|
||||
* active storage with the same names. This can happen in a number of ways,
|
||||
* commonly:
|
||||
* - if a user has created configuration with the same name as that provided
|
||||
* by the extension.
|
||||
* - if the extension provides default configuration that does not depend on
|
||||
* it and the extension has been uninstalled and is about to the
|
||||
* reinstalled.
|
||||
* Checks the configuration that will be installed for an extension.
|
||||
*
|
||||
* @param string $type
|
||||
* Type of extension to install.
|
||||
* @param string $name
|
||||
* Name of extension to install.
|
||||
*
|
||||
* @return array
|
||||
* Array of configuration objects that already exist keyed by collection.
|
||||
* @throws \Drupal\Core\Config\UnmetDependenciesException
|
||||
* @throws \Drupal\Core\Config\PreExistingConfigException
|
||||
*/
|
||||
public function findPreExistingConfiguration($type, $name);
|
||||
public function checkConfigurationToInstall($type, $name);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class ExtensionInstallStorage extends InstallStorage {
|
|||
* default collection.
|
||||
* @param bool $include_profile
|
||||
* (optional) Whether to include the install profile in extensions to
|
||||
* search.
|
||||
* search and to get overrides from.
|
||||
*/
|
||||
public function __construct(StorageInterface $config_storage, $directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION, $include_profile = TRUE) {
|
||||
$this->configStorage = $config_storage;
|
||||
|
|
@ -82,27 +82,24 @@ class ExtensionInstallStorage extends InstallStorage {
|
|||
$this->folders = array();
|
||||
$this->folders += $this->getComponentNames('core', array('core'));
|
||||
|
||||
$install_profile = Settings::get('install_profile');
|
||||
$extensions = $this->configStorage->read('core.extension');
|
||||
if (!empty($extensions['module'])) {
|
||||
$modules = $extensions['module'];
|
||||
if (!$this->includeProfile) {
|
||||
if ($install_profile = Settings::get('install_profile')) {
|
||||
unset($modules[$install_profile]);
|
||||
}
|
||||
}
|
||||
// Remove the install profile as this is handled later.
|
||||
unset($modules[$install_profile]);
|
||||
$this->folders += $this->getComponentNames('module', array_keys($modules));
|
||||
}
|
||||
if (!empty($extensions['theme'])) {
|
||||
$this->folders += $this->getComponentNames('theme', array_keys($extensions['theme']));
|
||||
}
|
||||
|
||||
// The install profile can override module default configuration. We do
|
||||
// this by replacing the config file path from the module/theme with the
|
||||
// install profile version if there are any duplicates.
|
||||
$profile_folders = $this->getComponentNames('profile', array(drupal_get_profile()));
|
||||
$folders_to_replace = array_intersect_key($profile_folders, $this->folders);
|
||||
if (!empty($folders_to_replace)) {
|
||||
$this->folders = array_merge($this->folders, $folders_to_replace);
|
||||
if ($this->includeProfile) {
|
||||
// The install profile can override module default configuration. We do
|
||||
// this by replacing the config file path from the module/theme with the
|
||||
// install profile version if there are any duplicates.
|
||||
$profile_folders = $this->getComponentNames('profile', array(drupal_get_profile()));
|
||||
$this->folders = $profile_folders + $this->folders;
|
||||
}
|
||||
}
|
||||
return $this->folders;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ class InstallStorage extends FileStorage {
|
|||
*/
|
||||
const CONFIG_INSTALL_DIRECTORY = 'config/install';
|
||||
|
||||
/**
|
||||
* Extension sub-directory containing optional configuration for installation.
|
||||
*/
|
||||
const CONFIG_OPTIONAL_DIRECTORY = 'config/optional';
|
||||
|
||||
/**
|
||||
* Extension sub-directory containing configuration schema.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Config\UnmetDependenciesException.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
|
||||
/**
|
||||
* An exception thrown if configuration has unmet dependencies.
|
||||
*/
|
||||
class UnmetDependenciesException extends ConfigException {
|
||||
|
||||
/**
|
||||
* A list of configuration objects that have unmet dependencies.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $configObjects = [];
|
||||
|
||||
/**
|
||||
* The name of the extension that is being installed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
/**
|
||||
* Gets the list of configuration objects that have unmet dependencies.
|
||||
*
|
||||
* @return array
|
||||
* A list of configuration objects that have unmet dependencies.
|
||||
*/
|
||||
public function getConfigObjects() {
|
||||
return $this->configObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the extension that is being installed.
|
||||
*
|
||||
* @return string
|
||||
* The name of the extension that is being installed.
|
||||
*/
|
||||
public function getExtension() {
|
||||
return $this->extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a translated message from the exception.
|
||||
*
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTranslatedMessage(TranslationInterface $string_translation, $extension) {
|
||||
return $string_translation->formatPlural(
|
||||
count($this->getConfigObjects()),
|
||||
'Unable to install @extension, %config_names has unmet dependencies.',
|
||||
'Unable to install @extension, %config_names have unmet dependencies.',
|
||||
[
|
||||
'%config_names' => implode(', ', $this->getConfigObjects()),
|
||||
'@extension' => $extension,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an exception for an extension and a list of configuration objects.
|
||||
*
|
||||
* @param $extension
|
||||
* The name of the extension that is being installed.
|
||||
* @param array $config_objects
|
||||
* A list of configuration object names that have unmet dependencies
|
||||
*
|
||||
* @return \Drupal\Core\Config\PreExistingConfigException
|
||||
*/
|
||||
public static function create($extension, array $config_objects) {
|
||||
$message = String::format('Configuration objects (@config_names) provided by @extension have unmet dependencies',
|
||||
array(
|
||||
'@config_names' => implode(', ', $config_objects),
|
||||
'@extension' => $extension
|
||||
)
|
||||
);
|
||||
$e = new static($message);
|
||||
$e->configObjects = $config_objects;
|
||||
$e->extension = $extension;
|
||||
return $e;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -151,17 +151,9 @@ class ModuleInstaller implements ModuleInstallerInterface {
|
|||
)));
|
||||
}
|
||||
|
||||
// Install profiles can not have config clashes. Configuration that
|
||||
// has the same name as a module's configuration will be used instead.
|
||||
if ($module != drupal_get_profile()) {
|
||||
// Validate default configuration of this module. Bail if unable to
|
||||
// install. Should not continue installing more modules because those
|
||||
// may depend on this one.
|
||||
$existing_configuration = $config_installer->findPreExistingConfiguration('module', $module);
|
||||
if (!empty($existing_configuration)) {
|
||||
throw PreExistingConfigException::create($module, $existing_configuration);
|
||||
}
|
||||
}
|
||||
// Check the validity of the default configuration. This will throw
|
||||
// exceptions if the configuration is not valid.
|
||||
$config_installer->checkConfigurationToInstall('module', $module);
|
||||
|
||||
$extension_config
|
||||
->set("module.$module", 0)
|
||||
|
|
@ -249,12 +241,6 @@ class ModuleInstaller implements ModuleInstallerInterface {
|
|||
->setSyncing(TRUE)
|
||||
->setSourceStorage($source_storage);
|
||||
}
|
||||
else {
|
||||
// If we're not in a config synchronization reset the source storage
|
||||
// so that the extension install storage will pick up the new
|
||||
// configuration.
|
||||
$config_installer->resetSourceStorage();
|
||||
}
|
||||
\Drupal::service('config.installer')->installDefaultConfig('module', $module);
|
||||
|
||||
// If the module has no current updates, but has some that were
|
||||
|
|
|
|||
|
|
@ -258,10 +258,7 @@ class ThemeHandler implements ThemeHandlerInterface {
|
|||
|
||||
// Validate default configuration of the theme. If there is existing
|
||||
// configuration then stop installing.
|
||||
$existing_configuration = $this->configInstaller->findPreExistingConfiguration('theme', $key);
|
||||
if (!empty($existing_configuration)) {
|
||||
throw PreExistingConfigException::create($key, $existing_configuration);
|
||||
}
|
||||
$this->configInstaller->checkConfigurationToInstall('theme', $key);
|
||||
|
||||
// The value is not used; the weight is ignored for themes currently.
|
||||
$extension_config
|
||||
|
|
@ -288,16 +285,6 @@ class ThemeHandler implements ThemeHandlerInterface {
|
|||
// Only install default configuration if this theme has not been installed
|
||||
// already.
|
||||
if (!isset($installed_themes[$key])) {
|
||||
// The default config installation storage only knows about the
|
||||
// currently installed list of themes, so it has to be reset in order to
|
||||
// pick up the default config of the newly installed theme. However, do
|
||||
// not reset the source storage when synchronizing configuration, since
|
||||
// that would needlessly trigger a reload of the whole configuration to
|
||||
// be imported.
|
||||
if (!$this->configInstaller->isSyncing()) {
|
||||
$this->configInstaller->resetSourceStorage();
|
||||
}
|
||||
|
||||
// Install default configuration of the theme.
|
||||
$this->configInstaller->installDefaultConfig('theme', $key);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ class BlockStorageUnitTest extends KernelTestBase {
|
|||
* Tests the installation of default blocks.
|
||||
*/
|
||||
public function testDefaultBlocks() {
|
||||
\Drupal::service('theme_handler')->install(['classy']);
|
||||
$entities = $this->controller->loadMultiple();
|
||||
$this->assertTrue(empty($entities), 'There are no blocks initially.');
|
||||
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@ description: 'Provides test blocks.'
|
|||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- block
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class BlockContentPageViewTest extends BlockContentTestBase {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_content', 'block_content_test');
|
||||
public static $modules = array('block_content_test');
|
||||
|
||||
/**
|
||||
* Checks block edit and fallback functionality.
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@ description: "Support module for custom block related testing."
|
|||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- block_content
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ dependencies:
|
|||
module:
|
||||
- block_content
|
||||
theme:
|
||||
- stark
|
||||
- classy
|
||||
id: foobargorilla
|
||||
theme: classy
|
||||
region: content
|
||||
|
|
|
|||
|
|
@ -5,4 +5,8 @@ cache: true
|
|||
targetEntityType: node
|
||||
dependencies:
|
||||
module:
|
||||
- book
|
||||
- node
|
||||
enforced:
|
||||
module:
|
||||
- book
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
|
|||
|
||||
// Install tables and config needed to render comments.
|
||||
$this->installSchema('comment', array('comment_entity_statistics'));
|
||||
$this->installConfig(array('system', 'filter'));
|
||||
$this->installConfig(array('system', 'filter', 'comment'));
|
||||
|
||||
// Comment rendering generates links, so build the router.
|
||||
$this->installSchema('system', array('router'));
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class CommentFieldAccessTest extends EntityUnitTestBase {
|
|||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('user'));
|
||||
$this->installConfig(array('user', 'comment'));
|
||||
$this->installSchema('comment', array('comment_entity_statistics'));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,16 @@ class ConfigEntityListTest extends WebTestBase {
|
|||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Delete the override config_test entity since it is not required by this
|
||||
// test.
|
||||
\Drupal::entityManager()->getStorage('config_test')->load('override')->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity list builder methods.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class ConfigImportRecreateTest extends KernelTestBase {
|
|||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field'));
|
||||
$this->installConfig(array('field', 'node'));
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
|
|
@ -94,8 +94,8 @@ class ConfigImportRecreateTest extends KernelTestBase {
|
|||
// will be recreated.
|
||||
$creates = $this->configImporter->getUnprocessedConfiguration('create');
|
||||
$deletes = $this->configImporter->getUnprocessedConfiguration('delete');
|
||||
$this->assertEqual(4, count($creates), 'There are 4 configuration items to create.');
|
||||
$this->assertEqual(4, count($deletes), 'There are 4 configuration items to delete.');
|
||||
$this->assertEqual(5, count($creates), 'There are 5 configuration items to create.');
|
||||
$this->assertEqual(5, count($deletes), 'There are 5 configuration items to delete.');
|
||||
$this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
|
||||
$this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.');
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class ConfigImporterTest extends KernelTestBase {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'system', 'config_import_test', 'config_test_language');
|
||||
public static $modules = array('config_test', 'system', 'config_import_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
|
@ -541,24 +541,4 @@ class ConfigImporterTest extends KernelTestBase {
|
|||
$this->assertTrue($this->container->get('config.storage')->exists($config_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests imported configuration entities with and without language information.
|
||||
*/
|
||||
function testLanguage() {
|
||||
// Test imported configuration with implicit language code.
|
||||
$data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english');
|
||||
$this->assertTrue(!isset($data['langcode']));
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.english')->get('langcode'),
|
||||
'en'
|
||||
);
|
||||
|
||||
// Test imported configuration with explicit language code.
|
||||
$data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french');
|
||||
$this->assertEqual($data['langcode'], 'fr');
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.french')->get('langcode'),
|
||||
'fr'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigInstallProfileOverrideTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\system\Entity\Action;
|
||||
use Drupal\tour\Entity\Tour;
|
||||
|
||||
/**
|
||||
* Tests installation and removal of configuration objects in install, disable
|
||||
* and uninstall functionality.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigInstallProfileOverrideTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing_config_overrides';
|
||||
|
||||
|
||||
/**
|
||||
* Tests install profile config changes.
|
||||
*/
|
||||
function testInstallProfileConfigOverwrite() {
|
||||
$config_name = 'system.cron';
|
||||
// The expected configuration from the system module.
|
||||
$expected_original_data = array(
|
||||
'threshold' => array(
|
||||
'autorun' => 0,
|
||||
'requirements_warning' => 172800,
|
||||
'requirements_error' => 1209600,
|
||||
),
|
||||
);
|
||||
// The expected active configuration altered by the install profile.
|
||||
$expected_profile_data = array(
|
||||
'threshold' => array(
|
||||
'autorun' => 0,
|
||||
'requirements_warning' => 259200,
|
||||
'requirements_error' => 1209600,
|
||||
),
|
||||
);
|
||||
|
||||
// Verify that the original data matches. We have to read the module config
|
||||
// file directly, because the install profile default system.cron.yml
|
||||
// configuration file was used to create the active configuration.
|
||||
$config_dir = drupal_get_path('module', 'system') . '/'. InstallStorage::CONFIG_INSTALL_DIRECTORY;
|
||||
$this->assertTrue(is_dir($config_dir));
|
||||
$source_storage = new FileStorage($config_dir);
|
||||
$data = $source_storage->read($config_name);
|
||||
$this->assertIdentical($data, $expected_original_data);
|
||||
|
||||
// Verify that active configuration matches the expected data, which was
|
||||
// created from the testing install profile's system.cron.yml file.
|
||||
$config = $this->config($config_name);
|
||||
$this->assertIdentical($config->get(), $expected_profile_data);
|
||||
|
||||
// Ensure that the configuration entity has the expected dependencies and
|
||||
// overrides.
|
||||
$action = Action::load('user_block_user_action');
|
||||
$this->assertEqual($action->label(), 'Overridden block the selected user(s)');
|
||||
$action = Action::load('user_cancel_user_action');
|
||||
$this->assertEqual($action->label(), 'Cancel the selected user account(s)', 'Default configuration that is not overridden is not affected.');
|
||||
|
||||
// Ensure that optional configuration can be overridden.
|
||||
$tour = Tour::load('language');
|
||||
$this->assertEqual(count($tour->getTips()), 1, 'Optional configuration can be overridden. The language tour only has one tip');
|
||||
$tour = Tour::load('language-add');
|
||||
$this->assertEqual(count($tour->getTips()), 3, 'Optional configuration that is not overridden is not affected.');
|
||||
|
||||
// Ensure that optional configuration from a profile is created if
|
||||
// dependencies are met.
|
||||
$this->assertEqual(Tour::load('testing_config_overrides')->label(), 'Config override test');
|
||||
|
||||
// Ensure that optional configuration from a profile is not created if
|
||||
// dependencies are not met. Cannot use the entity system since the entity
|
||||
// type does not exist.
|
||||
$optional_dir = drupal_get_path('module', 'testing_config_overrides') . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
|
||||
$optional_storage = new FileStorage($optional_dir);
|
||||
foreach (['config_test.dynamic.dotted.default', 'config_test.dynamic.override','config_test.dynamic.override_unmet'] as $id) {
|
||||
$this->assertTrue(\Drupal::config($id)->isNew(), "The config_test entity $id contained in the profile's optional directory does not exist.");
|
||||
// Make that we don't get false positives from the assertion above.
|
||||
$this->assertTrue($optional_storage->exists($id), "The config_test entity $id does exist in the profile's optional directory.");
|
||||
}
|
||||
|
||||
// Install the config_test module and ensure that the override from the
|
||||
// install profile is not used. Optional configuration can not override
|
||||
// configuration in a modules config/install directory.
|
||||
$this->container->get('module_installer')->install(['config_test']);
|
||||
$this->rebuildContainer();
|
||||
$config_test_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$this->assertEqual($config_test_storage->load('dotted.default')->label(), 'Default', 'The config_test entity is not overridden by the profile optional configuration.');
|
||||
// Test that override of optional configuration does work.
|
||||
$this->assertEqual($config_test_storage->load('override')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.');
|
||||
// Test that override of optional configuration which introduces an unmet
|
||||
// dependency does not get created.
|
||||
$this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.');
|
||||
|
||||
$this->container->get('module_installer')->install(['dblog']);
|
||||
$this->rebuildContainer();
|
||||
// Just installing db_log does not create the optional configuration.
|
||||
$this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.');
|
||||
// Install all available optional configuration.
|
||||
$this->container->get('config.installer')->installOptionalConfig();
|
||||
$this->assertEqual($config_test_storage->load('override_unmet')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.');
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigInstallProfileUnmetDependenciesTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\simpletest\InstallerTestBase;
|
||||
|
||||
/**
|
||||
* Tests install profile config overrides can not add unmet dependencies.
|
||||
*
|
||||
* @group Config
|
||||
*/
|
||||
class ConfigInstallProfileUnmetDependenciesTest extends InstallerTestBase {
|
||||
|
||||
/**
|
||||
* The installation profile to install.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing_config_overrides';
|
||||
|
||||
/**
|
||||
* Set to TRUE if the expected exception is thrown.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $expectedException = FALSE;
|
||||
|
||||
protected function setUp() {
|
||||
// Copy the testing_config_overrides install profile so we can change the
|
||||
// configuration to include a dependency that can not be met. File API
|
||||
// functions are not available yet.
|
||||
$dest = $this->siteDirectory . '/profiles/testing_config_overrides';
|
||||
mkdir($dest, 0777, TRUE);
|
||||
$source = DRUPAL_ROOT . '/core/profiles/testing_config_overrides';
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($iterator as $item) {
|
||||
if ($item->isDir()) {
|
||||
mkdir($dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
}
|
||||
else {
|
||||
copy($item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
}
|
||||
}
|
||||
|
||||
// Add a dependency that can not be met because User is installed before
|
||||
// Action.
|
||||
$config_file = $dest . DIRECTORY_SEPARATOR . InstallStorage::CONFIG_INSTALL_DIRECTORY . DIRECTORY_SEPARATOR . 'system.action.user_block_user_action.yml';
|
||||
$action = Yaml::decode(file_get_contents($config_file));
|
||||
$action['dependencies']['module'][] = 'action';
|
||||
file_put_contents($config_file, Yaml::encode($action));
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Override the error method so we can test for the expected exception.
|
||||
*/
|
||||
protected function error($message = '', $group = 'Other', array $caller = NULL) {
|
||||
if ($group == 'User notice') {
|
||||
// Since 'User notice' is set by trigger_error() which is used for debug
|
||||
// set the message to a status of 'debug'.
|
||||
return $this->assert('debug', $message, 'Debug', $caller);
|
||||
}
|
||||
if ($group == 'Drupal\Core\Config\UnmetDependenciesException') {
|
||||
$this->expectedException = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
return $this->assert('exception', $message, $group, $caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpSite() {
|
||||
// This step is not reached due to the exception.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Confirms that the installation succeeded.
|
||||
*/
|
||||
public function testInstalled() {
|
||||
if ($this->expectedException) {
|
||||
$this->pass('Expected Drupal\Core\Config\UnmetDependenciesException exception thrown');
|
||||
}
|
||||
else {
|
||||
$this->fail('Expected Drupal\Core\Config\UnmetDependenciesException exception thrown');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ namespace Drupal\config\Tests;
|
|||
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
|
|
@ -19,11 +20,17 @@ use Drupal\simpletest\KernelTestBase;
|
|||
*/
|
||||
class ConfigInstallTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['router']);
|
||||
|
||||
// Ensure the global variable being asserted by this test does not exist;
|
||||
// a previous test executed in this request/process might have set it.
|
||||
|
|
@ -48,11 +55,7 @@ class ConfigInstallTest extends KernelTestBase {
|
|||
$this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install does not exist.');
|
||||
|
||||
// Install the test module.
|
||||
$this->enableModules(array('config_test', 'config_schema_test'));
|
||||
$this->installConfig(array('config_test', 'config_schema_test'));
|
||||
|
||||
// After module installation the new schema should exist.
|
||||
$this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.');
|
||||
$this->installModules(array('config_test'));
|
||||
|
||||
// Verify that default module config exists.
|
||||
\Drupal::configFactory()->reset($default_config);
|
||||
|
|
@ -71,6 +74,13 @@ class ConfigInstallTest extends KernelTestBase {
|
|||
$this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
// Install the schema test module.
|
||||
$this->enableModules(array('config_schema_test'));
|
||||
$this->installConfig(array('config_schema_test'));
|
||||
|
||||
// After module installation the new schema should exist.
|
||||
$this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.');
|
||||
|
||||
// Ensure that data type casting is applied during config installation.
|
||||
$config = $this->config('config_schema_test.schema_in_install');
|
||||
$this->assertIdentical($config->get('integer'), 1);
|
||||
|
|
@ -170,8 +180,7 @@ class ConfigInstallTest extends KernelTestBase {
|
|||
);
|
||||
\Drupal::state()->set('config_collection_install_test.collection_names', $collections);
|
||||
// Install the test module.
|
||||
$this->enableModules(array('config_test', 'config_collection_install_test'));
|
||||
$this->installConfig(array('config_test'));
|
||||
$this->installModules(array('config_test', 'config_collection_install_test'));
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
|
|
@ -189,4 +198,56 @@ class ConfigInstallTest extends KernelTestBase {
|
|||
$this->assertIdentical(array('label' => 'entity'), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the configuration with unmet dependencies is not installed.
|
||||
*/
|
||||
public function testDependencyChecking() {
|
||||
$this->installModules(['config_test']);
|
||||
try {
|
||||
$this->installModules(['config_install_dependency_test']);
|
||||
$this->fail('Expected UnmetDependenciesException not thrown.');
|
||||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_install_dependency_test');
|
||||
$this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies');
|
||||
}
|
||||
$this->installModules(['config_other_module_config_test']);
|
||||
$this->installModules(['config_install_dependency_test']);
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests imported configuration entities with and without language information.
|
||||
*/
|
||||
function testLanguage() {
|
||||
$this->installModules(['config_test_language']);
|
||||
// Test imported configuration with implicit language code.
|
||||
$data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english');
|
||||
$this->assertTrue(!isset($data['langcode']));
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.english')->get('langcode'),
|
||||
'en'
|
||||
);
|
||||
|
||||
// Test imported configuration with explicit language code.
|
||||
$data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french');
|
||||
$this->assertEqual($data['langcode'], 'fr');
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.french')->get('langcode'),
|
||||
'fr'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a module.
|
||||
*
|
||||
* @param array $modules
|
||||
* The module names.
|
||||
*/
|
||||
protected function installModules(array $modules) {
|
||||
$this->container->get('module_installer')->install($modules);
|
||||
$this->container = \Drupal::getContainer();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,10 @@
|
|||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
|
||||
/**
|
||||
* Tests installation and removal of configuration objects in install, disable
|
||||
|
|
@ -119,43 +117,6 @@ class ConfigInstallWebTest extends WebTestBase {
|
|||
$this->assertIdentical($config_entity->get('label'), 'Default integration config label');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests install profile config changes.
|
||||
*/
|
||||
function testInstallProfileConfigOverwrite() {
|
||||
$config_name = 'system.cron';
|
||||
// The expected configuration from the system module.
|
||||
$expected_original_data = array(
|
||||
'threshold' => array(
|
||||
'autorun' => 0,
|
||||
'requirements_warning' => 172800,
|
||||
'requirements_error' => 1209600,
|
||||
),
|
||||
);
|
||||
// The expected active configuration altered by the install profile.
|
||||
$expected_profile_data = array(
|
||||
'threshold' => array(
|
||||
'autorun' => 0,
|
||||
'requirements_warning' => 259200,
|
||||
'requirements_error' => 1209600,
|
||||
),
|
||||
);
|
||||
|
||||
// Verify that the original data matches. We have to read the module config
|
||||
// file directly, because the install profile default system.cron.yml
|
||||
// configuration file was used to create the active configuration.
|
||||
$config_dir = drupal_get_path('module', 'system') . '/'. InstallStorage::CONFIG_INSTALL_DIRECTORY;
|
||||
$this->assertTrue(is_dir($config_dir));
|
||||
$source_storage = new FileStorage($config_dir);
|
||||
$data = $source_storage->read($config_name);
|
||||
$this->assertIdentical($data, $expected_original_data);
|
||||
|
||||
// Verify that active configuration matches the expected data, which was
|
||||
// created from the testing install profile's system.cron.yml file.
|
||||
$config = $this->config($config_name);
|
||||
$this->assertIdentical($config->get(), $expected_profile_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests pre-existing configuration detection.
|
||||
*/
|
||||
|
|
@ -214,4 +175,21 @@ class ConfigInstallWebTest extends WebTestBase {
|
|||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default) provided by config_clash_test_theme already exist in active configuration');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests unmet dependencies detection.
|
||||
*/
|
||||
public function testUnmetDependenciesInstall() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
// We need to install separately since config_install_dependency_test does
|
||||
// not depend on config_test and order is important.
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->assertRaw('Unable to install Config install dependency test, <em class="placeholder">config_test.dynamic.other_module_test_with_dependency</em> has unmet dependencies.');
|
||||
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_other_module_config_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->rebuildContainer();
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
|
|
@ -59,18 +57,10 @@ class ConfigOtherModuleTest extends WebTestBase {
|
|||
// 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 pre-existing default configuration throws
|
||||
// an error.
|
||||
$msg = "The expected PreExistingConfigException is thrown by reinstalling config_other_module_config_test.";
|
||||
try {
|
||||
$this->installModule('config_other_module_config_test');
|
||||
$this->fail($msg);
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$this->pass($msg);
|
||||
$this->assertEqual($e->getExtension(), 'config_other_module_config_test');
|
||||
$this->assertEqual($e->getConfigObjects(), [StorageInterface::DEFAULT_COLLECTION => ['config_test.dynamic.other_module_test']]);
|
||||
}
|
||||
// Re-enable module to test that pre-existing optional configuration does
|
||||
// not throw an error.
|
||||
$this->installModule('config_other_module_config_test');
|
||||
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('config_other_module_config_test'), 'The config_other_module_config_test module is installed.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
id: other_module_test_with_dependency
|
||||
label: 'Other module test with dependency'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: true
|
||||
langcode: en
|
||||
protected_property: Default
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- config_other_module_config_test
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Config install dependency test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# See \Drupal\config\Tests\ConfigInstallProfileOverrideTest
|
||||
id: override
|
||||
label: Default
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
status: 1
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# See \Drupal\config\Tests\ConfigInstallProfileOverrideTest
|
||||
# This configuration entity has dependencies which would be met.
|
||||
id: override_unmet
|
||||
label: Default
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
status: 1
|
||||
dependencies:
|
||||
module:
|
||||
- tour
|
||||
|
|
@ -3,3 +3,5 @@ type: module
|
|||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_test
|
||||
|
|
@ -32,7 +32,7 @@ class MessageEntityTest extends EntityUnitTestBase {
|
|||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('contact'));
|
||||
$this->installConfig(array('contact', 'contact_test'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class EditorFileUsageTest extends EntityUnitTestBase {
|
|||
$this->installEntitySchema('file');
|
||||
$this->installSchema('node', array('node_access'));
|
||||
$this->installSchema('file', array('file_usage'));
|
||||
$this->installConfig(['node']);
|
||||
|
||||
// Add text formats.
|
||||
$filtered_html_format = entity_create('filter_format', array(
|
||||
|
|
|
|||
|
|
@ -6,3 +6,6 @@ package: Testing
|
|||
version: VERSION
|
||||
dependencies:
|
||||
- entity_reference
|
||||
- node
|
||||
- user
|
||||
- views
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class FieldImportChangeTest extends FieldUnitTestBase {
|
|||
* Tests importing an updated field.
|
||||
*/
|
||||
function testImportChange() {
|
||||
$this->installConfig(['field_test_config']);
|
||||
$field_storage_id = 'field_test_import';
|
||||
$field_id = "entity_test.entity_test.$field_storage_id";
|
||||
$field_config_name = "field.field.$field_id";
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ class FieldImportDeleteTest extends FieldUnitTestBase {
|
|||
* Tests deleting field storages and fields as part of config import.
|
||||
*/
|
||||
public function testImportDelete() {
|
||||
$this->installConfig(['field_test_config']);
|
||||
// At this point there are 5 field configuration objects in the active
|
||||
// storage.
|
||||
// - field.storage.entity_test.field_test_import
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class EntityDisplayTest extends KernelTestBase {
|
|||
parent::setUp();
|
||||
$this->installSchema('system', array('router'));
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field'));
|
||||
$this->installConfig(array('field', 'node'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class FilterAPITest extends EntityUnitTestBase {
|
|||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', array('router'));
|
||||
$this->installConfig(array('system', 'filter'));
|
||||
$this->installConfig(array('system', 'filter', 'filter_test'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@ description: 'Tests filter hooks and functions.'
|
|||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- filter
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class EntityTest extends NormalizerTestBase {
|
|||
$this->installSchema('system', array('sequences'));
|
||||
$this->installSchema('comment', array('comment_entity_statistics'));
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['node', 'comment']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -427,3 +427,5 @@ display:
|
|||
id: block_1
|
||||
display_title: Block
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
|
|
@ -37,7 +37,7 @@ class NodeBodyFieldStorageTest extends KernelTestBase {
|
|||
$this->installSchema('user', 'users_data');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field'));
|
||||
$this->installConfig(array('field', 'node'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class NodeTokenReplaceTest extends TokenReplaceUnitTestBase {
|
|||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('filter'));
|
||||
$this->installConfig(array('filter', 'node'));
|
||||
|
||||
$node_type = entity_create('node_type', array('type' => 'article', 'name' => 'Article'));
|
||||
$node_type->save();
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ EOS;
|
|||
* Tests expected behavior of installConfig().
|
||||
*/
|
||||
function testInstallConfig() {
|
||||
// The user module has configuration that depends on system.
|
||||
$this->enableModules(array('system'));
|
||||
$module = 'user';
|
||||
|
||||
// Verify that default config can only be installed for enabled modules.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ namespace Drupal\system\Controller;
|
|||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Routing\RouteBuilderInterface;
|
||||
|
|
@ -144,6 +145,9 @@ class ThemeController extends ControllerBase {
|
|||
'error'
|
||||
);
|
||||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
drupal_set_message($e->getTranslatedMessage($this->getStringTranslation(), $theme), 'error');
|
||||
}
|
||||
|
||||
return $this->redirect('system.themes_page');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ModuleInstallerInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
|
|
@ -176,6 +177,13 @@ class ModulesListConfirmForm extends ConfirmFormBase {
|
|||
);
|
||||
return;
|
||||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
drupal_set_message(
|
||||
$e->getTranslatedMessage($this->getStringTranslation(), $this->modules['install'][$e->getExtension()]),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets module list after install process, flushes caches and displays a
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace Drupal\system\Form;
|
|||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\Core\Controller\TitleResolverInterface;
|
||||
use Drupal\Core\Access\AccessManagerInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
|
|
@ -536,6 +537,13 @@ class ModulesListForm extends FormBase {
|
|||
);
|
||||
return;
|
||||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
drupal_set_message(
|
||||
$e->getTranslatedMessage($this->getStringTranslation(), $modules['install'][$e->getExtension()]),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets module list after install process, flushes caches and displays a
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ class EntityCrudHookTest extends EntityUnitTestBase {
|
|||
$this->installSchema('file', array('file_usage'));
|
||||
$this->installSchema('node', array('node_access'));
|
||||
$this->installSchema('comment', array('comment_entity_statistics'));
|
||||
$this->installConfig(['node', 'comment']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ class InstallerExistingSettingsTest extends InstallerTestBase {
|
|||
'required' => TRUE,
|
||||
);
|
||||
|
||||
// Actually the install profile should be skipped to because it is written
|
||||
// to settings.php.
|
||||
// @todo https://www.drupal.org/node/2451369 Fix install_profile so that it
|
||||
// is written to an existing settings.php if possible or if set used.
|
||||
$this->settings['settings']['install_profile'] = (object) array(
|
||||
'value' => 'testing',
|
||||
'required' => TRUE,
|
||||
);
|
||||
|
||||
// Pre-configure database credentials.
|
||||
$connection_info = Database::getConnectionInfo();
|
||||
unset($connection_info['default']['pdo']);
|
||||
|
|
|
|||
|
|
@ -7,3 +7,7 @@ status: true
|
|||
langcode: en
|
||||
locked: false
|
||||
pattern: 'U'
|
||||
dependencies:
|
||||
enforced:
|
||||
theme:
|
||||
- test_basetheme
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@ version: VERSION
|
|||
core: 8.x
|
||||
required: true
|
||||
configure: user.admin_index
|
||||
dependencies:
|
||||
- system
|
||||
|
|
|
|||
|
|
@ -142,8 +142,8 @@ class ModuleTest extends ViewUnitTestBase {
|
|||
* Tests the load wrapper/helper functions.
|
||||
*/
|
||||
public function testLoadFunctions() {
|
||||
$this->enableModules(['text', 'node']);
|
||||
$this->installConfig(['node']);
|
||||
$this->enableModules(array('field', 'text', 'node'));
|
||||
$this->installConfig(array('node'));
|
||||
$storage = $this->container->get('entity.manager')->getStorage('view');
|
||||
|
||||
// Test views_view_is_enabled/disabled.
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class ViewExecutableTest extends ViewUnitTestBase {
|
|||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('comment');
|
||||
$this->installSchema('comment', array('comment_entity_statistics'));
|
||||
$this->installConfig(array('field'));
|
||||
$this->installConfig(array('system', 'field', 'node', 'comment'));
|
||||
|
||||
entity_create('node_type', array(
|
||||
'type' => 'page',
|
||||
|
|
|
|||
|
|
@ -7,3 +7,5 @@ dependencies:
|
|||
- node
|
||||
- block
|
||||
- dblog
|
||||
themes:
|
||||
- stark
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
id: user_block_user_action
|
||||
label: 'Overridden block the selected user(s)'
|
||||
status: true
|
||||
langcode: en
|
||||
type: user
|
||||
plugin: user_block_user_action
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
id: language
|
||||
module: language
|
||||
label: Language
|
||||
langcode: en
|
||||
routes:
|
||||
- route_name: entity.configurable_language.collection
|
||||
tips:
|
||||
language-overview:
|
||||
id: language-overview
|
||||
plugin: text
|
||||
label: Languages
|
||||
body: '<p>The "Languages" page allows you to add, edit, delete, and reorder languages for the site.</p>'
|
||||
weight: 1
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
id: dotted.default
|
||||
label: 'Default install profile override'
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
# Intentionally commented out to verify default status behavior.
|
||||
# status: 1
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
id: override
|
||||
label: Override
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
status: 1
|
||||
dependencies:
|
||||
module:
|
||||
- tour
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
id: override_unmet
|
||||
label: Override
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
status: 1
|
||||
dependencies:
|
||||
module:
|
||||
- dblog
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
id: testing_config_overrides
|
||||
module: testing_config_overrides
|
||||
label: Config override test
|
||||
langcode: en
|
||||
routes:
|
||||
- route_name: entity.configurable_language.collection
|
||||
tips:
|
||||
language-overview:
|
||||
id: language-overview
|
||||
plugin: text
|
||||
label: Languages
|
||||
body: '<p>The "Languages" page allows you to add, edit, delete, and reorder languages for the site.</p>'
|
||||
weight: 1
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
name: Testing config overrides
|
||||
type: profile
|
||||
description: 'Minimal profile for running tests with config overrides in a profile.'
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
hidden: true
|
||||
dependencies:
|
||||
- action
|
||||
- language
|
||||
- tour
|
||||
Loading…
Reference in New Issue