diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index 715138f..64f7252 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -434,7 +434,7 @@ protected function createExtensionChangelist() { * @return array * An array of extension names. */ - protected function getExtensionChangelist($type, $op = NULL) { + public function getExtensionChangelist($type, $op = NULL) { if ($op) { return $this->extensionChangelist[$type][$op]; } diff --git a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php index f17d5bd..10d3978 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php @@ -8,6 +8,7 @@ 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; @@ -37,6 +38,83 @@ public function onConfigImporterValidate(ConfigImporterEvent $event) { } } } + $config_importer = $event->getConfigImporter(); + if ($config_importer->getStorageComparer()->getSourceStorage()->exists('core.extension')) { + $this->validateModuleInstalls($config_importer); + $this->validateThemeInstalls($config_importer); + $this->validateDependencies($config_importer); + } + } + + /** + * Validates all modules being installed during a configuration import exist. + * + * @param ConfigImporter $config_importer + * The configuration importer. + */ + protected function validateModuleInstalls(ConfigImporter $config_importer) { + $modules = system_rebuild_module_data(); + foreach ($config_importer->getExtensionChangelist('module', 'install') as $module) { + if (!isset($modules[$module])) { + $config_importer->logError($this->t('Unable to install module %module since it does not exist.', array('%module' => $module))); + } + } + } + + /** + * Validates all themes being installed during a configuration import exist. + * + * @param ConfigImporter $config_importer + * The configuration importer. + */ + protected function validateThemeInstalls(ConfigImporter $config_importer) { + $themes = \Drupal::service('theme_handler')->rebuildThemeData(); + foreach ($config_importer->getExtensionChangelist('theme', 'install') as $theme) { + if (!isset($themes[$theme])) { + $config_importer->logError($this->t('Unable to install theme %theme since it does not exist.', array('%theme' => $theme))); + } + } + } + + /** + * Validates configuration being imported does not have unmet dependencies. + * + * @param 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']), + ]; + + foreach (['create', 'update'] as $op) { + foreach ($config_importer->getUnprocessedConfiguration($op) as $name) { + $data = $config_importer->getStorageComparer()->getSourceStorage()->read($name); + 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)) { + switch ($type) { + case 'module': + $config_importer->logError($this->t('Configuration %name depends on a module that will not be installed after import.', array('%name' => $name))); + break; + case 'theme': + $config_importer->logError($this->t('Configuration %name depends on a theme that will not be installed after import.', array('%name' => $name))); + break; + case 'config': + $config_importer->logError($this->t('Configuration %name depends on configuration that will not exist after import.', array('%name' => $name))); + break; + } + } + } + } + } + } + } } diff --git a/core/modules/system/src/SystemConfigSubscriber.php b/core/modules/system/src/SystemConfigSubscriber.php index 765b824..ed412a2 100644 --- a/core/modules/system/src/SystemConfigSubscriber.php +++ b/core/modules/system/src/SystemConfigSubscriber.php @@ -7,32 +7,58 @@ namespace Drupal\system; +use Drupal\Core\Config\ConfigEvents; use Drupal\Core\Config\ConfigImporterEvent; -use Drupal\Core\Config\ConfigImportValidateEventSubscriberBase; -use Drupal\Core\Config\StorageDispatcher; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * System Config subscriber. */ -class SystemConfigSubscriber extends ConfigImportValidateEventSubscriberBase { +class SystemConfigSubscriber implements EventSubscriberInterface { + use StringTranslationTrait; /** * Checks that the configuration synchronization is valid. * - * This event listener implements two checks: - * - prevents deleting all configuration. - * - checks that the system.site:uuid's in the source and target match. + * This event listener prevents deleting all configuration. If there is + * nothing to import then event propagation is stopped because there is no + * config import to validate. * * @param ConfigImporterEvent $event * The config import event. */ - public function onConfigImporterValidate(ConfigImporterEvent $event) { + public function onConfigImporterValidateNotEmpty(ConfigImporterEvent $event) { $importList = $event->getConfigImporter()->getStorageComparer()->getSourceStorage()->listAll(); if (empty($importList)) { $event->getConfigImporter()->logError($this->t('This import is empty and if applied would delete all of your configuration, so has been rejected.')); + $event->stopPropagation(); } + } + + /** + * Checks that the configuration synchronization is valid. + * + * This event listener checks that the system.site:uuid's in the source and + * target match. + * + * @param ConfigImporterEvent $event + * The config import event. + */ + public function onConfigImporterValidateSiteUUID(ConfigImporterEvent $event) { if (!$event->getConfigImporter()->getStorageComparer()->validateSiteUuid()) { $event->getConfigImporter()->logError($this->t('Site UUID in source storage does not match the target storage.')); } } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + // The empty check has a high priority so that is can stop propagation if + // there is no configuration to import. + $events[ConfigEvents::IMPORT_VALIDATE][] = array('onConfigImporterValidateNotEmpty', 512); + $events[ConfigEvents::IMPORT_VALIDATE][] = array('onConfigImporterValidateSiteUUID', 256); + return $events; + } }