system.install 82.9 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
  if ($phase == 'runtime') {
270
    // Check for update status module.
271
272
273
    if (!module_exists('update')) {
      $requirements['update status'] = array(
        'value' => $t('Not enabled'),
274
        'severity' => REQUIREMENT_WARNING,
275
276
277
278
279
280
281
282
283
        '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');
284
285
286
287
288
289
290
291
292
293

    // 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.'),
      );
    }
294
295
  }

296
297
298
  return $requirements;
}

299
/**
300
 * Implement hook_install().
301
 */
302
function system_install() {
303
  if (db_driver() == 'pgsql') {
304
305
    // 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
306
    // example, by the test framework when creating prefixed test databases),
307
308
    // the global names will already exist. We therefore avoid trying to create
    // them again in that case.
309

310
    // Create functions.
311
312
313
314
315
316
317
318
    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\''
    );
319
    // Don't use {} around pg_proc table.
320
    if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) {
321
322
      db_query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
        \'SELECT random();\'
323
324
        LANGUAGE \'sql\''
      );
325
    }
326

327
    // Don't use {} around pg_proc table.
328
    if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'concat'")->fetchField()) {
329
330
      db_query('CREATE OR REPLACE FUNCTION "concat"(text, text) RETURNS text AS
        \'SELECT $1 || $2;\'
331
332
        LANGUAGE \'sql\''
      );
333
334
335
336
337
338
339
340
341
    }
    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\''
    );
342

343
    db_query('CREATE OR REPLACE FUNCTION "substring_index"(text, text, integer) RETURNS text AS
344
345
346
      \'SELECT array_to_string((string_to_array($1, $2)) [1:$3], $2);\'
      LANGUAGE \'sql\''
    );
347
  }
348

349
  // Create tables.
350
  $modules = array('system', 'filter', 'user', 'node');
351
352
  foreach ($modules as $module) {
    drupal_install_schema($module);
353
  }
354

355
  // Load system theme data appropriately.
356
  system_get_theme_data();
357

358
359
360
361
  // 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.
362
  db_query("INSERT INTO {users} (name, mail) VALUES('%s', '%s')", '', '');
363
364
365
366
  // 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.
367
  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()));
368
369
  // This sets the above two users uid 0 (anonymous). We avoid an explicit 0
  // otherwise MySQL might insert the next auto_increment value.
370
  db_query("UPDATE {users} SET uid = uid - uid WHERE name = '%s'", '');
371
  // This sets uid 1 (superuser). We skip uid 2 but that's not a big problem.
372
  db_query("UPDATE {users} SET uid = 1 WHERE name = '%s'", 'placeholder-for-uid-1');
373

374
  // Built-in roles.
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
  $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();

  // Sanity check to ensure the anonymous and authenticated role IDs are the 
  // same as the drupal defined constants. In certain situations, this will 
  // 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();
  }
401

402
  $query = db_insert('role_permission')->fields(array('rid', 'permission'));
403
  // Anonymous role permissions.
404
405
406
407
  $query->values(array(
    'rid' => DRUPAL_ANONYMOUS_RID,
    'permission' => 'access content',
  ));
408
409

  // Authenticated role permissions.
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  foreach (array('access comments', 'access content', 'post comments', 'post comments without approval') as $permission) {
    $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();
436

437
  // Add text formats.
438
439
440
441
442
443
444
445
446
447
448
449
450
451
  $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();
452

453
  // Enable filters for each text format.
454
455

  // Filtered HTML:
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  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,
    ))
486
  // Full HTML:
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
    // 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'));
514

515
  $cron_key = md5(mt_rand());
516

517
  variable_set('cron_key', $cron_key);
518
519
}

520
/**
521
 * Implement hook_schema().
522
523
524
525
526
527
 */
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(
528
    '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.',
529
    'fields' => array(
530
      'name' => array(
531
        'description' => 'The name of the variable.',
532
533
534
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
535
536
        'default' => '',
      ),
537
      'value' => array(
538
        'description' => 'The value of the variable.',
539
540
        'type' => 'text',
        'not null' => TRUE,
541
        'size' => 'big',
542
      ),
543
    ),
544
    'primary key' => array('name'),
545
  );
546
547

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

  $schema['actions_aid'] = array(
589
    'description' => 'Stores action IDs for non-default actions.',
590
    'fields' => array(
591
      'aid' => array(
592
        'description' => 'Primary Key: Unique actions ID.',
593
594
        'type' => 'serial',
        'unsigned' => TRUE,
595
        'not null' => TRUE,
596
      ),
597
    ),
598
    'primary key' => array('aid'),
599
  );
600
601

  $schema['batch'] = array(
602
    'description' => 'Stores details about batches (processes that run in multiple HTTP requests).',
603
    'fields' => array(
604
      'bid' => array(
605
        'description' => 'Primary Key: Unique batch ID.',
606
607
        'type' => 'serial',
        'unsigned' => TRUE,
608
609
        'not null' => TRUE,
      ),
610
      'token' => array(
611
        '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.",
612
613
        'type' => 'varchar',
        'length' => 64,
614
615
        'not null' => TRUE,
      ),
616
      'timestamp' => array(
617
        'description' => 'A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.',
618
        'type' => 'int',
619
620
        'not null' => TRUE,
      ),
621
      'batch' => array(
622
        'description' => 'A serialized array containing the processing data for the batch.',
623
624
        'type' => 'text',
        'not null' => FALSE,
625
        'size' => 'big',
626
      ),
627
    ),
628
    'primary key' => array('bid'),
629
630
631
632
    'indexes' => array(
      'token' => array('token'),
    ),
  );
633

634
  $schema['blocked_ips'] = array(
635
    'description' => 'Stores blocked IP addresses.',
636
637
    'fields' => array(
       'iid' => array(
638
        'description' => 'Primary Key: unique ID for IP addresses.',
639
640
641
642
643
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'ip' => array(
644
        'description' => 'IP address',
645
646
647
648
649
650
651
652
653
654
655
656
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
        'default' => '',
      ),
    ),
    'indexes' => array(
      'blocked_ip' => array('ip'),
    ),
    'primary key' => array('iid'),
  );

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

  $schema['cache_form'] = $schema['cache'];
705
  $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.';
706
  $schema['cache_page'] = $schema['cache'];
707
  $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
708
  $schema['cache_menu'] = $schema['cache'];
709
  $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.';
710
711
  $schema['cache_path'] = $schema['cache'];
  $schema['cache_path']['description'] = 'Cache table for path alias lookup.';
712
  $schema['cache_registry'] = $schema['cache'];
713
  $schema['cache_registry']['description'] = 'Cache table for the code registry system to remember what code files need to be loaded on any given page.';
714
715

  $schema['files'] = array(
716
    'description' => 'Stores information for uploaded files.',
717
    'fields' => array(
718
      'fid' => array(
719
        'description' => 'File ID.',
720
721
        'type' => 'serial',
        'unsigned' => TRUE,
722
723
        'not null' => TRUE,
      ),
724
      'uid' => array(
725
        'description' => 'The {users}.uid of the user who is associated with the file.',
726
727
728
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
729
730
        'default' => 0,
      ),
731
      'filename' => array(
732
        '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.',
733
734
735
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
736
737
        'default' => '',
      ),
738
      'filepath' => array(
739
        'description' => 'Path of the file relative to Drupal root.',
740
741
742
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
743
744
        'default' => '',
      ),
745
      'filemime' => array(
746
        'description' => "The file's MIME type.",
747
748
749
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
750
751
        'default' => '',
      ),
752
      'filesize' => array(
753
        'description' => 'The size of the file in bytes.',
754
755
756
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
757
758
        'default' => 0,
      ),
759
      'status' => array(
760
        '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.',
761
762
        'type' => 'int',
        'not null' => TRUE,
763
764
        'default' => 0,
      ),
765
      'timestamp' => array(
766
        'description' => 'UNIX timestamp for when the file was added.',
767
768
769
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
770
        'default' => 0,
771
      ),
772
    ),
773
    'indexes' => array(
774
775
      'uid' => array('uid'),
      'status' => array('status'),
776
      'timestamp' => array('timestamp'),
777
    ),
778
    'primary key' => array('fid'),
779
780
781
    'foreign keys' => array(
      'uid' => array('users' => 'uid'),
    ),
782
  );
783
784

  $schema['flood'] = array(
785
    'description' => 'Flood controls the threshold of events, such as the number of contact attempts.',
786
    'fields' => array(
787
      'fid' => array(
788
        'description' => 'Unique flood event ID.',
789
        'type' => 'serial',
790
791
        'not null' => TRUE,
      ),
792
      'event' => array(
793
        'description' => 'Name of event (e.g. contact).',
794
795
796
        'type' => 'varchar',
        'length' => 64,
        'not null' => TRUE,
797
798
        'default' => '',
      ),
799
      'hostname' => array(
800
        'description' => 'Hostname of the visitor.',
801
802
803
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
804
805
        'default' => '',
      ),
806
      'timestamp' => array(
807
        'description' => 'Timestamp of the event.',
808
809
        'type' => 'int',
        'not null' => TRUE,
810
        'default' => 0,
811
      ),
812
    ),
813
    'primary key' => array('fid'),
814
815
816
    'indexes' => array(
      'allow' => array('event', 'hostname', 'timestamp'),
    ),
817
  );
818
819

  $schema['history'] = array(
820
    'description' => 'A record of which {users} have read which {node}s.',
821
    'fields' => array(
822
      'uid' => array(
823
        'description' => 'The {users}.uid that read the {node} nid.',
824
825
        'type' => 'int',
        'not null' => TRUE,
826
827
        'default' => 0,
      ),
828
      'nid' => array(
829
        'description' => 'The {node}.nid that was read.',
830
831
        'type' => 'int',
        'not null' => TRUE,
832
833
        'default' => 0,
      ),
834
      'timestamp' => array(
835
        'description' => 'The Unix timestamp at which the read occurred.',
836
837
        'type' => 'int',
        'not null' => TRUE,
838
        'default' => 0,
839
      ),
840
    ),
841
    'primary key' => array('uid', 'nid'),
842
843
844
    'indexes' => array(
      'nid' => array('nid'),
    ),
845
  );
846
  $schema['menu_router'] = array(
847
    'description' => 'Maps paths to various callbacks (access, page and title)',
848
    'fields' => array(
849
      'path' => array(
850
        'description' => 'Primary Key: the Drupal path this entry describes',
851
852
853
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
854
855
        'default' => '',
      ),
856
      'load_functions' => array(
857
        '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.',
858
        'type' => 'text',
859
        'not null' => TRUE,
860
      ),
861
      'to_arg_functions' => array(
862
        '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.',
863
        'type' => 'text',
864
        'not null' => TRUE,
865
      ),
866
      'access_callback' => array(
867
        'description' => 'The callback which determines the access to this router path. Defaults to user_access.',
868
869
870
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
871
872
        'default' => '',
      ),
873
      'access_arguments' => array(
874
        'description' => 'A serialized array of arguments for the access callback.',
875
        'type' => 'text',
876
877
        'not null' => FALSE,
      ),
878
      'page_callback' => array(
879
        'description' => 'The name of the function that renders the page.',
880
881
882
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
883
884
        'default' => '',
      ),
885
      'page_arguments' => array(
886
        'description' => 'A serialized array of arguments for the page callback.',
887
        'type' => 'text',
888
889
        'not null' => FALSE,
      ),
890
      'fit' => array(
891
        'description' => 'A numeric representation of how specific the path is.',
892
893
        'type' => 'int',
        'not null' => TRUE,
894
895
        'default' => 0,
      ),
896
      'number_parts' => array(
897
        'description' => 'Number of parts in this router path.',
898
899
900
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
901
902
        'size' => 'small',
      ),
903
      'tab_parent' => array(
904
        'description' => 'Only for local tasks (tabs) - the router path of the parent page (which may also be a local task).',
905
906
907
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
908
909
        'default' => '',
      ),
910
      'tab_root' => array(
911
        '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.',
912
913
914
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
915
916
        'default' => '',
      ),
917
      'title' => array(
918
        'description' => 'The title for the current page, or the title for the tab if this is a local task.',
919
920
921
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
922
923
        'default' => '',
      ),
924
      'title_callback' => array(
925
        'description' => 'A function which will alter the title. Defaults to t()',
926
927
928
        'type' => 'varchar',
        'length'