system.install 82.7 KB
Newer Older
Dries's avatar
 
Dries committed
1
<?php
2
// $Id$
3

4 5 6 7 8
/**
 * @file
 * Install, update and uninstall functions for the system module.
 */

9 10
/**
 * Test and report Drupal installation requirements.
11 12 13 14 15
 *
 * @param $phase
 *   The current system installation phase.
 * @return
 *   An array of system requirements.
16 17
 */
function system_requirements($phase) {
18
  global $base_url;
19 20
  $requirements = array();
  // Ensure translations don't break at install time
21
  $t = get_t();
22 23 24 25 26 27

  // Report Drupal version
  if ($phase == 'runtime') {
    $requirements['drupal'] = array(
      'title' => $t('Drupal'),
      'value' => VERSION,
Steven Wittens's avatar
Steven Wittens committed
28 29
      'severity' => REQUIREMENT_INFO,
      'weight' => -10,
30 31 32
    );
  }

33
  // Web server information.
Steven Wittens's avatar
Steven Wittens committed
34
  $software = $_SERVER['SERVER_SOFTWARE'];
35 36
  $requirements['webserver'] = array(
    'title' => $t('Web server'),
Steven Wittens's avatar
Steven Wittens committed
37
    'value' => $software,
38 39 40 41 42
  );

  // Test PHP version
  $requirements['php'] = array(
    'title' => $t('PHP'),
43
    'value' => ($phase == 'runtime') ? l(phpversion(), 'admin/reports/status/php') : phpversion(),
44 45 46 47 48
  );
  if (version_compare(phpversion(), DRUPAL_MINIMUM_PHP) < 0) {
    $requirements['php']['description'] = $t('Your PHP installation is too old. Drupal requires at least PHP %version.', array('%version' => DRUPAL_MINIMUM_PHP));
    $requirements['php']['severity'] = REQUIREMENT_ERROR;
  }
49

50 51 52 53 54 55 56 57 58 59 60
  // Make sure the tokenizer extension is enabled, which is required by the code registry.
  $requirements['php_tokenizer'] = array(
    'title' => $t('PHP tokenizer'),
    'value' => $t('Enabled'),
  );
  if (!function_exists('token_get_all')) {
    $requirements['php_tokenizer']['value'] = $t('Not enabled');
    $requirements['php_tokenizer']['description'] = $t('Drupal requires the <a href="@url">tokenizer extension</a> to be enabled.', array('@url' => 'http://php.net/tokenizer'));
    $requirements['php_tokenizer']['severity'] = REQUIREMENT_ERROR;
  }

61 62 63 64 65
  // Test PHP register_globals setting.
  $requirements['php_register_globals'] = array(
    'title' => $t('PHP register globals'),
  );
  $register_globals = trim(ini_get('register_globals'));
66
  // Unfortunately, ini_get() may return many different values, and we can't
67 68 69 70 71 72 73 74 75 76 77 78 79
  // be certain which values mean 'on', so we instead check for 'not off'
  // since we never want to tell the user that their site is secure
  // (register_globals off), when it is in fact on. We can only guarantee
  // register_globals is off if the value returned is 'off', '', or 0.
  if (!empty($register_globals) && strtolower($register_globals) != 'off') {
    $requirements['php_register_globals']['description'] = $t('<em>register_globals</em> is enabled. Drupal requires this configuration directive to be disabled. Your site may not be secure when <em>register_globals</em> is enabled. The PHP manual has instructions for <a href="http://php.net/configuration.changes">how to change configuration settings</a>.');
    $requirements['php_register_globals']['severity'] = REQUIREMENT_ERROR;
    $requirements['php_register_globals']['value'] = $t("Enabled ('@value')", array('@value' => $register_globals));
  }
  else {
    $requirements['php_register_globals']['value'] = $t('Disabled');
  }

80
  // Test PHP memory_limit
81
  $memory_limit = ini_get('memory_limit');
82 83
  $requirements['php_memory_limit'] = array(
    'title' => $t('PHP memory limit'),
84
    'value' => $memory_limit == -1 ? t('-1 (Unlimited)') : $memory_limit,
85
  );
86

87
  if ($memory_limit && $memory_limit != -1 && parse_size($memory_limit) < parse_size(DRUPAL_MINIMUM_PHP_MEMORY_LIMIT)) {
88 89 90
    $description = '';
    if ($phase == 'install') {
      $description = $t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the installation process.', array('%memory_minimum_limit' => DRUPAL_MINIMUM_PHP_MEMORY_LIMIT));
91 92 93
    }
    elseif ($phase == 'update') {
      $description = $t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the update process.', array('%memory_minimum_limit' => DRUPAL_MINIMUM_PHP_MEMORY_LIMIT));
94
    }
95 96
    elseif ($phase == 'runtime') {
      $description = $t('Depending on your configuration, Drupal can run with a %memory_limit PHP memory limit. However, a %memory_minimum_limit PHP memory limit or above is recommended, especially if your site uses additional custom or contributed modules.', array('%memory_limit' => $memory_limit, '%memory_minimum_limit' => DRUPAL_MINIMUM_PHP_MEMORY_LIMIT));
97
    }
98

99 100
    if (!empty($description)) {
      if ($php_ini_path = get_cfg_var('cfg_file_path')) {
101
        $description .= ' ' . $t('Increase the memory limit by editing the memory_limit parameter in the file %configuration-file and then restart your web server (or contact your system administrator or hosting provider for assistance).', array('%configuration-file' => $php_ini_path));
102 103
      }
      else {
104
        $description .= ' ' . $t('Contact your system administrator or hosting provider for assistance with increasing your PHP memory limit.');
105
      }
106

107
      $requirements['php_memory_limit']['description'] = $description . ' ' . $t('See the <a href="@url">Drupal requirements</a> for more information.', array('@url' => 'http://drupal.org/requirements'));
108
      $requirements['php_memory_limit']['severity'] = REQUIREMENT_WARNING;
109
    }
110
  }
111

112 113
  // Test settings.php file writability
  if ($phase == 'runtime') {
114
    $conf_dir = drupal_verify_install_file(conf_path(), FILE_NOT_WRITABLE, 'dir');
115
    $conf_file = drupal_verify_install_file(conf_path() . '/settings.php', FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE);
116
    if (!$conf_dir || !$conf_file) {
117 118 119
      $requirements['settings.php'] = array(
        'value' => $t('Not protected'),
        'severity' => REQUIREMENT_ERROR,
120
        'description' => '',
121
      );
122 123 124 125
      if (!$conf_dir) {
        $requirements['settings.php']['description'] .= $t('The directory %file is not protected from modifications and poses a security risk. You must change the directory\'s permissions to be non-writable. ', array('%file' => conf_path()));
      }
      if (!$conf_file) {
126
        $requirements['settings.php']['description'] .= $t('The file %file is not protected from modifications and poses a security risk. You must change the file\'s permissions to be non-writable.', array('%file' => conf_path() . '/settings.php'));
127
      }
128 129 130 131 132 133 134 135 136
    }
    else {
      $requirements['settings.php'] = array(
        'value' => $t('Protected'),
      );
    }
    $requirements['settings.php']['title'] = $t('Configuration file');
  }

137
  // Report cron status.
138
  if ($phase == 'runtime') {
139 140 141 142 143
    // Cron warning threshold defaults to two days.
    $threshold_warning = variable_get('cron_threshold_warning', 172800);
    // Cron error threshold defaults to two weeks.
    $threshold_error = variable_get('cron_threshold_error', 1209600);
    // Cron configuration help text.
144
    $help = $t('For more information, see the online handbook entry for <a href="@cron-handbook">configuring cron jobs</a>.', array('@cron-handbook' => 'http://drupal.org/cron'));
145

146
    // Determine when cron last ran.
147
    $cron_last = variable_get('cron_last');
148 149 150
    if (!is_numeric($cron_last)) {
      $cron_last = variable_get('install_time', 0);
    }
151

152 153
    // Determine severity based on time since cron last ran.
    $severity = REQUIREMENT_OK;
154
    if (REQUEST_TIME - $cron_last > $threshold_error) {
155
      $severity = REQUIREMENT_ERROR;
156
    }
157
    elseif (REQUEST_TIME - $cron_last > $threshold_warning) {
158 159 160 161
      $severity = REQUIREMENT_WARNING;
    }

    // Set summary and description based on values determined above.
162 163 164 165
    $summary = $t('Last run !time ago', array('!time' => format_interval(REQUEST_TIME - $cron_last)));
    $description = '';
    if ($severity != REQUIREMENT_OK) {
      $description = $t('Cron has not run recently.') . ' ' . $help;
166
    }
167

168
    $description .= ' ' . $t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/reports/status/run-cron')));
169
    $description .= '<br />' . $t('To run cron from outside the site, go to <a href="!cron">!cron</a>', array('!cron' => url($base_url . '/cron.php', array('external' => TRUE, 'query' => 'cron_key=' . variable_get('cron_key', 'drupal')))));
170

171 172 173 174
    $requirements['cron'] = array(
      'title' => $t('Cron maintenance tasks'),
      'severity' => $severity,
      'value' => $summary,
175
      'description' => $description
176
    );
177 178
  }

179
  // Test files directory
180
  $directory = file_directory_path();
181 182 183
  $requirements['file system'] = array(
    'title' => $t('File system'),
  );
184 185 186 187 188 189

  // For installer, create the directory if possible.
  if ($phase == 'install' && !is_dir($directory) && @mkdir($directory)) {
    @chmod($directory, 0775); // Necessary for non-webserver users.
  }

190 191 192
  $is_writable = is_writable($directory);
  $is_directory = is_dir($directory);
  if (!$is_writable || !$is_directory) {
193 194
    $description = '';
    $requirements['file system']['value'] = $t('Not writable');
195 196
    if (!$is_directory) {
      $error = $t('The directory %directory does not exist.', array('%directory' => $directory));
197 198
    }
    else {
199 200
      $error = $t('The directory %directory is not writable.', array('%directory' => $directory));
    }
201
    // The files directory requirement check is done only during install and runtime.
202
    if ($phase == 'runtime') {
203
      $description = $error . ' ' . $t('You may need to set the correct directory at the <a href="@admin-file-system">file system settings page</a> or change the current directory\'s permissions so that it is writable.', array('@admin-file-system' => url('admin/settings/file-system')));
204
    }
205
    elseif ($phase == 'install') {
206 207
      // For the installer UI, we need different wording. 'value' will
      // be treated as version, so provide none there.
208
      $description = $error . ' ' . $t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually, or ensure that the installer has the permissions to create it automatically. For more information, please see INSTALL.txt or the <a href="@handbook_url">online handbook</a>.', array('@handbook_url' => 'http://drupal.org/server-permissions'));
209 210
      $requirements['file system']['value'] = '';
    }
211 212 213 214
    if (!empty($description)) {
      $requirements['file system']['description'] = $description;
      $requirements['file system']['severity'] = REQUIREMENT_ERROR;
    }
215 216 217 218 219 220 221
  }
  else {
    if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) {
      $requirements['file system']['value'] = $t('Writable (<em>public</em> download method)');
    }
    else {
      $requirements['file system']['value'] = $t('Writable (<em>private</em> download method)');
222 223 224
    }
  }

225 226 227
  // See if updates are available in update.php.
  if ($phase == 'runtime') {
    $requirements['update'] = array(
228
      'title' => $t('Database updates'),
229 230 231 232 233 234 235 236 237 238 239 240
      'severity' => REQUIREMENT_OK,
      'value' => $t('Up to date'),
    );

    // Check installed modules.
    foreach (module_list() as $module) {
      $updates = drupal_get_schema_versions($module);
      if ($updates !== FALSE) {
        $default = drupal_get_installed_schema_version($module);
        if (max($updates) > $default) {
          $requirements['update']['severity'] = REQUIREMENT_ERROR;
          $requirements['update']['value'] = $t('Out of date');
241
          $requirements['update']['description'] = $t('Some modules have database schema updates to install. You should run the <a href="@update">database update script</a> immediately.', array('@update' => base_path() . 'update.php'));
242 243 244 245 246 247
          break;
        }
      }
    }
  }

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
  // Verify the update.php access setting
  if ($phase == 'runtime') {
    if (!empty($GLOBALS['update_free_access'])) {
      $requirements['update access'] = array(
        'value' => $t('Not protected'),
        'severity' => REQUIREMENT_ERROR,
        'description' => $t('The update.php script is accessible to everyone without authentication check, which is a security risk. You must change the $update_free_access value in your settings.php back to FALSE.'),
      );
    }
    else {
      $requirements['update access'] = array(
        'value' => $t('Protected'),
      );
    }
    $requirements['update access']['title'] = $t('Access to update.php');
  }

265
  // Test Unicode library
266
  include_once DRUPAL_ROOT . '/includes/unicode.inc';
267 268
  $requirements = array_merge($requirements, unicode_requirements());

269 270 271 272 273 274 275 276 277 278 279
  // Verify if the DOM PHP 5 extension is available.
  $has_dom = class_exists('DOMDocument');
  if (!$has_dom) {
    $requirements['php_dom'] = array(
      'title' => $t('PHP DOM Extension'),
      'value' => $t('Not found'),
      'severity' => REQUIREMENT_ERROR,
      'description' => $t("The DOM extension is part of PHP 5 core, but doesn't seem to be enabled on your system. You need to enable the DOM extension on your PHP installation."),
    );
  }

280
  if ($phase == 'runtime') {
281
    // Check for update status module.
282 283 284
    if (!module_exists('update')) {
      $requirements['update status'] = array(
        'value' => $t('Not enabled'),
285
        'severity' => REQUIREMENT_WARNING,
286
        'description' => $t('Update notifications are not enabled. It is <strong>highly recommended</strong> that you enable the update status module from the <a href="@module">module administration page</a> in order to stay up-to-date on new releases. For more information please read the <a href="@update">Update status handbook page</a>.', array('@update' => 'http://drupal.org/handbook/modules/update', '@module' => url('admin/structure/modules'))),
287 288 289 290 291 292 293 294
      );
    }
    else {
      $requirements['update status'] = array(
        'value' => $t('Enabled'),
      );
    }
    $requirements['update status']['title'] = $t('Update notifications');
295 296 297 298 299 300 301 302 303 304

    // Check that Drupal can issue HTTP requests.
    if (variable_get('drupal_http_request_fails', TRUE) && !system_check_http_request()) {
      $requirements['http requests'] = array(
        'title' => $t('HTTP request status'),
        'value' => $t('Fails'),
        'severity' => REQUIREMENT_ERROR,
        'description' => $t('Your system or network configuration does not allow Drupal to access web pages, resulting in reduced functionality. This could be due to your webserver configuration or PHP settings, and should be resolved in order to download information about available updates, fetch aggregator feeds, sign in via OpenID, or use other network-dependent services.  If you are certain that Drupal can access web pages but you are still seeing this message, you may add <code>$conf[\'drupal_http_request_fails\'] = FALSE;</code> to the bottom of your settings.php file.'),
      );
    }
305 306
  }

307 308 309
  return $requirements;
}

310
/**
311
 * Implement hook_install().
312
 */
313
function system_install() {
314
  if (db_driver() == 'pgsql') {
315 316
    // We create some functions using global names instead of prefixing them
    // like we do with table names. If this function is ever called again (for
317
    // example, by the test framework when creating prefixed test databases),
318 319
    // the global names will already exist. We therefore avoid trying to create
    // them again in that case.
320

321
    // Create functions.
322 323 324 325 326 327 328 329
    db_query('CREATE OR REPLACE FUNCTION "greatest"(numeric, numeric) RETURNS numeric AS
      \'SELECT CASE WHEN (($1 > $2) OR ($2 IS NULL)) THEN $1 ELSE $2 END;\'
      LANGUAGE \'sql\''
    );
    db_query('CREATE OR REPLACE FUNCTION "greatest"(numeric, numeric, numeric) RETURNS numeric AS
      \'SELECT greatest($1, greatest($2, $3));\'
      LANGUAGE \'sql\''
    );
330
    // Don't use {} around pg_proc table.
331
    if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) {
332 333
      db_query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
        \'SELECT random();\'
334 335
        LANGUAGE \'sql\''
      );
336
    }
337

338
    // Don't use {} around pg_proc table.
339
    if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'concat'")->fetchField()) {
340 341
      db_query('CREATE OR REPLACE FUNCTION "concat"(text, text) RETURNS text AS
        \'SELECT $1 || $2;\'
342 343
        LANGUAGE \'sql\''
      );
344 345 346 347 348 349 350 351 352
    }
    db_query('CREATE OR REPLACE FUNCTION "if"(boolean, text, text) RETURNS text AS
      \'SELECT CASE WHEN $1 THEN $2 ELSE $3 END;\'
      LANGUAGE \'sql\''
    );
    db_query('CREATE OR REPLACE FUNCTION "if"(boolean, integer, integer) RETURNS integer AS
      \'SELECT CASE WHEN $1 THEN $2 ELSE $3 END;\'
      LANGUAGE \'sql\''
    );
353

354
    db_query('CREATE OR REPLACE FUNCTION "substring_index"(text, text, integer) RETURNS text AS
355 356 357
      \'SELECT array_to_string((string_to_array($1, $2)) [1:$3], $2);\'
      LANGUAGE \'sql\''
    );
358
  }
359

360
  // Create tables.
361
  $modules = array('system', 'filter', 'user', 'node');
362 363
  foreach ($modules as $module) {
    drupal_install_schema($module);
364
  }
365

366
  // Load system theme data appropriately.
367
  system_get_theme_data();
368

369 370 371 372
  // Inserting uid 0 here confuses MySQL -- the next user might be created as
  // uid 2 which is not what we want. So we insert the first user here, the
  // anonymous user. uid is 1 here for now, but very soon it will be changed
  // to 0.
373
  db_query("INSERT INTO {users} (name, mail) VALUES('%s', '%s')", '', '');
374 375 376 377
  // We need some placeholders here as name and mail are uniques and data is
  // presumed to be a serialized array. Install will change uid 1 immediately
  // anyways. So we insert the superuser here, the uid is 2 here for now, but
  // very soon it will be changed to 1.
378
  db_query("INSERT INTO {users} (name, mail, created, status, data) VALUES('%s', '%s', %d, %d, '%s')", 'placeholder-for-uid-1', 'placeholder-for-uid-1', REQUEST_TIME, 1, serialize(array()));
379 380
  // This sets the above two users uid 0 (anonymous). We avoid an explicit 0
  // otherwise MySQL might insert the next auto_increment value.
381
  db_query("UPDATE {users} SET uid = uid - uid WHERE name = '%s'", '');
382
  // This sets uid 1 (superuser). We skip uid 2 but that's not a big problem.
383
  db_query("UPDATE {users} SET uid = 1 WHERE name = '%s'", 'placeholder-for-uid-1');
384

385
  // Built-in roles.
386 387 388 389 390 391 392 393 394 395
  $rid_anonymous = db_insert('role')
    ->fields(array('name'))
    ->values(array('name' => 'anonymous user'))
    ->execute();

  $rid_authenticated = db_insert('role')
    ->fields(array('name'))
    ->values(array('name' => 'authenticated user'))
    ->execute();

396 397
  // Sanity check to ensure the anonymous and authenticated role IDs are the
  // same as the drupal defined constants. In certain situations, this will
398 399 400 401 402 403 404 405 406 407 408 409 410 411
  // not be true.
  if ($rid_anonymous != DRUPAL_ANONYMOUS_RID) {
    db_update('role')
      ->fields(array('rid' => DRUPAL_ANONYMOUS_RID))
      ->condition('rid', $rid_anonymous)
      ->execute();
  }

  if ($rid_authenticated != DRUPAL_AUTHENTICATED_RID) {
    db_update('role')
      ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID))
      ->condition('rid', $rid_authenticated)
      ->execute();
  }
412

413
  $query = db_insert('role_permission')->fields(array('rid', 'permission'));
414
  // Anonymous role permissions.
415 416 417 418
  $query->values(array(
    'rid' => DRUPAL_ANONYMOUS_RID,
    'permission' => 'access content',
  ));
419 420

  // Authenticated role permissions.
421
  foreach (array('access comments', 'access content', 'post comments', 'post comments without approval', 'view own unpublished content') as $permission) {
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
    $query->values(array(
      'rid' => DRUPAL_AUTHENTICATED_RID,
      'permission' => $permission,
    ));
  }
  $query->execute();

  variable_set('theme_default', 'garland');

  db_update('system')
    ->fields(array('status' => 1))
    ->condition('type', 'theme')
    ->condition('name', 'garland')
    ->execute();

  db_insert('node_access')
    ->fields(array(
      'nid' => 0,
      'gid' => 0,
      'realm' => 'all',
      'grant_view' => 1,
      'grant_update' => 0,
      'grant_delete' => 0,
    ))
    ->execute();
447

448
  // Add text formats.
449 450 451 452 453 454 455 456 457 458 459 460 461 462
  $filtered_html_format = db_insert('filter_format')
    ->fields(array(
      'name' => 'Filtered HTML',
      'roles' => ',' . DRUPAL_ANONYMOUS_RID . ',' . DRUPAL_AUTHENTICATED_RID . ',',
      'cache' => 1,
    ))
    ->execute();
  $full_html_format = db_insert('filter_format')
    ->fields(array(
      'name' => 'Full HTML',
      'roles' => '',
      'cache' => 1,
    ))
    ->execute();
463

464
  // Enable filters for each text format.
465 466

  // Filtered HTML:
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
  db_insert('filter')
    ->fields(array('format', 'module', 'delta', 'weight'))
    // URL filter.
    ->values(array(
      'format' => $filtered_html_format,
      'module' => 'filter',
      'delta' => 2,
      'weight' => 0,
    ))
    // HTML filter.
    ->values(array(
      'format' => $filtered_html_format,
      'module' => 'filter',
      'delta' => 0,
      'weight' => 1,
    ))
    // Line break filter.
    ->values(array(
      'format' => $filtered_html_format,
      'module' => 'filter',
      'delta' => 1,
      'weight' => 2,
    ))
    // HTML corrector filter.
    ->values(array(
      'format' => $filtered_html_format,
      'module' => 'filter',
      'delta' => 3,
      'weight' => 10,
    ))
497
  // Full HTML:
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
    // URL filter.
    ->values(array(
      'format' => $full_html_format,
      'module' => 'filter',
      'delta' => 2,
      'weight' => 0,
    ))
    // Line break filter.
    ->values(array(
      'format' => $full_html_format,
      'module' => 'filter',
      'delta' => 1,
      'weight' => 1,
    ))
    // HTML corrector filter.
    ->values(array(
      'format' => $full_html_format,
      'module' => 'filter',
      'delta' => 3,
      'weight' => 10,
    ))
    ->execute();

  // Set the default input format to Filtered HTML.
  variable_set('filter_default_format', $filtered_html_format);

  variable_set('node_options_forum', array(0 => 'status'));
525

526
  $cron_key = md5(mt_rand());
527

528
  variable_set('cron_key', $cron_key);
529 530
}

531
/**
532
 * Implement hook_schema().
533 534 535 536 537 538
 */
function system_schema() {
  // NOTE: {variable} needs to be created before all other tables, as
  // some database drivers, e.g. Oracle and DB2, will require variable_get()
  // and variable_set() for overcoming some database specific limitations.
  $schema['variable'] = array(
539
    'description' => 'Named variable/value pairs created by Drupal core or any other module or theme. All variables are cached in memory at the start of every Drupal request so developers should not be careless about what is stored here.',
540
    'fields' => array(
541
      'name' => array(
542
        'description' => 'The name of the variable.',
543 544 545
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
546 547
        'default' => '',
      ),
548
      'value' => array(
549
        'description' => 'The value of the variable.',
550 551
        'type' => 'text',
        'not null' => TRUE,
552
        'size' => 'big',
553
      ),
554
    ),
555
    'primary key' => array('name'),
556
  );
557 558

  $schema['actions'] = array(
559
    'description' => 'Stores action information.',
560
    'fields' => array(
561
      'aid' => array(
562
        'description' => 'Primary Key: Unique actions ID.',
563 564 565
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
566 567
        'default' => '0',
      ),
568
      'type' => array(
569
        'description' => 'The object that that action acts on (node, user, comment, system or custom types.)',
570 571 572
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
573 574
        'default' => '',
      ),
575
      'callback' => array(
576
        'description' => 'The callback function that executes when the action runs.',
577 578 579
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
580 581
        'default' => '',
      ),
582
      'parameters' => array(
583
        'description' => 'Parameters to be passed to the callback function.',
584 585
        'type' => 'text',
        'not null' => TRUE,
586 587
        'size' => 'big',
      ),
588
      'description' => array(
589
        'description' => 'Description of the action.',
590 591 592
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
593
        'default' => '0',
594
      ),
595
    ),
596
    'primary key' => array('aid'),
597
  );
598 599

  $schema['actions_aid'] = array(
600
    'description' => 'Stores action IDs for non-default actions.',
601
    'fields' => array(
602
      'aid' => array(
603
        'description' => 'Primary Key: Unique actions ID.',
604 605
        'type' => 'serial',
        'unsigned' => TRUE,
606
        'not null' => TRUE,
607
      ),
608
    ),
609
    'primary key' => array('aid'),
610
  );
611 612

  $schema['batch'] = array(
613
    'description' => 'Stores details about batches (processes that run in multiple HTTP requests).',
614
    'fields' => array(
615
      'bid' => array(
616
        'description' => 'Primary Key: Unique batch ID.',
617 618
        'type' => 'serial',
        'unsigned' => TRUE,
619 620
        'not null' => TRUE,
      ),
621
      'token' => array(
622
        'description' => "A string token generated against the current user's session id and the batch id, used to ensure that only the user who submitted the batch can effectively access it.",
623 624
        'type' => 'varchar',
        'length' => 64,
625 626
        'not null' => TRUE,
      ),
627
      'timestamp' => array(
628
        'description' => 'A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.',
629
        'type' => 'int',
630 631
        'not null' => TRUE,
      ),
632
      'batch' => array(
633
        'description' => 'A serialized array containing the processing data for the batch.',
634 635
        'type' => 'text',
        'not null' => FALSE,
636
        'size' => 'big',
637
      ),
638
    ),
639
    'primary key' => array('bid'),
640 641 642 643
    'indexes' => array(
      'token' => array('token'),
    ),
  );
644

645
  $schema['blocked_ips'] = array(
646
    'description' => 'Stores blocked IP addresses.',
647 648
    'fields' => array(
       'iid' => array(
649
        'description' => 'Primary Key: unique ID for IP addresses.',
650 651 652 653 654
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'ip' => array(
655
        'description' => 'IP address',
656 657 658 659 660 661 662 663 664 665 666 667
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
        'default' => '',
      ),
    ),
    'indexes' => array(
      'blocked_ip' => array('ip'),
    ),
    'primary key' => array('iid'),
  );

668
  $schema['cache'] = array(
669
    'description' => 'Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.',
670
    'fields' => array(
671
      'cid' => array(
672
        'description' => 'Primary Key: Unique cache ID.',
673 674 675
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
676 677
        'default' => '',
      ),
678
      'data' => array(
679
        'description' => 'A collection of data to cache.',
680 681
        'type' => 'blob',
        'not null' => FALSE,
682 683
        'size' => 'big',
      ),
684
      'expire' => array(
685
        'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
686 687
        'type' => 'int',
        'not null' => TRUE,
688 689
        'default' => 0,
      ),
690
      'created' => array(
691
        'description' => 'A Unix timestamp indicating when the cache entry was created.',
692 693
        'type' => 'int',
        'not null' => TRUE,
694 695
        'default' => 0,
      ),
696
      'headers' => array(
697
        'description' => 'Any custom HTTP headers to be added to cached data.',
698
        'type' => 'text',
699 700
        'not null' => FALSE,
      ),
701
      'serialized' => array(
702
        'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
703 704 705
        'type' => 'int',
        'size' => 'small',
        'not null' => TRUE,
706
        'default' => 0,
707
      ),
708 709 710 711
    ),
    'indexes' => array(
      'expire' => array('expire'),
    ),
712
    'primary key' => array('cid'),
713
  );
714 715

  $schema['cache_form'] = $schema['cache'];
716
  $schema['cache_form']['description'] = 'Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.';
717
  $schema['cache_page'] = $schema['cache'];
718
  $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
719
  $schema['cache_menu'] = $schema['cache'];
720
  $schema['cache_menu']['description'] = 'Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.';
721 722
  $schema['cache_path'] = $schema['cache'];
  $schema['cache_path']['description'] = 'Cache table for path alias lookup.';
723
  $schema['cache_registry'] = $schema['cache'];
724
  $schema['cache_registry']['description'] = 'Cache table for the code registry system to remember what code files need to be loaded on any given page.';
725 726

  $schema['files'] = array(
727
    'description' => 'Stores information for uploaded files.',
728
    'fields' => array(
729
      'fid' => array(
730
        'description' => 'File ID.',
731 732
        'type' => 'serial',
        'unsigned' => TRUE,
733 734
        'not null' => TRUE,
      ),
735
      'uid' => array(
736
        'description' => 'The {users}.uid of the user who is associated with the file.',
737 738 739
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
740 741
        'default' => 0,
      ),
742
      'filename' => array(
743
        'description' => 'Name of the file with no path components. This may differ from the basename of the filepath if the file is renamed to avoid overwriting an existing file.',
744 745 746
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
747 748
        'default' => '',
      ),
749
      'filepath' => array(
750
        'description' => 'Path of the file relative to Drupal root.',
751 752 753
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
754 755
        'default' => '',
      ),
756
      'filemime' => array(
757
        'description' => "The file's MIME type.",
758 759 760
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
761 762
        'default' => '',
      ),
763
      'filesize' => array(
764
        'description' => 'The size of the file in bytes.',
765 766 767
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
768 769
        'default' => 0,
      ),
770
      'status' => array(
771
        'description' => 'A bitmapped field indicating the status of the file. The least significant bit indicates temporary (0) or permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.',
772 773
        'type' => 'int',
        'not null' => TRUE,
774 775
        'default' => 0,
      ),
776
      'timestamp' => array(
777
        'description' => 'UNIX timestamp for when the file was added.',
778 779 780
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
781
        'default' => 0,
782
      ),
783
    ),
784
    'indexes' => array(
785 786
      'uid' => array('uid'),
      'status' => array('status'),
787
      'timestamp' => array('timestamp'),
788
    ),
789
    'primary key' => array('fid'),
790 791 792
    'foreign keys' => array(
      'uid' => array('users' => 'uid'),
    ),
793
  );
794 795

  $schema['flood'] = array(
796
    'description' => 'Flood controls the threshold of events, such as the number of contact attempts.',
797
    'fields' => array(
798
      'fid' => array(
799
        'description' => 'Unique flood event ID.',
800
        'type' => 'serial',
801 802
        'not null' => TRUE,
      ),
803
      'event' => array(
804
        'description' => 'Name of event (e.g. contact).',
805 806 807
        'type' => 'varchar',
        'length' => 64,
        'not null' => TRUE,
808 809
        'default' => '',
      ),
810
      'hostname' => array(
811
        'description' => 'Hostname of the visitor.',
812 813 814
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
815 816
        'default' => '',
      ),
817
      'timestamp' => array(
818
        'description' => 'Timestamp of the event.',
819 820
        'type' => 'int',
        'not null' => TRUE,
821
        'default' => 0,
822
      ),
823
    ),
824
    'primary key' => array('fid'),
825 826 827
    'indexes' => array(
      'allow' => array('event', 'hostname', 'timestamp'),
    ),
828
  );
829 830

  $schema['history'] = array(
831
    'description' => 'A record of which {users} have read which {node}s.',
832
    'fields' => array(