system.install 125 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
13
14
 */
function system_requirements($phase) {
  $requirements = array();
  // Ensure translations don't break at install time
15
  $t = get_t();
16
17
18
19
20
21

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

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

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

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

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

70
71
72
73
  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));
74
75
76
    }
    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));
77
    }
78
79
    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));
80
    }
81

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

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

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

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

    // Determine when cron last ran. If never, use the install time to
    // determine the warning or error status.
137
    $cron_last = variable_get('cron_last', NULL);
138
139
140
141
142
    $never_run = FALSE;
    if (!is_numeric($cron_last)) {
      $never_run = TRUE;
      $cron_last = variable_get('install_time', 0);
    }
143

144
145
    // Determine severity based on time since cron last ran.
    $severity = REQUIREMENT_OK;
146
    if (REQUEST_TIME - $cron_last > $threshold_error) {
147
      $severity = REQUIREMENT_ERROR;
148
    }
149
    else if ($never_run || (REQUEST_TIME - $cron_last > $threshold_warning)) {
150
151
152
153
154
155
      $severity = REQUIREMENT_WARNING;
    }

    // If cron hasn't been run, and the user is viewing the main
    // administration page, instead of an error, we display a helpful reminder
    // to configure cron jobs.
156
    if ($never_run && $severity != REQUIREMENT_ERROR && $_GET['q'] == 'admin' && user_access('administer site configuration')) {
157
      drupal_set_message($t('Cron has not run. Please visit the <a href="@status">status report</a> for more information.', array('@status' => url('admin/reports/status'))));
158
159
    }

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

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

176
177
178
179
    $requirements['cron'] = array(
      'title' => $t('Cron maintenance tasks'),
      'severity' => $severity,
      'value' => $summary,
180
      'description' => $description
181
    );
182
183
  }

184
  // Test files directory
185
  $directory = file_directory_path();
186
187
188
  $requirements['file system'] = array(
    'title' => $t('File system'),
  );
189
190
191
192
193
194

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

195
196
197
  $is_writable = is_writable($directory);
  $is_directory = is_dir($directory);
  if (!$is_writable || !$is_directory) {
198
199
    $description = '';
    $requirements['file system']['value'] = $t('Not writable');
200
201
    if (!$is_directory) {
      $error = $t('The directory %directory does not exist.', array('%directory' => $directory));
202
203
    }
    else {
204
205
      $error = $t('The directory %directory is not writable.', array('%directory' => $directory));
    }
206
    // The files directory requirement check is done only during install and runtime.
207
    if ($phase == 'runtime') {
208
      $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')));
209
    }
210
    elseif ($phase == 'install') {
211
212
      // For the installer UI, we need different wording. 'value' will
      // be treated as version, so provide none there.
213
      $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'));
214
215
      $requirements['file system']['value'] = '';
    }
216
217
218
219
    if (!empty($description)) {
      $requirements['file system']['description'] = $description;
      $requirements['file system']['severity'] = REQUIREMENT_ERROR;
    }
220
221
222
223
224
225
226
  }
  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)');
227
228
229
    }
  }

230
231
232
  // See if updates are available in update.php.
  if ($phase == 'runtime') {
    $requirements['update'] = array(
233
      'title' => $t('Database updates'),
234
235
236
237
238
239
240
241
242
243
244
245
      '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');
246
          $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'));
247
248
249
250
251
252
          break;
        }
      }
    }
  }

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  // 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');
  }

270
  // Test Unicode library
271
  include_once DRUPAL_ROOT . '/includes/unicode.inc';
272
273
  $requirements = array_merge($requirements, unicode_requirements());

274
275
276
277
278
  // Check for update status module.
  if ($phase == 'runtime') {
    if (!module_exists('update')) {
      $requirements['update status'] = array(
        'value' => $t('Not enabled'),
279
        'severity' => REQUIREMENT_WARNING,
280
281
282
283
284
285
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/build/modules'))),
      );
    }
    else {
      $requirements['update status'] = array(
        'value' => $t('Enabled'),
      );
287
288
289
290
291
292
293
294
      if (variable_get('drupal_http_request_fails', FALSE)) {
        $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.'),
        );
      }
295
296
297
298
    }
    $requirements['update status']['title'] = $t('Update notifications');
  }

299
300
301
  return $requirements;
}

302
303
304
/**
 * Implementation of hook_install().
 */
305
function system_install() {
306
  if (db_driver() == 'pgsql') {
307
308
309
310
311
312
    // We create some custom types and functions using global names instead of
    // prefixing them like we do with table names. If this function is ever
    // called again (for example, by the test framework when creating prefixed
    // test databases), the global names will already exist. We therefore avoid
    // trying to create them again in that case.

313
    // Create unsigned types.
314
315
316
317
318
319
320
321
322
    if (!db_result(db_query("SELECT COUNT(*) FROM pg_constraint WHERE conname = 'int_unsigned_check'"))) {
      db_query("CREATE DOMAIN int_unsigned integer CHECK (VALUE >= 0)");
    }
    if (!db_result(db_query("SELECT COUNT(*) FROM pg_constraint WHERE conname = 'smallint_unsigned_check'"))) {
      db_query("CREATE DOMAIN smallint_unsigned smallint CHECK (VALUE >= 0)");
    }
    if (!db_result(db_query("SELECT COUNT(*) FROM pg_constraint WHERE conname = 'bigint_unsigned_check'"))) {
      db_query("CREATE DOMAIN bigint_unsigned bigint CHECK (VALUE >= 0)");
    }
323

324
    // Create functions.
325
326
327
328
329
330
331
332
333
334
335
    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();\'
336
337
        LANGUAGE \'sql\''
      );
338
    }
339

340
341
342
    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;\'
343
344
        LANGUAGE \'sql\''
      );
345
346
347
348
349
350
351
352
353
    }
    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\''
    );
354
  }
355

356
  // Create tables.
357
  $modules = array('system', 'filter', 'block', 'user', 'node', 'comment', 'taxonomy');
358
359
  foreach ($modules as $module) {
    drupal_install_schema($module);
360
  }
361

362
363
  // Load system theme data appropriately.
  system_theme_data();
364

365
366
367
368
369
370
371
372
373
  // 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.
374
  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()));
375
376
377
378
379
  // 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');
380

381
  // Built-in roles.
382
383
  db_query("INSERT INTO {role} (name) VALUES ('%s')", 'anonymous user');
  db_query("INSERT INTO {role} (name) VALUES ('%s')", 'authenticated user');
384

385
386
387
388
389
390
391
392
  // 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');
393

394
395
  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');
396
397
398
  db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'user', 'login', 'garland', 1, 0, 'left', '', -1);
  db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'user', 'navigation', 'garland', 1, 0, 'left', '', -1);
  db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'system', 'powered-by', 'garland', 1, 10, 'footer', '', -1);
399

400
  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);
401

402
  // Add input formats.
403
404
  db_query("INSERT INTO {filter_formats} (name, roles, cache) VALUES ('%s', '%s', %d)", 'Filtered HTML', ',1,2,', 1);
  db_query("INSERT INTO {filter_formats} (name, roles, cache) VALUES ('%s', '%s', %d)", 'Full HTML', '', 1);
405
406
407
408
409

  // Enable filters for each input format.

  // Filtered HTML:
  // URL filter.
410
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 2, 0);
411
  // HTML filter.
412
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 0, 1);
413
  // Line break filter.
414
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 1, 2);
415
  // HTML corrector filter.
416
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 3, 10);
417
418
419

  // Full HTML:
  // URL filter.
420
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 2, 0);
421
  // Line break filter.
422
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 1, 1);
423
  // HTML corrector filter.
424
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 3, 10);
425

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

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

430
  $cron_key = serialize(md5(mt_rand()));
431

432
  db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", 'cron_key', $cron_key);
433
434
}

435
436
437
438
439
440
441
442
/**
 * 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(
443
    'description' => t('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.'),
444
    'fields' => array(
445
446
447
448
449
      'name' => array(
        'description' => t('The name of the variable.'),
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
450
451
        'default' => '',
      ),
452
453
454
455
      'value' => array(
        'description' => t('The value of the variable.'),
        'type' => 'text',
        'not null' => TRUE,
456
        'size' => 'big',
457
      ),
458
    ),
459
    'primary key' => array('name'),
460
  );
461
462

  $schema['actions'] = array(
463
    'description' => t('Stores action information.'),
464
    'fields' => array(
465
466
467
468
469
      'aid' => array(
        'description' => t('Primary Key: Unique actions ID.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
470
471
        'default' => '0',
      ),
472
473
474
475
476
      'type' => array(
        'description' => t('The object that that action acts on (node, user, comment, system or custom types.)'),
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
477
478
        'default' => '',
      ),
479
480
481
482
483
      'callback' => array(
        'description' => t('The callback function that executes when the action runs.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
484
485
        'default' => '',
      ),
486
487
488
489
      'parameters' => array(
        'description' => t('Parameters to be passed to the callback function.'),
        'type' => 'text',
        'not null' => TRUE,
490
491
        'size' => 'big',
      ),
492
493
494
495
496
      'description' => array(
        'description' => t('Description of the action.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
497
        'default' => '0',
498
      ),
499
    ),
500
    'primary key' => array('aid'),
501
  );
502
503

  $schema['actions_aid'] = array(
504
    'description' => t('Stores action IDs for non-default actions.'),
505
    'fields' => array(
506
507
508
509
      'aid' => array(
        'description' => t('Primary Key: Unique actions ID.'),
        'type' => 'serial',
        'unsigned' => TRUE,
510
        'not null' => TRUE,
511
      ),
512
    ),
513
    'primary key' => array('aid'),
514
  );
515
516

  $schema['batch'] = array(
517
    'description' => t('Stores details about batches (processes that run in multiple HTTP requests).'),
518
    'fields' => array(
519
520
521
522
      'bid' => array(
        'description' => t('Primary Key: Unique batch ID.'),
        'type' => 'serial',
        'unsigned' => TRUE,
523
524
        'not null' => TRUE,
      ),
525
526
527
528
      'token' => array(
        'description' => t("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."),
        'type' => 'varchar',
        'length' => 64,
529
530
        'not null' => TRUE,
      ),
531
532
533
      'timestamp' => array(
        'description' => t('A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.'),
        'type' => 'int',
534
535
        'not null' => TRUE,
      ),
536
537
538
539
      'batch' => array(
        'description' => t('A serialized array containing the processing data for the batch.'),
        'type' => 'text',
        'not null' => FALSE,
540
        'size' => 'big',
541
      ),
542
    ),
543
    'primary key' => array('bid'),
544
545
546
547
    'indexes' => array(
      'token' => array('token'),
    ),
  );
548

549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
  $schema['blocked_ips'] = array(
    'description' => t('Stores blocked IP addresses.'),
    'fields' => array(
       'iid' => array(
        'description' => t('Primary Key: unique ID for IP addresses.'),
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'ip' => array(
        'description' => t('IP address'),
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
        'default' => '',
      ),
    ),
    'indexes' => array(
      'blocked_ip' => array('ip'),
    ),
    'primary key' => array('iid'),
  );

572
  $schema['cache'] = array(
573
    'description' => t('Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.'),
574
    'fields' => array(
575
576
577
578
579
      'cid' => array(
        'description' => t('Primary Key: Unique cache ID.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
580
581
        'default' => '',
      ),
582
583
584
585
      'data' => array(
        'description' => t('A collection of data to cache.'),
        'type' => 'blob',
        'not null' => FALSE,
586
587
        'size' => 'big',
      ),
588
589
590
591
      'expire' => array(
        'description' => t('A Unix timestamp indicating when the cache entry should expire, or 0 for never.'),
        'type' => 'int',
        'not null' => TRUE,
592
593
        'default' => 0,
      ),
594
595
596
597
      'created' => array(
        'description' => t('A Unix timestamp indicating when the cache entry was created.'),
        'type' => 'int',
        'not null' => TRUE,
598
599
        'default' => 0,
      ),
600
601
602
      'headers' => array(
        'description' => t('Any custom HTTP headers to be added to cached data.'),
        'type' => 'text',
603
604
        'not null' => FALSE,
      ),
605
606
607
608
609
      'serialized' => array(
        'description' => t('A flag to indicate whether content is serialized (1) or not (0).'),
        'type' => 'int',
        'size' => 'small',
        'not null' => TRUE,
610
        'default' => 0,
611
      ),
612
613
614
615
    ),
    'indexes' => array(
      'expire' => array('expire'),
    ),
616
    'primary key' => array('cid'),
617
  );
618
619

  $schema['cache_form'] = $schema['cache'];
620
  $schema['cache_form']['description'] = t('Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.');
621
  $schema['cache_page'] = $schema['cache'];
622
  $schema['cache_page']['description'] = t('Cache table used to store compressed pages for anonymous users, if page caching is enabled.');
623
  $schema['cache_menu'] = $schema['cache'];
624
  $schema['cache_menu']['description'] = t('Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.');
625
626
  $schema['cache_registry'] = $schema['cache'];
  $schema['cache_registry']['description'] = t('Cache table for the code registry system to remember what code files need to be loaded on any given page.');
627
628

  $schema['files'] = array(
629
    'description' => t('Stores information for uploaded files.'),
630
    'fields' => array(
631
      'fid' => array(
632
        'description' => t('File ID.'),
633
634
        'type' => 'serial',
        'unsigned' => TRUE,
635
636
        'not null' => TRUE,
      ),
637
638
639
640
641
      'uid' => array(
        'description' => t('The {users}.uid of the user who is associated with the file.'),
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
642
643
        'default' => 0,
      ),
644
      'filename' => array(
645
        'description' => t('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.'),
646
647
648
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
649
650
        'default' => '',
      ),
651
652
653
654
655
      'filepath' => array(
        'description' => t('Path of the file relative to Drupal root.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
656
657
        'default' => '',
      ),
658
      'filemime' => array(
659
        'description' => t("The file's MIME type."),
660
661
662
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
663
664
        'default' => '',
      ),
665
666
667
668
669
      'filesize' => array(
        'description' => t('The size of the file in bytes.'),
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
670
671
        'default' => 0,
      ),
672
      'status' => array(
673
        'description' => t('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.'),
674
675
        'type' => 'int',
        'not null' => TRUE,
676
677
        'default' => 0,
      ),
678
679
680
681
682
      'timestamp' => array(
        'description' => t('UNIX timestamp for when the file was added.'),
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
683
        'default' => 0,
684
      ),
685
    ),
686
    'indexes' => array(
687
688
      'uid' => array('uid'),
      'status' => array('status'),
689
      'timestamp' => array('timestamp'),
690
    ),
691
    'primary key' => array('fid'),
692
  );
693
694

  $schema['flood'] = array(
695
    'description' => t('Flood controls the threshold of events, such as the number of contact attempts.'),
696
    'fields' => array(
697
698
699
      'fid' => array(
        'description' => t('Unique flood event ID.'),
        'type' => 'serial',
700
701
        'not null' => TRUE,
      ),
702
703
704
705
706
      'event' => array(
        'description' => t('Name of event (e.g. contact).'),
        'type' => 'varchar',
        'length' => 64,
        'not null' => TRUE,
707
708
        'default' => '',
      ),
709
710
711
712
713
      'hostname' => array(
        'description' => t('Hostname of the visitor.'),
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
714
715
        'default' => '',
      ),
716
717
718
719
      'timestamp' => array(
        'description' => t('Timestamp of the event.'),
        'type' => 'int',
        'not null' => TRUE,
720
        'default' => 0,
721
      ),
722
    ),
723
    'primary key' => array('fid'),
724
725
726
    'indexes' => array(
      'allow' => array('event', 'hostname', 'timestamp'),
    ),
727
  );
728
729

  $schema['history'] = array(
730
    'description' => t('A record of which {users} have read which {node}s.'),
731
    'fields' => array(
732
733
734
735
      'uid' => array(
        'description' => t('The {users}.uid that read the {node} nid.'),
        'type' => 'int',
        'not null' => TRUE,
736
737
        'default' => 0,
      ),
738
739
740
741
      'nid' => array(
        'description' => t('The {node}.nid that was read.'),
        'type' => 'int',
        'not null' => TRUE,
742
743
        'default' => 0,
      ),
744
745
746
747
      'timestamp' => array(
        'description' => t('The Unix timestamp at which the read occurred.'),
        'type' => 'int',
        'not null' => TRUE,
748
        'default' => 0,
749
      ),
750
    ),
751
    'primary key' => array('uid', 'nid'),
752
753
754
    'indexes' => array(
      'nid' => array('nid'),
    ),
755
  );
756
  $schema['menu_router'] = array(
757
    'description' => t('Maps paths to various callbacks (access, page and title)'),
758
    'fields' => array(
759
760
761
762
763
      'path' => array(
        'description' => t('Primary Key: the Drupal path this entry describes'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
764
765
        'default' => '',
      ),
766
767
      'load_functions' => array(
        'description' => t('A serialized array of function names (like node_load) to be called to load an object corresponding to a part of the current path.'),
768
        'type' => 'text',
769
        'not null' => TRUE,
770
      ),
771
      'to_arg_functions' => array(
772
        'description' => t('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.'),
773
        'type' => 'text',
774
        'not null' => TRUE,
775
      ),
776
777
778
779
780
      'access_callback' => array(
        'description' => t('The callback which determines the access to this router path. Defaults to user_access.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
781
782
        'default' => '',
      ),
783
784
785
      'access_arguments' => array(
        'description' => t('A serialized array of arguments for the access callback.'),
        'type' => 'text',
786
787
        'not null' => FALSE,
      ),
788
789
790
791
792
      'page_callback' => array(
        'description' => t('The name of the function that renders the page.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
793
794
        'default' => '',
      ),
795
796
797
      'page_arguments' => array(
        'description' => t('A serialized array of arguments for the page callback.'),
        'type' => 'text',
798
799
        'not null' => FALSE,
      ),
800
801
802
803
      'fit' => array(
        'description' => t('A numeric representation of how specific the path is.'),
        'type' => 'int',
        'not null' => TRUE,
804
805
        'default' => 0,
      ),
806
807
808
809
810
      'number_parts' => array(
        'description' => t('Number of parts in this router path.'),
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
811
812
        'size' => 'small',
      ),
813
814
815
816
817
      'tab_parent' => array(
        'description' => t('Only for local tasks (tabs) - the router path of the parent page (which may also be a local task).'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
818
819
        'default' => '',
      ),
820
      'tab_root' => array(
821
        'description' => t('Router path of the closest non-tab parent page. For pages that are not local tasks, this will be the same as the path.'),
822
823
824
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
825
826
        'default' => '',
      ),
827
828
829
830
831
      'title' => array(
        'description' => t('The title for the current page, or the title for the tab if this is a local task.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
832
833
        'default' => '',
      ),
834
835
836
837
838
      'title_callback' => array(
        'description' => t('A function which will alter the title. Defaults to t()'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
839
840
        'default' => '',
      ),
841
      'title_arguments' => array(
842
        'description' => t('A serialized array of arguments for the title callback. If empty, the title will be used as the sole argument for the title callback.'),
843
844
845
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
846
847
        'default' => '',
      ),
848
849
850
851
      'type' => array(
        'description' => t('Numeric representation of the type of the menu item, like MENU_LOCAL_TASK.'),
        'type' => 'int',
        'not null' => TRUE,
852
853
        'default' => 0,
      ),
854
855
856
857
858
      'block_callback' => array(
        'description' => t('Name of a function used to render the block on the system administration page for this item.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
859
860
        'default' => '',
      ),
861
862
863
      'description' => array(
        'description' => t('A description of this item.'),
        'type' => 'text',
864
865
        'not null' => TRUE,
      ),
866
867
868
869
870
      'position' => array(
        'description' => t('The position of the block (left or right) on the system administration page for this item.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
871
872
        'default' => '',
      ),
873
874
875
876
      'weight' => array(
        'description' => t('Weight of the element. Lighter weights are higher up, heavier weights go down.'),
        'type' => 'int',
        'not null' => TRUE,
877
878
879
        'default' => 0,
      ),
    ),
880
    'indexes' => array(
881
      'fit' => array('fit'),
882
883
      'tab_parent' => array('tab_parent'),
    ),
884
    'primary key' => array('path'),
885
  );
886
887

  $schema['menu_links'] = array(
888
    'description' => t('Contains the individual links within a menu.'),
889
    'fields' => array(
890
891
892
893
894
     'menu_name' => array(
        'description' => t("The menu name. All links with the same menu name (such as 'navigation') are part of the same menu."),
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
895
896
        'default' => '',
      ),
897
898
899
900
      'mlid' => array(
        'description' => t('The menu link ID (mlid) is the integer primary key.'),
        'type' => 'serial',
        'unsigned' => TRUE,
901
902
        'not null' => TRUE,
      ),
903
904
905
906
907
      'plid' => array(
        'description' => t('The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.'),
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
908
909
        'default' => 0,
      ),
910
      'link_path' => array(
911
        'description' => t('The Drupal path or external path this link points to.'),
912
913
914
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
915
916
        'default' => '',
      ),
917
918
919
920
921
      'router_path' => array(
        'description' => t('For links corresponding to a Drupal path (external = 0), this connects the link to a {menu_router}.path for joins.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
922
923
        'default' => '',
      ),
924
925
926
927
928
      'link_title' => array(
      'description' => t('The text displayed for the link, which may be modified by a title callback stored in {menu_router}.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
929
930
        'default' => '',
      ),
931
932
933
      'options' => array(
        'description' => t('A serialized array of options to be passed to the url() or l() function, such as a query string or HTML attributes.'),
        'type' => 'text',
934
935
        'not null' => FALSE,
      ),
936
937
938
939
940
      'module' => array(
        'description' => t('The name of the module that generated this link.'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
941
942
        'default' => 'system',
      ),
943
944
945
946
947
      'hidden' => array(
        'description' => t('A flag for whether the link should be rendered in menus. (1 = a disabled menu item that may be shown on admin screens, -1 = a menu callback, 0 = a normal, visible link)'),
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
948
949
        'size' => 'small',
      ),
950
951
952
953
954
      'external' => array(
        'description' => t('A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).'),
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
955
956
        'size' => 'small',
      ),
957
958
959
960
961
      'has_children' => array(
        'description' => t('Flag indicating whether any links have this link as a parent (1 = children exist, 0 = no children).'),
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
962
963
        'size' => 'small',
      ),
964
965
966
967
968
      'expanded' => array(
        'description' => t('Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)'),
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
969
970
        'size' => 'small',
      ),
971
972
973
974
      'weight' => array(
        'description' => t('Link weight among links in the same menu at the same depth.'),
        'type' => 'int',
        'not null' => TRUE,
975
976
        'default' => 0,
      ),
977
978
979
980
981
      'depth' => array(
        'description' => t('The depth relative to the top level. A link with plid == 0 will have depth == 1.'),
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
982
983
        'size' => 'small',
      ),
984
985