diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 6e9636f..4b21b6c 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -9,11 +9,11 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseExceptionWrapper; +use Drupal\Core\Extension\InfoParser; use Drupal\Core\Form\FormState; use Drupal\Core\Installer\Exception\AlreadyInstalledException; use Drupal\Core\Installer\Exception\InstallerException; use Drupal\Core\Installer\Exception\NoProfilesException; -use Drupal\Core\Installer\Exception\TooManyDistributionsException; use Drupal\Core\Installer\InstallerKernel; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManager; @@ -1214,14 +1214,20 @@ function _install_select_profile(&$install_state) { } } // Check for a distribution. If the site has more than one distribution force - // the user to choose which will ensure that settings.php has to be written. - try { - if ($distribution = \Drupal::service('kernel')->getDistribution()) { - return $distribution; + // the user to choose. + $listing = new ExtensionDiscovery(\Drupal::root()); + $listing->setProfileDirectories([]); + $info_parser = new InfoParser(); + $distributions = []; + foreach ($listing->scan('profile') as $profile) { + $info = $info_parser->parse($profile->getPathname()); + if (!empty($info['distribution'])) { + $distributions[] = $profile->getName(); } } - catch (TooManyDistributionsException $e) { - // The user must choose. + // If there is only one distribution then use it. + if (count($distributions) === 1) { + return reset($distributions); } // Get all visible (not hidden) profiles. @@ -2174,34 +2180,20 @@ function install_display_requirements($install_state, $requirements) { } /** - * Installation task; writes profile to settings.php (absent a distribution). + * Installation task; writes profile to settings.php if possible. * * @param array $install_state * An array of information about the current installation state. * * @see _install_select_profile() + * + * @deprecated in Drupal 8.3.0 and will be removed before Drupal 9.0.0. The + * install profile is written to core.extension. */ function install_write_profile($install_state) { - $settings_value = Settings::get('install_profile'); - // We need to write to settings.php if the value in settings.php does not - // equal the selected profile. - $need_to_write = $settings_value !== $install_state['parameters']['profile']; - // However, if we're dealing with a distribution and the profile is not - // writable do not write the value to settings.php if the current value is not - // set. - $distribution = FALSE; - try { - $distribution = \Drupal::service('kernel')->getDistribution(); - } - catch (TooManyDistributionsException $e) { - // The user will have chosen. - } - - if ($settings_value == '' && $distribution && !is_writable(\Drupal::service('site.path') . '/settings.php')) { - $need_to_write = FALSE; - } - - if ($need_to_write) { + // Only need to write to settings.php if it is possible. The primary storage + // for the install profile is the core.extension configuration. + if (is_writable(\Drupal::service('site.path') . '/settings.php')) { // Remember the profile which was used. $settings['settings']['install_profile'] = (object) array( 'value' => $install_state['parameters']['profile'], diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index b7865b3..9670d78 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -190,12 +190,13 @@ public static function root() { */ public static function installProfile() { if (static::hasContainer()) { - return static::getContainer()->getParameter('install_profile'); + $profile = static::getContainer()->getParameter('install_profile'); } else { $config_storage = BootstrapConfigStorageFactory::getDatabaseStorage(); - return $config_storage->read('core.extension')['profile']; + $profile = $config_storage->read('core.extension')['profile']; } + return empty($profile) ? NULL : $profile; } /** diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index f1fc596..92e5181 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -14,10 +14,8 @@ use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drupal\Core\DependencyInjection\YamlFileLoader; use Drupal\Core\Extension\ExtensionDiscovery; -use Drupal\Core\Extension\InfoParser; use Drupal\Core\File\MimeType\MimeTypeGuesser; use Drupal\Core\Http\TrustedHostsRequestFactory; -use Drupal\Core\Installer\Exception\TooManyDistributionsException; use Drupal\Core\Language\Language; use Drupal\Core\Site\Settings; use Symfony\Cmf\Component\Routing\RouteObjectInterface; @@ -1513,56 +1511,16 @@ protected function addServiceFiles(array $service_yamls) { * The name of the any active install profile or distribution. */ protected function getInstallProfile() { - $install_profile = Settings::get('install_profile'); + $config = $this->getConfigStorage()->read('core.extension'); + // Ensure empty strings are NULLs. + $install_profile = empty($config['profile']) ? NULL : $config['profile']; - if (empty($install_profile) && ($config = $this->getConfigStorage()->read('core.extension')) && isset($config['profile'])) { - $install_profile = $config['profile']; + // If system_update_8300() has not yet run fallback to using settings. + if (empty($install_profile)) { + $install_profile = Settings::get('install_profile'); } - elseif (empty($install_profile)) { - $install_profile = $this->getDistribution(); - } - return $install_profile; - } - /** - * Get the name of any discovered profile that is a distribution. - * - * Scans the filesystem looking for all installation profiles. Returns the one - * that has a 'distribution' entry in its info.yml file. If multiple profiles - * are distributions, an exception will be thrown. - * - * This is used in two places: - * 1) During installation, if there is a single distribution, then - * the installer will not write the installation profile name - * to settings.php. - * 2) Whenever DrupalKernel::getInstallProfile() is called, if there - * is no installation profile name noted in settings.php, then - * it will call this function to determine the distribution - * to use. - * - * @return string|FALSE - * The machine name of any discovered distribution. FALSE if there are no - * distributions. - * - * @throws \Drupal\Core\Installer\Exception\TooManyDistributionsException - * Thrown when a site has more than one distribution installation profile. - */ - protected function getDistribution() { - $listing = new ExtensionDiscovery($this->root); - $listing->setProfileDirectories([]); - $info_parser = new InfoParser(); - $distributions = []; - foreach ($listing->scan('profile') as $profile) { - $info = $info_parser->parse($profile->getPathname()); - if (!empty($info['distribution'])) { - $distributions[] = $profile->getName(); - } - } - // There can be only one. - if (count($distributions) > 1) { - throw new TooManyDistributionsException('A site can only have one distribution, multiple installation profiles discovered: ' . implode(', ', $distributions)); - } - return reset($distributions); + return $install_profile; } } diff --git a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php index ad2eb5e..24b4c70 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php @@ -8,7 +8,6 @@ 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. @@ -112,9 +111,10 @@ protected function validateModules(ConfigImporter $config_importer) { } } - // Settings is safe to use because settings.php is written before any module - // is installed. - $install_profile = Settings::get('install_profile'); + // Get the install profile from the site's configuration. + $current_core_extension = $config_importer->getStorageComparer()->getTargetStorage()->read('core.extension'); + $install_profile = isset($current_core_extension['profile']) ? $current_core_extension['profile'] : NULL; + // Ensure that all modules being uninstalled are not required by modules // that will be installed after the import. $uninstalls = $config_importer->getExtensionChangelist('module', 'uninstall'); @@ -129,10 +129,15 @@ protected function validateModules(ConfigImporter $config_importer) { } // Ensure that the install profile is not being uninstalled. - if (in_array($install_profile, $uninstalls)) { + if (in_array($install_profile, $uninstalls, TRUE)) { $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))); } + + // Ensure the profile is not changing. + if ($install_profile !== $core_extension['profile']) { + $config_importer->logError($this->t('Cannot change the install profile from %new_profile to %profile once Drupal is installed.', array('%profile' => $install_profile, '%new_profile' => $core_extension['profile']))); + } } /** diff --git a/core/lib/Drupal/Core/Installer/InstallerKernel.php b/core/lib/Drupal/Core/Installer/InstallerKernel.php index 55e8af6..adeb5c5 100644 --- a/core/lib/Drupal/Core/Installer/InstallerKernel.php +++ b/core/lib/Drupal/Core/Installer/InstallerKernel.php @@ -66,11 +66,4 @@ public function getInstallProfile() { return $profile; } - /** - * {@inheritdoc} - */ - public function getDistribution() { - return parent::getDistribution(); - } - } diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 96c5cf6..4a50c43 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -231,7 +231,7 @@ protected function setUp() { // \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work. // Write directly to active storage to avoid early instantiation of // the event dispatcher which can prevent modules from registering events. - \Drupal::service('config.storage')->write('core.extension', array('module' => array(), 'theme' => array())); + \Drupal::service('config.storage')->write('core.extension', array('module' => array(), 'theme' => array(), 'profile' => '')); // Collect and set a fixed module list. $class = get_class($this); diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php index bb73472..2fe5d4f 100644 --- a/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php +++ b/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php @@ -5,8 +5,6 @@ use Drupal\Component\Serialization\Yaml; use Drupal\Core\Database\Database; use Drupal\Core\DrupalKernel; -use Drupal\Core\Extension\ExtensionDiscovery; -use Drupal\Core\Installer\Exception\TooManyDistributionsException; use Drupal\Core\Site\Settings; use Drupal\simpletest\InstallerTestBase; use Symfony\Component\HttpFoundation\Request; @@ -124,44 +122,11 @@ public function testInstalled() { // Confirm that Drupal recognizes this distribution as the current profile. $this->assertEqual(\Drupal::installProfile(), 'mydistro'); $this->assertNull(Settings::get('install_profile'), 'The install profile has not been written to settings.php.'); + $this->assertEqual($this->config('core.extension')->get('profile'), 'mydistro', 'The install profile has been written to core.extension configuration.'); $this->rebuildContainer(); $this->pass('Container can be rebuilt even though distribution is not written to settings.php.'); - - // Create another installation profile which is a distribution. - $info = [ - 'type' => 'profile', - 'core' => \Drupal::CORE_COMPATIBILITY, - 'name' => 'Distribution profile 2', - 'distribution' => [ - 'name' => 'My Distribution 2', - 'install' => [ - 'theme' => 'bartik', - ], - ], - ]; - // File API functions are not available yet. - $path = $this->siteDirectory . '/profiles/mydistro2'; - mkdir($path, 0777, TRUE); - file_put_contents("$path/mydistro2.info.yml", Yaml::encode($info)); - - // Ensure that no install profile is configured yet. - \Drupal::configFactory()->getEditable('core.extension') - ->set('profile', NULL) - ->save(); - - // Test that a site will multiple distributions will get an exception when - // rebuilding the container. In order to do this we need to reset the - // discovered files in ExtensionDiscovery. - try { - ExtensionDiscovery::reset(); - $this->rebuildContainer(); - $this->fail('TooManyDistributionsException exception thrown.'); - } - catch (TooManyDistributionsException $e) { - $this->pass('TooManyDistributionsException exception thrown.'); - $this->assertEqual('A site can only have one distribution, multiple installation profiles discovered: mydistro, mydistro2', $e->getMessage()); - } + $this->assertEqual(\Drupal::installProfile(), 'mydistro'); } } diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileTest.php index 01e25e6..569fe36 100644 --- a/core/modules/system/src/Tests/Installer/DistributionProfileTest.php +++ b/core/modules/system/src/Tests/Installer/DistributionProfileTest.php @@ -73,6 +73,7 @@ public function testInstalled() { // Confirm that Drupal recognizes this distribution as the current profile. $this->assertEqual(\Drupal::installProfile(), 'mydistro'); $this->assertEqual(Settings::get('install_profile'), 'mydistro', 'The install profile has been written to settings.php.'); + $this->assertEqual($this->config('core.extension')->get('profile'), 'mydistro', 'The install profile has been written to core.extension configuration.'); } } diff --git a/core/modules/system/src/Tests/Installer/MultipleDistributionsProfileTest.php b/core/modules/system/src/Tests/Installer/MultipleDistributionsProfileTest.php index 4ac889e..0dab57f 100644 --- a/core/modules/system/src/Tests/Installer/MultipleDistributionsProfileTest.php +++ b/core/modules/system/src/Tests/Installer/MultipleDistributionsProfileTest.php @@ -3,8 +3,6 @@ namespace Drupal\system\Tests\Installer; use Drupal\Component\Serialization\Yaml; -use Drupal\Core\Extension\ExtensionDiscovery; -use Drupal\Core\Installer\Exception\TooManyDistributionsException; use Drupal\Core\Site\Settings; use Drupal\simpletest\InstallerTestBase; @@ -83,27 +81,11 @@ public function testInstalled() { // Confirm that Drupal recognizes this distribution as the current profile. $this->assertEqual(\Drupal::installProfile(), 'distribution_one'); $this->assertEqual(Settings::get('install_profile'), 'distribution_one', 'The install profile has been written to settings.php.'); + $this->assertEqual($this->config('core.extension')->get('profile'), 'distribution_one', 'The install profile has been written to core.extension configuration.'); - // Test that a site will multiple distributions will get an exception when - // calling \Drupal\Core\DrupalKernel::getDistribution(). - try { - $kernel = $this->container->get('kernel'); - // getDistribution() is protected in the DrupalKernel class. - // Call setAccessible(TRUE) so that we can call it to test its behavior. - $getDistributionMethod = new \ReflectionMethod($kernel, 'getDistribution'); - $getDistributionMethod->setAccessible(TRUE); - $getDistributionMethod->invokeArgs($kernel, array()); - $this->fail('TooManyDistributionsException exception thrown.'); - } - catch (TooManyDistributionsException $e) { - $this->pass('TooManyDistributionsException exception thrown.'); - } - // To mirror the test in DistributionProfileExistingSettingsTest we reset - // the discovered files in ExtensionDiscovery. - // @see \Drupal\system\Tests\Installer\DistributionProfileExistingSettingsTest::testInstalled() - ExtensionDiscovery::reset(); $this->rebuildContainer(); - $this->pass('Container can be rebuilt as distribution is written to settings.php.'); + $this->pass('Container can be rebuilt as distribution is written to configuration.'); + $this->assertEqual(\Drupal::installProfile(), 'distribution_one'); } } diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php index 945e994..bde5644 100644 --- a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php @@ -669,6 +669,34 @@ public function testInstallProfile() { } /** + * Tests install profile validation during configuration import. + * + * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber + */ + public function testInstallProfileMisMatch() { + $sync = $this->container->get('config.storage.sync'); + + $extensions = $sync->read('core.extension'); + // Change the install profile. + $extensions['profile'] = 'this_will_not_work'; + $sync->write('core.extension', $extensions); + + try { + $this->configImporter->reset()->import(); + $this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.'); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.'); + $error_log = $this->configImporter->getErrors(); + // Install profiles can not be changed. Note that KernelTestBase currently + // does not use an install profile. This situation should be impossible + // to get in but site's can removed the install profile setting from + // settings.php so the test is valid. + $this->assertEqual(['Cannot change the install profile from this_will_not_work to once Drupal is installed.'], $error_log); + } + } + + /** * Tests config_get_config_directory(). */ public function testConfigGetConfigDirectory() { diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 94a1e04..ffcaaba 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -270,6 +270,11 @@ * by the user. * * @see install_select_profile() + * + * @deprecated in Drupal 8.3.0 and will be removed before Drupal 9.0.0. The + * install profile is written to the core.extension confoguration. If a + * service requires the install profile use the 'install_profile' container + * parameter. Functional code can use \Drupal::installProfile(). */ # $settings['install_profile'] = '';