diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index e8ef040..becc6e9 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -2299,8 +2299,17 @@ function install_display_requirements($install_state, $requirements) { * An array of information about the current installation state. */ function install_write_profile($install_state) { - $is_mismatch = \Drupal::installProfile() !== $install_state['parameters']['profile']; - if ($is_mismatch) { + $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. + if ($settings_value == '' && \Drupal::service('kernel')->getDistribution() && !is_writable(\Drupal::service('site.path') . '/settings.php')) { + $need_to_write = FALSE; + } + if ($need_to_write) { // Remember the profile which was used. $settings['settings']['install_profile'] = (object) array( 'value' => $install_state['parameters']['profile'], diff --git a/core/includes/install.inc b/core/includes/install.inc index 31d472a..ae23d4e 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -315,7 +315,7 @@ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) { } // Write the new settings file. - if (file_put_contents(DRUPAL_ROOT . '/' . $settings_file, $buffer) === FALSE) { + if (@file_put_contents(DRUPAL_ROOT . '/' . $settings_file, $buffer) === FALSE) { throw new Exception(t('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file))); } else { diff --git a/core/modules/simpletest/src/InstallerTestBase.php b/core/modules/simpletest/src/InstallerTestBase.php index c340c92..084c93e 100644 --- a/core/modules/simpletest/src/InstallerTestBase.php +++ b/core/modules/simpletest/src/InstallerTestBase.php @@ -121,7 +121,7 @@ protected function setUp() { ->set('app.root', DRUPAL_ROOT); \Drupal::setContainer($this->container); - $this->drupalGet($GLOBALS['base_url'] . '/core/install.php'); + $this->drupalGet($this->getInstallationUrl()); // Select language. $this->setUpLanguage(); @@ -215,4 +215,13 @@ protected function refreshVariables() { } } + /** + * Gets the URL at which the installation starts. + * + * @return string + */ + protected function getInstallationUrl() { + return $GLOBALS['base_url'] . '/core/install.php'; + } + } diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php index f6548eb..c999d6a 100644 --- a/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php +++ b/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php @@ -80,16 +80,18 @@ protected function setUp() { mkdir($this->settings['config_directories'][CONFIG_ACTIVE_DIRECTORY]->value, 0777, TRUE); mkdir($this->settings['config_directories'][CONFIG_STAGING_DIRECTORY]->value, 0777, TRUE); parent::setUp(); - $filename = $this->siteDirectory . '/settings.php'; - // Make the settings file read-only. - // Not using File API; a potential error must trigger a PHP warning. - chmod($filename, 0444); } /** * Overrides InstallerTest::setUpLanguage(). */ protected function setUpLanguage() { + // Make settings file not writable. + $filename = $this->siteDirectory . '/settings.php'; + // Make the settings file read-only. + // Not using File API; a potential error must trigger a PHP warning. + chmod($filename, 0444); + // Verify that the distribution name appears. $this->assertRaw($this->info['distribution']['name']); // Verify that the requested theme is used. diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileBrokenTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileBrokenTest.php new file mode 100644 index 0000000..4db9a75 --- /dev/null +++ b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileBrokenTest.php @@ -0,0 +1,133 @@ +settings['settings']['hash_salt'] = (object) array( + 'value' => __CLASS__, + 'required' => TRUE, + ); + + // Pre-configure database credentials. + $connection_info = Database::getConnectionInfo(); + unset($connection_info['default']['pdo']); + unset($connection_info['default']['init_commands']); + + $this->settings['databases']['default'] = (object) array( + 'value' => $connection_info, + 'required' => TRUE, + ); + + // During interactive install we'll change this to a different profile and + // this test will ensure that the new value is written to settings.php. + $this->settings['settings']['install_profile'] = (object) array( + 'value' => 'minimal', + 'required' => TRUE, + ); + + // Pre-configure config directories. + $this->settings['config_directories'] = array( + CONFIG_ACTIVE_DIRECTORY => (object) array( + 'value' => conf_path() . '/files/config_active', + 'required' => TRUE, + ), + CONFIG_STAGING_DIRECTORY => (object) array( + 'value' => conf_path() . '/files/config_staging', + 'required' => TRUE, + ), + ); + mkdir($this->settings['config_directories'][CONFIG_ACTIVE_DIRECTORY]->value, 0777, TRUE); + mkdir($this->settings['config_directories'][CONFIG_STAGING_DIRECTORY]->value, 0777, TRUE); + + // @todo Remove HTML once https://www.drupal.org/node/2514044 is fixed. + $this->exceptionMessage = htmlspecialchars('Failed to modify ' . $this->siteDirectory . '/settings.php' . '. Verify the file permissions.'); + parent::setUp(); + } + + /** + * {@inheritdoc} + */ + protected function getInstallationUrl() { + // Make settings file not writable. This will break the installer. + $filename = $this->siteDirectory . '/settings.php'; + // Make the settings file read-only. + // Not using File API; a potential error must trigger a PHP warning. + chmod($filename, 0444); + return $GLOBALS['base_url'] . '/core/install.php?langcode=en&profile=testing'; + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + // This step is skipped, because there is a lagcode as a query param. + } + + /** + * {@inheritdoc} + */ + protected function setUpProfile() { + // This step is skipped, because there is a profile as a query param. + } + + /** + * {@inheritdoc} + */ + protected function setUpSettings() { + // This step should not appear, since settings.php is fully configured + // already. + } + + protected function setUpSite() { + // This step should not appear, since settings.php could not be written. + } + + /** + * Verifies that installation did not succeed. + */ + public function testBrokenInstaller() { + $this->assertText($this->exceptionMessage); + } + + /** + * {@inheritdoc} + */ + protected function error($message = '', $group = 'Other', array $caller = NULL) { + if ($group == 'Exception' && $message == $this->exceptionMessage) { + // Ignore the expected exception. + return FALSE; + } + return parent::error($message, $group, $caller); + } + +} diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileTest.php new file mode 100644 index 0000000..37bb7c7 --- /dev/null +++ b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileTest.php @@ -0,0 +1,108 @@ +settings['settings']['hash_salt'] = (object) array( + 'value' => __CLASS__, + 'required' => TRUE, + ); + + // Pre-configure database credentials. + $connection_info = Database::getConnectionInfo(); + unset($connection_info['default']['pdo']); + unset($connection_info['default']['init_commands']); + + $this->settings['databases']['default'] = (object) array( + 'value' => $connection_info, + 'required' => TRUE, + ); + + // During interactive install we'll change this to a different profile and + // this test will ensure that the new value is written to settings.php. + $this->settings['settings']['install_profile'] = (object) array( + 'value' => 'minimal', + 'required' => TRUE, + ); + + // Pre-configure config directories. + $this->settings['config_directories'] = array( + CONFIG_ACTIVE_DIRECTORY => (object) array( + 'value' => conf_path() . '/files/config_active', + 'required' => TRUE, + ), + CONFIG_STAGING_DIRECTORY => (object) array( + 'value' => conf_path() . '/files/config_staging', + 'required' => TRUE, + ), + ); + mkdir($this->settings['config_directories'][CONFIG_ACTIVE_DIRECTORY]->value, 0777, TRUE); + mkdir($this->settings['config_directories'][CONFIG_STAGING_DIRECTORY]->value, 0777, TRUE); + + parent::setUp(); + } + + /** + * {@inheritdoc} + */ + protected function getInstallationUrl() { + return $GLOBALS['base_url'] . '/core/install.php?langcode=en&profile=testing'; + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + // This step is skipped, because there is a lagcode as a query param. + } + + /** + * {@inheritdoc} + */ + protected function setUpProfile() { + // This step is skipped, because there is a profile as a query param. + } + + /** + * {@inheritdoc} + */ + protected function setUpSettings() { + // This step should not appear, since settings.php is fully configured + // already. + } + + /** + * Verifies that installation succeeded. + */ + public function testInstaller() { + $this->assertUrl('user/1'); + $this->assertResponse(200); + $this->assertEqual('testing', \Drupal::installProfile()); + $this->assertEqual('testing', Settings::get('install_profile'), 'Profile was correctly changed to testing in Settings.php'); + } + +} diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php index 08ae978..4c22a19 100644 --- a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php +++ b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Installer; +use Drupal\Core\Site\Settings; use Drupal\simpletest\InstallerTestBase; use Drupal\Core\Database\Database; use Drupal\Core\DrupalKernel; @@ -85,6 +86,7 @@ public function testInstaller() { $this->assertUrl('user/1'); $this->assertResponse(200); $this->assertEqual('testing', drupal_get_profile(), 'Profile was changed from minimal to testing during interactive install.'); + $this->assertEqual('testing', Settings::get('install_profile'), 'Profile was correctly changed to testing in Settings.php'); } }