diff -u b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php --- b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php @@ -120,8 +120,9 @@ } } - // Get a list of installed profiles. - $installed_profiles = \Drupal::service('profile_handler')->getProfiles(); + // Get a list of parent profiles and the main profile. + $parent_profiles = \Drupal::service('profile_handler')->getProfiles(); + $main_profile = array_pop($parent_profiles); // Ensure that all modules being uninstalled are not required by modules // that will be installed after the import. @@ -136,14 +137,20 @@ } } - // Ensure that none of the installed profiles are being uninstalled. - if ($profile_uninstalls = array_intersect_key($installed_profiles, array_flip($uninstalls))) { + // Ensure that the active profile is not being uninstalled. + if (in_array($main_profile, $uninstalls, TRUE)) { + $profile_name = $module_data[$main_profile]->info['name']; + $config_importer->logError($this->t('Unable to uninstall the %profile profile since it is the main install profile.', ['%profile' => $profile_name])); + } + + // Ensure that none of the parent profiles are being uninstalled. + if ($profile_uninstalls = array_intersect_key($parent_profiles, array_flip($uninstalls))) { foreach ($profile_uninstalls as $profile) { - $profile_names[] = $profile->info['name']; + $profile_names[] = $module_data[$profile]->info['name']; } $message = $this->formatPlural(count($profile_names), - 'Unable to uninstall the %profile profile since it is an installed profile.', - 'Unable to uninstall the %profile profiles since they are installed profiles.', + 'Unable to uninstall the %profile profile profile since it is a parent of another installed profile.', + 'Unable to uninstall the %profile profiles since they are parents of another installed profile.', ['%profile' => implode(', ', $profile_names)] ); $config_importer->logError($message); diff -u b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php --- b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php +++ b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php @@ -56,7 +56,7 @@ $this->drupalPostForm('admin/config/development/configuration', [], t('Import all')); $this->assertText('The configuration cannot be imported because it failed validation for the following reasons:'); - $this->assertText('Unable to uninstall the Testing config import profile since it is an installed profile.'); + $this->assertText('Unable to uninstall the Testing config import profile since it is the main install profile.'); // Uninstall dependencies of testing_config_import. $core['module']['testing_config_import'] = 0; diff -u b/core/profiles/testing_inherited/testing_inherited.info.yml b/core/profiles/testing_inherited/testing_inherited.info.yml --- b/core/profiles/testing_inherited/testing_inherited.info.yml +++ b/core/profiles/testing_inherited/testing_inherited.info.yml @@ -15,6 +15,7 @@ dependencies: - block - config + - syslog themes: - stable only in patch2: unchanged: --- /dev/null +++ b/core/modules/config/src/Tests/ConfigImportBaseInstallProfileTest.php @@ -0,0 +1,83 @@ +webUser = $this->drupalCreateUser(['synchronize configuration']); + $this->drupalLogin($this->webUser); + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + } + + /** + * Tests config importer cannot uninstall install profiles. + * + * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber + */ + public function testInstallProfileValidation() { + $sync = $this->container->get('config.storage.sync'); + $this->copyConfig($this->container->get('config.storage'), $sync); + $core = $sync->read('core.extension'); + + // Ensure install sub-profiles can not be uninstalled. + unset($core['module']['testing_inherited']); + $sync->write('core.extension', $core); + + $this->drupalPostForm('admin/config/development/configuration', [], t('Import all')); + $this->assertText('The configuration cannot be imported because it failed validation for the following reasons:'); + $this->assertText('Unable to uninstall the Testing Inherited profile since it is the main install profile.'); + + // Ensure that parent profile can not be uninstalled. + unset($core['module']['testing']); + $sync->write('core.extension', $core); + + $this->drupalPostForm('admin/config/development/configuration', [], t('Import all')); + $this->assertText('The configuration cannot be imported because it failed validation for the following reasons:'); + $this->assertText('Unable to uninstall the Testing profile since it is a parent of another installed profile.'); + + // Uninstall dependencies of main profile. + $core['module']['testing_inherited'] = 0; + unset($core['module']['syslog']); + $sync->write('core.extension', $core); + $sync->deleteAll('syslog.'); + $this->drupalPostForm('admin/config/development/configuration', [], t('Import all')); + $this->assertText('The configuration was imported successfully.'); + $this->rebuildContainer(); + $this->assertFalse(\Drupal::moduleHandler()->moduleExists('syslog'), 'The syslog module has been uninstalled.'); + + // Uninstall dependencies of parent profile. + $core['module']['testing'] = 0; + unset($core['module']['dynamic_page_cache']); + $sync->write('core.extension', $core); + $sync->deleteAll('syslog.'); + $this->drupalPostForm('admin/config/development/configuration', [], t('Import all')); + $this->assertText('The configuration was imported successfully.'); + $this->rebuildContainer(); + $this->assertFalse(\Drupal::moduleHandler()->moduleExists('syslog'), 'The dynamic_page_cache module has been uninstalled.'); + } + +}