00001 <?php
00002
00003
00022 define('MAINTENANCE_MODE', 'update');
00023
00051 function db_add_column(&$ret, $table, $column, $type, $attributes = array()) {
00052 if (array_key_exists('not null', $attributes) and $attributes['not null']) {
00053 $not_null = 'NOT NULL';
00054 }
00055 if (array_key_exists('default', $attributes)) {
00056 if (is_null($attributes['default'])) {
00057 $default_val = 'NULL';
00058 $default = 'default NULL';
00059 }
00060 elseif ($attributes['default'] === FALSE) {
00061 $default = '';
00062 }
00063 else {
00064 $default_val = "$attributes[default]";
00065 $default = "default $attributes[default]";
00066 }
00067 }
00068
00069 $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column $type");
00070 if (!empty($default)) {
00071 $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET $default");
00072 }
00073 if (!empty($not_null)) {
00074 if (!empty($default)) {
00075 $ret[] = update_sql("UPDATE {" . $table . "} SET $column = $default_val");
00076 }
00077 $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET NOT NULL");
00078 }
00079 }
00080
00107 function db_change_column(&$ret, $table, $column, $column_new, $type, $attributes = array()) {
00108 if (array_key_exists('not null', $attributes) and $attributes['not null']) {
00109 $not_null = 'NOT NULL';
00110 }
00111 if (array_key_exists('default', $attributes)) {
00112 if (is_null($attributes['default'])) {
00113 $default_val = 'NULL';
00114 $default = 'default NULL';
00115 }
00116 elseif ($attributes['default'] === FALSE) {
00117 $default = '';
00118 }
00119 else {
00120 $default_val = "$attributes[default]";
00121 $default = "default $attributes[default]";
00122 }
00123 }
00124
00125 $ret[] = update_sql("ALTER TABLE {" . $table . "} RENAME $column TO " . $column . "_old");
00126 $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column_new $type");
00127 $ret[] = update_sql("UPDATE {" . $table . "} SET $column_new = " . $column . "_old");
00128 if ($default) {
00129 $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET $default");
00130 }
00131 if ($not_null) {
00132 $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET NOT NULL");
00133 }
00134 $ret[] = update_sql("ALTER TABLE {" . $table . "} DROP " . $column . "_old");
00135 }
00136
00155 function update_do_one($module, $number, &$context) {
00156
00157
00158 if (!empty($context['results'][$module]['#abort'])) {
00159 return;
00160 }
00161
00162 $function = $module . '_update_' . $number;
00163 if (function_exists($function)) {
00164 $ret = $function($context['sandbox']);
00165 }
00166
00167 if (isset($ret['#finished'])) {
00168 $context['finished'] = $ret['#finished'];
00169 unset($ret['#finished']);
00170 }
00171
00172 if (!isset($context['results'][$module])) {
00173 $context['results'][$module] = array();
00174 }
00175 if (!isset($context['results'][$module][$number])) {
00176 $context['results'][$module][$number] = array();
00177 }
00178 $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
00179
00180 if (!empty($ret['#abort'])) {
00181 $context['results'][$module]['#abort'] = TRUE;
00182 }
00183
00184 if ($context['finished'] == 1 && empty($context['results'][$module]['#abort'])) {
00185 drupal_set_installed_schema_version($module, $number);
00186 }
00187
00188 $context['message'] = 'Updating ' . check_plain($module) . ' module';
00189 }
00190
00191 function update_selection_page() {
00192 $output = '<p>The version of Drupal you are updating from has been automatically detected. You can select a different version, but you should not need to.</p>';
00193 $output .= '<p>Click Update to start the update process.</p>';
00194
00195 drupal_set_title('Drupal database update');
00196 $output .= drupal_get_form('update_script_selection_form');
00197
00198 update_task_list('select');
00199
00200 return $output;
00201 }
00202
00203 function update_script_selection_form() {
00204 $form = array();
00205 $form['start'] = array(
00206 '#tree' => TRUE,
00207 '#type' => 'fieldset',
00208 '#title' => 'Select versions',
00209 '#collapsible' => TRUE,
00210 '#collapsed' => TRUE,
00211 );
00212
00213
00214 $form['start']['system'] = array();
00215
00216 $modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE);
00217 foreach ($modules as $module => $schema_version) {
00218 $updates = drupal_get_schema_versions($module);
00219
00220 if (!update_check_incompatibility($module) && $updates !== FALSE && $schema_version >= 0) {
00221
00222
00223 $last_removed = module_invoke($module, 'update_last_removed');
00224 if ($schema_version < $last_removed) {
00225 $form['start'][$module] = array(
00226 '#value' => '<em>' . $module . '</em> module can not be updated. Its schema version is ' . $schema_version . '. Updates up to and including ' . $last_removed . ' have been removed in this release. In order to update <em>' . $module . '</em> module, you will first <a href="http://drupal.org/upgrade">need to upgrade</a> to the last version in which these updates were available.',
00227 '#prefix' => '<div class="warning">',
00228 '#suffix' => '</div>',
00229 );
00230 $form['start']['#collapsed'] = FALSE;
00231 continue;
00232 }
00233 $updates = drupal_map_assoc($updates);
00234 $updates[] = 'No updates available';
00235 $default = $schema_version;
00236 foreach (array_keys($updates) as $update) {
00237 if ($update > $schema_version) {
00238 $default = $update;
00239 break;
00240 }
00241 }
00242 $form['start'][$module] = array(
00243 '#type' => 'select',
00244 '#title' => $module . ' module',
00245 '#default_value' => $default,
00246 '#options' => $updates,
00247 );
00248 }
00249 }
00250
00251 $form['has_js'] = array(
00252 '#type' => 'hidden',
00253 '#default_value' => FALSE,
00254 '#attributes' => array('id' => 'edit-has_js'),
00255 );
00256 $form['submit'] = array(
00257 '#type' => 'submit',
00258 '#value' => 'Update',
00259 );
00260 return $form;
00261 }
00262
00263 function update_batch() {
00264 global $base_url;
00265
00266 $operations = array();
00267
00268 foreach ($_POST['start'] as $module => $version) {
00269 drupal_set_installed_schema_version($module, $version - 1);
00270 $updates = drupal_get_schema_versions($module);
00271 $max_version = max($updates);
00272 if ($version <= $max_version) {
00273 foreach ($updates as $update) {
00274 if ($update >= $version) {
00275 $operations[] = array('update_do_one', array($module, $update));
00276 }
00277 }
00278 }
00279 }
00280 $batch = array(
00281 'operations' => $operations,
00282 'title' => 'Updating',
00283 'init_message' => 'Starting updates',
00284 'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.',
00285 'finished' => 'update_finished',
00286 );
00287 batch_set($batch);
00288 batch_process($base_url . '/update.php?op=results', $base_url . '/update.php');
00289 }
00290
00291 function update_finished($success, $results, $operations) {
00292
00293 drupal_flush_all_caches();
00294
00295 $_SESSION['update_results'] = $results;
00296 $_SESSION['update_success'] = $success;
00297 $_SESSION['updates_remaining'] = $operations;
00298 }
00299
00300 function update_results_page() {
00301 drupal_set_title('Drupal database update');
00302
00303 $links[] = '<a href="' . base_path() . '">Main page</a>';
00304 $links[] = '<a href="' . base_path() . '?q=admin">Administration pages</a>';
00305
00306 update_task_list();
00307
00308 if (module_exists('dblog')) {
00309 $log_message = ' All errors have been <a href="' . base_path() . '?q=admin/reports/dblog">logged</a>.';
00310 }
00311 else {
00312 $log_message = ' All errors have been logged.';
00313 }
00314
00315 if ($_SESSION['update_success']) {
00316 $output = '<p>Updates were attempted. If you see no failures below, you may proceed happily to the <a href="' . base_path() . '?q=admin">administration pages</a>. Otherwise, you may need to update your database manually.' . $log_message . '</p>';
00317 }
00318 else {
00319 list($module, $version) = array_pop(reset($_SESSION['updates_remaining']));
00320 $output = '<p class="error">The update process was aborted prematurely while running <strong>update #' . $version . ' in ' . $module . '.module</strong>.' . $log_message;
00321 if (module_exists('dblog')) {
00322 $output .= ' You may need to check the <code>watchdog</code> database table manually.';
00323 }
00324 $output .= '</p>';
00325 }
00326
00327 if (!empty($GLOBALS['update_free_access'])) {
00328 $output .= "<p><strong>Reminder: don't forget to set the <code>\$update_free_access</code> value in your <code>settings.php</code> file back to <code>FALSE</code>.</strong></p>";
00329 }
00330
00331 $output .= theme('item_list', $links);
00332
00333
00334 if (!empty($_SESSION['update_results'])) {
00335 $output .= '<div id="update-results">';
00336 $output .= '<h2>The following queries were executed</h2>';
00337 foreach ($_SESSION['update_results'] as $module => $updates) {
00338 $output .= '<h3>' . $module . ' module</h3>';
00339 foreach ($updates as $number => $queries) {
00340 if ($number != '#abort') {
00341 $output .= '<h4>Update #' . $number . '</h4>';
00342 $output .= '<ul>';
00343 foreach ($queries as $query) {
00344 if ($query['success']) {
00345 $output .= '<li class="success">' . $query['query'] . '</li>';
00346 }
00347 else {
00348 $output .= '<li class="failure"><strong>Failed:</strong> ' . $query['query'] . '</li>';
00349 }
00350 }
00351 if (!count($queries)) {
00352 $output .= '<li class="none">No queries</li>';
00353 }
00354 }
00355 $output .= '</ul>';
00356 }
00357 }
00358 $output .= '</div>';
00359 }
00360 unset($_SESSION['update_results']);
00361 unset($_SESSION['update_success']);
00362
00363 return $output;
00364 }
00365
00366 function update_info_page() {
00367
00368 _drupal_flush_css_js();
00369
00370 if (db_table_exists('cache_update')) {
00371 cache_clear_all('*', 'cache_update', TRUE);
00372 }
00373
00374 update_task_list('info');
00375 drupal_set_title('Drupal database update');
00376 $output = '<p>Use this utility to update your database whenever a new release of Drupal or a module is installed.</p><p>For more detailed information, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>';
00377 $output .= "<ol>\n";
00378 $output .= "<li><strong>Back up your database</strong>. This process will change your database values and in case of emergency you may need to revert to a backup.</li>\n";
00379 $output .= "<li><strong>Back up your code</strong>. Hint: when backing up module code, do not leave that backup in the 'modules' or 'sites/*/modules' directories as this may confuse Drupal's auto-discovery mechanism.</li>\n";
00380 $output .= '<li>Put your site into <a href="' . base_path() . '?q=admin/settings/site-maintenance">maintenance mode</a>.</li>' . "\n";
00381 $output .= "<li>Install your new files in the appropriate location, as described in the handbook.</li>\n";
00382 $output .= "</ol>\n";
00383 $output .= "<p>When you have performed the steps above, you may proceed.</p>\n";
00384 $output .= '<form method="post" action="update.php?op=selection"><input type="submit" value="Continue" /></form>';
00385 $output .= "\n";
00386 return $output;
00387 }
00388
00389 function update_access_denied_page() {
00390 drupal_set_title('Access denied');
00391 return '<p>Access denied. You are not authorized to access this page. Please log in as the admin user (the first user you created). If you cannot log in, you will have to edit <code>settings.php</code> to bypass this access check. To do this:</p>
00392 <ol>
00393 <li>With a text editor find the settings.php file on your system. From the main Drupal directory that you installed all the files into, go to <code>sites/your_site_name</code> if such directory exists, or else to <code>sites/default</code> which applies otherwise.</li>
00394 <li>There is a line inside your settings.php file that says <code>$update_free_access = FALSE;</code>. Change it to <code>$update_free_access = TRUE;</code>.</li>
00395 <li>As soon as the update.php script is done, you must change the settings.php file back to its original form with <code>$update_free_access = FALSE;</code>.</li>
00396 <li>To avoid having this problem in future, remember to log in to your website as the admin user (the user you first created) before you backup your database at the beginning of the update process.</li>
00397 </ol>';
00398 }
00399
00405 function update_create_batch_table() {
00406
00407
00408 if (db_table_exists('batch')) {
00409 return;
00410 }
00411
00412 $schema['batch'] = array(
00413 'fields' => array(
00414 'bid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
00415 'token' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE),
00416 'timestamp' => array('type' => 'int', 'not null' => TRUE),
00417 'batch' => array('type' => 'text', 'not null' => FALSE, 'size' => 'big')
00418 ),
00419 'primary key' => array('bid'),
00420 'indexes' => array('token' => array('token')),
00421 );
00422
00423 $ret = array();
00424 db_create_table($ret, 'batch', $schema['batch']);
00425 return $ret;
00426 }
00427
00432 function update_fix_compatibility() {
00433 $ret = array();
00434 $incompatible = array();
00435 $query = db_query("SELECT name, type, status FROM {system} WHERE status = 1 AND type IN ('module','theme')");
00436 while ($result = db_fetch_object($query)) {
00437 if (update_check_incompatibility($result->name, $result->type)) {
00438 $incompatible[] = $result->name;
00439 }
00440 }
00441 if (!empty($incompatible)) {
00442 $ret[] = update_sql("UPDATE {system} SET status = 0 WHERE name IN ('" . implode("','", $incompatible) . "')");
00443 }
00444 return $ret;
00445 }
00446
00450 function update_check_incompatibility($name, $type = 'module') {
00451 static $themes, $modules;
00452
00453
00454 if (empty($themes) || empty($modules)) {
00455 $themes = system_theme_data();
00456 $modules = module_rebuild_cache();
00457 }
00458
00459 if ($type == 'module' && isset($modules[$name])) {
00460 $file = $modules[$name];
00461 }
00462 else if ($type == 'theme' && isset($themes[$name])) {
00463 $file = $themes[$name];
00464 }
00465 if (!isset($file)
00466 || !isset($file->info['core'])
00467 || $file->info['core'] != DRUPAL_CORE_COMPATIBILITY
00468 || version_compare(phpversion(), $file->info['php']) < 0) {
00469 return TRUE;
00470 }
00471 return FALSE;
00472 }
00473
00489 function update_fix_d6_requirements() {
00490 $ret = array();
00491
00492 if (drupal_get_installed_schema_version('system') < 6000 && !variable_get('update_d6_requirements', FALSE)) {
00493 $spec = array('type' => 'int', 'size' => 'small', 'default' => 0, 'not null' => TRUE);
00494 db_add_field($ret, 'cache', 'serialized', $spec);
00495 db_add_field($ret, 'cache_filter', 'serialized', $spec);
00496 db_add_field($ret, 'cache_page', 'serialized', $spec);
00497 db_add_field($ret, 'cache_menu', 'serialized', $spec);
00498
00499 db_add_field($ret, 'system', 'info', array('type' => 'text'));
00500 db_add_field($ret, 'system', 'owner', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
00501 if (db_table_exists('locales_target')) {
00502 db_add_field($ret, 'locales_target', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''));
00503 }
00504 if (db_table_exists('locales_source')) {
00505 db_add_field($ret, 'locales_source', 'textgroup', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => 'default'));
00506 db_add_field($ret, 'locales_source', 'version', array('type' => 'varchar', 'length' => 20, 'not null' => TRUE, 'default' => 'none'));
00507 }
00508 variable_set('update_d6_requirements', TRUE);
00509
00510
00511 $schema['cache_block'] = array(
00512 'fields' => array(
00513 'cid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
00514 'data' => array('type' => 'blob', 'not null' => FALSE, 'size' => 'big'),
00515 'expire' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
00516 'created' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
00517 'headers' => array('type' => 'text', 'not null' => FALSE),
00518 'serialized' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0)
00519 ),
00520 'indexes' => array('expire' => array('expire')),
00521 'primary key' => array('cid'),
00522 );
00523 db_create_table($ret, 'cache_block', $schema['cache_block']);
00524 }
00525
00526 return $ret;
00527 }
00528
00532 function update_task_list($active = NULL) {
00533
00534 $tasks = array(
00535 'info' => 'Overview',
00536 'select' => 'Select updates',
00537 'run' => 'Run updates',
00538 'finished' => 'Review log',
00539 );
00540
00541 drupal_set_content('left', theme('task_list', $tasks, $active));
00542 }
00543
00547 function update_check_requirements() {
00548
00549 $requirements = module_invoke('system', 'requirements', 'update');
00550 $severity = drupal_requirements_severity($requirements);
00551
00552
00553 if ($severity != REQUIREMENT_OK) {
00554 foreach ($requirements as $requirement) {
00555 if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) {
00556 $message = isset($requirement['description']) ? $requirement['description'] : '';
00557 if (isset($requirement['value']) && $requirement['value']) {
00558 $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')';
00559 }
00560 drupal_set_message($message, 'warning');
00561 }
00562 }
00563 }
00564 }
00565
00566
00567
00568 ini_set('display_errors', FALSE);
00569
00570 require_once './includes/bootstrap.inc';
00571
00572
00573
00574 $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
00575 if (empty($op)) {
00576
00577 drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
00578
00579 require_once './includes/install.inc';
00580 require_once './includes/file.inc';
00581 require_once './modules/system/system.install';
00582
00583
00584 include_once './includes/module.inc';
00585 $module_list['system']['filename'] = 'modules/system/system.module';
00586 $module_list['filter']['filename'] = 'modules/filter/filter.module';
00587 module_list(TRUE, FALSE, FALSE, $module_list);
00588 drupal_load('module', 'system');
00589 drupal_load('module', 'filter');
00590
00591
00592 drupal_init_language();
00593
00594
00595 drupal_maintenance_theme();
00596
00597
00598 update_check_requirements();
00599
00600
00601
00602 $messages = drupal_set_message();
00603 if (!empty($messages['warning'])) {
00604 drupal_maintenance_theme();
00605 print theme('update_page', '<form method="post" action="update.php?op=info"><input type="submit" value="Continue" /></form>', FALSE);
00606 exit;
00607 }
00608 install_goto('update.php?op=info');
00609 }
00610
00611 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
00612 drupal_maintenance_theme();
00613
00614
00615
00616 update_create_batch_table();
00617
00618
00619
00620 ini_set('display_errors', TRUE);
00621
00622
00623 if (!empty($update_free_access) || $user->uid == 1) {
00624
00625 include_once './includes/install.inc';
00626 include_once './includes/batch.inc';
00627 drupal_load_updates();
00628
00629 update_fix_d6_requirements();
00630 update_fix_compatibility();
00631
00632 $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
00633 switch ($op) {
00634
00635 case 'info':
00636 $output = update_info_page();
00637 break;
00638
00639 case 'selection':
00640 $output = update_selection_page();
00641 break;
00642
00643 case 'Update':
00644 update_batch();
00645 break;
00646
00647 case 'results':
00648 $output = update_results_page();
00649 break;
00650
00651
00652 default:
00653 update_task_list('run');
00654 $output = _batch_page();
00655 break;
00656 }
00657 }
00658 else {
00659 $output = update_access_denied_page();
00660 }
00661 if (isset($output) && $output) {
00662
00663 $progress_page = ($batch = batch_get()) && isset($batch['running']);
00664 print theme('update_page', $output, !$progress_page);
00665 }