diff --git a/core/includes/batch.inc b/core/includes/batch.inc index 5fc76a8..ff0b4cb 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -439,18 +439,17 @@ function _batch_finished() { $queue->deleteQueue(); } } + // Clean-up the session. Not needed for CLI updates. + if (isset($_SESSION)) { + unset($_SESSION['batches'][$batch['id']]); + if (empty($_SESSION['batches'])) { + unset($_SESSION['batches']); + } + } } $_batch = $batch; $batch = NULL; - // Clean-up the session. Not needed for CLI updates. - if (isset($_SESSION)) { - unset($_SESSION['batches'][$batch['id']]); - if (empty($_SESSION['batches'])) { - unset($_SESSION['batches']); - } - } - // Redirect if needed. if ($_batch['progressive']) { // Revert the 'destination' that was saved in batch_process(). diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index a15710f..8778f33 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1450,6 +1450,27 @@ function bootstrap_hooks() { */ function t($string, array $args = array(), array $options = array()) { static $custom_strings; + $locale_exists = &drupal_static(__FUNCTION__); + + // Check whether locale() is available and operational. + if (!isset($locale_exits)) { + // The availability of locale() requires more explanation: + // - Whether the module system has been loaded. + // In early bootstrap, in the installer, update.php, and also in early + // error/exception conditions, the module system might not be loaded yet. + // - Whether Locale module is enabled. + // The mere existence of locale() does not imply that Locale module is + // actually enabled and its database tables are installed. Since PHP code + // cannot be unloaded, this is typically the case in the environment that + // is executing a test. + // - Whether Locale module actually has been loaded and locale() exists. + // The Locale module may be enabled, but the actual .module might not be + // loaded. This is typically the case in update.php, which needs to + // populate the full module list to invoke hook_requirements(), but must + // not load any actual modules and APIs, since they are not guaranteed to + // be operational. + $locale_exists = function_exists('locale') && function_exists('module_exists') && module_exists('locale'); + } // Merge in default. if (empty($options['langcode'])) { @@ -1471,7 +1492,7 @@ function t($string, array $args = array(), array $options = array()) { $string = $custom_strings[$options['langcode']][$options['context']][$string]; } // Translate with locale module if enabled. - elseif ($options['langcode'] != LANGUAGE_SYSTEM && ($options['langcode'] != 'en' || variable_get('locale_translate_english', FALSE)) && function_exists('locale')) { + elseif ($options['langcode'] != LANGUAGE_SYSTEM && ($options['langcode'] != 'en' || variable_get('locale_translate_english', FALSE)) && $locale_exists) { $string = locale($string, $options['context'], $options['langcode']); } if (empty($args)) { @@ -2550,7 +2571,12 @@ function drupal_fast_404() { * Returns TRUE if a Drupal installation is currently being attempted. */ function drupal_installation_attempted() { - return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install'; + // This cannot rely on the MAINTENANCE_MODE constant, since that would prevent + // tests from using the non-interactive installer, in which case Drupal is + // only happens to be installed within the same request, but subsequently + // executed code does not involve the installer at all. + // @see install_drupal() + return isset($GLOBALS['install_state']) && empty($GLOBALS['install_state']['installation_finished']); } /** diff --git a/core/includes/cache.inc b/core/includes/cache.inc index 73bb05f..554fe95 100644 --- a/core/includes/cache.inc +++ b/core/includes/cache.inc @@ -24,13 +24,12 @@ * @see Drupal\Core\Cache\CacheBackendInterface */ function cache($bin = 'cache') { + $cache_objects = &drupal_static(__FUNCTION__, array()); + // Temporary backwards compatibiltiy layer, allow old style prefixed cache // bin names to be passed as arguments. $bin = str_replace('cache_', '', $bin); - // We do not use drupal_static() here because we do not want to change the - // storage of a cache bin mid-request. - static $cache_objects; if (!isset($cache_objects[$bin])) { $cache_backends = cache_get_backends(); $class = isset($cache_backends[$bin]) ? $cache_backends[$bin] : $cache_backends['cache']; diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 2278112..c624412 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -92,19 +92,27 @@ function install_drupal($settings = array()) { throw $e; } } + // After execution, all tasks might be complete, in which case + // $install_state['installation_finished'] is TRUE. In case the last task + // has been processed, remove the global $install_state, so other code can + // reliably check whether it is running during the installer. + // @see drupal_installation_attempted() + $state = $install_state; + unset($install_state); + // All available tasks for this page request are now complete. Interactive // installations can send output to the browser or redirect the user to the // next page. - if ($install_state['interactive']) { - if ($install_state['parameters_changed']) { + if ($state['interactive']) { + if ($state['parameters_changed']) { // Redirect to the correct page if the URL parameters have changed. - install_goto(install_redirect_url($install_state)); + install_goto(install_redirect_url($state)); } elseif (isset($output)) { // Display a page only if some output is available. Otherwise it is // possible that we are printing a JSON page and theme output should // not be shown. - install_display_output($output, $install_state); + install_display_output($output, $state); } } } @@ -222,7 +230,9 @@ function install_state_defaults() { */ function install_begin_request(&$install_state) { // Add any installation parameters passed in via the URL. - $install_state['parameters'] += $_GET; + if ($install_state['interactive']) { + $install_state['parameters'] += $_GET; + } // Validate certain core settings that are used throughout the installation. if (!empty($install_state['parameters']['profile'])) { @@ -238,11 +248,10 @@ function install_begin_request(&$install_state) { if (!$install_state['interactive']) { drupal_override_server_variables($install_state['server']); } - // The user agent header is used to pass a database prefix in the request when // running tests. However, for security reasons, it is imperative that no // installation be permitted using such a prefix. - if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) { + elseif (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) { header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); exit; } diff --git a/core/includes/module.inc b/core/includes/module.inc index d9eab10..db1947d 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -11,26 +11,30 @@ /** * Loads all the modules that have been enabled in the system table. * - * @param $bootstrap + * @param bool $bootstrap * Whether to load only the reduced set of modules loaded in "bootstrap mode" - * for cached pages. See bootstrap.inc. + * for cached pages. See bootstrap.inc. Pass NULL to only check the current + * status without loading of modules. + * @param bool $reset + * (optional) Internal use only. Whether to reset the internal statically + * cached flag of whether modules have been loaded. If TRUE, all modules are + * (re)loaded in the same call. Used by the testing framework to override and + * persist a limited module list for the duration of a unit test (in which no + * module system exists). * - * @return - * If $bootstrap is NULL, return a boolean indicating whether all modules - * have been loaded. + * @return bool + * A Boolean indicating whether all modules have been loaded. This means all + * modules; the load status of bootstrap modules cannot be checked. */ -function module_load_all($bootstrap = FALSE) { - // Already loaded code cannot be unloaded, but new modules may be added within - // a request, which should be loaded as well. - // Use the advanced drupal_static() pattern, since this is called very often. - // @see theme() - static $drupal_static_fast; - if (!isset($drupal_static_fast)) { - $drupal_static_fast['has_run'] = &drupal_static(__FUNCTION__, FALSE); +function module_load_all($bootstrap = FALSE, $reset = FALSE) { + static $has_run = FALSE; + + if ($reset) { + $has_run = FALSE; } - $has_run = &$drupal_static_fast['has_run']; - if (isset($bootstrap)) { + // Unless $boostrap is NULL, load the requested set of modules. + if (isset($bootstrap) && !$has_run) { $type = $bootstrap ? 'bootstrap' : 'module_enabled'; foreach (module_list($type) as $module) { drupal_load('module', $module); @@ -61,14 +65,29 @@ function module_load_all($bootstrap = FALSE) { * list will persist until the next call with a new $fixed_list passed in. * Primarily intended for internal use (e.g., in install.php and update.php). * Use module_list_reset() to undo the $fixed_list override. + * @param bool $reset + * (optional) Whether to reset/remove the $fixed_list. * * @return array * An associative array whose keys and values are the names of the modules in * the list. + * + * @see module_list_reset() */ -function module_list($type = 'module_enabled', array $fixed_list = NULL) { - // This static is only used for $fixed_list. - $module_list = &drupal_static(__FUNCTION__); +function module_list($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) { + // This static is only used for $fixed_list. It must not be a drupal_static(), + // since any call to drupal_static_reset() in unit tests would cause an + // attempt to retrieve the list of modules from the database (which does not + // exist). + static $module_list; + + if ($reset) { + $module_list = NULL; + // Do nothing if no $type and no $fixed_list have been passed. + if (!isset($type) && !isset($fixed_list)) { + return; + } + } // The list that will be be returned. Separate from $module_list in order // to not duplicate the static cache of system_list(). @@ -94,7 +113,7 @@ function module_list($type = 'module_enabled', array $fixed_list = NULL) { * Subsequent calls to module_list() will no longer use a fixed list. */ function module_list_reset() { - drupal_static_reset('module_list'); + module_list(NULL, NULL, TRUE); } /** diff --git a/core/includes/update.inc b/core/includes/update.inc index 5582bb4..6794ca7 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -578,6 +578,11 @@ function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $ * @see update_batch() */ function update_finished($success, $results, $operations) { + // Unlock the module system to flush all caches. + module_list_reset(); + // Load all modules, so data structures can be properly rebuilt. + module_load_all(FALSE, TRUE); + // Clear the caches in case the data has been updated. drupal_flush_all_caches(); diff --git a/core/modules/field/modules/field_sql_storage/field_sql_storage.install b/core/modules/field/modules/field_sql_storage/field_sql_storage.install index 2229ef4..0bbdc5a 100644 --- a/core/modules/field/modules/field_sql_storage/field_sql_storage.install +++ b/core/modules/field/modules/field_sql_storage/field_sql_storage.install @@ -111,6 +111,9 @@ function field_sql_storage_update_8000(&$sandbox) { // Retrieve field data. $fields = _update_7000_field_read_fields(array('storage_type' => 'field_sql_storage')); + // Load private table name helper functions in field_sql_storage.module. + drupal_load('module', 'field_sql_storage'); + // Update schema. foreach ($fields as $field) { $data_table = _field_sql_storage_tablename($field); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 9654f9c..048da36 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -663,6 +663,14 @@ protected function changeDatabasePrefix() { } Database::addConnectionInfo('default', 'default', $connection_info['default']); + // Additionally override global $databases, since the installer does not use + // the Database connection info. + // @see install_verify_database_settings() + // @see install_database_errors() + // @todo Fix installer to use Database connection info. + global $databases; + $databases['default']['default'] = $connection_info['default']; + // Indicate the database prefix was set up correctly. $this->setupDatabasePrefix = TRUE; } @@ -694,7 +702,10 @@ protected function prepareEnvironment() { // Save further contextual information. $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); $this->originalProfile = drupal_get_profile(); - $this->originalUser = $user; + $this->originalUser = clone $user; + + // Ensure that the current session is not changed by the new environment. + drupal_save_session(FALSE); // Save and clean the shutdown callbacks array because it is static cached // and will be changed by the test run. Otherwise it will contain callbacks @@ -777,13 +788,17 @@ protected function tearDown() { // Restore original database connection. Database::removeConnection('default'); Database::renameConnection('simpletest_original_default', 'default'); + // @see TestBase::changeDatabasePrefix() + global $databases; + $connection_info = Database::getConnectionInfo('default'); + $databases['default']['default'] = $connection_info['default']; // Reset all static variables. drupal_static_reset(); - // Restore has_run state. - $has_run = &drupal_static('module_load_all'); - $has_run = TRUE; + // Reset module list and module load status. + module_list_reset(); + module_load_all(FALSE, TRUE); // Restore original in-memory configuration. $conf = $this->originalConf; diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 210b058..1add46c 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -590,6 +590,13 @@ protected function setUp() { global $user, $conf; $language_interface = language(LANGUAGE_TYPE_INTERFACE); + // When running tests through the Simpletest UI (vs. on the command line), + // Simpletest's batch conflicts with the installer's batch. Batch API does + // not support the concept of nested batches (in which the nested is not + // progressive), so we need to temporarily pretend there was no batch. + // Backup the currently running Simpletest batch. + $this->originalBatch = batch_get(); + // Create the database prefix for this test. $this->prepareDatabasePrefix(); @@ -612,16 +619,75 @@ protected function setUp() { return FALSE; } - // Preset the 'install_profile' system variable, so the first call into - // system_rebuild_module_data() (in drupal_install_system()) will register - // the test's profile as a module. Without this, the installation profile of - // the parent site (executing the test) is registered, and the test - // profile's hook_install() and other hook implementations are never invoked. - $conf['install_profile'] = $this->profile; + // Set the 'simpletest_parent_profile' variable to add the parent profile's + // search path to the child site's search paths. + // @see drupal_system_listing() + $conf['simpletest_parent_profile'] = $this->originalProfile; + + // Set installer parameters. + // @see install.php, install.core.inc + $connection_info = Database::getConnectionInfo('default'); + $this->root_user = (object) array( + 'name' => 'admin', + 'mail' => 'admin@example.com', + 'pass_raw' => $this->randomName(), + ); + $settings = array( + 'interactive' => FALSE, + 'parameters' => array( + 'profile' => $this->profile, + 'langcode' => 'en', + ), + 'forms' => array( + 'install_settings_form' => array( + 'driver' => $connection_info['default']['driver'], + 'username' => $connection_info['default']['username'], + 'host' => $connection_info['default']['host'], + 'port' => $connection_info['default']['port'], + 'password' => $connection_info['default']['password'], + 'database' => $connection_info['default']['database'], + 'prefix' => $connection_info['default']['prefix'], + ), + 'install_configure_form' => array( + 'site_name' => 'Drupal', + 'site_mail' => 'simpletest@example.com', + 'account' => array( + 'name' => $this->root_user->name, + 'mail' => $this->root_user->mail, + 'pass' => array( + 'pass1' => $this->root_user->pass_raw, + 'pass2' => $this->root_user->pass_raw, + ), + ), + // form_type_checkboxes_value() requires NULL instead of FALSE values + // for programmatic form submissions to disable a checkbox. + 'update_status_module' => array( + 1 => NULL, + 2 => NULL, + ), + ), + ), + ); + + // Replace the global $user session with an anonymous user to resemble a + // regular installation. + $user = drupal_anonymous_user(); + + // Reset the static batch to remove Simpletest's batch operations. + $batch = &batch_get(); + $batch = array(); - // Perform the actual Drupal installation. - include_once DRUPAL_ROOT . '/core/includes/install.inc'; - drupal_install_system(); + // Execute the non-interactive installer. + require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; + install_drupal($settings); + + // Restore the original Simpletest batch. + $batch = &batch_get(); + $batch = $this->originalBatch; + + // Revert install_begin_request() cache and lock service overrides. + unset($conf['cache_classes']); + unset($conf['lock_backend']); // Set path variables. variable_set('file_public_path', $this->public_files_directory); @@ -631,16 +697,8 @@ protected function setUp() { // Set the 'simpletest_parent_profile' variable to add the parent profile's // search path to the child site's search paths. // @see drupal_system_listing() - // @todo This may need to be primed like 'install_profile' above. variable_set('simpletest_parent_profile', $this->originalProfile); - // Include the testing profile. - variable_set('install_profile', $this->profile); - $profile_details = install_profile_info($this->profile, 'en'); - - // Install the modules specified by the testing profile. - module_enable($profile_details['dependencies'], FALSE); - // Collect modules to install. $class = get_class($this); $modules = array(); @@ -655,14 +713,6 @@ protected function setUp() { $this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', $modules)))); } - // Run the profile tasks. - $install_profile_module_exists = db_query("SELECT 1 FROM {system} WHERE type = 'module' AND name = :name", array( - ':name' => $this->profile, - ))->fetchField(); - if ($install_profile_module_exists) { - module_enable(array($this->profile), FALSE); - } - // Create a new DrupalKernel for testing purposes, now that all required // modules have been enabled. This also stores a new dependency injection // container in drupal_container(). Drupal\simpletest\TestBase::tearDown() @@ -678,25 +728,6 @@ protected function setUp() { // Reset/rebuild all data structures after enabling the modules. $this->resetAll(); - // Run cron once in that environment, as install.php does at the end of - // the installation process. - drupal_cron_run(); - - // Ensure that the session is not written to the new environment and replace - // the global $user session with uid 1 from the new test site. - drupal_save_session(FALSE); - // Login as uid 1. - $user = user_load(1); - - // Restore necessary variables. - variable_set('install_task', 'done'); - config('system.site')->set('mail', 'simpletest@example.com')->save(); - variable_set('date_default_timezone', date_default_timezone_get()); - - // Set up English language. - unset($conf['language_default']); - $language_interface = language_default(); - // Use the test mail class instead of the default mail handler class. variable_set('mail_system', array('default-system' => 'Drupal\Core\Mail\VariableLog')); diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php index a0f6c59..86456e5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php @@ -191,13 +191,13 @@ protected function performUpgrade($register_errors = TRUE) { $update_url = $GLOBALS['base_url'] . '/core/update.php'; $this->drupalGet($update_url, array('external' => TRUE)); if (!$this->assertResponse(200)) { - return FALSE; + throw new Exception('Initial GET to update.php did not return HTTP 200 status.'); } // Continue. $this->drupalPost(NULL, array(), t('Continue')); if (!$this->assertResponse(200)) { - return FALSE; + throw new Exception('POST to continue update.php did not return HTTP 200 status.'); } // The test should pass if there are no pending updates. @@ -211,7 +211,7 @@ protected function performUpgrade($register_errors = TRUE) { // Go! $this->drupalPost(NULL, array(), t('Apply pending updates')); if (!$this->assertResponse(200)) { - return FALSE; + throw new Exception('POST to update.php to apply pending updates did not return HTTP 200 status.'); } // Check for errors during the update process. @@ -222,36 +222,30 @@ protected function performUpgrade($register_errors = TRUE) { $this->fail($message); } } - if (!empty($this->upgradeErrors)) { // Upgrade failed, the installation might be in an inconsistent state, // don't process. - return FALSE; + throw new Exception('Errors during update process.'); } // Check if there still are pending updates. $this->drupalGet($update_url, array('external' => TRUE)); $this->drupalPost(NULL, array(), t('Continue')); if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) { - return FALSE; + throw new Exception('update.php still shows pending updates after execution.'); } // Upgrade succeed, rebuild the environment so that we can call the API // of the child site directly from this request. $this->upgradedSite = TRUE; - // Reload module list. For modules that are enabled in the test database, - // but not on the test client, we need to load the code here. + // Reload module list for modules that are enabled in the test database + // but not on the test client. system_list_reset(); - foreach (module_list() as $module) { - drupal_load('module', $module); - } - - // Reload hook implementations module_implements_reset(); + module_load_all(FALSE, TRUE); // Rebuild caches. - drupal_static_reset(); drupal_flush_all_caches(); // Reload global $conf array and permissions. diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 67957da..a22b2d8 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -351,11 +351,11 @@ function user_install() { * The 'Member for' extra field has moved one level up in the array. */ function user_update_8000() { - $settings = field_bundle_settings('user', 'user'); + $settings = update_variable_get('field_bundle_settings_user__user', array()); if (isset($settings['extra_fields']['display']['summary'])) { $settings['extra_fields']['display']['member_for'] = $settings['extra_fields']['display']['summary']; unset($settings['extra_fields']['display']['summary']); - field_bundle_settings('user', 'user', $settings); + update_variable_set('field_bundle_settings_user__user', $settings); } } diff --git a/core/update.php b/core/update.php index b8a726d..35b42b4 100644 --- a/core/update.php +++ b/core/update.php @@ -129,6 +129,11 @@ function update_script_selection_form($form, &$form_state) { '#links' => update_helpful_links(), ); + // Unlock the module system to flush all caches. + module_list_reset(); + // Load all modules, so data structures can be properly rebuilt. + module_load_all(FALSE, TRUE); + // No updates to run, so caches won't get flushed later. Clear them now. drupal_flush_all_caches(); } @@ -366,10 +371,6 @@ function update_check_requirements($skip_warnings = FALSE) { } } -// Some unavoidable errors happen because the database is not yet up-to-date. -// 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 . '/core/includes/bootstrap.inc'; @@ -378,6 +379,23 @@ function update_check_requirements($skip_warnings = FALSE) { require_once DRUPAL_ROOT . '/core/includes/file.inc'; require_once DRUPAL_ROOT . '/core/includes/unicode.inc'; require_once DRUPAL_ROOT . '/core/includes/schema.inc'; + +// The module list must be limited to System module for almost all parts of the +// update process to prevent module APIs and hooks from being invoked too early +// (trying to act on data structures that are not updated yet). +// This especially applies to early bootstrap preparations when upgrading to a +// new major version of Drupal core. Unfortunately, however, the initial +// information and update selection pages (and forms) need to be able to invoke +// hook_requirements() in all modules and also access other APIs. Therefore, the +// effective module list has to be switched back and forth multiple times during +// the update.php process. +include_once DRUPAL_ROOT . '/core/includes/module.inc'; +$module_list['system']['filename'] = 'core/modules/system/system.module'; + +// Lock the module list for the update bootstrap and performing initial system +// changes. +module_list(NULL, $module_list); + update_prepare_d8_bootstrap(); // Determine if the current user has access to run update.php. @@ -404,9 +422,6 @@ function update_check_requirements($skip_warnings = FALSE) { require_once DRUPAL_ROOT . '/core/modules/system/system.install'; // Load module basics. - include_once DRUPAL_ROOT . '/core/includes/module.inc'; - $module_list['system']['filename'] = 'core/modules/system/system.module'; - module_list(NULL, $module_list); drupal_load('module', 'system'); // Reset the module_implements() cache so that any new hook implementations @@ -419,6 +434,9 @@ function update_check_requirements($skip_warnings = FALSE) { // Set up theme system for the maintenance page. drupal_maintenance_theme(); + // Unlock the module system to perform update requirements checks. + module_list_reset(); + // Check the update requirements for Drupal. Only report on errors at this // stage, since the real requirements check happens further down. update_check_requirements(TRUE); @@ -448,6 +466,9 @@ function update_check_requirements($skip_warnings = FALSE) { update_fix_compatibility(); + // Unlock the module system to perform update requirements checks. + module_list_reset(); + // Check the update requirements for all modules. If there are warnings, but // no errors, skip reporting them if the user has provided a URL parameter // acknowledging the warnings and indicating a desire to continue anyway. See @@ -456,6 +477,9 @@ function update_check_requirements($skip_warnings = FALSE) { $skip_warnings = !empty($continue); update_check_requirements($skip_warnings); + // Re-lock the module system. + module_list(NULL, $module_list); + switch ($op) { // update.php ops. @@ -483,6 +507,11 @@ function update_check_requirements($skip_warnings = FALSE) { break; case 'results': + // Unlock the module system after performing updates. + module_list_reset(); + // Load all modules to flush all caches and rebuild data structures. + module_load_all(FALSE, TRUE); + $output = update_results_page(); break;