diff --git a/core/--cached b/core/--cached new file mode 100644 index 0000000..e69de29 diff --git a/core/includes/config.inc b/core/includes/config.inc index 0011533..11000b6 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -195,6 +195,47 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto } /** + * Validates a set of configuration changes during import. + * + * This hook allows modules to validate a set of configuration changes during + * import before we make any changes. Modules that wish to participate in this + * should implement hook_config_import_validate(). + * + * To stop an import from happening, hook_config_import_validate() + * implementations should throw a ConfigImportInvalidChangeExecption and set + * a meaningful message. + * + * @param array $config_changes + * An array of changes to be written. + * @param Drupal\Core\Config\StorageInterface $source_storage + * The storage to synchronize configuration from. + * @param Drupal\Core\Config\StorageInterface $target_storage + * The storage to synchronize configuration to. + */ +function config_import_validate_changes(array $config_changes, StorageInterface $source_storage, StorageInterface $target_storage) { + $changes_by_module = array(); + foreach (array('delete', 'create', 'change') as $op) { + foreach ($config_changes[$op] as $key => $name) { + $module = strtok($name, '.'); + if (empty($changes_by_module[$module])) { + $changes_by_module[$module] = array( + 'delete' => array(), + 'create' => array(), + 'change' => array(), + ); + } + $changes_by_module[$module][$op][] = $name; + } + } + $hook = 'config_import_validate'; + foreach ($changes_by_module as $module => $changes) { + if (module_exists($module) && module_hook($module, $hook)) { + module_invoke($module, $hook, $changes, $source_storage, $target_storage); + } + } +} + +/** * Imports configuration into the active configuration. * * @return bool|null @@ -211,6 +252,15 @@ function config_import() { return; } + // Let modules validate changes before we make any changes. + try { + config_import_validate_changes($config_changes, $source_storage, $target_storage); + } + catch (ConfigException $e) { + watchdog_exception('config_import', $e); + return FALSE; + } + if (!lock()->acquire(CONFIG_IMPORT_LOCK)) { // Another request is synchronizing configuration. // Return a negative result for UI purposes. We do not differentiate between diff --git a/core/lib/Drupal/Core/Config/ConfigImportInvalidChangeException.php b/core/lib/Drupal/Core/Config/ConfigImportInvalidChangeException.php new file mode 100644 index 0000000..72fbec2 --- /dev/null +++ b/core/lib/Drupal/Core/Config/ConfigImportInvalidChangeException.php @@ -0,0 +1,16 @@ +listAll() as $system_default_config_name) { + if (in_array($system_default_config_name, $changes['delete'])) { + throw new ConfigImportInvalidChangeException("Deleting default system config is invalid: '$name'."); + } + } +} + +/** * Theme callback for the default batch page. */ function _system_batch_theme() { diff --git a/core/modules/user/user.module b/core/modules/user/user.module index aa5d085..1ed210e 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1,6 +1,7 @@ get('plugin.manager.views.display'); + $display_definitions = $display_manager->getDefinitions(); + + foreach (array_merge($changes['create'], $changes['change']) as $name) { + if (strpos($name, 'views.view') === 0) { + $views[] = $name; + } + } + foreach ($views as $name) { + $view = entity_create('view', $source_storage->read()); + if (!$view->id()) { + throw new ConfigImportInvalidChangeException('View has no id.'); + } + if (!$displays = $view->get('display')) { + throw new ConfigImportInvalidChangeException('View has no displays.'); + } + foreach ($displays as $display) { + if (!isset($display_definitions[$display['display_plugin']]) || !is_object($display_manager->createInstance($display['display_plugin']))) { + throw new ConfigImportInvalidChangeException('View display plugin cannot be found.'); + } + } + } +} + +/** * Helper function for menu loading. This will automatically be * called in order to 'load' a views argument; primarily it * will be used to perform validation.