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

284
285
286
  return $requirements;
}

287
288
289
/**
 * Implementation of hook_install().
 */
290
function system_install() {
291
  if (db_driver() == 'pgsql') {
292
293
294
295
296
297
    // 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.

298
    // Create unsigned types.
299
300
301
302
303
304
305
306
307
    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)");
    }
308

309
    // Create functions.
310
311
312
313
314
315
316
317
318
319
320
    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();\'
321
322
        LANGUAGE \'sql\''
      );
323
    }
324

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

341
  // Create tables.
342
  $modules = array('system', 'filter', 'block', 'user', 'node', 'comment', 'taxonomy');
343
344
  foreach ($modules as $module) {
    drupal_install_schema($module);
345
  }
346

347
348
  // Load system theme data appropriately.
  system_theme_data();
349

350
351
352
353
354
355
356
357
358
  // 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.
359
  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()));
360
361
362
363
364
  // 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');
365

366
  // Built-in roles.
367
368
  db_query("INSERT INTO {role} (name) VALUES ('%s')", 'anonymous user');
  db_query("INSERT INTO {role} (name) VALUES ('%s')", 'authenticated user');
369

370
371
372
373
374
375
376
377
  // 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');
378

379
380
  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');
381
382
383
  db_query("INSERT INTO {block} (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 {block} (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 {block} (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);
384

385
  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);
386

387
  // Add input formats.
388
389
  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);
390
391
392
393
394

  // Enable filters for each input format.

  // Filtered HTML:
  // URL filter.
395
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 2, 0);
396
  // HTML filter.
397
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 0, 1);
398
  // Line break filter.
399
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 1, 2);
400
  // HTML corrector filter.
401
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 1, 'filter', 3, 10);
402
403
404

  // Full HTML:
  // URL filter.
405
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 2, 0);
406
  // Line break filter.
407
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 1, 1);
408
  // HTML corrector filter.
409
  db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", 2, 'filter', 3, 10);
410

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

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

415
  $cron_key = serialize(md5(mt_rand()));
416

417
  db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", 'cron_key', $cron_key);
418
419
}

420
421
422
423
424
425
426
427
/**
 * 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(
428
    '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.',
429
    'fields' => array(
430
      'name' => array(
431
        'description' => 'The name of the variable.',
432
433
434
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
435
436
        'default' => '',
      ),
437
      'value' => array(
438
        'description' => 'The value of the variable.',
439
440
        'type' => 'text',
        'not null' => TRUE,
441
        'size' => 'big',
442
      ),
443
    ),
444
    'primary key' => array('name'),
445
  );
446
447

  $schema['actions'] = array(
448
    'description' => 'Stores action information.',
449
    'fields' => array(
450
      'aid' => array(
451
        'description' => 'Primary Key: Unique actions ID.',
452
453
454
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
455
456
        'default' => '0',
      ),
457
      'type' => array(
458
        'description' => 'The object that that action acts on (node, user, comment, system or custom types.)',
459
460
461
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
462
463
        'default' => '',
      ),
464
      'callback' => array(
465
        'description' => 'The callback function that executes when the action runs.',
466
467
468
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
469
470
        'default' => '',
      ),
471
      'parameters' => array(
472
        'description' => 'Parameters to be passed to the callback function.',
473
474
        'type' => 'text',
        'not null' => TRUE,
475
476
        'size' => 'big',
      ),
477
      'description' => array(
478
        'description' => 'Description of the action.',
479
480
481
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
482
        'default' => '0',
483
      ),
484
    ),
485
    'primary key' => array('aid'),
486
  );
487
488

  $schema['actions_aid'] = array(
489
    'description' => 'Stores action IDs for non-default actions.',
490
    'fields' => array(
491
      'aid' => array(
492
        'description' => 'Primary Key: Unique actions ID.',
493
494
        'type' => 'serial',
        'unsigned' => TRUE,
495
        'not null' => TRUE,
496
      ),
497
    ),
498
    'primary key' => array('aid'),
499
  );
500
501

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

534
  $schema['blocked_ips'] = array(
535
    'description' => 'Stores blocked IP addresses.',
536
537
    'fields' => array(
       'iid' => array(
538
        'description' => 'Primary Key: unique ID for IP addresses.',
539
540
541
542
543
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'ip' => array(
544
        'description' => 'IP address',
545
546
547
548
549
550
551
552
553
554
555
556
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
        'default' => '',
      ),
    ),
    'indexes' => array(
      'blocked_ip' => array('ip'),
    ),
    'primary key' => array('iid'),
  );

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

  $schema['cache_form'] = $schema['cache'];
605
  $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.';
606
  $schema['cache_page'] = $schema['cache'];
607
  $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
608
  $schema['cache_menu'] = $schema['cache'];
609
  $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.';
610
  $schema['cache_registry'] = $schema['cache'];
611
  $schema['cache_registry']['description'] = 'Cache table for the code registry system to remember what code files need to be loaded on any given page.';
612
613

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

  $schema['flood'] = array(
680
    'description' => 'Flood controls the threshold of events, such as the number of contact attempts.',
681
    'fields' => array(
682
      'fid' => array(
683
        'description' => 'Unique flood event ID.',
684
        'type' => 'serial',
685
686
        'not null' => TRUE,
      ),
687
      'event' => array(
688
        'description' => 'Name of event (e.g. contact).',
689
690
691
        'type' => 'varchar',
        'length' => 64,
        'not null' => TRUE,
692
693
        'default' => '',
      ),
694
      'hostname' => array(
695
        'description' => 'Hostname of the visitor.',
696
697
698
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
699
700
        'default' => '',
      ),
701
      'timestamp' => array(
702
        'description' => 'Timestamp of the event.',
703
704
        'type' => 'int',
        'not null' => TRUE,
705
        'default' => 0,
706
      ),
707
    ),
708
    'primary key' => array('fid'),
709
710
711
    'indexes' => array(
      'allow' => array('event', 'hostname', 'timestamp'),
    ),
712
  );
713
714

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