Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 184 |
| ConfigImportSubscriber | |
0.00% |
0 / 1 |
|
11.11% |
1 / 9 |
2970 | |
0.00% |
0 / 184 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| onConfigImporterValidate | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 21 |
|||
| validateModules | |
0.00% |
0 / 1 |
156 | |
0.00% |
0 / 40 |
|||
| validateThemes | |
0.00% |
0 / 1 |
110 | |
0.00% |
0 / 29 |
|||
| validateDependencies | |
0.00% |
0 / 1 |
342 | |
0.00% |
0 / 76 |
|||
| getThemeData | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
| getModuleData | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
| getNames | |
100.00% |
1 / 1 |
2 | |
100.00% |
0 / 0 |
|||
| anonymous function | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\EventSubscriber\ConfigImportSubscriber. | |
| */ | |
| namespace Drupal\Core\EventSubscriber; | |
| use Drupal\Core\Config\Config; | |
| use Drupal\Core\Config\ConfigImporter; | |
| use Drupal\Core\Config\ConfigImporterEvent; | |
| use Drupal\Core\Config\ConfigImportValidateEventSubscriberBase; | |
| use Drupal\Core\Config\ConfigNameException; | |
| use Drupal\Core\Extension\ThemeHandlerInterface; | |
| use Drupal\Core\Site\Settings; | |
| /** | |
| * Config import subscriber for config import events. | |
| */ | |
| class ConfigImportSubscriber extends ConfigImportValidateEventSubscriberBase { | |
| /** | |
| * Theme data. | |
| * | |
| * @var \Drupal\Core\Extension\Extension[] | |
| */ | |
| protected $themeData; | |
| /** | |
| * Module data. | |
| * | |
| * @var \Drupal\Core\Extension\Extension[] | |
| */ | |
| protected $moduleData; | |
| /** | |
| * The theme handler. | |
| * | |
| * @var \Drupal\Core\Extension\ThemeHandlerInterface | |
| */ | |
| protected $themeHandler; | |
| /** | |
| * Constructs the ConfigImportSubscriber. | |
| * | |
| * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler | |
| * The theme handler. | |
| */ | |
| public function __construct(ThemeHandlerInterface $theme_handler) { | |
| $this->themeHandler = $theme_handler; | |
| } | |
| /** | |
| * Validates the configuration to be imported. | |
| * | |
| * @param \Drupal\Core\Config\ConfigImporterEvent $event | |
| * The Event to process. | |
| * | |
| * @throws \Drupal\Core\Config\ConfigNameException | |
| */ | |
| public function onConfigImporterValidate(ConfigImporterEvent $event) { | |
| foreach (array('delete', 'create', 'update') as $op) { | |
| foreach ($event->getConfigImporter()->getUnprocessedConfiguration($op) as $name) { | |
| try { | |
| Config::validateName($name); | |
| } | |
| catch (ConfigNameException $e) { | |
| $message = $this->t('The config name @config_name is invalid.', array('@config_name' => $name)); | |
| $event->getConfigImporter()->logError($message); | |
| } | |
| } | |
| } | |
| $config_importer = $event->getConfigImporter(); | |
| if ($config_importer->getStorageComparer()->getSourceStorage()->exists('core.extension')) { | |
| $this->validateModules($config_importer); | |
| $this->validateThemes($config_importer); | |
| $this->validateDependencies($config_importer); | |
| } | |
| else { | |
| $config_importer->logError($this->t('The core.extension configuration does not exist.')); | |
| } | |
| } | |
| /** | |
| * Validates module installations and uninstallations. | |
| * | |
| * @param \Drupal\Core\Config\ConfigImporter $config_importer | |
| * The configuration importer. | |
| */ | |
| protected function validateModules(ConfigImporter $config_importer) { | |
| $core_extension = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension'); | |
| // Get a list of modules with dependency weights as values. | |
| $module_data = $this->getModuleData(); | |
| $nonexistent_modules = array_keys(array_diff_key($core_extension['module'], $module_data)); | |
| foreach ($nonexistent_modules as $module) { | |
| $config_importer->logError($this->t('Unable to install the %module module since it does not exist.', array('%module' => $module))); | |
| } | |
| // Ensure that all modules being installed have their dependencies met. | |
| $installs = $config_importer->getExtensionChangelist('module', 'install'); | |
| foreach ($installs as $module) { | |
| $missing_dependencies = []; | |
| foreach (array_keys($module_data[$module]->requires) as $required_module) { | |
| if (!isset($core_extension['module'][$required_module])) { | |
| $missing_dependencies[] = $module_data[$required_module]->info['name']; | |
| } | |
| } | |
| if (!empty($missing_dependencies)) { | |
| $module_name = $module_data[$module]->info['name']; | |
| $message = $this->formatPlural(count($missing_dependencies), | |
| 'Unable to install the %module module since it requires the %required_module module.', | |
| 'Unable to install the %module module since it requires the %required_module modules.', | |
| array('%module' => $module_name, '%required_module' => implode(', ', $missing_dependencies)) | |
| ); | |
| $config_importer->logError($message); | |
| } | |
| } | |
| // Settings is safe to use because settings.php is written before any module | |
| // is installed. | |
| $install_profile = Settings::get('install_profile'); | |
| // Ensure that all modules being uninstalled are not required by modules | |
| // that will be installed after the import. | |
| $uninstalls = $config_importer->getExtensionChangelist('module', 'uninstall'); | |
| foreach ($uninstalls as $module) { | |
| foreach (array_keys($module_data[$module]->required_by) as $dependent_module) { | |
| if ($module_data[$dependent_module]->status && !in_array($dependent_module, $uninstalls, TRUE) && $dependent_module !== $install_profile) { | |
| $module_name = $module_data[$module]->info['name']; | |
| $dependent_module_name = $module_data[$dependent_module]->info['name']; | |
| $config_importer->logError($this->t('Unable to uninstall the %module module since the %dependent_module module is installed.', array('%module' => $module_name, '%dependent_module' => $dependent_module_name))); | |
| } | |
| } | |
| } | |
| // Ensure that the install profile is not being uninstalled. | |
| if (in_array($install_profile, $uninstalls)) { | |
| $profile_name = $module_data[$install_profile]->info['name']; | |
| $config_importer->logError($this->t('Unable to uninstall the %profile profile since it is the install profile.', array('%profile' => $profile_name))); | |
| } | |
| } | |
| /** | |
| * Validates theme installations and uninstallations. | |
| * | |
| * @param \Drupal\Core\Config\ConfigImporter $config_importer | |
| * The configuration importer. | |
| */ | |
| protected function validateThemes(ConfigImporter $config_importer) { | |
| $core_extension = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension'); | |
| // Get all themes including those that are not installed. | |
| $theme_data = $this->getThemeData(); | |
| $installs = $config_importer->getExtensionChangelist('theme', 'install'); | |
| foreach ($installs as $key => $theme) { | |
| if (!isset($theme_data[$theme])) { | |
| $config_importer->logError($this->t('Unable to install the %theme theme since it does not exist.', array('%theme' => $theme))); | |
| // Remove non-existing installs from the list so we can validate theme | |
| // dependencies later. | |
| unset($installs[$key]); | |
| } | |
| } | |
| // Ensure that all themes being installed have their dependencies met. | |
| foreach ($installs as $theme) { | |
| foreach (array_keys($theme_data[$theme]->requires) as $required_theme) { | |
| if (!isset($core_extension['theme'][$required_theme])) { | |
| $theme_name = $theme_data[$theme]->info['name']; | |
| $required_theme_name = $theme_data[$required_theme]->info['name']; | |
| $config_importer->logError($this->t('Unable to install the %theme theme since it requires the %required_theme theme.', array('%theme' => $theme_name, '%required_theme' => $required_theme_name))); | |
| } | |
| } | |
| } | |
| // Ensure that all themes being uninstalled are not required by themes that | |
| // will be installed after the import. | |
| $uninstalls = $config_importer->getExtensionChangelist('theme', 'uninstall'); | |
| foreach ($uninstalls as $theme) { | |
| foreach (array_keys($theme_data[$theme]->required_by) as $dependent_theme) { | |
| if ($theme_data[$dependent_theme]->status && !in_array($dependent_theme, $uninstalls, TRUE)) { | |
| $theme_name = $theme_data[$theme]->info['name']; | |
| $dependent_theme_name = $theme_data[$dependent_theme]->info['name']; | |
| $config_importer->logError($this->t('Unable to uninstall the %theme theme since the %dependent_theme theme is installed.', array('%theme' => $theme_name, '%dependent_theme' => $dependent_theme_name))); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Validates configuration being imported does not have unmet dependencies. | |
| * | |
| * @param \Drupal\Core\Config\ConfigImporter $config_importer | |
| * The configuration importer. | |
| */ | |
| protected function validateDependencies(ConfigImporter $config_importer) { | |
| $core_extension = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension'); | |
| $existing_dependencies = [ | |
| 'config' => $config_importer->getStorageComparer()->getSourceStorage()->listAll(), | |
| 'module' => array_keys($core_extension['module']), | |
| 'theme' => array_keys($core_extension['theme']), | |
| ]; | |
| $theme_data = $this->getThemeData(); | |
| $module_data = $this->getModuleData(); | |
| // Validate the dependencies of all the configuration. We have to validate | |
| // the entire tree because existing configuration might depend on | |
| // configuration that is being deleted. | |
| foreach ($config_importer->getStorageComparer()->getSourceStorage()->listAll() as $name) { | |
| // Ensure that the config owner is installed. This checks all | |
| // configuration including configuration entities. | |
| list($owner,) = explode('.', $name, 2); | |
| if ($owner !== 'core') { | |
| $message = FALSE; | |
| if (!isset($core_extension['module'][$owner]) && isset($module_data[$owner])) { | |
| $message = $this->t('Configuration %name depends on the %owner module that will not be installed after import.', array( | |
| '%name' => $name, | |
| '%owner' => $module_data[$owner]->info['name'] | |
| )); | |
| } | |
| elseif (!isset($core_extension['theme'][$owner]) && isset($theme_data[$owner])) { | |
| $message = $this->t('Configuration %name depends on the %owner theme that will not be installed after import.', array( | |
| '%name' => $name, | |
| '%owner' => $theme_data[$owner]->info['name'] | |
| )); | |
| } | |
| elseif (!isset($core_extension['module'][$owner]) && !isset($core_extension['theme'][$owner])) { | |
| $message = $this->t('Configuration %name depends on the %owner extension that will not be installed after import.', array( | |
| '%name' => $name, | |
| '%owner' => $owner | |
| )); | |
| } | |
| if ($message) { | |
| $config_importer->logError($message); | |
| continue; | |
| } | |
| } | |
| $data = $config_importer->getStorageComparer()->getSourceStorage()->read($name); | |
| // Configuration entities have dependencies on modules, themes, and other | |
| // configuration entities that we can validate. Their content dependencies | |
| // are not validated since we assume that they are soft dependencies. | |
| // Configuration entities can be identified by having 'dependencies' and | |
| // 'uuid' keys. | |
| if (isset($data['dependencies']) && isset($data['uuid'])) { | |
| $dependencies_to_check = array_intersect_key($data['dependencies'], array_flip(['module', 'theme', 'config'])); | |
| foreach ($dependencies_to_check as $type => $dependencies) { | |
| $diffs = array_diff($dependencies, $existing_dependencies[$type]); | |
| if (!empty($diffs)) { | |
| $message = FALSE; | |
| switch ($type) { | |
| case 'module': | |
| $message = $this->formatPlural( | |
| count($diffs), | |
| 'Configuration %name depends on the %module module that will not be installed after import.', | |
| 'Configuration %name depends on modules (%module) that will not be installed after import.', | |
| array('%name' => $name, '%module' => implode(', ', $this->getNames($diffs, $module_data))) | |
| ); | |
| break; | |
| case 'theme': | |
| $message = $this->formatPlural( | |
| count($diffs), | |
| 'Configuration %name depends on the %theme theme that will not be installed after import.', | |
| 'Configuration %name depends on themes (%theme) that will not be installed after import.', | |
| array('%name' => $name, '%theme' => implode(', ', $this->getNames($diffs, $theme_data))) | |
| ); | |
| break; | |
| case 'config': | |
| $message = $this->formatPlural( | |
| count($diffs), | |
| 'Configuration %name depends on the %config configuration that will not exist after import.', | |
| 'Configuration %name depends on configuration (%config) that will not exist after import.', | |
| array('%name' => $name, '%config' => implode(', ', $diffs)) | |
| ); | |
| break; | |
| } | |
| if ($message) { | |
| $config_importer->logError($message); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Gets theme data. | |
| * | |
| * @return \Drupal\Core\Extension\Extension[] | |
| */ | |
| protected function getThemeData() { | |
| if (!isset($this->themeData)) { | |
| $this->themeData = $this->themeHandler->rebuildThemeData(); | |
| } | |
| return $this->themeData; | |
| } | |
| /** | |
| * Gets module data. | |
| * | |
| * @return \Drupal\Core\Extension\Extension[] | |
| */ | |
| protected function getModuleData() { | |
| if (!isset($this->moduleData)) { | |
| $this->moduleData = system_rebuild_module_data(); | |
| } | |
| return $this->moduleData; | |
| } | |
| /** | |
| * Gets human readable extension names. | |
| * | |
| * @param array $names | |
| * A list of extension machine names. | |
| * @param \Drupal\Core\Extension\Extension[] $extension_data | |
| * Extension data. | |
| * | |
| * @return array | |
| * A list of human-readable extension names, or machine names if | |
| * human-readable names are not available. | |
| */ | |
| protected function getNames(array $names, array $extension_data) { | |
| return array_map(function ($name) use ($extension_data) { | |
| if (isset($extension_data[$name])) { | |
| $name = $extension_data[$name]->info['name']; | |
| } | |
| return $name; | |
| }, $names); | |
| } | |
| } |