From bbe7b77475964e5ad67a2fff9da07e820e699d4b Mon Sep 17 00:00:00 2001 From: Dries Buytaert <dries@buytaert.net> Date: Sat, 9 May 2009 18:35:01 +0000 Subject: [PATCH] - Patch #304163 by chx, David_Rothstein: allow update.php to regenerate settings.php --- includes/bootstrap.inc | 2 +- includes/install.inc | 58 -------------- install.php | 58 ++++++++++++++ update.php | 175 +++++++++++++++++++++++++---------------- 4 files changed, 165 insertions(+), 128 deletions(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 127f31f6bc1c..58958249889b 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1167,7 +1167,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO */ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) { if ($message) { - if (!isset($_SESSION['messages']) && function_exists('drupal_set_session')) { + if (!isset($_SESSION['messages'])) { drupal_set_session('messages', array()); } diff --git a/includes/install.inc b/includes/install.inc index 6f043bf4c9ef..2af751c5bd08 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -996,61 +996,3 @@ function drupal_check_module($module) { } return TRUE; } - -/** - * Check installation requirements and report any errors. - */ -function install_check_requirements($profile, $verify) { - // Check the profile requirements. - $requirements = $profile ? drupal_check_profile($profile) : array(); - - // If Drupal is not set up already, we need to create a settings file. - if (!$verify) { - $writable = FALSE; - $conf_path = './' . conf_path(FALSE, TRUE); - $settings_file = $conf_path . '/settings.php'; - $file = $conf_path; - $exists = FALSE; - // Verify that the directory exists. - if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) { - // Check to make sure a settings.php already exists. - $file = $settings_file; - if (drupal_verify_install_file($settings_file, FILE_EXIST)) { - $exists = TRUE; - // If it does, make sure it is writable. - $writable = drupal_verify_install_file($settings_file, FILE_READABLE|FILE_WRITABLE); - $exists = TRUE; - } - } - - if (!$exists) { - $requirements['settings file exists'] = array( - 'title' => st('Settings file'), - 'value' => st('The settings file does not exist.'), - 'severity' => REQUIREMENT_ERROR, - 'description' => st('The @drupal installer requires that you create a settings file as part of the installation process. Copy the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>.', array('@drupal' => drupal_install_profile_name(), '%file' => $file, '%default_file' => $conf_path .'/default.settings.php', '@install_txt' => base_path() .'INSTALL.txt')), - ); - } - else { - $requirements['settings file exists'] = array( - 'title' => st('Settings file'), - 'value' => st('The %file file exists.', array('%file' => $file)), - ); - if (!$writable) { - $requirements['settings file writable'] = array( - 'title' => st('Settings file'), - 'value' => st('The settings file is not writable.'), - 'severity' => REQUIREMENT_ERROR, - 'description' => st('The @drupal installer requires write permissions to %file during the installation process. If you are unsure how to grant file permissions, please consult the <a href="@handbook_url">online handbook</a>.', array('@drupal' => drupal_install_profile_name(), '%file' => $file, '@handbook_url' => 'http://drupal.org/server-permissions')), - ); - } - else { - $requirements['settings file'] = array( - 'title' => st('Settings file'), - 'value' => st('Settings file is writable.'), - ); - } - } - } - return $requirements; -} diff --git a/install.php b/install.php index 1fa053ddf2d9..67e65084dafc 100644 --- a/install.php +++ b/install.php @@ -886,6 +886,64 @@ function install_reserved_tasks() { return array('configure', 'profile-install', 'profile-install-batch', 'locale-initial-import', 'locale-initial-batch', 'profile-finished', 'locale-remaining-batch', 'finished', 'done'); } +/** + * Check installation requirements and report any errors. + */ +function install_check_requirements($profile, $verify) { + // Check the profile requirements. + $requirements = drupal_check_profile($profile); + + // If Drupal is not set up already, we need to create a settings file. + if (!$verify) { + $writable = FALSE; + $conf_path = './' . conf_path(FALSE, TRUE); + $settings_file = $conf_path . '/settings.php'; + $file = $conf_path; + $exists = FALSE; + // Verify that the directory exists. + if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) { + // Check to make sure a settings.php already exists. + $file = $settings_file; + if (drupal_verify_install_file($settings_file, FILE_EXIST)) { + $exists = TRUE; + // If it does, make sure it is writable. + $writable = drupal_verify_install_file($settings_file, FILE_READABLE|FILE_WRITABLE); + $exists = TRUE; + } + } + + if (!$exists) { + $requirements['settings file exists'] = array( + 'title' => st('Settings file'), + 'value' => st('The settings file does not exist.'), + 'severity' => REQUIREMENT_ERROR, + 'description' => st('The @drupal installer requires that you create a settings file as part of the installation process. Copy the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>.', array('@drupal' => drupal_install_profile_name(), '%file' => $file, '%default_file' => $conf_path .'/default.settings.php', '@install_txt' => base_path() .'INSTALL.txt')), + ); + } + else { + $requirements['settings file exists'] = array( + 'title' => st('Settings file'), + 'value' => st('The %file file exists.', array('%file' => $file)), + ); + if (!$writable) { + $requirements['settings file writable'] = array( + 'title' => st('Settings file'), + 'value' => st('The settings file is not writable.'), + 'severity' => REQUIREMENT_ERROR, + 'description' => st('The @drupal installer requires write permissions to %file during the installation process. If you are unsure how to grant file permissions, please consult the <a href="@handbook_url">online handbook</a>.', array('@drupal' => drupal_install_profile_name(), '%file' => $file, '@handbook_url' => 'http://drupal.org/server-permissions')), + ); + } + else { + $requirements['settings file'] = array( + 'title' => st('Settings file'), + 'value' => st('Settings file is writable.'), + ); + } + } + } + return $requirements; +} + /** * Add the installation task list to the current page. */ diff --git a/update.php b/update.php index e47d11507965..69cf108accd9 100644 --- a/update.php +++ b/update.php @@ -584,10 +584,30 @@ function update_fix_d6_requirements() { * changes to the database should be made here. */ function update_prepare_d7_bootstrap() { - // Allow the database system to work even though the registry has not - // been created yet. - drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); + // Allow the bootstrap to proceed even if a Drupal 6 settings.php file is + // still being used. include_once DRUPAL_ROOT . '/includes/install.inc'; + drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + global $databases, $db_url, $update_rewrite_settings; + if (empty($databases) && !empty($db_url)) { + $databases = update_parse_db_url($db_url); + // Record the fact that the settings.php file will need to be rewritten. + $update_rewrite_settings = TRUE; + $settings_file = conf_path() . '/settings.php'; + $writable = drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_WRITABLE); + $requirements = array( + 'settings file' => array( + 'title' => 'Settings file', + 'value' => $writable ? 'The settings file is writable.' : 'The settings file is not writable.', + 'severity' => $writable ? REQUIREMENT_OK : REQUIREMENT_ERROR, + 'description' => $writable ? '' : 'Drupal requires write permissions to <em>' . $settings_file . '</em> during the update process. If you are unsure how to grant file permissions, please consult the <a href="http://drupal.org/server-permissions">online handbook</a>.', + ), + ); + update_extra_requirements($requirements); + } + // Allow the database system to work even if the registry has not been + // created yet. + drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); drupal_install_init_database(); spl_autoload_unregister('drupal_autoload_class'); spl_autoload_unregister('drupal_autoload_interface'); @@ -605,12 +625,57 @@ function update_prepare_d7_bootstrap() { } } +/** + * Perform Drupal 6.x to 7.x updates that are required for update.php + * to function properly. + * + * This function runs when update.php is run the first time for 7.x, + * even before updates are selected or performed. It is important + * that if updates are not ultimately performed that no changes are + * made which make it impossible to continue using the prior version. + */ +function update_fix_d7_requirements() { + // Rewrite the settings.php file if necessary. + // @see update_prepare_d7_bootstrap(). + global $update_rewrite_settings, $db_url; + if (!empty($update_rewrite_settings)) { + $databases = update_parse_db_url($db_url); + file_put_contents(conf_path() . '/settings.php', "\n" . '$databases = ' . var_export($databases, TRUE) . ';', FILE_APPEND); + } +} + +/** + * Parse database connection URLs (in the old, pre-Drupal 7 format) and + * return them as an array of database connection information. + */ +function update_parse_db_url($db_url) { + $databases = array(); + if (!is_array($db_url)) { + $db_url = array('default' => $db_url); + } + foreach ($db_url as $database => $url) { + $url = parse_url($url); + $databases[$database]['default'] = array( + // MySQLi uses the mysql driver. + 'driver' => $url['scheme'] == 'mysqli' ? 'mysql' : $url['scheme'], + // Remove the leading slash to get the database name. + 'database' => substr(urldecode($url['path']), 1), + 'username' => urldecode($url['user']), + 'password' => isset($url['pass']) ? urldecode($url['pass']) : '', + 'host' => urldecode($url['host']), + 'port' => isset($url['port']) ? urldecode($url['port']) : '', + ); + } + return $databases; +} + /** * Add the update task list to the current page. */ function update_task_list($active = NULL) { // Default list of tasks. $tasks = array( + 'requirements' => 'Verify requirements', 'info' => 'Overview', 'select' => 'Review updates', 'run' => 'Run updates', @@ -621,58 +686,34 @@ function update_task_list($active = NULL) { } /** - * Check update requirements and report any errors. + * Returns (and optionally stores) extra requirements that only apply during + * particular parts of the update.php process. */ -function update_check_requirements() { - global $db_url, $databases; - $requirements = array(); - - // If we will rewrite the settings.php then we need to make sure it is - // writeable. - if (empty($databases) && !empty($db_url) && is_string($db_url)) { - $requirements = install_check_requirements('', FALSE); - } - $warnings = FALSE; - - // Check the system module requirements only. - $requirements += module_invoke('system', 'requirements', 'update'); - $severity = drupal_requirements_severity($requirements); - - // If there are issues, report them. - if ($severity != REQUIREMENT_OK) { - foreach ($requirements as $requirement) { - if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) { - $message = isset($requirement['description']) ? $requirement['description'] : ''; - if (isset($requirement['value']) && $requirement['value']) { - $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')'; - } - $warnings = TRUE; - drupal_set_message($message, 'warning'); - } - } +function update_extra_requirements($requirements = NULL) { + static $extra_requirements = array(); + if (isset($requirements)) { + $extra_requirements += $requirements; } - return $warnings; + return $extra_requirements; } /** - * Converts Drupal 6 $db_url to Drupal 7 $databases array. + * Check update requirements and report any errors. */ -function update_check_d7_settings() { - global $db_url, $databases; - - if (empty($databases) && !empty($db_url) && is_string($db_url)) { - $url = parse_url($db_url); - $driver = substr($db_url, 0, strpos($db_url, '://')); - if ($driver == 'mysqli') { - $driver = 'mysql'; - } - $databases['default']['default']['driver'] = $driver; - $databases['default']['default']['database'] = substr($url['path'], 1); - foreach (array('user' => 'username', 'pass' => 'password', 'host' => 'host', 'port' => 'port') as $old_key => $new_key) { - $databases['default']['default'][$new_key] = isset($url[$old_key]) ? urldecode($url[$old_key]) : ''; - } - $conf_path = conf_path(); - file_put_contents($conf_path .'/settings.php', "\n" . '$databases = '. var_export($databases, TRUE) . ';', FILE_APPEND); +function update_check_requirements() { + // Check the system module and update.php requirements only. + $requirements = module_invoke('system', 'requirements', 'update'); + $requirements += update_extra_requirements(); + $severity = drupal_requirements_severity($requirements); + + // If there are issues, report them. + if ($severity == REQUIREMENT_ERROR) { + update_task_list('requirements'); + drupal_set_title('Requirements problem'); + $status_report = theme('status_report', $requirements); + $status_report .= 'Please check the error messages and <a href="' . request_uri() . '">try again</a>.'; + print theme('update_page', $status_report); + exit(); } } @@ -680,15 +721,20 @@ function update_check_d7_settings() { // Our custom error handler is not yet installed, so we just suppress them. ini_set('display_errors', FALSE); +// We prepare a minimal bootstrap for the update requirements check to avoid +// reaching the PHP memory limit. require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; +update_prepare_d7_bootstrap(); -// We only load DRUPAL_BOOTSTRAP_CONFIGURATION for the update requirements -// check to avoid reaching the PHP memory limit. -$op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; -if (empty($op)) { - // Minimum load of components. - drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); +// Determine if the current user has access to run update.php. +drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION); +$update_access_allowed = !empty($update_free_access) || $user->uid == 1; +// Only allow the requirements check to proceed if the current user has access +// to run updates (since it may expose sensitive information about the site's +// configuration). +$op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; +if (empty($op) && $update_access_allowed) { require_once DRUPAL_ROOT . '/includes/install.inc'; require_once DRUPAL_ROOT . '/includes/file.inc'; require_once DRUPAL_ROOT . '/modules/system/system.install'; @@ -708,22 +754,12 @@ function update_check_d7_settings() { drupal_maintenance_theme(); // Check the update requirements for Drupal. - $warnings = update_check_requirements(); - - // Display the warning messages (if any) in a dedicated maintenance page, - // or redirect to the update information page if no message. - if ($warnings) { - drupal_maintenance_theme(); - print theme('update_page', '<form method="post" action="update.php?op=info"><input type="submit" value="Continue" /></form>', FALSE); - exit; - } - // Write D7 settings file. - update_check_d7_settings(); + update_check_requirements(); + // Redirect to the update information page if all requirements were met. install_goto('update.php?op=info'); } -update_prepare_d7_bootstrap(); drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); drupal_maintenance_theme(); @@ -731,14 +767,15 @@ function update_check_d7_settings() { // not passed through the error handler) will cause a message to be printed. ini_set('display_errors', TRUE); -// Access check: -if (!empty($update_free_access) || $user->uid == 1) { +// Only proceed with updates if the user is allowed to run them. +if ($update_access_allowed) { include_once DRUPAL_ROOT . '/includes/install.inc'; include_once DRUPAL_ROOT . '/includes/batch.inc'; drupal_load_updates(); update_fix_d6_requirements(); + update_fix_d7_requirements(); update_fix_compatibility(); $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; -- GitLab