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

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

  // Report Drupal version
  if ($phase == 'runtime') {
    $requirements['drupal'] = array(
      'title' => $t('Drupal'),
      'value' => VERSION,
Steven Wittens's avatar
Steven Wittens committed
23
24
      'severity' => REQUIREMENT_INFO,
      'weight' => -10,
25
26
27
    );
  }

28
  // Web server information.
Steven Wittens's avatar
Steven Wittens committed
29
  $software = $_SERVER['SERVER_SOFTWARE'];
30
31
  $requirements['webserver'] = array(
    'title' => $t('Web server'),
Steven Wittens's avatar
Steven Wittens committed
32
    'value' => $software,
33
34
35
36
37
  );

  // Test PHP version
  $requirements['php'] = array(
    'title' => $t('PHP'),
38
    'value' => ($phase == 'runtime') ? l(phpversion(), 'admin/reports/status/php') : phpversion(),
39
40
41
42
43
  );
  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;
  }
44

45
46
47
48
49
  // Test PHP register_globals setting.
  $requirements['php_register_globals'] = array(
    'title' => $t('PHP register globals'),
  );
  $register_globals = trim(ini_get('register_globals'));
50
  // Unfortunately, ini_get() may return many different values, and we can't
51
52
53
54
55
56
57
58
59
60
61
62
63
  // 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');
  }

64
  // Test PHP memory_limit
65
  $memory_limit = ini_get('memory_limit');
66
67
  $requirements['php_memory_limit'] = array(
    'title' => $t('PHP memory limit'),
68
    'value' => $memory_limit,
69
  );
70

71
72
73
74
  if ($memory_limit && parse_size($memory_limit) < parse_size(DRUPAL_MINIMUM_PHP_MEMORY_LIMIT)) {
    $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));
75
76
77
    }
    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));
78
    }
79
80
    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));
81
    }
82

83
84
    if (!empty($description)) {
      if ($php_ini_path = get_cfg_var('cfg_file_path')) {
85
        $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));
86
87
      }
      else {
88
        $description .= ' ' . $t('Contact your system administrator or hosting provider for assistance with increasing your PHP memory limit.');
89
      }
90

91
      $requirements['php_memory_limit']['description'] = $description . ' ' . $t('See the <a href="@url">Drupal requirements</a> for more information.', array('@url' => 'http://drupal.org/requirements'));
92
      $requirements['php_memory_limit']['severity'] = REQUIREMENT_WARNING;
93
    }
94
  }
95

96
97
98
99
100
101
102
103
  // Test DB version
  global $db_type;
  if (function_exists('db_status_report')) {
    $requirements += db_status_report($phase);
  }

  // Test settings.php file writability
  if ($phase == 'runtime') {
104
    $conf_dir = drupal_verify_install_file(conf_path(), FILE_NOT_WRITABLE, 'dir');
105
    $conf_file = drupal_verify_install_file(conf_path() . '/settings.php', FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE);
106
    if (!$conf_dir || !$conf_file) {
107
108
109
      $requirements['settings.php'] = array(
        'value' => $t('Not protected'),
        'severity' => REQUIREMENT_ERROR,
110
        'description' => '',
111
      );
112
113
114
115
      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) {
116
        $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'));
117
      }
118
119
120
121
122
123
124
125
126
    }
    else {
      $requirements['settings.php'] = array(
        'value' => $t('Protected'),
      );
    }
    $requirements['settings.php']['title'] = $t('Configuration file');
  }

127
  // Report cron status.
128
  if ($phase == 'runtime') {
129
130
131
132
133
    // 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.
134
    $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'));
135

136
    // Determine when cron last ran.
137
    $cron_last = variable_get('cron_last');
138
139
140
    if (!is_numeric($cron_last)) {
      $cron_last = variable_get('install_time', 0);
    }
141

142
143
    // Determine severity based on time since cron last ran.
    $severity = REQUIREMENT_OK;
144
    if (REQUEST_TIME - $cron_last > $threshold_error) {
145
      $severity = REQUIREMENT_ERROR;
146
    }
147
    elseif (REQUEST_TIME - $cron_last > $threshold_warning) {
148
149
150
151
      $severity = REQUIREMENT_WARNING;
    }

    // Set summary and description based on values determined above.
152
153
154
155
    $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;
156
    }
157

158
    $description .= ' ' . $t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/reports/status/run-cron')));
159
    $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')))));
160

161
162
163
164
    $requirements['cron'] = array(
      'title' => $t('Cron maintenance tasks'),
      'severity' => $severity,
      'value' => $summary,
165
      'description' => $description
166
    );
167
168
  }

169
  // Test files directory
170
  $directory = file_directory_path();
171
172
173
  $requirements['file system'] = array(
    'title' => $t('File system'),
  );
174
175
176
177
178
179

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

180
181
182
  $is_writable = is_writable($directory);
  $is_directory = is_dir($directory);
  if (!$is_writable || !$is_directory) {
183
184
    $description = '';
    $requirements['file system']['value'] = $t('Not writable');
185
186
    if (!$is_directory) {
      $error = $t('The directory %directory does not exist.', array('%directory' => $directory));
187
188
    }
    else {
189
190
      $error = $t('The directory %directory is not writable.', array('%directory' => $directory));
    }
191
    // The files directory requirement check is done only during install and runtime.
192
    if ($phase == 'runtime') {
193
      $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')));
194
    }
195
    elseif ($phase == 'install') {
196
197
      // For the installer UI, we need different wording. 'value' will
      // be treated as version, so provide none there.
198
      $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'));
199
200
      $requirements['file system']['value'] = '';
    }
201
202
203
204
    if (!empty($description)) {
      $requirements['file system']['description'] = $description;
      $requirements['file system']['severity'] = REQUIREMENT_ERROR;
    }
205
206
207
208
209
210
211
  }
  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)');
212
213
214
    }
  }

215
216
217
  // See if updates are available in update.php.
  if ($phase == 'runtime') {
    $requirements['update'] = array(
218
      'title' => $t('Database updates'),
219
220
221
222
223
224
225
226
227
228
229
230
      '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');
231
          $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'));
232
233
234
235
236
237
          break;
        }
      }
    }
  }

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  // 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');
  }

255
  // Test Unicode library
256
  include_once DRUPAL_ROOT . '/includes/unicode.inc';
257
258
  $requirements = array_merge($requirements, unicode_requirements());

259
  if ($phase == 'runtime') {
260
    // Check for update status module.
261
262
263
    if (!module_exists('update')) {
      $requirements['update status'] = array(
        'value' => $t('Not enabled'),
264
        'severity' => REQUIREMENT_WARNING,
265
266
267
268
269
270
271
272
273
        '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/build/modules'))),
      );
    }
    else {
      $requirements['update status'] = array(
        'value' => $t('Enabled'),
      );
    }
    $requirements['update status']['title'] = $t('Update notifications');
274
275
276
277
278
279
280
281
282
283

    // 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.'),
      );
    }
284
285
  }

286
287
288
  return $requirements;
}

289
290
291
/**
 * Implementation of hook_install().
 */
292
function system_install() {
293
  if (db_driver() == 'pgsql') {
294
295
    // 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
296
    // example, by the test framework when creating prefixed test databases),
297
298
    // the global names will already exist. We therefore avoid trying to create
    // them again in that case.
299

300
    // Create functions.
301
302
303
304
305
306
307
308
309
310
311
    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\''
    );
    if (!db_result(db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'"))) {
      db_query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
        \'SELECT random();\'
312
313
        LANGUAGE \'sql\''
      );
314
    }
315

316
317
318
    if (!db_result(db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'concat'"))) {
      db_query('CREATE OR REPLACE FUNCTION "concat"(text, text) RETURNS text AS
        \'SELECT $1 || $2;\'
319
320
        LANGUAGE \'sql\''
      );
321
322
323
324
325
326
327
328
329
    }
    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\''
    );
330
  }
331

332
  // Create tables.
333
  $modules = array('system', 'filter', 'user', 'node');
334
335
  foreach ($modules as $module) {
    drupal_install_schema($module);
336
  }
337

338
339
  // Load system theme data appropriately.
  system_theme_data();
340

341
342
343
344
345
346
347
348
349
  // 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.
  db_query("INSERT INTO {users} (name, mail) VALUES('%s', '%s')", '', '');
  // 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.
350
  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()));
351
352
353
354
355
  // This sets the above two users uid 0 (anonymous). We avoid an explicit 0
  // otherwise MySQL might insert the next auto_increment value.
  db_query("UPDATE {users} SET uid = uid - uid WHERE name = '%s'", '');
  // This sets uid 1 (superuser). We skip uid 2 but that's not a big problem.
  db_query("UPDATE {users} SET uid = 1 WHERE name = '%s'", 'placeholder-for-uid-1');
356

357
  // Built-in roles.
358
359
  db_query("INSERT INTO {role} (name) VALUES ('%s')", 'anonymous user');
  db_query("INSERT INTO {role} (name) VALUES ('%s')", 'authenticated user');
360

361
362
363
364
365
366
367
368
  // Anonymous role permissions.
  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 1, 'access content');

  // Authenticated role permissions.
  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'access comments');
  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'access content');
  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'post comments');
  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'post comments without approval');
369

370
371
  db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", 'theme_default', 's:7:"garland";');
  db_query("UPDATE {system} SET status = %d WHERE type = '%s' AND name = '%s'", 1, 'theme', 'garland');
372

373
  db_query("INSERT INTO {node_access} (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, '%s', %d, %d, %d)", 0, 0, 'all', 1, 0, 0);
374

375
  // Add text formats.
376
377
  db_query("INSERT INTO {filter_format} (name, roles, cache) VALUES ('%s', '%s', %d)", 'Filtered HTML', ',1,2,', 1);
  db_query("INSERT INTO {filter_format} (name, roles, cache) VALUES ('%s', '%s', %d)", 'Full HTML', '', 1);
378

379
  // Enable filters for each text format.
380
381
382

  // Filtered HTML:
  // URL filter.
383
  db_query("INSERT INTO {filter} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 2, 0);
384
  // HTML filter.
385
  db_query("INSERT INTO {filter} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 0, 1);
386
  // Line break filter.
387
  db_query("INSERT INTO {filter} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 1, 2);
388
  // HTML corrector filter.
389
  db_query("INSERT INTO {filter} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 3, 10);
390
391
392

  // Full HTML:
  // URL filter.
393
  db_query("INSERT INTO {filter} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 2, 0);
394
  // Line break filter.
395
  db_query("INSERT INTO {filter} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 1, 1);
396
  // HTML corrector filter.
397
  db_query("INSERT INTO {filter} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 3, 10);
398

399
  db_query("INSERT INTO {variable} (name, value) VALUES ('%s','%s')", 'filter_html_1', 'i:1;');
400

401
  db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", 'node_options_forum', 'a:1:{i:0;s:6:"status";}');
402

403
  $cron_key = serialize(md5(mt_rand()));
404

405
  db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", 'cron_key', $cron_key);
406
407
}

408
409
410
411
412
413
414
415
/**
 * Implementation of hook_schema().
 */
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(
416
    '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.',
417
    'fields' => array(
418
      'name' => array(
419
        'description' => 'The name of the variable.',
420
421
422
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
423
424
        'default' => '',
      ),
425
      'value' => array(
426
        'description' => 'The value of the variable.',
427
428
        'type' => 'text',
        'not null' => TRUE,
429
        'size' => 'big',
430
      ),
431
    ),
432
    'primary key' => array('name'),
433
  );
434
435

  $schema['actions'] = array(
436
    'description' => 'Stores action information.',
437
    'fields' => array(
438
      'aid' => array(
439
        'description' => 'Primary Key: Unique actions ID.',
440
441
442
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
443
444
        'default' => '0',
      ),
445
      'type' => array(
446
        'description' => 'The object that that action acts on (node, user, comment, system or custom types.)',
447
448
449
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
450
451
        'default' => '',
      ),
452
      'callback' => array(
453
        'description' => 'The callback function that executes when the action runs.',
454
455
456
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
457
458
        'default' => '',
      ),
459
      'parameters' => array(
460
        'description' => 'Parameters to be passed to the callback function.',
461
462
        'type' => 'text',
        'not null' => TRUE,
463
464
        'size' => 'big',
      ),
465
      'description' => array(
466
        'description' => 'Description of the action.',
467
468
469
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
470
        'default' => '0',
471
      ),
472
    ),
473
    'primary key' => array('aid'),
474
  );
475
476

  $schema['actions_aid'] = array(
477
    'description' => 'Stores action IDs for non-default actions.',
478
    'fields' => array(
479
      'aid' => array(
480
        'description' => 'Primary Key: Unique actions ID.',
481
482
        'type' => 'serial',
        'unsigned' => TRUE,
483
        'not null' => TRUE,
484
      ),
485
    ),
486
    'primary key' => array('aid'),
487
  );
488
489

  $schema['batch'] = array(
490
    'description' => 'Stores details about batches (processes that run in multiple HTTP requests).',
491
    'fields' => array(
492
      'bid' => array(
493
        'description' => 'Primary Key: Unique batch ID.',
494
495
        'type' => 'serial',
        'unsigned' => TRUE,
496
497
        'not null' => TRUE,
      ),
498
      'token' => array(
499
        '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.",
500
501
        'type' => 'varchar',
        'length' => 64,
502
503
        'not null' => TRUE,
      ),
504
      'timestamp' => array(
505
        'description' => 'A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.',
506
        'type' => 'int',
507
508
        'not null' => TRUE,
      ),
509
      'batch' => array(
510
        'description' => 'A serialized array containing the processing data for the batch.',
511
512
        'type' => 'text',
        'not null' => FALSE,
513
        'size' => 'big',
514
      ),
515
    ),
516
    'primary key' => array('bid'),
517
518
519
520
    'indexes' => array(
      'token' => array('token'),
    ),
  );
521

522
  $schema['blocked_ips'] = array(
523
    'description' => 'Stores blocked IP addresses.',
524
525
    'fields' => array(
       'iid' => array(
526
        'description' => 'Primary Key: unique ID for IP addresses.',
527
528
529
530
531
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'ip' => array(
532
        'description' => 'IP address',
533
534
535
536
537
538
539
540
541
542
543
544
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
        'default' => '',
      ),
    ),
    'indexes' => array(
      'blocked_ip' => array('ip'),
    ),
    'primary key' => array('iid'),
  );

545
  $schema['cache'] = array(
546
    'description' => 'Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.',
547
    'fields' => array(
548
      'cid' => array(
549
        'description' => 'Primary Key: Unique cache ID.',
550
551
552
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
553
554
        'default' => '',
      ),
555
      'data' => array(
556
        'description' => 'A collection of data to cache.',
557
558
        'type' => 'blob',
        'not null' => FALSE,
559
560
        'size' => 'big',
      ),
561
      'expire' => array(
562
        'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
563
564
        'type' => 'int',
        'not null' => TRUE,
565
566
        'default' => 0,
      ),
567
      'created' => array(
568
        'description' => 'A Unix timestamp indicating when the cache entry was created.',
569
570
        'type' => 'int',
        'not null' => TRUE,
571
572
        'default' => 0,
      ),
573
      'headers' => array(
574
        'description' => 'Any custom HTTP headers to be added to cached data.',
575
        'type' => 'text',
576
577
        'not null' => FALSE,
      ),
578
      'serialized' => array(
579
        'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
580
581
582
        'type' => 'int',
        'size' => 'small',
        'not null' => TRUE,
583
        'default' => 0,
584
      ),
585
586
587
588
    ),
    'indexes' => array(
      'expire' => array('expire'),
    ),
589
    'primary key' => array('cid'),
590
  );
591
592

  $schema['cache_form'] = $schema['cache'];
593
  $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.';
594
  $schema['cache_page'] = $schema['cache'];
595
  $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
596
  $schema['cache_menu'] = $schema['cache'];
597
  $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.';
598
  $schema['cache_registry'] = $schema['cache'];
599
  $schema['cache_registry']['description'] = 'Cache table for the code registry system to remember what code files need to be loaded on any given page.';
600
601

  $schema['files'] = array(
602
    'description' => 'Stores information for uploaded files.',
603
    'fields' => array(
604
      'fid' => array(
605
        'description' => 'File ID.',
606
607
        'type' => 'serial',
        'unsigned' => TRUE,
608
609
        'not null' => TRUE,
      ),
610
      'uid' => array(
611
        'description' => 'The {users}.uid of the user who is associated with the file.',
612
613
614
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
615
616
        'default' => 0,
      ),
617
      'filename' => array(
618
        '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.',
619
620
621
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
622
623
        'default' => '',
      ),
624
      'filepath' => array(
625
        'description' => 'Path of the file relative to Drupal root.',
626
627
628
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
629
630
        'default' => '',
      ),
631
      'filemime' => array(
632
        'description' => "The file's MIME type.",
633
634
635
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
636
637
        'default' => '',
      ),
638
      'filesize' => array(
639
        'description' => 'The size of the file in bytes.',
640
641
642
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
643
644
        'default' => 0,
      ),
645
      'status' => array(
646
        'description' => 'A bitmapped field indicating the status of the file the least sigifigant bit indicates temporary (1) or permanent (0). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.',
647
648
        'type' => 'int',
        'not null' => TRUE,
649
650
        'default' => 0,
      ),
651
      'timestamp' => array(
652
        'description' => 'UNIX timestamp for when the file was added.',
653
654
655
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
656
        'default' => 0,
657
      ),
658
    ),
659
    'indexes' => array(
660
661
      'uid' => array('uid'),
      'status' => array('status'),
662
      'timestamp' => array('timestamp'),
663
    ),
664
    'primary key' => array('fid'),
665
  );
666
667

  $schema['flood'] = array(
668
    'description' => 'Flood controls the threshold of events, such as the number of contact attempts.',
669
    'fields' => array(
670
      'fid' => array(
671
        'description' => 'Unique flood event ID.',
672
        'type' => 'serial',
673
674
        'not null' => TRUE,
      ),
675
      'event' => array(
676
        'description' => 'Name of event (e.g. contact).',
677
678
679
        'type' => 'varchar',
        'length' => 64,
        'not null' => TRUE,
680
681
        'default' => '',
      ),
682
      'hostname' => array(
683
        'description' => 'Hostname of the visitor.',
684
685
686
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
687
688
        'default' => '',
      ),
689
      'timestamp' => array(
690
        'description' => 'Timestamp of the event.',
691
692
        'type' => 'int',
        'not null' => TRUE,
693
        'default' => 0,
694
      ),
695
    ),
696
    'primary key' => array('fid'),
697
698
699
    'indexes' => array(
      'allow' => array('event', 'hostname', 'timestamp'),
    ),
700
  );
701
702

  $schema['history'] = array(
703
    'description' => 'A record of which {users} have read which {node}s.',
704
    'fields' => array(
705
      'uid' => array(
706
        'description' => 'The {users}.uid that read the {node} nid.',
707
708
        'type' => 'int',
        'not null' => TRUE,
709
710
        'default' => 0,
      ),
711
      'nid' => array(
712
        'description' => 'The {node}.nid that was read.',
713
714
        'type' => 'int',
        'not null' => TRUE,
715
716
        'default' => 0,
      ),
717
      'timestamp' => array(
718
        'description' => 'The Unix timestamp at which the read occurred.',
719
720
        'type' => 'int',
        'not null' => TRUE,
721
        'default' => 0,
722
      ),
723
    ),
724
    'primary key' => array('uid', 'nid'),
725
726
727
    'indexes' => array(
      'nid' => array('nid'),
    ),
728
  );
729
  $schema['menu_router'] = array(
730
    'description' => 'Maps paths to various callbacks (access, page and title)',
731
    'fields' => array(
732
      'path' => array(
733
        'description' => 'Primary Key: the Drupal path this entry describes',
734
735
736
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
737
738
        'default' => '',
      ),
739
      'load_functions' => array(
740
        'description' => 'A serialized array of function names (like node_load) to be called to load an object corresponding to a part of the current path.',
741
        'type' => 'text',
742
        'not null' => TRUE,
743
      ),
744
      'to_arg_functions' => array(
745
        'description' => 'A serialized array of function names (like user_uid_optional_to_arg) to be called to replace a part of the router path with another string.',
746
        'type' => 'text',
747
        'not null' => TRUE,
748
      ),
749
      'access_callback' => array(
750
        'description' => 'The callback which determines the access to this router path. Defaults to user_access.',
751
752
753
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
754
755
        'default' => '',
      ),
756
      'access_arguments' => array(
757
        'description' => 'A serialized array of arguments for the access callback.',
758
        'type' => 'text',
759
760
        'not null' => FALSE,
      ),
761
      'page_callback' => array(
762
        'description' => 'The name of the function that renders the page.',
763
764
765
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
766
767
        'default' => '',
      ),
768
      'page_arguments' => array(
769
        'description' => 'A serialized array of arguments for the page callback.',
770
        'type' => 'text',
771
772
        'not null' => FALSE,
      ),
773
      'fit' => array(
774
        'description' => 'A numeric representation of how specific the path is.',
775
776
        'type' => 'int',
        'not null' => TRUE,
777
778
        'default' => 0,
      ),
779
      'number_parts' => array(
780
        'description' => 'Number of parts in this router path.',
781
782
783
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
784
785
        'size' => 'small',
      ),
786
      'tab_parent' => array(
787
        'description' => 'Only for local tasks (tabs) - the router path of the parent page (which may also be a local task).',
788
789
790
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
791
792
        'default' => '',
      ),
793
      'tab_root' => array(
794
        'description' => 'Router path of the closest non-tab parent page. For pages that are not local tasks, this will be the same as the path.',
795
796
797
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
798
799
        'default' => '',
      ),
800
      'title' => array(
801
        'description' => 'The title for the current page, or the title for the tab if this is a local task.',
802
803
804
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
805
806
        'default' => '',
      ),
807
      'title_callback' => array(
808
        'description' => 'A function which will alter the title. Defaults to t()',
809
810
811
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
812
813
        'default' => '',
      ),
814
      'title_arguments' => array(
815
        'description' => 'A serialized array of arguments for the title callback. If empty, the title will be used as the sole argument for the title callback.',
816
817
818
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
819
820
        'default' => '',
      ),
821
      'type' => array(
822
        'description' => 'Numeric representation of the type of the menu item, like MENU_LOCAL_TASK.',
823
824
        'type' => 'int',
        'not null' => TRUE,