Commit a33b4338 authored by Gábor Hojtsy's avatar Gábor Hojtsy

#176003 by yched, KarenS, dvessel: put module installs into a batch, solving the following issues:

 - possible timeouts with installing/enabling lots of modules at once in core
 - enable install profiles to have more modules without fear of timeouts on install
 - bootstrap Drupal before each module load, so previously enabled modules are bootstrapped
 - let modules run their hook_requirements() (although actually calling them will be possibly fixed in another patch)
parent 5622bce2
......@@ -294,19 +294,41 @@ function drupal_verify_profile($profile, $locale) {
}
/**
* Install a profile (i.e. a set of modules) from scratch.
* The profile must be verified first using drupal_verify_profile().
* Calls the install function and updates the system table for a given list of
* modules.
*
* @param profile
* The name of the profile to install.
* @param module_list
* An array of modules to install.
* The modules to install.
*/
function drupal_install_modules($module_list = array()) {
array_filter($module_list, '_drupal_install_module');
module_enable($module_list);
}
/**
* Callback to install an individual profile module.
*
* Used during installation to install modules one at a time and then
* enable them, or to install a number of modules at one time
* from admin/build/modules.
*/
function drupal_install_profile($profile, $module_list) {
// The system module is a special case; we can't bootstrap until it's
// installed, so we can't use the normal installation function.
$module_list = array_diff($module_list, array('system'));
function _drupal_install_module($module) {
if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
module_load_install($module);
module_invoke($module, 'install');
$versions = drupal_get_schema_versions($module);
drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
return TRUE;
}
}
/**
* Callback to install the system module.
*
* Separated from the installation of other modules so core system
* functions can be made available while other modules are installed.
*/
function drupal_install_system() {
$system_path = dirname(drupal_get_filename('module', 'system', NULL));
require_once './'. $system_path .'/system.install';
module_invoke('system', 'install');
......@@ -315,34 +337,9 @@ function drupal_install_profile($profile, $module_list) {
db_query("INSERT INTO {system} (filename, name, type, owner, status, throttle, bootstrap, schema_version) VALUES('%s', '%s', '%s', '%s', %d, %d, %d, %d)", $system_path .'/system.module', 'system', 'module', '', 1, 0, 0, $system_version);
// Now that we've installed things properly, bootstrap the full Drupal environment
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Install schemas for profile and all its modules.
module_rebuild_cache();
drupal_install_modules($module_list);
}
/**
* Calls the install function and updates the system table for a given list of
* modules.
*
* @param module_list
* The modules to install.
*/
function drupal_install_modules($module_list = array()) {
$enable_modules = array();
foreach ($module_list as $module) {
if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
module_load_install($module);
module_invoke($module, 'install');
$versions = drupal_get_schema_versions($module);
drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
$enable_modules[] = $module;
}
}
module_enable($enable_modules);
}
/**
* Calls the uninstall function and updates the system table for a given module.
......
......@@ -261,7 +261,10 @@ function module_enable($module_list) {
foreach ($invoke_modules as $module) {
module_invoke($module, 'enable');
// Check if node_access table needs rebuilding.
if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
// We check for the existence of node_access_needs_rebuild() since
// at install time, module_enable() could be called while node.module
// is not enabled yet.
if (function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
node_access_needs_rebuild(TRUE);
}
}
......
......@@ -1632,7 +1632,7 @@ function template_preprocess(&$variables, $hook) {
$variables['is_admin'] = FALSE;
$variables['is_front'] = FALSE;
$variables['logged_in'] = FALSE;
if ($variables['db_is_active'] = db_is_active()) {
if ($variables['db_is_active'] = db_is_active() && !defined('MAINTENANCE_MODE')) {
// Check for administrators.
if (user_access('access administration pages')) {
$variables['is_admin'] = TRUE;
......
......@@ -127,7 +127,7 @@ function theme_install_page($content) {
// Special handling of status messages
if (isset($messages['status'])) {
$warnings = count($messages['status']) > 1 ? st('The following installation warnings should be carefully reviewed, but in most cases may be safely ignored') : st('The following installation warning should be carefully reviewed, but in most cases may be safely ignored');
$title = count($messages['status']) > 1 ? st('The following installation warnings should be carefully reviewed, but in most cases may be safely ignored') : st('The following installation warning should be carefully reviewed, but in most cases may be safely ignored');
$variables['messages'] .= '<h4>'. $title .':</h4>';
$variables['messages'] .= theme('status_messages', 'status');
}
......
......@@ -119,18 +119,12 @@ function install_main() {
install_change_settings($profile, $install_locale);
}
// Perform actual installation defined in the profile.
drupal_install_profile($profile, $modules);
// Warn about settings.php permissions risk
$settings_dir = './'. conf_path();
$settings_file = $settings_dir .'/settings.php';
if (!drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file($settings_dir, FILE_NOT_WRITABLE, 'dir')) {
drupal_set_message(st('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, please consult the <a href="@handbook_url">on-line handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/getting-started')), 'error');
}
else {
drupal_set_message(st('All necessary changes to %dir and %file have been made. They have been set to read-only for security.', array('%dir' => $settings_dir, '%file' => $settings_file)));
}
// Install system.module.
drupal_install_system();
// Save the list of other modules to install for the 'profile-install'
// task. variable_set() can be used now that system.module is installed
// and drupal is bootstrapped.
variable_set('install_profile_modules', array_diff($modules, array('system')));
}
// The database is set up, turn to further tasks.
......@@ -624,13 +618,43 @@ function install_tasks($profile, $task) {
// Build a page for final tasks.
if (empty($task)) {
variable_set('install_task', 'locale-initial-import');
$task = 'locale-initial-import';
variable_set('install_task', 'profile-install');
$task = 'profile-install';
}
// We are using a list of if constructs here to allow for
// passing from one task to the other in the same request.
// Install profile modules.
if ($task == 'profile-install') {
$modules = variable_get('install_profile_modules', array());
$files = module_rebuild_cache();
variable_del('install_profile_modules');
$operations = array();
foreach ($modules as $module) {
$operations[] = array('_install_module_batch', array($module, $files[$module]->info['name']));
}
$batch = array(
'operations' => $operations,
'finished' => '_install_profile_batch_finished',
'title' => t('Installing @drupal', array('@drupal' => drupal_install_profile_name())),
'error_message' => t('The installation has encountered an error.'),
);
// Start a batch, switch to 'profile-install-batch' task. We need to
// set the variable here, because batch_process() redirects.
variable_set('install_task', 'profile-install-batch');
batch_set($batch);
batch_process($url, $url);
}
// We are running a batch install of the profile's modules.
// This might run in multiple HTTP requests, constantly redirecting
// to the same address, until the batch finished callback is invoked
// and the task advances to 'locale-initial-import'.
if ($task == 'profile-install-batch') {
include_once 'includes/batch.inc';
$output = _batch_page();
}
// Import interface translations for the enabled modules.
if ($task == 'locale-initial-import') {
if (!empty($install_locale) && ($install_locale != 'en')) {
......@@ -652,11 +676,6 @@ function install_tasks($profile, $task) {
// Found nothing to import or not foreign language, go to next task.
$task = 'configure';
}
// We are running a batch import of interface translation files.
// This might run in multiple HTTP requests, constantly redirecting
// to the same address, until the batch finished callback is invoked
// and the task advances to 'configure'.
if ($task == 'locale-initial-batch') {
include_once 'includes/batch.inc';
include_once 'includes/locale.inc';
......@@ -677,6 +696,16 @@ function install_tasks($profile, $task) {
$output = $form;
drupal_set_title(st('Configure site'));
// Warn about settings.php permissions risk
$settings_dir = './'. conf_path();
$settings_file = $settings_dir .'/settings.php';
if (!drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file($settings_dir, FILE_NOT_WRITABLE, 'dir')) {
drupal_set_message(st('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, please consult the <a href="@handbook_url">on-line handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/getting-started')), 'error');
}
else {
drupal_set_message(st('All necessary changes to %dir and %file have been made. They have been set to read-only for security.', array('%dir' => $settings_dir, '%file' => $settings_file)));
}
// Add JavaScript validation.
_user_password_dynamic_validation();
drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module');
......@@ -748,8 +777,8 @@ function install_tasks($profile, $task) {
// Display a 'finished' page to user.
if ($task == 'finished') {
drupal_set_title(st('@drupal installation complete', array('@drupal' => drupal_install_profile_name())));
$output = '<p>'. st('Congratulations, @drupal has been successfully installed.', array('@drupal' => drupal_install_profile_name())) .'</p>';
$messages = drupal_set_message();
$output = '<p>'. st('Congratulations, @drupal has been successfully installed.', array('@drupal' => drupal_install_profile_name())) .'</p>';
$output .= '<p>'. (isset($messages['error']) ? st('Please review the messages above before continuing on to <a href="@url">your new site</a>.', array('@url' => url(''))) : st('You may now visit <a href="@url">your new site</a>.', array('@url' => url('')))) .'</p>';
$task = 'done';
}
......@@ -777,6 +806,29 @@ function install_tasks($profile, $task) {
}
}
/**
* Batch callback for batch installation of modules.
*/
function _install_module_batch($module, $module_name, &$context) {
_drupal_install_module($module);
// We enable the installed module right away, so that the module will be
// loaded by drupal_bootstrap in subsequent batch requests, and other
// modules possibly depending on it can safely perform their installation
// steps.
module_enable(array($module));
$context['results'][] = $module;
$context['message'] = 'Installed '. $module_name. ' module.';
}
/**
* Finished callback for the modules install batch.
*
* Advance installer task to language import.
*/
function _install_profile_batch_finished($success, $results) {
variable_set('install_task', 'locale-initial-import');
}
/**
* Finished callback for the first locale import batch.
*
......@@ -799,7 +851,7 @@ function _install_locale_remaining_batch_finished($success, $results) {
* The list of reserved tasks to run in the installer.
*/
function install_reserved_tasks() {
return array('configure', 'locale-initial-import', 'locale-initial-batch', 'profile-finished', 'locale-remaining-batch', 'finished', 'done');
return array('configure', 'profile-install', 'profile-install-batch', 'locale-initial-import', 'locale-initial-batch', 'profile-finished', 'locale-remaining-batch', 'finished', 'done');
}
/**
......@@ -855,21 +907,24 @@ function install_check_requirements($profile, $verify) {
function install_task_list($active = NULL) {
// Default list of tasks.
$tasks = array(
'profile-select' => st('Choose profile'),
'locale-select' => st('Choose language'),
'requirements' => st('Verify requirements'),
'database' => st('Set up database'),
'locale-initial-batch' => st('Set up translations'),
'configure' => st('Configure site'),
'profile-select' => st('Choose profile'),
'locale-select' => st('Choose language'),
'requirements' => st('Verify requirements'),
'database' => st('Set up database'),
'profile-install-batch' => st('Install profile'),
'locale-initial-batch' => st('Set up translations'),
'configure' => st('Configure site'),
);
$profiles = install_find_profiles();
$profile = isset($_GET['profile']) && isset($profiles[$_GET['profile']]) ? $_GET['profile'] : '.';
$locales = install_find_locales($profile);
// Remove select profile if we have only one.
// If we have only one profile, remove 'Choose profile'
// and rename 'Install profile'.
if (count($profiles) == 1) {
unset($tasks['profile-select']);
$tasks['profile-install-batch'] = st('Install site');
}
// Add tasks defined by the profile.
......
......@@ -42,9 +42,10 @@ function default_profile_task_list() {
* Perform any final installation tasks for this profile.
*
* The installer goes through the profile-select -> locale-select
* -> requirements -> database -> locale-initial-batch -> configure
* -> locale-remaining-batch -> finished -> done tasks in this order,
* if you don't implement this function in your profile.
* -> requirements -> database -> profile-install-batch
* -> locale-initial-batch -> configure -> locale-remaining-batch
* -> finished -> done tasks, in this order, if you don't implement
* this function in your profile.
*
* If this function is implemented, you can have any number of
* custom tasks to perform after 'configure', implementing a state
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment