update.php 26.2 KB
Newer Older
1
<?php
Dries's avatar
   
Dries committed
2
// $Id$
Dries's avatar
   
Dries committed
3

4
5
6
7
8
/**
 * Root directory of Drupal installation.
 */
define('DRUPAL_ROOT', dirname(realpath(__FILE__)));

Dries's avatar
   
Dries committed
9
10
11
12
/**
 * @file
 * Administrative page for handling updates from one Drupal version to another.
 *
13
 * Point your browser to "http://www.example.com/update.php" and follow the
Dries's avatar
   
Dries committed
14
15
16
 * instructions.
 *
 * If you are not logged in as administrator, you will need to modify the access
17
18
 * check statement inside your settings.php file. After finishing the upgrade,
 * be sure to open settings.php again, and change it back to its original state!
Dries's avatar
   
Dries committed
19
 */
Dries's avatar
   
Dries committed
20

21
22
23
24
25
26
/**
 * Global flag to identify update.php run, and so avoid various unwanted
 * operations, such as hook_init() and hook_exit() invokes, css/js preprocessing
 * and translation, and solve some theming issues. This flag is checked on several
 * places in Drupal code (not just update.php).
 */
27
28
define('MAINTENANCE_MODE', 'update');

29
/**
30
31
 * Add a column to a database using syntax appropriate for PostgreSQL.
 * Save result of SQL commands in $ret array.
32
33
 *
 * Note: when you add a column with NOT NULL and you are not sure if there are
34
35
36
37
38
 * already rows in the table, you MUST also add DEFAULT. Otherwise PostgreSQL
 * won't work when the table is not empty, and db_add_column() will fail.
 * To have an empty string as the default, you must use: 'default' => "''"
 * in the $attributes array. If NOT NULL and DEFAULT are set the PostgreSQL
 * version will set values of the added column in old rows to the
39
40
41
 * DEFAULT value.
 *
 * @param $ret
42
 *   Array to which results will be added.
43
 * @param $table
44
 *   Name of the table, without {}
45
 * @param $column
46
 *   Name of the column
47
 * @param $type
48
 *   Type of column
49
 * @param $attributes
50
51
 *   Additional optional attributes. Recognized attributes:
 *     not null => TRUE|FALSE
52
 *     default  => NULL|FALSE|value (the value must be enclosed in '' marks)
53
 * @return
54
 *   nothing, but modifies $ret parameter.
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
 */
function db_add_column(&$ret, $table, $column, $type, $attributes = array()) {
  if (array_key_exists('not null', $attributes) and $attributes['not null']) {
    $not_null = 'NOT NULL';
  }
  if (array_key_exists('default', $attributes)) {
    if (is_null($attributes['default'])) {
      $default_val = 'NULL';
      $default = 'default NULL';
    }
    elseif ($attributes['default'] === FALSE) {
      $default = '';
    }
    else {
      $default_val = "$attributes[default]";
      $default = "default $attributes[default]";
Dries's avatar
   
Dries committed
71
    }
72
  }
73

74
  $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column $type");
75
  if (!empty($default)) {
76
    $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET $default");
77
78
  }
  if (!empty($not_null)) {
Dries's avatar
Dries committed
79
    if (!empty($default)) {
80
      $ret[] = update_sql("UPDATE {" . $table . "} SET $column = $default_val");
81
    }
82
    $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET NOT NULL");
83
  }
Dries's avatar
   
Dries committed
84
85
}

86
/**
87
88
 * Change a column definition using syntax appropriate for PostgreSQL.
 * Save result of SQL commands in $ret array.
89
 *
90
91
92
 * Remember that changing a column definition involves adding a new column
 * and dropping an old one. This means that any indices, primary keys and
 * sequences from serial-type columns are dropped and might need to be
93
94
 * recreated.
 *
95
 * @param $ret
96
 *   Array to which results will be added.
97
 * @param $table
98
 *   Name of the table, without {}
99
 * @param $column
100
 *   Name of the column to change
101
 * @param $column_new
102
 *   New name for the column (set to the same as $column if you don't want to change the name)
103
 * @param $type
104
 *   Type of column
105
 * @param $attributes
106
107
108
 *   Additional optional attributes. Recognized attributes:
 *     not null => TRUE|FALSE
 *     default  => NULL|FALSE|value (with or without '', it won't be added)
109
 * @return
110
 *   nothing, but modifies $ret parameter.
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
 */
function db_change_column(&$ret, $table, $column, $column_new, $type, $attributes = array()) {
  if (array_key_exists('not null', $attributes) and $attributes['not null']) {
    $not_null = 'NOT NULL';
  }
  if (array_key_exists('default', $attributes)) {
    if (is_null($attributes['default'])) {
      $default_val = 'NULL';
      $default = 'default NULL';
    }
    elseif ($attributes['default'] === FALSE) {
      $default = '';
    }
    else {
      $default_val = "$attributes[default]";
      $default = "default $attributes[default]";
    }
  }

130
131
132
  $ret[] = update_sql("ALTER TABLE {" . $table . "} RENAME $column TO " . $column . "_old");
  $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column_new $type");
  $ret[] = update_sql("UPDATE {" . $table . "} SET $column_new = " . $column . "_old");
133
  if ($default) {
134
    $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET $default");
135
136
  }
  if ($not_null) {
137
    $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET NOT NULL");
138
  }
139
  $ret[] = update_sql("ALTER TABLE {" . $table . "} DROP " . $column . "_old");
140
141
}

142
143
144
145
/**
 * Perform one update and store the results which will later be displayed on
 * the finished page.
 *
146
147
148
149
150
151
152
 * An update function can force the current and all later updates for this
 * module to abort by returning a $ret array with an element like:
 * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong');
 * The schema version will not be updated in this case, and all the
 * aborted updates will continue to appear on update.php as updates that
 * have not yet been run.
 *
153
154
155
156
 * @param $module
 *   The module whose update will be run.
 * @param $number
 *   The update number to run.
157
 * @param $context
Dries's avatar
Dries committed
158
 *   The batch context array
159
 */
160
function update_do_one($module, $number, &$context) {
161
162
163
164
165
166
  // If updates for this module have been aborted
  // in a previous step, go no further.
  if (!empty($context['results'][$module]['#abort'])) {
    return;
  }

167
  $function = $module . '_update_' . $number;
168
  if (function_exists($function)) {
169
    $ret = $function($context['sandbox']);
170
171
  }

172
  if (isset($ret['#finished'])) {
173
    $context['finished'] = $ret['#finished'];
174
175
    unset($ret['#finished']);
  }
176

177
178
  if (!isset($context['results'][$module])) {
    $context['results'][$module] = array();
179
  }
180
181
  if (!isset($context['results'][$module][$number])) {
    $context['results'][$module][$number] = array();
182
  }
183
  $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
184

185
186
187
  if (!empty($ret['#abort'])) {
    $context['results'][$module]['#abort'] = TRUE;
  }
188
189
  // Record the schema update if it was completed successfully.
  if ($context['finished'] == 1 && empty($context['results'][$module]['#abort'])) {
190
191
    drupal_set_installed_schema_version($module, $number);
  }
192

193
  $context['message'] = 'Updating ' . check_plain($module) . ' module';
194
195
196
}

function update_selection_page() {
197
  drupal_set_title('Drupal database update');
198
  $output = drupal_get_form('update_script_selection_form');
199

200
201
  update_task_list('select');

202
203
204
205
  return $output;
}

function update_script_selection_form() {
206
  $form = $all = array();
207
  $form['start'] = array(
208
209
210
    '#tree' => TRUE,
    '#type' => 'fieldset',
    '#collapsed' => TRUE,
211
    '#collapsible' => TRUE,
212
  );
213
214
215
216

  // Ensure system.module's updates appear first
  $form['start']['system'] = array();

217
218
  $modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE);
  foreach ($modules as $module => $schema_version) {
219
    $pending = array();
220
    $updates = drupal_get_schema_versions($module);
221
222
223
224
225
226
227
    // Skip incompatible module updates completely, otherwise test schema versions.
    if (!update_check_incompatibility($module) && $updates !== FALSE && $schema_version >= 0) {
      // module_invoke returns NULL for nonexisting hooks, so if no updates
      // are removed, it will == 0.
      $last_removed = module_invoke($module, 'update_last_removed');
      if ($schema_version < $last_removed) {
        $form['start'][$module] = array(
228
229
          '#title' => $module,
          '#item'  => '<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.',
230
231
232
233
234
          '#prefix' => '<div class="warning">',
          '#suffix' => '</div>',
        );
        continue;
      }
235
      $updates = drupal_map_assoc($updates);
236
      foreach (array_keys($updates) as $update) {
237
        if ($update > $schema_version) {
238
239
240
241
242
243
244
245
246
247
248
249
          // The description for an update comes from its Doxygen.
          $func = new ReflectionFunction($module. '_update_'. $update);
          $description = str_replace(array("\n", '*', '/'), '', $func->getDocComment());
          $pending[] = "$update - $description";
          if (!isset($default)) {
            $default = $update;
          }
        }
      }
      if (!empty($pending)) {
        if (!isset($default)) {
          $default = $schema_version;
250
        }
251
252
253
254
255
256
257
        $form['start'][$module] = array(
          '#type' => 'hidden',
          '#value' => $default,
        );
        $form['start'][$module. '_updates'] = array(
          '#markup' => theme('item_list', $pending, $module . ' module'),
        );
258
      }
259
    }
260
261
    unset($default);
    $all += $pending;
262
263
  }

264
  if (empty($all)) {
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
    drupal_set_message(t('No pending updates.'));
    unset($form);
    $form['links'] = array(
      '#markup' => theme('item_list', update_helpful_links()),
    );
  }
  else {
    $form['help'] = array(
      '#markup' => '<p>The version of Drupal you are updating from has been automatically detected.</p>',
      '#weight' => -5,
    );
    $form['start']['#title'] = strtr('!num pending updates', array('!num' => count($all)));
    $form['has_js'] = array(
      '#type' => 'hidden',
      '#default_value' => FALSE,
      '#attributes' => array('id' => 'edit-has_js'),
    );
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => 'Apply pending updates',
    );
  }
287
  return $form;
288
289
}

290
291
292
function update_batch() {
  global $base_url;

293
294
295
296
  // During the update, toggle site maintenance so that schema changes do not
  // affect visiting users.
  $_SESSION['site_offline'] = variable_get('site_offline', FALSE);
  if ($_SESSION['site_offline'] == FALSE) {
297
298
299
    variable_set('site_offline', TRUE);
  }

300
  $operations = array();
301
  // Set the installed version so updates start at the correct place.
302
  foreach ($_POST['start'] as $module => $version) {
303
    drupal_set_installed_schema_version($module, $version - 1);
304
305
    $updates = drupal_get_schema_versions($module);
    $max_version = max($updates);
306
    if ($version <= $max_version) {
307
308
      foreach ($updates as $update) {
        if ($update >= $version) {
309
          $operations[] = array('update_do_one', array($module, $update));
310
        }
311
312
313
      }
    }
  }
314
315
316
317
  $batch = array(
    'operations' => $operations,
    'title' => 'Updating',
    'init_message' => 'Starting updates',
Dries's avatar
Dries committed
318
    '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.',
319
320
321
    'finished' => 'update_finished',
  );
  batch_set($batch);
322
  batch_process($base_url . '/update.php?op=results', $base_url . '/update.php');
323
324
}

325
326
function update_finished($success, $results, $operations) {
  // clear the caches in case the data has been updated.
327
  drupal_flush_all_caches();
328

329
330
331
  $_SESSION['update_results'] = $results;
  $_SESSION['update_success'] = $success;
  $_SESSION['updates_remaining'] = $operations;
332
333
334
335
336
337
338
  
  // Now that the update is done, we can disable site maintenance if it was
  // previously turned off.
  if (isset($_SESSION['site_offline']) && $_SESSION['site_offline'] == FALSE) {
    variable_set('site_offline', FALSE);
    unset($_SESSION['site_offline']);
  }
339
340
}

341
function update_helpful_links() {
342
  // NOTE: we can't use l() here because the URL would point to 'update.php?q=admin'.
343
344
  $links[] = '<a href="' . base_path() . '">Main page</a>';
  $links[] = '<a href="' . base_path() . '?q=admin">Administration pages</a>';
345
346
347
348
349
350
  return $links;
}

function update_results_page() {
  drupal_set_title('Drupal database update');
  $links = update_helpful_links();
351

352
  update_task_list();
353
  // Report end result
354
  if (module_exists('dblog')) {
355
    $log_message = ' All errors have been <a href="' . base_path() . '?q=admin/reports/dblog">logged</a>.';
356
357
358
359
360
  }
  else {
    $log_message = ' All errors have been logged.';
  }

361
  if ($_SESSION['update_success']) {
362
    $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>';
363
364
  }
  else {
365
    list($module, $version) = array_pop(reset($_SESSION['updates_remaining']));
366
    $output = '<p class="error">The update process was aborted prematurely while running <strong>update #' . $version . ' in ' . $module . '.module</strong>.' . $log_message;
367
368
369
370
    if (module_exists('dblog')) {
      $output .= ' You may need to check the <code>watchdog</code> database table manually.';
    }
    $output .= '</p>';
371
372
  }

373
374
  if (!empty($GLOBALS['update_free_access'])) {
    $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>";
375
  }
376

377
378
379
  $output .= theme('item_list', $links);

  // Output a list of queries executed
380
  if (!empty($_SESSION['update_results'])) {
381
382
383
    $output .= '<div id="update-results">';
    $output .= '<h2>The following queries were executed</h2>';
    foreach ($_SESSION['update_results'] as $module => $updates) {
384
      $output .= '<h3>' . $module . ' module</h3>';
385
      foreach ($updates as $number => $queries) {
386
        if ($number != '#abort') {
387
          $output .= '<h4>Update #' . $number . '</h4>';
388
389
390
          $output .= '<ul>';
          foreach ($queries as $query) {
            if ($query['success']) {
391
              $output .= '<li class="success">' . $query['query'] . '</li>';
392
393
            }
            else {
394
              $output .= '<li class="failure"><strong>Failed:</strong> ' . $query['query'] . '</li>';
395
            }
396
          }
397
398
          if (!count($queries)) {
            $output .= '<li class="none">No queries</li>';
399
400
401
402
403
404
405
          }
        }
        $output .= '</ul>';
      }
    }
    $output .= '</div>';
  }
406
407
  unset($_SESSION['update_results']);
  unset($_SESSION['update_success']);
408

409
  return $output;
410
411
}

412
function update_info_page() {
413
414
  // Change query-strings on css/js files to enforce reload for all users.
  _drupal_flush_css_js();
415
416
  // Flush the cache of all data for the update status module.
  if (db_table_exists('cache_update')) {
417
    cache_clear_all('*', 'cache_update', TRUE);
418
  }
419

420
  update_task_list('info');
Steven Wittens's avatar
Steven Wittens committed
421
  drupal_set_title('Drupal database update');
422
423
424
425
  $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>';
  $output .= "<ol>\n";
  $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";
  $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";
426
  $output .= '<li>Put your site into <a href="' . base_path() . '?q=admin/settings/site-maintenance">maintenance mode</a>.</li>' . "\n";
427
428
429
430
431
  $output .= "<li>Install your new files in the appropriate location, as described in the handbook.</li>\n";
  $output .= "</ol>\n";
  $output .= "<p>When you have performed the steps above, you may proceed.</p>\n";
  $output .= '<form method="post" action="update.php?op=selection"><input type="submit" value="Continue" /></form>';
  $output .= "\n";
432
433
434
435
436
  return $output;
}

function update_access_denied_page() {
  drupal_set_title('Access denied');
437
  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>
438
<ol>
439
440
441
 <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>
 <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>
 <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>
442
443
 <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>
</ol>';
444
}
445

446
447
448
449
450
451
452
453
454
455
456
457
/**
 * Create the batch table.
 *
 * This is part of the Drupal 5.x to 6.x migration.
 */
function update_create_batch_table() {

  // If batch table exists, update is not necessary
  if (db_table_exists('batch')) {
    return;
  }

458
459
460
461
462
463
464
465
466
467
468
  $schema['batch'] = array(
    'fields' => array(
      'bid'       => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
      'token'     => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE),
      'timestamp' => array('type' => 'int', 'not null' => TRUE),
      'batch'     => array('type' => 'text', 'not null' => FALSE, 'size' => 'big')
    ),
    'primary key' => array('bid'),
    'indexes' => array('token' => array('token')),
  );

469
  $ret = array();
470
  db_create_table($ret, 'batch', $schema['batch']);
471
472
473
  return $ret;
}

474
475
476
477
478
479
480
481
482
/**
 * Disable anything in the {system} table that is not compatible with the
 * current version of Drupal core.
 */
function update_fix_compatibility() {
  $ret = array();
  $incompatible = array();
  $query = db_query("SELECT name, type, status FROM {system} WHERE status = 1 AND type IN ('module','theme')");
  while ($result = db_fetch_object($query)) {
483
    if (update_check_incompatibility($result->name, $result->type)) {
484
      $incompatible[] = $result->name;
485
486
487
    }
  }
  if (!empty($incompatible)) {
488
    $ret[] = update_sql("UPDATE {system} SET status = 0 WHERE name IN ('" . implode("','", $incompatible) . "')");
489
490
491
492
  }
  return $ret;
}

493
494
495
496
497
498
499
500
/**
 * Helper function to test compatibility of a module or theme.
 */
function update_check_incompatibility($name, $type = 'module') {
  static $themes, $modules;

  // Store values of expensive functions for future use.
  if (empty($themes) || empty($modules)) {
501
    $themes = _system_theme_data();
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
    $modules = module_rebuild_cache();
  }

  if ($type == 'module' && isset($modules[$name])) {
    $file = $modules[$name];
  }
  else if ($type == 'theme' && isset($themes[$name])) {
    $file = $themes[$name];
  }
  if (!isset($file)
      || !isset($file->info['core'])
      || $file->info['core'] != DRUPAL_CORE_COMPATIBILITY
      || version_compare(phpversion(), $file->info['php']) < 0) {
    return TRUE;
  }
  return FALSE;
}

520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
/**
 * Perform Drupal 5.x to 6.x updates that are required for update.php
 * to function properly.
 *
 * This function runs when update.php is run the first time for 6.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.
 * Just adding columns is safe.  However, renaming the
 * system.description column to owner is not.  Therefore, we add the
 * system.owner column and leave it to system_update_6008() to copy
 * the data from description and remove description. The same for
 * renaming locales_target.locale to locales_target.language, which
 * will be finished by locale_update_6002().
 */
function update_fix_d6_requirements() {
  $ret = array();

  if (drupal_get_installed_schema_version('system') < 6000 && !variable_get('update_d6_requirements', FALSE)) {
    $spec = array('type' => 'int', 'size' => 'small', 'default' => 0, 'not null' => TRUE);
    db_add_field($ret, 'cache', 'serialized', $spec);
    db_add_field($ret, 'cache_filter', 'serialized', $spec);
    db_add_field($ret, 'cache_page', 'serialized', $spec);
    db_add_field($ret, 'cache_menu', 'serialized', $spec);

    db_add_field($ret, 'system', 'info', array('type' => 'text'));
    db_add_field($ret, 'system', 'owner', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
    if (db_table_exists('locales_target')) {
      db_add_field($ret, 'locales_target', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''));
    }
    if (db_table_exists('locales_source')) {
      db_add_field($ret, 'locales_source', 'textgroup', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => 'default'));
552
      db_add_field($ret, 'locales_source', 'version', array('type' => 'varchar', 'length' => 20, 'not null' => TRUE, 'default' => 'none'));
553
554
    }
    variable_set('update_d6_requirements', TRUE);
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569

    // Create the cache_block table. See system_update_6027() for more details.
    $schema['cache_block'] = array(
      'fields' => array(
        'cid'        => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
        'data'       => array('type' => 'blob', 'not null' => FALSE, 'size' => 'big'),
        'expire'     => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
        'created'    => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
        'headers'    => array('type' => 'text', 'not null' => FALSE),
        'serialized' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0)
      ),
      'indexes' => array('expire' => array('expire')),
      'primary key' => array('cid'),
    );
    db_create_table($ret, 'cache_block', $schema['cache_block']);
570
571
572
573
574
  }

  return $ret;
}

575
576
577
578
579
580
581
/**
 * Add the update task list to the current page.
 */
function update_task_list($active = NULL) {
  // Default list of tasks.
  $tasks = array(
    'info' => 'Overview',
582
    'select' => 'Review updates',
583
584
585
586
    'run' => 'Run updates',
    'finished' => 'Review log',
  );

587
  drupal_set_content('left', theme('task_list', $tasks, $active));
588
589
}

590
591
592
593
594
595
596
597
598
599
600
601
602
603
/**
 * Check update requirements and report any errors.
 */
function update_check_requirements() {
  // 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']) {
604
          $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')';
605
606
607
608
609
610
611
        }
        drupal_set_message($message, 'warning');
      }
    }
  }
}

612
// Some unavoidable errors happen because the database is not yet up-to-date.
613
// Our custom error handler is not yet installed, so we just suppress them.
614
615
ini_set('display_errors', FALSE);

616
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
617
618
619
620
621
622
623
624

// 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);

625
626
627
  require_once DRUPAL_ROOT . '/includes/install.inc';
  require_once DRUPAL_ROOT . '/includes/file.inc';
  require_once DRUPAL_ROOT . '/modules/system/system.install';
628
629

  // Load module basics.
630
  include_once DRUPAL_ROOT . '/includes/module.inc';
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  $module_list['system']['filename'] = 'modules/system/system.module';
  $module_list['filter']['filename'] = 'modules/filter/filter.module';
  module_list(TRUE, FALSE, FALSE, $module_list);
  drupal_load('module', 'system');
  drupal_load('module', 'filter');

  // Set up $language, since the installer components require it.
  drupal_init_language();

  // Set up theme system for the maintenance page.
  drupal_maintenance_theme();

  // Check the update requirements for Drupal.
  update_check_requirements();

  // Display the warning messages (if any) in a dedicated maintenance page,
  // or redirect to the update information page if no message.
  $messages = drupal_set_message();
  if (!empty($messages['warning'])) {
    drupal_maintenance_theme();
    print theme('update_page', '<form method="post" action="update.php?op=info"><input type="submit" value="Continue" /></form>', FALSE);
    exit;
  }
  install_goto('update.php?op=info');
}
656

657
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
Steven Wittens's avatar
Steven Wittens committed
658
drupal_maintenance_theme();
659

660
661
// This must happen *after* drupal_bootstrap(), since it calls
// variable_(get|set), which only works after a full bootstrap.
662
update_create_batch_table();
663

664
665
666
667
// Turn error reporting back on. From now on, only fatal errors (which are
// not passed through the error handler) will cause a message to be printed.
ini_set('display_errors', TRUE);

668
// Access check:
669
if (!empty($update_free_access) || $user->uid == 1) {
670

671
672
  include_once DRUPAL_ROOT . '/includes/install.inc';
  include_once DRUPAL_ROOT . '/includes/batch.inc';
673
  drupal_load_updates();
Dries's avatar
   
Dries committed
674

675
  update_fix_d6_requirements();
676
  update_fix_compatibility();
677

678
679
  $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
  switch ($op) {
680
    // update.php ops
681
    case 'info':
682
      $output = update_info_page();
683
684
      break;

685
686
    case 'selection':
      $output = update_selection_page();
687
688
      break;

689
    case 'Apply pending updates':
690
      update_batch();
691
692
      break;

693
694
    case 'results':
      $output = update_results_page();
695
696
      break;

697
    // Regular batch ops : defer to batch processing API
698
    default:
699
700
      update_task_list('run');
      $output = _batch_page();
701
      break;
Kjartan's avatar
Kjartan committed
702
703
704
  }
}
else {
705
  $output = update_access_denied_page();
706
}
707
if (isset($output) && $output) {
708
709
710
  // We defer the display of messages until all updates are done.
  $progress_page = ($batch = batch_get()) && isset($batch['running']);
  print theme('update_page', $output, !$progress_page);
711
}