$table) { $out .= schema_phpprint_table($name, $table); } return $out; } function schema_phpprint_table($name, $table) { $cols = array(); if (isset($table['fields'])) { foreach ($table['fields'] as $colname => $col) { $cols[] = "'$colname' => " . schema_phpprint_column($col, TRUE); } } $unique = $index = array(); if (isset($table['unique keys'])) { foreach ($table['unique keys'] as $keyname => $key) { $unique[] = "'$keyname' => " . schema_phpprint_key($key); } } if (isset($table['indexes'])) { foreach ($table['indexes'] as $keyname => $key) { $index[] = "'$keyname' => " . schema_phpprint_key($key); } } if ($table['description']) { $description = $table['description']; } else { $description = t('TODO: please describe this table!'); } $out = ''; $out .= "\$schema['" . $name . "'] = array(\n"; $out .= " 'description' => '$description',\n"; $out .= " 'fields' => array(\n "; $out .= implode(",\n ", $cols); $out .= ",\n ),\n"; if (isset($table['primary key'])) { $out .= " 'primary key' => array('" . implode("', '", $table['primary key']) . "'),\n"; } if (count($unique) > 0) { $out .= " 'unique keys' => array(\n "; $out .= implode(",\n ", $unique); $out .= "\n ),\n"; } if (count($index) > 0) { $out .= " 'indexes' => array(\n "; $out .= implode(",\n ", $index); $out .= ",\n ),\n"; } $out .= ");\n"; return $out; } function schema_phpprint_column($col, $multiline=FALSE) { $attrs = array(); if (isset($col['description']) && $col['description']) { $description = $col['description']; } else { $description = t('TODO: please describe this field!'); } unset($col['description']); $attrs[] = "'description' => '$description'"; if ($col['type'] == 'varchar' || $col['size'] == 'normal') { unset($col['size']); } foreach (array('type', 'unsigned', 'size', 'length', 'not null', 'default') as $attr) { if (isset($col[$attr])) { if (is_string($col[$attr])) { $attrs[] = "'$attr' => '$col[$attr]'"; } elseif (is_bool($col[$attr])) { $attrs[] = "'$attr' => " . ($col[$attr] ? 'TRUE' : 'FALSE'); } else { $attrs[] = "'$attr' => $col[$attr]"; } unset($col[$attr]); } } foreach (array_keys($col) as $attr) { if (is_string($col[$attr])) { $attrs[] = "'$attr' => '$col[$attr]'"; } else { $attrs[] = "'$attr' => $col[$attr]"; } } if ($multiline) { return "array(\n " . implode(",\n ", $attrs) . ",\n )"; } return "array(" . implode(', ', $attrs) . ")"; } function schema_phpprint_key($keys) { $ret = array(); foreach ($keys as $key) { if (is_array($key)) { $ret[] = "array('$key[0]', $key[1])"; } else { $ret[] = "'$key'"; } } return "array(" . implode(", ", $ret) . ")"; } ////////////////////////////////////////////////////////////////////// // Schema comparison functions ////////////////////////////////////////////////////////////////////// function schema_unprefix_table($name) { // TODO: Deal with D7 prefixing global $db_prefix; static $_db_prefix; if (is_array($db_prefix)) { if (!isset($_db_prefix)) { foreach ($db_prefix as $key => $val) { $_db_prefix[$val . $key] = $key; } } if (isset($_db_prefix[$name])) { return $_db_prefix[$name]; } elseif (!empty($db_prefix['default']) && preg_match('@^' . $db_prefix['default'] . '(.*)@', $name, $m)) { return $m[1]; } else { // On pgsql, key and index names are also prefixed // (e.g. 'prefix_blocks_roles_rid_idx'). foreach ($db_prefix as $key => $val) { if (($key != 'default' && preg_match('@^' . $val . '(' . $key . '.*)@', $name, $m)) || ($key == 'default' && preg_match('@^' . $val . '(.*)@', $name, $m))) { return $m[1]; } } return $name; } } elseif (!empty($db_prefix) && preg_match('@^' . $db_prefix . '(.*)@', $name, $m)) { return $m[1]; } return $name; } function schema_dbobject() { static $engines = array(); $class_name = 'SchemaDatabaseSchema_' . db_driver(); if (!isset($engines[$class_name])) { $engines[$class_name] = new $class_name(Database::getConnection()); } return $engines[$class_name]; } /** * Converts a column's Schema type into an engine-specific data type. */ function schema_engine_type($col, $table, $field, $engine = NULL) { $map = schema_dbobject()->getFieldTypeMap(); $size = (isset($col['size']) ? $col['size'] : 'normal'); $type = $col['type'] . ':' . $size; if (isset($map[$type])) { return $map[$type]; } else { drupal_set_message(t('%table.%field: no %engine type for Schema type %type.', array('%engine' => $engine, '%type' => $type, '%table' => $table, '%field' => $field)), 'warning'); return $col['type']; } } /** * Convert an engine-specific data type into a Schema type. */ function schema_schema_type($type, $table, $field, $engine = NULL) { $map = schema_dbobject()->schema_type_map(); $type = strtolower($type); if (isset($map[$type])) { return explode(':', $map[$type]); } else { if (!variable_get('schema_suppress_type_warnings', FALSE)) { drupal_set_message(t('Field @table.@field: no Schema type for @engine type @type.', array('@engine' => $engine, '@type' => $type, '@table' => $table, '@field' => $field)), 'warning'); } return array($type, 'normal'); } } /** * Compares two complete schemas. * @param $ref is considered the reference copy * @param $inspect is compared against it. If $inspect is NULL, a * schema for the active database is generated and used. */ function schema_compare_schemas($ref, $inspect = NULL) { if (!isset($inspect)) { $inspect = schema_dbobject()->inspect(variable_get('schema_database_connection', 'default')); } $info = array(); // Error checks to consider adding: // All type serial columns must be in an index or key. // All columns in a primary or unique key must be NOT NULL. // Error check: column type and default type must match foreach ($ref as $t_name => $table) { if (!isset($table['fields']) || !is_array($table['fields'])) { drupal_set_message(t('Table %table: Missing or invalid \'fields\' array.', array('%table' => $t_name)), 'warning'); continue; } foreach ($table['fields'] as $c_name => $col) { switch ($col['type']) { case 'int': case 'float': case 'numeric': if (isset($col['default']) && (! is_numeric($col['default']) || is_string($col['default']))) { $info['warn'][] = t('%table.%column is type %type but its default %default is PHP type %phptype', array('%table' => $t_name, '%column' => $c_name, '%type' => $col['type'], '%default' => $col['default'], '%phptype' => gettype($col['default']))); } break; default: if (isset($col['default']) && !is_string($col['default'])) { $info['warn'][] = t('%table.%column is type %type but its default %default is PHP type %phptype', array('%table' => $t_name, '%column' => $c_name, '%type' => $col['type'], '%default' => $col['default'], '%phptype' => gettype($col['default']))); } break; } } } // Error check: 'text' and 'blob' columns cannot have a default value foreach ($ref as $t_name => $table) { if (!isset($table['fields'])) { continue; } foreach ($table['fields'] as $c_name => $col) { switch ($col['type']) { case 'text': case 'blob': if (isset($col['default'])) { $info['warn'][] = t('%table.%column is type %type and may not have a default value', array('%table' => $t_name, '%column' => $c_name, '%type' => $col['type'])); } break; } } } // Error check: primary keys must be 'not null' foreach ($ref as $t_name => $table) { if (isset($table['primary key'])) { $keys = db_field_names($table['primary key']); foreach ($keys as $key) { if (!isset($table['fields'][$key]['not null']) || $table['fields'][$key]['not null'] != TRUE) { $info['warn'][] = t('%table.%column is part of the primary key but is not specified to be \'not null\'.', array('%table' => $t_name, '%column' => $key)); } } } } foreach ($ref as $name => $table) { if (isset($table['module'])) { $module = $table['module']; } else { $module = ''; } if (!isset($inspect[$name])) { $info['missing'][$module][$name] = array('status' => 'missing'); } else { $status = schema_compare_table($table, $inspect[$name]); $info[$status['status']][$module][$name] = $status; unset($inspect[$name]); } } foreach ($inspect as $name => $table) { $info['extra'][] = $name; } return $info; } /** * Compares a reference specification (such as one returned by a * module's hook_schema) to an inspected specification from the * database. * @param $inspect if not provided, the database is inspected. */ function schema_compare_table($ref, $inspect = NULL) { $_db_type = db_driver(); if (!isset($inspect)) { // TODO: Handle prefixing the D7 way // $ref_name = db_prefix_tables('{' . $ref['name'] . '}'); $ref_name = $ref['name']; $inspect = schema_dbobject()->inspect( variable_get('schema_database_connection', 'default'), $ref_name); $inspect = $inspect[$ref['name']]; } if (!isset($inspect)) { return array('status' => 'missing'); } $reasons = $notes = array(); $col_keys = array_flip( array('type', 'size', 'not null', 'length', 'unsigned', 'default', 'scale', 'precision')); foreach ($ref['fields'] as $colname => $col) { // Many Schema types can map to the same engine type (e.g. in // PostgresSQL, text:{small,medium,big} are all just text). When // we inspect the database, we see the common type, but the // reference we are comparing against can have a specific type. // We therefore run the reference's specific type through the // type conversion cycle to get its common type for comparison. // // Sadly, we need a special-case hack for 'serial'. $serial = ($col['type'] == 'serial' ? TRUE : FALSE); $name = isset($ref['name']) ? $ref['name'] : ''; $dbtype = schema_engine_type($col, $name, $colname); list($col['type'], $col['size']) = schema_schema_type($dbtype, $name, $colname); if ($serial) { $col['type'] = 'serial'; } // If an engine-specific type is specified, use it. XXX $inspect // will contain the schema type for the engine type, if one // exists, whereas dbtype_type contains the engine type. if (isset($col[$_db_type . '_type'])) { $col['type'] = $col[$_db_type . '_type']; } $col = array_intersect_key($col, $col_keys); if (!isset($inspect['fields'][$colname])) { $reasons[] = "$colname: not in database"; continue; } // Account for schemas that contain unnecessary 'default' => NULL if (!isset($col['default']) || (is_null($col['default']) && !isset($inspect['fields'][$colname]['default']))) { unset($col['default']); } $kdiffs = array(); foreach ($col_keys as $key => $val) { if (!( // First part tests that item exists for both and has same value in both places (isset($col[$key]) && !is_null($col[$key]) && $col[$key] !== FALSE && isset($inspect['fields'][$colname][$key]) && $inspect['fields'][$colname][$key] !== FALSE && $col[$key] == $inspect['fields'][$colname][$key] ) // Second test is that it does not exist or exists but is null in both places || ((!isset($col[$key]) || is_null($col[$key]) || $col[$key] === FALSE) && (!isset($inspect['fields'][$colname][$key]) || $inspect['fields'][$colname][$key] === FALSE) ) ) ) { // One way or another, difference between the two so note it to explicitly identify it later. $kdiffs[] = $key; } } if (count($kdiffs) != 0) { $reasons[] = "column $colname - difference" . ( count($kdiffs) > 1 ? 's' : '') . " on: " . implode(', ', $kdiffs) . "
declared: " . schema_phpprint_column($col) . '
actual: ' . schema_phpprint_column($inspect['fields'][$colname]); } unset($inspect['fields'][$colname]); } foreach ($inspect['fields'] as $colname => $col) { $reasons[] = "$colname: unexpected column in database"; } if (isset($ref['primary key'])) { if (!isset($inspect['primary key'])) { $reasons[] = "primary key: missing in database"; } elseif ($ref['primary key'] !== $inspect['primary key']) { $reasons[] = ("primary key:
declared: " . schema_phpprint_key($ref['primary key']) . '
actual: ' . schema_phpprint_key($inspect['primary key'])); } } elseif (isset($inspect['primary key'])) { $reasons[] = "primary key: missing in schema"; } foreach (array('unique keys', 'indexes') as $type) { if (isset($ref[$type])) { foreach ($ref[$type] as $keyname => $key) { if (!isset($inspect[$type][$keyname])) { $reasons[] = "$type $keyname: missing in database"; continue; } // $key is column list if ($key !== $inspect[$type][$keyname]) { $reasons[] = ("$type $keyname:
declared: " . schema_phpprint_key($key) . '
actual: ' . schema_phpprint_key($inspect[$type][$keyname])); } unset($inspect[$type][$keyname]); } } if (isset($inspect[$type])) { foreach ($inspect[$type] as $keyname => $col) { // this is not an error, the dba might have added it on purpose $notes[] = "$type $keyname: unexpected (not an error)"; } } } $status = (count($reasons) ? 'different' : 'same'); return array('status' => $status, 'reasons' => $reasons, 'notes' => $notes); } ////////////////////////////////////////////////////////////////////// // Schema administration and UI ////////////////////////////////////////////////////////////////////// /** * Implement of hook_init(). * Perform setup tasks. */ /** * TODO: Remove this unless someone can explain its purpose. Is it a means for * other modules to add support for other engines? In that case, it is unnecessary - * defining a SchemaDatabaseSchema_ class will do the trick. */ /* function schema_init() { schema_require(); } function schema_require() { static $done = 0; if ($done++) { return; } // Load all our module 'on behalfs' so they will be available for // any module (including this one) that needs them. // TODO: What precisely is the purpose of this? To allow other $path = drupal_get_path('module', 'schema'); $files = drupal_system_listing('/schema_.*\.inc$/', $path . '/modules', 'name', 0); foreach ($files as $file) { // The filename format is very specific. It must be schema_MODULENAME.inc $module = substr_replace($file->name, '', 0, 7); require_once("./$file->filename"); } } */ /** * Implement of hook_permission(). */ function schema_permission() { return array( 'administer schema' => array( 'title' => t('Administer schema module'), ), ); } /** * Implement of hook_menu(). * Define menu items and page callbacks. * admin/structure/schema calls local task(default): schema_compare() * admin/structure/schema/compare calls local task: schema_compare() * admin/structure/schema/describe calls local task: schema_describe() * admin/structure/schema/inspect calls local task: schema_inspect() * admin/structure/schema/sql calls local task: schema_sql() * admin/structure/schema/show calls local task: schema_show() */ function schema_menu() { $items['admin/structure/schema'] = array( 'title' => 'Schema', 'description' => 'Manage the database schema for this system.', 'page callback' => 'drupal_get_form', 'page arguments' => array('schema_compare'), 'access arguments' => array('administer schema'), ); $items['admin/structure/schema/compare'] = array( 'title' => 'Compare', 'type' => MENU_DEFAULT_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('schema_compare'), 'weight' => -10, 'access arguments' => array('administer schema'), ); $items['admin/structure/schema/describe'] = array( 'title' => 'Describe', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('schema_describe'), 'weight' => -8, 'access arguments' => array('administer schema'), ); $items['admin/structure/schema/inspect'] = array( 'title' => 'Inspect', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('schema_inspect'), 'weight' => -6, 'access arguments' => array('administer schema'), ); $items['admin/structure/schema/sql'] = array( 'title' => 'SQL', 'type' => MENU_LOCAL_TASK, 'page callback' => 'schema_sql', 'weight' => -4, 'access arguments' => array('administer schema'), ); // This can't work unless we rename the functions in database.*.inc. global $_schema_engines; if (FALSE && isset($_schema_engines) && is_array($_schema_engines)) { foreach ($_schema_engines as $engine) { $items['admin/structure/schema/sql/' . $engine] = array( 'title' => $engine, 'type' => ($engine == db_driver() ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK), 'page callback' => 'schema_sql', 'callback arguments' => $engine, 'access arguments' => array('administer schema'), ); } } $items['admin/structure/schema/show'] = array( 'title' => 'Show', 'type' => MENU_LOCAL_TASK, 'page callback' => 'schema_show', 'weight' => -2, 'access arguments' => array('administer schema'), ); $items['admin/structure/schema/settings'] = array( 'title' => 'Settings', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('schema_settings'), 'weight' => 0, 'access arguments' => array('administer schema'), ); return $items; } function _schema_process_description($desc) { return preg_replace('@{([a-z_]+)}@i', '$1', $desc); } /** * "Compare" menu callback. * This function just massages the data returned by * schema_compare_schemas() into HTML. */ function schema_compare() { $form = array(); $form['description'] = array( '#prefix' => '
', '#markup' => t('This page compares the live database as it currently exists against the combination of all schema information provided by all enabled modules'), '#suffix' => '
', ); $states = array( 'same' => t('Match'), 'different' => t('Mismatch'), 'missing' => t('Missing'), ); $descs = array( 'same' => 'Tables for which the schema and database agree.', 'different' => 'Tables for which the schema and database are different.', 'missing' => 'Tables in the schema that are not present in the database.', ); $schema = drupal_get_schema(NULL, TRUE); $info = schema_compare_schemas($schema); // The info array is keyed by state (same/different/missing/extra/warn). For missing, // the value is a simple array of table names. For warn, it is a simple array of warnings. // Get those out of the way first $warnings = $info['warn']; foreach ($warnings as $message) { drupal_set_message($message, 'warning'); } unset($info['warn']); $form['extra'] = array( '#type' => 'fieldset', '#title' => t('Extra (@count)', array('@count' => isset($info['extra']) ? count($info['extra']) : 0)), '#description' => t('Tables in the database that are not present in the schema. This indicates previously installed modules that are disabled but not un-installed or modules that do not use the Schema API.'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 50, // This goes last ); $form['extra']['tablelist'] = array( '#theme' => 'item_list', '#items' => $info['extra'], ); unset($info['extra']); // For the other states, the value is an array keyed by module name. Each value // in that array is an array keyed by tablename, and each of those values is an // array containing 'status' (same as the state), an array of reasons, and an array of notes. $weight = 0; foreach ($info as $state => $modules) { // We'll fill in the fieldset title below, once we have the counts $form[$state] = array( '#type' => 'fieldset', '#description' => t($descs[$state]), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => $weight++, ); $counts[$state] = 0; foreach ($modules as $module => $tables) { $counts[$state] += count($tables); $form[$state][$module] = array( '#type' => 'fieldset', '#title' => t($module), '#collapsible' => TRUE, '#collapsed' => TRUE, ); switch ($state) { case 'same': case 'missing': $form[$state][$module]['tablelist'] = array( '#theme' => 'item_list', '#items' => array_keys($tables), ); break; case 'different': $items = array(); foreach ($tables as $name => $stuff) { $form[$state][$module][$name] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => $name, ); $form[$state][$module][$name]['reasons'] = array( '#theme' => 'item_list', '#items' => array_merge($tables[$name]['reasons'], $tables[$name]['notes']), ); } break; } } } // Fill in counts in titles foreach ($states as $state => $description) { $form[$state]['#title'] = t('@state (@count)', array('@state' => $states[$state], '@count' => isset($counts[$state]) ? $counts[$state] : 0)); } return $form; } /** * "Describe" menu callback. */ function schema_describe() { $schema = drupal_get_schema(NULL, TRUE); ksort($schema); $row_hdrs = array(t('Name'), t('Type[:Size]'), t('Null?'), t('Default')); $form = array(); $form['description'] = array( '#prefix' => '
', '#markup' => "This page describes the Drupal database schema. Click on a table name to see that table's description and fields. Table names within a table or field description are hyperlinks to that table's description.", '#suffix' => '
', ); $default_table_description = t('TODO: please describe this table!'); $default_field_description = t('TODO: please describe this field!'); foreach ($schema as $t_name => $t_spec) { $rows = array(); foreach ($t_spec['fields'] as $c_name => $c_spec) { $row = array(); $row[] = $c_name; $type = $c_spec['type']; if (!empty($c_spec['length'])) { $type .= '(' . $c_spec['length'] . ')'; } if (!empty($c_spec['scale']) && !empty($c_spec['precision'])) { $type .= '(' . $c_spec['precision'] . ', ' . $c_spec['scale'] . ' )'; } if (!empty($c_spec['size']) && $c_spec['size'] != 'normal') { $type .= ':' . $c_spec['size']; } if ($c_spec['type'] == 'int' && !empty($c_spec['unsigned'])) { $type .= ', unsigned'; } $row[] = $type; $row[] = !empty($c_spec['not null']) ? 'NO' : 'YES'; $row[] = isset($c_spec['default']) ? (is_string($c_spec['default']) ? '\'' . $c_spec['default'] . '\'' : $c_spec['default']) : ''; $rows[] = $row; if (!empty($c_spec['description']) && $c_spec['description'] != $default_field_description) { $desc = _schema_process_description($c_spec['description']); $rows[] = array(array('colspan' => count($row_hdrs), 'data' => $desc)); } else { drupal_set_message(_schema_process_description(t('Field {!table}.@field has no description.', array('!table' => $t_name, '@field' => $c_name))), 'warning'); } } if (empty($t_spec['description']) || $t_spec['description'] == $default_table_description) { drupal_set_message(_schema_process_description(t('Table {!table} has no description.', array('!table' => $t_name))), 'warning'); } $form[$t_name] = array( '#type' => 'fieldset', '#title' => t('@table (@module module)', array('@table' => $t_name, '@module' => isset($t_spec['module']) ? $t_spec['module'] : '')), '#description' => !empty($t_spec['description']) ? _schema_process_description($t_spec['description']) : '', '#collapsible' => TRUE, '#collapsed' => TRUE, '#attributes' => array('id' => 'table-' . $t_name), ); $form[$t_name]['content'] = array( '#theme' => 'table', '#header' => $row_hdrs, '#rows' => $rows, ); } return $form; } /** * "Inspect" menu callback. */ function schema_inspect() { $form = array(); $form['description'] = array( '#prefix' => '
', '#markup' => t("This page shows the live database schema as it currently exists on this system. Known tables are grouped by the module that defines them; unknown tables are all grouped together.
To implement hook_schema() for a module that has existing tables, copy the schema structure for those tables directly into the module's hook_schema() and return \$schema."), '#suffix' => '
', ); $mods = module_list(); sort($mods); $mods = array_flip($mods); $schema = drupal_get_schema(NULL, TRUE); $inspect = schema_dbobject()->inspect(variable_get('schema_database_connection', 'default')); foreach ($inspect as $name => $table) { $module = isset($schema[$name]['module']) ? $schema[$name]['module'] : 'Unknown'; if (!isset($form[$module])) { $form[$module] = array( '#type' => 'fieldset', '#access' => TRUE, '#title' => check_plain($module), '#collapsible' => TRUE, '#collapsed' => ($module != 'Unknown'), '#weight' => ($module == 'Unknown' ? 0 : $mods[$module]+1), ); } $form[$module][$name] = array( '#type' => 'markup', '#markup' => ''); } return $form; } /** * "SQL" menu callback. */ function schema_sql($engine = NULL) { $schema = drupal_get_schema(NULL, TRUE); $connection = Database::getConnection(); $sql = ''; foreach ($schema as $name => $table) { if (substr($name, 0, 1) == '#') { continue; } if ($engine) { $stmts = call_user_func('schema_' . $engine . '_create_table_sql', $table); } else { $stmts = schema_dbobject()->getCreateTableSql($name, $table); } $sql .= implode(";\n", $stmts) . ";\n\n"; } $output = t('

This page shows the CREATE TABLE statements that the Schema API generates for the selected database engine for each table defined by a module. It is for debugging purposes.

'); $output .= ""; return $output; } /** * "Show" menu callback. * Displays drupal schema as php code, so you can reuse it * as you need. */ function schema_show() { $schema = drupal_get_schema(NULL, TRUE); $show = var_export($schema, 1); $output = t('

This page displays the Drupal database schema data structure. It is for debugging purposes.

'); $output .= ""; return $output; } function schema_settings($form, &$form_state) { global $databases; if (count($databases) > 1) { $form['schema_database_connection'] = array( '#type' => 'select', '#title' => t('Database connection to use'), '#default_value' => variable_get('schema_database_connection', 'default'), '#options' => array_combine(array_keys($databases), array_keys($databases)), '#description' => t('If you use a secondary database other than the default Drupal database you can select it here and use schema\'s "compare" and "inspect" functions on that other database.'), ); } $form['schema_status_report'] = array( '#type' => 'checkbox', '#title' => t('Include schema comparison reports in site status report'), '#default_value' => variable_get('schema_status_report', TRUE), '#description' => t('When checked, schema comparison reports are run on the Administer page, and included in the site status report.'), ); $form['schema_suppress_type_warnings'] = array( '#type' => 'checkbox', '#title' => t('Suppress schema warnings.'), '#default_value' => variable_get('schema_suppress_type_warnings', FALSE), '#description' => t('When checked, missing schema type warnings will be suppressed.'), ); return system_settings_form($form); }