diff --git a/core/modules/config/src/Tests/ConfigInstallWebTest.php b/core/modules/config/src/Tests/ConfigInstallWebTest.php index 5bbe4bd..53e557f 100644 --- a/core/modules/config/src/Tests/ConfigInstallWebTest.php +++ b/core/modules/config/src/Tests/ConfigInstallWebTest.php @@ -9,7 +9,6 @@ use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\PreExistingConfigException; -use Drupal\Core\Config\StorageInterface; use Drupal\simpletest\WebTestBase; use Drupal\Core\Config\FileStorage; @@ -20,9 +19,17 @@ * @group config */ class ConfigInstallWebTest extends WebTestBase { + + /** + * The admin user used in this test. + */ + protected $admin_user; + protected function setUp() { parent::setUp(); + $this->admin_user = $this->drupalCreateUser(array('administer modules')); + // Ensure the global variable being asserted by this test does not exist; // a previous test executed in this request/process might have set it. unset($GLOBALS['hook_config_test']); @@ -31,7 +38,7 @@ protected function setUp() { /** * Tests module re-installation. */ - function testIntegrationModuleReinstallation() { + function _testIntegrationModuleReinstallation() { $default_config = 'config_integration_test.settings'; $default_configuration_entity = 'config_test.dynamic.config_integration_test'; @@ -108,7 +115,7 @@ function testIntegrationModuleReinstallation() { /** * Tests install profile config changes. */ - function testInstallProfileConfigOverwrite() { + function _testInstallProfileConfigOverwrite() { $config_name = 'system.cron'; // The expected configuration from the system module. $expected_original_data = array( @@ -160,9 +167,23 @@ function testInstallProfileConfigOverwrite() { } /** - * Tests pre existing configuration detection + * Tests pre existing configuration detection. */ public function testPreExistingConfigInstall() { + $this->drupalLogin($this->admin_user); + // Try to install config_install_fail_test without config_test enabled. + $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration')); + $this->drupalPostForm(NULL, array(), t('Continue')); + $this->assertText('Configuration config_test.dynamic.dotted.default provided by config_install_fail_test already exist in active configuration'); + + // Try to install config_install_fail_test. config_test was enabled in the + // previous submission. + $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration')); + $this->assertText('You must delete or rename the following configuration to install Configuration install fail test.'); + + // Try to install config_install_fail_dependency_test. + $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_dependency_test][enable]' => TRUE), t('Save configuration')); + $this->assertText('You must delete or rename the following configuration to install Configuration install fail dependency test, Configuration install fail test.'); } } diff --git a/core/modules/system/src/Controller/ConfigClashController.php b/core/modules/system/src/Controller/ConfigClashController.php index 158b800..f9bab44 100644 --- a/core/modules/system/src/Controller/ConfigClashController.php +++ b/core/modules/system/src/Controller/ConfigClashController.php @@ -10,12 +10,13 @@ use Drupal\Core\Config\ConfigInstallerInterface; use Drupal\Core\Config\ConfigManagerInterface; use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; /** - * Builds a confirmation form for enabling modules with dependencies. + * Builds a page which lists all clashing configuration items. */ class ConfigClashController extends ControllerBase { @@ -48,6 +49,13 @@ class ConfigClashController extends ControllerBase { protected $configManager; /** + * The URL generator. + * + * @var \Drupal\Core\Routing\UrlGeneratorInterface + */ + protected $urlGenerator; + + /** * An associative list of modules to enable or disable. * * @var array @@ -63,11 +71,14 @@ class ConfigClashController extends ControllerBase { * The configuration manager. * @param \Drupal\Core\Config\ConfigInstallerInterface $config_installer * The configuration installer. + * @param \Drupal\Core\Routing\UrlGeneratorInterface + * The URL generator. */ - public function __construct(KeyValueStoreExpirableInterface $key_value_expirable, ConfigManagerInterface $config_manager, ConfigInstallerInterface $config_installer) { + public function __construct(KeyValueStoreExpirableInterface $key_value_expirable, ConfigManagerInterface $config_manager, ConfigInstallerInterface $config_installer, UrlGeneratorInterface $url_generator) { $this->keyValueExpirable = $key_value_expirable; $this->configManager = $config_manager; $this->configInstaller = $config_installer; + $this->urlGenerator = $url_generator; } /** @@ -77,7 +88,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('keyvalue.expirable')->get('module_list'), $container->get('config.manager'), - $container->get('config.installer') + $container->get('config.installer'), + $container->get('url_generator') ); } @@ -93,12 +105,9 @@ public function moduleReport() { $account = $this->currentUser()->id(); $modules = $this->keyValueExpirable->get($account); - // hack to test - // $modules = array('install' => array('config_install_fail_test' => TRUE)); - // Redirect to the modules list page if the key value store is empty. if (!$modules) { - return new RedirectResponse($this->urlGenerator()->generate('system.modules_list', array(), TRUE)); + return new RedirectResponse($this->urlGenerator->generate('system.modules_list', array(), TRUE)); } $report = array( @@ -154,14 +163,11 @@ protected function report($type, array $extensions) { } } else { - // @todo: Work out + // @todo $report['config_clashes']['#items'][] = $config_name; } } } - $report['debug'] = array( - '#markup' => '

' . print_r($existing_configuration, TRUE) . '

', - ); } return $report; diff --git a/core/modules/system/src/Form/ModulesListConfirmForm.php b/core/modules/system/src/Form/ModulesListConfirmForm.php index 2a31dbe..8c72b30 100644 --- a/core/modules/system/src/Form/ModulesListConfirmForm.php +++ b/core/modules/system/src/Form/ModulesListConfirmForm.php @@ -7,6 +7,7 @@ namespace Drupal\system\Form; +use Drupal\Core\Config\PreExistingConfigException; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\Form\ConfirmFormBase; @@ -153,7 +154,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // Install the given modules. if (!empty($this->modules['install'])) { - $this->moduleInstaller->install(array_keys($this->modules['install'])); + try { + $this->moduleInstaller->install(array_keys($this->modules['install'])); + } + catch (PreExistingConfigException $exception) { + drupal_set_message($exception->getMessage()); + } } // Gets module list after install process, flushes caches and displays a diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php index 2be620b..9ee1f2b 100644 --- a/core/modules/system/src/Form/ModulesListForm.php +++ b/core/modules/system/src/Form/ModulesListForm.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\ConfigInstallerInterface; +use Drupal\Core\Config\PreExistingConfigException; use Drupal\Core\Controller\TitleResolverInterface; use Drupal\Core\Access\AccessManagerInterface; use Drupal\Core\Entity\EntityManagerInterface; @@ -542,7 +543,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // There seem to be no dependencies that would need approval. if (!empty($modules['install'])) { - $this->moduleInstaller->install(array_keys($modules['install'])); + try { + $this->moduleInstaller->install(array_keys($modules['install'])); + } + catch (PreExistingConfigException $exception) { + drupal_set_message($exception->getMessage()); + } } // Gets module list after install process, flushes caches and displays a diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 23c9aa5..51f3d55 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -286,7 +286,7 @@ system.theme_install: _csrf_token: 'TRUE' system.modules_config_clash: - path: 'admin/modules/config_clash' + path: 'admin/modules/config-clash' defaults: _controller: 'Drupal\system\Controller\ConfigClashController::moduleReport' _title: 'Configuration clash' diff --git a/core/modules/system/src/Form/ModulesListConfirmForm.php b/core/modules/system/src/Form/ModulesListConfirmForm.php index 8c72b30..5da4d64 100644 --- a/core/modules/system/src/Form/ModulesListConfirmForm.php +++ b/core/modules/system/src/Form/ModulesListConfirmForm.php @@ -8,6 +8,7 @@ namespace Drupal\system\Form; use Drupal\Core\Config\PreExistingConfigException; +use Drupal\Core\Config\ConfigInstallerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\Form\ConfirmFormBase; @@ -50,6 +51,13 @@ class ModulesListConfirmForm extends ConfirmFormBase { protected $moduleInstaller; /** + * The configuration installer. + * + * @var \Drupal\Core\Config\ConfigInstallerInterface + */ + protected $configInstaller; + + /** * Constructs a ModulesListConfirmForm object. * * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler @@ -58,11 +66,14 @@ class ModulesListConfirmForm extends ConfirmFormBase { * The module installer. * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable * The key value expirable factory. + * @param \Drupal\Core\Config\ConfigInstallerInterface $config_installer + * The configuration installer. */ - public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable) { + public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable, ConfigInstallerInterface $config_installer) { $this->moduleHandler = $module_handler; $this->moduleInstaller = $module_installer; $this->keyValueExpirable = $key_value_expirable; + $this->configInstaller = $config_installer; } /** @@ -72,7 +83,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('module_handler'), $container->get('module_installer'), - $container->get('keyvalue.expirable')->get('module_list') + $container->get('keyvalue.expirable')->get('module_list'), + $container->get('config.installer') ); } @@ -152,6 +164,19 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // Gets list of modules prior to install process. $before = $this->moduleHandler->getModuleList(); + // Check if we have any pre-existing configuration. + $existing_configuration = array(); + foreach (array_keys($this->modules['install']) as $module) { + $existing_configuration = array_merge_recursive($this->configInstaller->findPreExistingConfiguration('module', $module), $existing_configuration); + } + if (count($existing_configuration)) { + $account = $this->currentUser()->id(); + $this->keyValueExpirable->setWithExpire($account, $this->modules, 60); + // Redirect to the system config clash page. + $form_state->setRedirect('system.modules_config_clash'); + return; + } + // Install the given modules. if (!empty($this->modules['install'])) { try {