Index: dba.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/dba/dba.module,v retrieving revision 1.46 diff -u -r1.46 dba.module --- dba.module 20 Jan 2007 04:11:50 -0000 1.46 +++ dba.module 6 May 2007 01:48:23 -0000 @@ -1,31 +1,25 @@ , June 2004. -** PostgreSQL functionality provided by AAM -*/ +/** + * @file + * Allows administrators direct access to their Drupal database. + * Written by Jeremy Andrews , June 2004. + * PostgreSQL functionality provided by AAM + */ define('DBA_BACKUP_EXCLUDE', 'accesslog, cache, search_index, search_total, watchdog'); -// standard Drupal functions +// Standard Drupal functions function dba_perm() { - return array ('dba view database', 'dba administer database'); + return array('dba view database', 'dba administer database'); } function dba_help($section = '') { switch ($section) { - case 'admin/modules/dba': - case 'admin/modules#description': - $output = t('Directly administer your Drupal database.'); - break; case 'admin/help#dba': - $output .= t(' -

The dba module allows site administrators a method for direct database administration. This is a dangerous module, in that it gives unlimited access and control over the active database. With this module, it is possible to corrupt or delete an entire drupal database. Use at your own risk.

-'); + $output .= t('The dba module allows site administrators a method for direct database administration. This is a dangerous module, in that it gives unlimited access and control over the active database. With this module, it is possible to corrupt or delete an entire drupal database. Use at your own risk.'); break; } return $output; @@ -34,85 +28,155 @@ function dba_menu($may_cache) { $items = array(); $access = user_access('dba view database') || user_access('dba administer database'); + $admin_access = user_access('dba administer database'); if ($may_cache) { - // provide menus to dbas with view permissions - $items[] = array('path' => 'admin/database', 'title' => t('database'), - 'callback' => 'dba_admin_overview', - 'access' => $access); - // tabs - $items[] = array('path' => 'admin/database/table', 'title' => t('tables'), - 'callback' => 'dba_admin_overview', 'type' => MENU_DEFAULT_LOCAL_TASK); - $items[] = array('path' => 'admin/database/query', - 'title' => t('query'), 'callback' => 'dba_query', - 'access' => user_access('dba administer database'), - 'type' => MENU_LOCAL_TASK, 'weight' => 8); - $items[] = array('path' => 'admin/database/script', - 'title' => t('run script'), 'callback' => 'dba_run_script', - 'access' => user_access('dba administer database'), - 'type' => MENU_LOCAL_TASK, 'weight' => 10); - } - else if (strstr(drupal_get_path_alias($_GET['q']), 'admin/database')) { - // you can only view or describe one table at a time - $tables = dba_get_active_tables(array(), 0); + // Provide menus to dbas with view permissions + $items[] = array('path' => 'admin/build/database', + 'title' => t('Database'), + 'description' => t("View and edit your site's database directly."), + 'callback' => 'drupal_get_form', + 'callback arguments' => 'dba_database_overview_form', + 'access' => $access, + ); + // Tabs + $items[] = array('path' => 'admin/build/database/table', + 'title' => t('Tables'), + 'callback' => 'drupal_get_form', + 'callback arguments' => 'dba_database_overview_form', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + $items[] = array('path' => 'admin/build/database/query', + 'title' => t('Query'), + 'callback' => 'drupal_get_form', + 'callback arguments' => array('dba_query_page'), + 'access' => $admin_access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 8); + $items[] = array('path' => 'admin/build/database/script', + 'title' => t('Run script'), + 'callback' => 'drupal_get_form', + 'callback arguments' => array('dba_run_script'), + 'access' => $admin_access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 10, + ); + $items[] = array( + 'path' => 'admin/settings/dba', + 'title' => t('Database administration'), + 'description' => t('Control automatic backups and other settings.'), + 'callback' => 'drupal_get_form', + 'callback arguments' => array('dba_settings_form'), + 'access' => $admin_access, + ); + } + else if (strstr(drupal_get_path_alias($_GET['q']), 'admin/build/database')) { + // You can only view or describe one table at a time + $tables = dba_get_active_tables(0); if (!empty($tables)) { - // regular subtabs - $items[] = array('path' => "admin/database/table/$tables/view", - 'title' => t('view'), 'callback' => 'dba_admin_tables_view', - 'access' => $access, 'type' => MENU_LOCAL_TASK, 'weight' => 0); - $items[] = array('path' => "admin/database/table/$tables/describe", - 'title' => t('describe'), 'callback' => 'dba_admin_tables_describe', - 'access' => $access, 'type' => MENU_LOCAL_TASK, 'weight' => 2); + // Regular subtabs + $items[] = array('path' => "admin/build/database/table/$tables/view", + 'title' => t('View'), + 'callback' => 'dba_admin_tables_view', + 'access' => $access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 0, + ); + $items[] = array('path' => "admin/build/database/table/$tables/describe", + 'title' => t('Describe'), + 'callback' => 'dba_admin_tables_describe', + 'access' => $access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 2, + ); if (_is_mysql()) { - $items[] = array('path' => "admin/database/table/$tables/check", - 'title' => t('check'), 'callback' => 'dba_admin_tables_check', - 'access' => $access, 'type' => MENU_LOCAL_TASK, 'weight' => 4); - $items[] = array('path' => "admin/database/table/$tables/optimize", - 'title' => t('optimize'), 'callback' => 'dba_admin_tables_optimize', - 'access' => $access, 'type' => MENU_LOCAL_TASK, 'weight' => 4); - } - // subtabs for dbas with administer permissions - $items[] = array('path' => "admin/database/table/$tables/backup", - 'title' => t('backup'), 'callback' => 'dba_admin_tables_backup', - 'access' => user_access('dba administer database'), - 'type' => MENU_LOCAL_TASK, 'weight' => 8); - $items[] = array('path' => "admin/database/table/$tables/empty", - 'title' => t('empty'), 'callback' => 'dba_admin_tables_empty', - 'access' => user_access('dba administer database'), - 'type' => MENU_LOCAL_TASK, 'weight' => 8); - $items[] = array('path' => "admin/database/table/$tables/drop", - 'title' => t('drop'), 'callback' => 'dba_admin_tables_drop', - 'access' => user_access('dba administer database'), - 'type' => MENU_LOCAL_TASK, 'weight' => 10); - } - // administrative callbacks - $items[] = array('path' => "admin/database/backup", - 'title' => t('backup'), 'callback' => 'dba_admin_tables_backup', - 'access' => user_access('dba administer database'), 'weight' => 15, - 'type' => arg(2) == 'backup' ? MENU_LOCAL_TASK : MENU_CALLBACK); - $items[] = array('path' => "admin/database/drop", - 'title' => t('drop'), 'callback' => 'dba_admin_tables_drop', - 'access' => user_access('dba administer database'), 'weight' => 15, - 'type' => arg(2) == 'drop' ? MENU_LOCAL_TASK : MENU_CALLBACK); - $items[] = array('path' => "admin/database/empty", - 'title' => t('empty'), 'callback' => 'dba_admin_tables_empty', - 'access' => user_access('dba administer database'), 'weight' => 15, - 'type' => arg(2) == 'empty' ? MENU_LOCAL_TASK : MENU_CALLBACK); + $items[] = array('path' => "admin/build/database/table/$tables/check", + 'title' => t('Check'), + 'callback' => 'dba_admin_tables_check', + 'access' => $access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 4, + ); + $items[] = array('path' => "admin/build/database/table/$tables/optimize", + 'title' => t('Optimize'), + 'callback' => 'dba_admin_tables_optimize', + 'access' => $access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 4, + ); + } + // Subtabs for dbas with administer permissions + $items[] = array('path' => "admin/build/database/table/$tables/backup", + 'title' => t('Backup'), + 'callback' => 'dba_admin_tables_verify_op', + 'callback arguments' => array('backup'), + 'access' => $admin_access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 8, + ); + $items[] = array('path' => "admin/build/database/table/$tables/empty", + 'title' => t('Empty'), + 'callback' => 'dba_admin_tables_verify_op', + 'callback arguments' => array('empty'), + 'access' => $admin_access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 8, + ); + $items[] = array('path' => "admin/build/database/table/$tables/drop", + 'title' => t('Drop'), + 'callback' => 'dba_admin_tables_verify_op', + 'callback arguments' => array('drop'), + 'access' => $admin_access, + 'type' => MENU_LOCAL_TASK, + 'weight' => 10, + ); + } + // Administrative callbacks + $items[] = array('path' => "admin/build/database/backup", + 'title' => t('Backup'), + 'callback' => 'dba_admin_tables_verify_op', + 'callback arguments' => array('backup'), + 'access' => $admin_access, + 'weight' => 15, + 'type' => (arg(3) == 'backup' ? MENU_LOCAL_TASK : MENU_CALLBACK), + ); + $items[] = array('path' => "admin/build/database/empty", + 'title' => t('Empty'), + 'callback' => 'dba_admin_tables_verify_op', + 'callback arguments' => array('empty'), + 'access' => $admin_access, + 'weight' => 15, + 'type' => (arg(3) == 'empty' ? MENU_LOCAL_TASK : MENU_CALLBACK), + ); + $items[] = array('path' => "admin/build/database/drop", + 'title' => t('Drop'), + 'callback' => 'dba_admin_tables_verify_op', + 'callback arguments' => array('drop'), + 'access' => $admin_access, + 'weight' => 15, + 'type' => (arg(3) == 'drop' ? MENU_LOCAL_TASK : MENU_CALLBACK), + ); if (_is_mysql()) { - $items[] = array('path' => "admin/database/check", - 'title' => t('check'), 'callback' => 'dba_admin_tables_check', - 'access' => user_access('dba administer database'), 'weight' => 15, - 'type' => arg(2) == 'check' ? MENU_LOCAL_TASK : MENU_CALLBACK); - $items[] = array('path' => "admin/database/optimize", - 'title' => t('optimize'), 'callback' => 'dba_admin_tables_optimize', - 'access' => user_access('dba administer database'), 'weight' => 15, - 'type' => arg(2) == 'optimize' ? MENU_LOCAL_TASK : MENU_CALLBACK); + $items[] = array('path' => "admin/build/database/check", + 'title' => t('Check'), + 'callback' => 'dba_admin_tables_check', + 'access' => $admin_access, + 'weight' => 15, + 'type' => (arg(3) == 'check' ? MENU_LOCAL_TASK : MENU_CALLBACK), + ); + $items[] = array('path' => "admin/build/database/optimize", + 'title' => t('Optimize'), + 'callback' => 'dba_admin_tables_optimize', + 'access' => $admin_access, + 'weight' => 15, + 'type' => (arg(3) == 'optimize' ? MENU_LOCAL_TASK : MENU_CALLBACK), + ); } } return $items; } -function dba_settings() { - if ( !user_access('dba administer database') ) { +function dba_settings_form() { + if (!user_access('dba administer database')) { drupal_access_denied(); module_invoke_all('exit'); exit; @@ -130,9 +194,9 @@ '#description' => t('Default filename to use when backing up multiple tables. If backing up only one table, the filename will default to the name of the table. You will have an opportunity to modify this filename when you actually perform the backup. If automatically backing up tables, the name will be prepended with the current date and time.'), '#default_value' => variable_get('dba_default_filename', 'backup.sql'), '#size' => 30, - '#maxlength' => 64 + '#maxlength' => 64, ); - + $period = drupal_map_assoc(array(0, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); $period[0] = t('disabled'); $form['backup']['dba_auto_backup_interval'] = array( @@ -143,9 +207,9 @@ '#description' => t('Select how often you wish to have your database automatically backed up. Requires crontab.'), ); $backup_interval = variable_get('dba_auto_backup_interval', 0); - $backup_path = variable_get('dba_auto_backup_path', file_directory_temp()); - $backup_exclude = variable_get('dba_auto_backup_exclude_tables', DBA_BACKUP_EXCLUDE); - + $backup_path = variable_get('dba_auto_backup_path', file_directory_temp()); + $backup_exclude = variable_get('dba_auto_backup_exclude_tables', DBA_BACKUP_EXCLUDE); + if ($backup_interval) { $attributes = array('enabled' => 'enabled'); } @@ -153,50 +217,64 @@ $attributes = array('disabled' => 'disabled'); } $form['backup']['dba_auto_backup_path'] = array( - '#type' => 'textfield', - '#title' => t('Automatic backup path'), - '#default_value' => $backup_path, - '#size' => 30, - '#maxlength' => 255, - '#description' => t('If automatic backups are enabled, you must specify a directory where you would like to store the backup files. The path must be absolute and for security reasons should not be accesible to the web.'), + '#type' => 'textfield', + '#title' => t('Automatic backup path'), + '#default_value' => $backup_path, + '#size' => 30, + '#maxlength' => 255, + '#description' => t('If automatic backups are enabled, you must specify a directory where you would like to store the backup files. The path must be absolute and for security reasons should not be accesible to the web.'), '#attributes' => $attributes); - + if (function_exists('bzcompress')) { - $form['backup']['dba_auto_backup_bzip2'] = array('#type' => 'checkbox', '#title' => t('Compress automatic backups'), '#return_value' => 1, '#default_value' => variable_get('dba_auto_backup_bzip2', 0), '#description' => t('Enable this option to compress automatic backups with bzip2.'), '#attributes' => $attributes); + $form['backup']['dba_auto_backup_bzip2'] = array( + '#type' => 'checkbox', + '#title' => t('Compress automatic backups'), + '#return_value' => 1, + '#default_value' => variable_get('dba_auto_backup_bzip2', 0), + '#description' => t('Enable this option to compress automatic backups with bzip2.'), + '#attributes' => $attributes, + ); } else if (function_exists('gzencode')) { - $form['backup']['dba_auto_backup_gzip'] = array('#type' => 'checkbox', '#title' => t('Compress automatic backups'), '#return_value' => 1, '#default_value' => variable_get('dba_auto_backup_gzip', 0), '#description' => t('Enable this option to compress automatic backups with zlib.'), '#attributes' => $attributes); + $form['backup']['dba_auto_backup_gzip'] = array( + '#type' => 'checkbox', + '#title' => t('Compress automatic backups'), + '#return_value' => 1, + '#default_value' => variable_get('dba_auto_backup_gzip', 0), + '#description' => t('Enable this option to compress automatic backups with zlib.'), + '#attributes' => $attributes, + ); } $form['backup']['dba_auto_backup_mail'] = array( - '#type' => 'checkbox', - '#title' => t('Mail backup to administrator'), - '#return_value' => 1, - '#default_value' => variable_get('dba_auto_backup_mail', 0), - '#description' => t("Enable this option to have a copy of the database backup files mailed to your administrator's email address."), + '#type' => 'checkbox', + '#title' => t('Mail backup to administrator'), + '#return_value' => 1, + '#default_value' => variable_get('dba_auto_backup_mail', 0), + '#description' => t("Enable this option to have a copy of the database backup files mailed to your administrator's email address."), '#attributes' => $attributes); $form['backup']['dba_auto_backup_exclude_tables'] = array( - '#type' => 'textfield', - '#title' => t('Automatic backup excluded tables'), - '#default_value' => $backup_exclude, - '#description' => t("If automatic backups are enabled, you can specify a space-separated list of table names where you only want the table definition (schema) backed up, but not the actual data. This is useful for tables that can be rebuilt (such as the tables related to search indexing) or the watchdog table, which holds log events but no actual site content. Only saving the schema and not the data for these tables can greatly reduce the size of the backups, without losing real content."), + '#type' => 'textfield', + '#title' => t('Automatic backup excluded tables'), + '#default_value' => $backup_exclude, + '#description' => t("If automatic backups are enabled, you can specify a space-separated list of table names where you only want the table definition (schema) backed up, but not the actual data. This is useful for tables that can be rebuilt (such as the tables related to search indexing) or the watchdog table, which holds log events but no actual site content. Only saving the schema and not the data for these tables can greatly reduce the size of the backups, without losing real content."), '#attributes' => $attributes); // MySQL - if ( _is_mysql() ) { + if (_is_mysql()) { $form['mysql_options'] = array( '#type' => 'fieldset', '#title' => t('MySQL options'), '#collapsible' => TRUE, - '#collapsed' => FALSE + '#collapsed' => FALSE, ); - $form['mysql_options']['dba_default_check_type'] = array ( + $form['mysql_options']['dba_default_check_type'] = array( '#type' => 'radios', '#title' => t('Default check type'), '#description' => t('MySQL databases support many types of database integrity checks. Select your preferred default type from the list above. Medium is the MySQL recommended default type.'), '#default_value' => variable_get('dba_default_check_type', 'MEDIUM'), '#options' => array('QUICK' => t('Quick'), 'FAST' => t('Fast'), 'CHANGED' => t('Changed'), 'MEDIUM' => t('Medium'), 'EXTENDED' => t('Extended')) ); - $form['mysql_options']['dba_repair'] = array ( + $form['mysql_options']['dba_repair'] = array( '#type' => 'radios', '#title' => t('Repair option'), '#description' => t('By default, the dba module will only display a repair button if a table has been determined to need a repair. Alternatively, you can make the module always display a repair button, or never display a repair button.'), @@ -207,12 +285,13 @@ // Add a validation callback to make sure the backup path is writable. $setting_valid = array('dba_settings_validate' => array()); $form['#validate'] = isset($form['#validate']) ? array_merge($form['#validate'], $setting_valid) : $setting_valid; - return $form; + + return system_settings_form($form); } function dba_settings_validate($form_id, $form_values, $form) { if (!file_check_directory($form_values['dba_auto_backup_path'])) { - form_set_error('dba_auto_backup_path', t('The automatic backup path does not exist, or is not writeable. Automatic backups will not begin until you fix this error.')); + form_set_error('dba_auto_backup_path', t('The automatic backup path does not exist, or is not writeable. Automatic backups will not begin until you fix this error.')); } elseif ($test = tempnam($form_values['dba_auto_backup_path'], 'dba.')) { file_delete($test); @@ -224,7 +303,7 @@ function dba_cron() { if ($interval = variable_get('dba_auto_backup_interval', 0)) { - // see if it's time for another auto-backup + // See if it's time for another auto-backup if ((time() - $interval) >= variable_get('dba_auto_backup_last', 0)) { dba_auto_backup(); } @@ -233,31 +312,31 @@ function dba_auto_backup() { $path = variable_get('dba_auto_backup_path', file_directory_temp()); - // see what tables (if any) the admin wants us to only backup + // See what tables (if any) the admin wants us to only backup // the schema, not the actual data. we need it as an array, so we - // lookup the setting as a string, then split() it into an array. + // lookup the setting as a string, then split() it into an array. $exclude_tables_str = variable_get('dba_auto_backup_exclude_tables', DBA_BACKUP_EXCLUDE); - $exclude_tables = split( '[ ,]', $exclude_tables_str ); - // make sure we have permission to save our backup file - if (file_check_directory($path)) { + $exclude_tables = split('[ ,]', $exclude_tables_str); + // Make sure we have permission to save our backup file + if (file_check_directory($path, FILE_CREATE_DIRECTORY)) { $database = dba_get_database(); - $filename = format_date(time(), 'custom', 'Y-md-Hi_'). variable_get('dba_default_filename', 'backup.sql'); - + $filename = format_date(time(), 'custom', 'Y-md-Hi_') . variable_get('dba_default_filename', 'backup.sql'); + $backup = "-- Drupal dba.module database dump\n"; $backup .= "--\n"; $backup .= "-- Database: $database\n"; $backup .= "-- Date: ". format_date(time(), 'large') ."\n\n"; - $tables = dba_get_tables(); + $tables = dba_get_tables(); foreach ($tables as $table) { $backup .= dba_backup_table($table, TRUE, FALSE, in_array($table, $exclude_tables) ? FALSE : TRUE); } - - // optionally bzip2 compress auto-backup file + + // Optionally bzip2 compress auto-backup file if (variable_get('dba_auto_backup_bzip2', 0)) { $backup = bzcompress($backup, 9); $filename = $filename .'.bz2'; } - // otherwise, optionally gzip compress auto-backup file + // Otherwise, optionally gzip compress auto-backup file else if (variable_get('dba_auto_backup_gzip', 0)) { if (version_compare(phpversion(), '4.2', '>=')) { $backup = gzencode($backup, 9, FORCE_GZIP); @@ -267,13 +346,13 @@ } $filename = $filename .'.gz'; } - + if ($fp = fopen($path ."/$filename", 'wb')) { fwrite($fp, $backup); fclose($fp); - variable_set('dba_auto_backup_last', time()); - - // if enabled, email a copy of the backup to the site administrator + variable_set('dba_auto_backup_last', time()); + + // If enabled, email a copy of the backup to the site administrator if (variable_get('dba_auto_backup_mail', 0)) { $attachment = new stdClass(); $attachment->path = $path ."/$filename"; @@ -289,23 +368,19 @@ */ function dba_admin_tables_view() { $output = ''; - if (user_access('dba view database')) { - // by default, just view the table - $output = dba_table_overview(arg(3)); - if (user_access('dba administer database')) { - if (arg(5) && arg(6) && arg(7)) { - switch(arg(5)) { - case 'delete': - $output = dba_delete_row(arg(3), arg(6), arg(7)); - break; - case 'edit': - $output = dba_edit_row(arg(3), arg(6), arg(7)); - break; - } - } + // By default, just view the table + if ((!arg(6) && !arg(7) && !arg(8)) || !user_access('dba administer database')) { + return dba_table_overview(arg(4)); + } + if (arg(6) && arg(7) && arg(8)) { + switch (arg(6)) { + case 'delete': + return drupal_get_form('dba_delete_row', arg(4), arg(7), arg(8)); + + case 'edit': + return drupal_get_form('dba_edit_row', arg(4), arg(7), arg(8)); } } - print theme('page', $output); } /** @@ -314,24 +389,16 @@ function dba_admin_tables_describe() { $output = ''; if (user_access('dba view database')) { - $output = dba_table_describe(arg(3)); + $output = dba_table_describe(arg(4)); } print theme('page', $output); } /** - * MySQL only: check/repair the selected table. + * MySQL only: check the selected table(s). */ function dba_admin_tables_check() { - switch ($_POST['op']) { - case 'Repair': - case 'Repair': - $output = dba_check_tables('repair'); - break; - default: - $output = dba_check_tables('check'); - } - print theme('page', $output); + return drupal_get_form('dba_check_tables'); } /** @@ -340,71 +407,45 @@ function dba_admin_tables_optimize() { $output = ''; if (user_access('dba administer database')) { - $edit = $_POST['edit']; - $output = dba_tables_optimize($edit); + $output = dba_tables_optimize(); unset($_SESSION['dba_tables']); } print theme('page', $output); } /** - * Backup the selected table(s). - */ -function dba_admin_tables_backup() { - dba_admin_tables_verify_op('backup'); -} - -/** - * Empty the selected table(s). + * Backup, empty or drop the selected table(s). */ -function dba_admin_tables_empty() { - dba_admin_tables_verify_op('empty'); -} - -/** - * Drop the selected table(s). - */ -function dba_admin_tables_drop() { - dba_admin_tables_verify_op('drop'); -} - function dba_admin_tables_verify_op($op) { - $edit = $_POST['edit']; - $tables = dba_get_active_tables($edit, 0); + $tables = dba_get_active_tables(0); unset($_SESSION['dba_tables']); if (empty($tables)) { - drupal_set_message(t('You must select the tables to %op.', array('%op' => _dba_ops($op))), 'error'); - drupal_goto('admin/database'); + drupal_set_message(t('You must select the tables to !op.', array('!op' => _dba_ops($op))), 'error'); + drupal_goto('admin/build/database'); } $edit['tables'] = $tables; - $output = dba_verify($edit, $op); - print theme('page', $output); -} - - -// dba module specific functions -function dba_admin_overview() { - return dba_database_overview_form(); + + return drupal_get_form('dba_verify', $edit, $op); } function theme_dba_database_overview_form($form) { - $output = ''; - $rows = array(); + $output = ''; + $rows = array(); $database = dba_get_database(); - drupal_set_title(t('View database %database', array('%database' => theme('placeholder', $database)))); - - // it'd be great to use the pager and tablesort, but doesn't appear possible - $header = array('', t('tables'), t('rows')); + drupal_set_title(t('View database %database', array('%database' => $database))); + + // It'd be great to use the pager and tablesort, but doesn't appear possible + $header = array('', t('Tables'), t('Rows')); $tables = dba_get_tables(); foreach ($tables as $table) { - $count = dba_get_row_count($table); - $checkbox = form_render($form[$table]); - $rows[] = array($checkbox, l($table, "admin/database/table/$table/view"), $count); + $count = dba_get_row_count($table); + $checkbox = drupal_render($form[$table]); + $rows[] = array($checkbox, l($table, "admin/build/database/table/$table/view"), $count); } $output .= dba_select_all_js(); $output .= theme('table', $header, $rows); $output .= dba_select_all_js(); - $output .= form_render($form); + $output .= drupal_render($form); drupal_set_html_head(checkoff_head()); return $output; } @@ -419,20 +460,20 @@ ); } if (_is_mysql()) { - $form['check'] = array('#type' => 'submit', '#value' => t('Check')); + $form['check'] = array('#type' => 'submit', '#value' => t('Check')); $form['optimize'] = array('#type' => 'submit', '#value' => t('Optimize')); } if (user_access('dba administer database')) { $form['backup'] = array('#type' => 'submit', '#value' => t('Backup')); - $form['empty'] = array('#type' => 'submit', '#value' => t('Empty')); - $form['drop'] = array('#type' => 'submit', '#value' => t('Drop')); + $form['empty'] = array('#type' => 'submit', '#value' => t('Empty')); + $form['drop'] = array('#type' => 'submit', '#value' => t('Drop')); } - return drupal_get_form('dba_database_overview_form', $form); + return $form; } function dba_database_overview_form_submit($form_id, $form_values) { $op = isset($_POST['op']) ? $_POST['op'] : ''; - $tables = dba_get_active_tables($form_values, 0); + $tables = dba_get_active_tables(0, $form_values); if (empty($tables)) { drupal_set_message(t('You must select the table(s) to %op.', array('%op' => $op)), 'error'); return; @@ -440,28 +481,32 @@ $_SESSION['dba_tables'] = $tables; switch ($op) { case t('Check'): - drupal_goto('admin/database/check'); + drupal_goto('admin/build/database/check'); break; + case t('Optimize'): - drupal_goto('admin/database/optimize'); + drupal_goto('admin/build/database/optimize'); break; + case t('Backup'): - drupal_goto('admin/database/backup'); + drupal_goto('admin/build/database/backup'); break; + case t('Empty'): - drupal_goto('admin/database/empty'); + drupal_goto('admin/build/database/empty'); break; + case t('Drop'): - drupal_goto('admin/database/drop'); + drupal_goto('admin/build/database/drop'); break; } } function dba_select_all_js() { - $output = ""; + $output = ""; $output .= t('select all'); $output .= "  |  "; - $output .= ""; + $output .= ""; $output .= t('clear all'); $output .= "
"; return $output; @@ -470,46 +515,46 @@ function dba_delete_row($table, $key, $keyid) { $rows = array(); $keyid = str_replace('__2F_', '/', $keyid); - $display = theme('placeholder', $table); $result = db_query("SELECT * FROM %s WHERE %s = '%s'", $table, $key, $keyid); $row = db_fetch_array($result); - $rows[] = (array)($row); - $header = array_keys($row); - $output = (theme('table', $header, $rows)); + $rows[] = array_map('check_plain', (array)$row); + $header = array_map('check_plain', array_keys($row)); + $form = array(); + $form['row'] = array('#value' => theme('table', $header, $rows)); $form['table'] = array('#type' => 'hidden', '#value' => $table); - $form['key'] = array('#type' => 'hidden', '#value' => $key); + $form['key'] = array('#type' => 'hidden', '#value' => $key); $form['keyid'] = array('#type' => 'hidden', '#value' => $keyid); - $output .= confirm_form('dba_delete_row_form', $form, - t('Are you sure you want to delete this row from the "%table" table?', array('%table' => $display)), - "admin/database/table/$table/view", - t('By clicking "Delete row" you will permanently remove this row from the %table table. This action cannot be undone.', array('%table' => $display)), - t('Delete row'), - t('Cancel')); - return $output; + $form = confirm_form($form, + t('Are you sure you want to delete this row from the "%table" table?', array('%table' => $table)), + "admin/build/database/table/$table/view", + t('By clicking "Delete row" you will permanently remove this row from the %table table. This action cannot be undone.', array('%table' => $table)), + t('Delete row'), + t('Cancel')); + return $form; } function dba_edit_row($table, $key, $keyid) { - $rows = array(); - $keyid = str_replace('__2F_', '/', $keyid); - $display = theme('placeholder', $table); + $rows = array(); + $keyid = str_replace('__2F_', '/', $keyid); $result = db_query("SELECT * FROM %s WHERE %s = '%s'", $table, $key, $keyid); - $row = db_fetch_array($result); + $row = db_fetch_array($result); $header = array_keys($row); foreach ($row as $k => $value) { if ($k == $key) { - $form['key'] = array('#type' =>'markup', '#value' => $value); + $form['key'] = array('#type' => 'markup', '#value' => $value); } else { + // We store all fields in sub-array 'fields' to avoid naming collisions. $size = strlen($value); if ($size > 255) { - $form[$k] = array( + $form['field'][$k] = array( '#type' => 'textarea', '#default_value' => $value, '#cols' => 70, '#rows' => 10, ); } else { - $form[$k] = array( + $form['field'][$k] = array( '#type' => 'textfield', '#default_value' => $value, '#size' => $size, '#maxlength' => 255, @@ -522,43 +567,43 @@ '#value' => implode(',', $header), ); $form['table'] = array('#type' => 'hidden', '#value' => $table); - $form['key'] = array('#type' => 'hidden', '#value' => $key); + $form['key'] = array('#type' => 'hidden', '#value' => $key); $form['keyid'] = array('#type' => 'hidden', '#value' => $keyid); - $output .= confirm_form('dba_edit_row_form', $form, - t('Edit row from the "%table" table', array('%table' => $display)), - "admin/database/table/$table/view", - t('By clicking "Edit row" you will save any changes you make to this row of the %table table. This action cannot be undone.', array('%table' => $display)), - t('Edit row'), - t('Cancel')); - return $output; + $form = confirm_form($form, + t('Edit row from the "%table" table', array('%table' => $table)), + "admin/build/database/table/$table/view", + t('By clicking "Edit row" you will save any changes you make to this row of the %table table. This action cannot be undone.', array('%table' => $table)), + t('Edit row'), + t('Cancel') + ); + return $form; } -function theme_dba_edit_row_form($form) { +function theme_dba_edit_row($form) { $header = explode(',', $form['header']['#value']); - $key = $form['key']['#value']; - $keyid = $form['keyid']['#value']; - $rows = array(); - $row = array(); + $key = $form['key']['#value']; + $keyid = $form['keyid']['#value']; + $rows = array(); + $row = array(); foreach ($header as $k => $name) { if ($name == $key) { $row[] = $keyid; } else { - $row[] = form_render($form[$name]); + $row[] = drupal_render($form['field'][$name]); } } $rows[] = $row; $output = theme('table', $header, $rows); - $output .= form_render($form); + $output .= drupal_render($form); return $output; } - -function dba_edit_row_form_submit($form_id, $form_values) { +function dba_edit_row_submit($form_id, $form_values) { if (user_access('dba administer database')) { - $key = $form_values['key']; - $keyid = $form_values['keyid']; - $table = $form_values['table']; + $key = $form_values['key']; + $keyid = $form_values['keyid']; + $table = $form_values['table']; $fields = dba_get_fields($table); foreach ($fields as $field) { if ($field != $key) { @@ -571,63 +616,63 @@ } } } - // TODO: Manual prefixing + // @todo Manual prefixing $query = "UPDATE $table SET $query WHERE $key = '$keyid'"; - drupal_set_message($query); + drupal_set_message(check_plain($query)); // Use _db_query so we preserve {}'s _db_query($query); } - drupal_goto("admin/database/table/$table/view"); + drupal_goto("admin/build/database/table/$table/view"); } -function dba_delete_row_form_submit($form_id, $form_values) { +function dba_delete_row_submit($form_id, $form_values) { if (user_access('dba administer database')) { - $key = $form_values['key']; + $key = $form_values['key']; $keyid = $form_values['keyid']; $table = $form_values['table']; $query = "DELETE FROM $table WHERE $key = '$keyid'"; - drupal_set_message($query); + drupal_set_message(check_plain($query)); $query = "DELETE FROM {%s} WHERE %s = '%s'"; db_query($query, $table, $key, $keyid); } - drupal_goto("admin/database/table/$table/view"); + drupal_goto("admin/build/database/table/$table/view"); } function dba_table_overview($table) { $rows = array(); - $tables = dba_get_active_tables($edit); + $tables = dba_get_active_tables(); $quantity = sizeof(explode(',', $tables)); if ($quantity == 1) { - drupal_set_title(t('View table \'%table\'', array('%table' => "$table"))); - + drupal_set_title(t('View table %table', array('%table' => $table))); + if (user_access('dba administer database')) { $primary = dba_get_primary_key($table); } else { $primary = NULL; } - + $fields = dba_get_fields($table); foreach ($fields as $field) { $header[] = array('data' => "$field", 'field' => "$field"); } - + $sql = "SELECT * FROM {$table}"; $sql .= tablesort_sql($header); $result = pager_query($sql, 20); - + if (!is_null($primary)) { $header[] = t('actions'); } - + if (db_num_rows($result)) { while ($row = db_fetch_array($result)) { - $line = array_values($row); + $line = array_map('check_plain', array_values($row)); if (!is_null($primary)) { - $id = "{$row[$primary]}"; - $id = str_replace('/', '__2F_', $id); - $actions = '['. l(t('edit'), "admin/database/table/$table/view/edit/$primary/$id") .']'; - $actions .= ' ['. l(t('delete'), "admin/database/table/$table/view/delete/$primary/$id") .']'; + $id = "{$row[$primary]}"; + $id = str_replace('/', '__2F_', $id); + $actions = '['. l(t('edit'), "admin/build/database/table/$table/view/edit/$primary/$id") .']'; + $actions .= ' ['. l(t('delete'), "admin/build/database/table/$table/view/delete/$primary/$id") .']'; $line[] = $actions; } $rows[] = $line; @@ -644,7 +689,7 @@ } else { drupal_set_message(t('Unable to view more than one table at a time.'), 'error'); - $output .= dba_database_overview(); + $output .= dba_database_overview_form(); } return $output; } @@ -652,7 +697,7 @@ function dba_get_primary_key($table) { if (_is_mysql()) { $rows = array(); - $tables = dba_get_active_tables($edit); + $tables = dba_get_active_tables(); $quantity = sizeof(explode(',', $tables)); if ($quantity == 1) { $result = dba_describe_table($table, FALSE); @@ -667,7 +712,7 @@ } } else { - // not MySQL, so currently unsupported + // Not MySQL, so currently unsupported return; } return; @@ -675,10 +720,10 @@ function dba_table_describe($table) { $rows = array(); - $tables = dba_get_active_tables($edit); + $tables = dba_get_active_tables(); $quantity = sizeof(explode(',', $tables)); if ($quantity == 1) { - drupal_set_title(t('Describe table \'%table\'', array('%table' => "$table"))); + drupal_set_title(t('Describe table %table', array('%table' => $table))); $result = dba_describe_table($table); while ($row = db_fetch_array($result)) { if (!$header) { @@ -695,76 +740,120 @@ return $output; } -function dba_query() { - $edit = $_POST['edit']; - drupal_set_title(t('Query database')); - if (!empty($edit['dba_query'])) { - unset ($output); - // execute each sql statement individually - foreach (explode(';', $edit['dba_query']) as $sql) { +function dba_query_page() { + if (user_access('dba administer database')) { + drupal_set_title(t('Query database')); + $output = drupal_get_form('dba_query_form'); + } + return $output; +} + +function dba_query_form() { + // Now, add a text area for the admin to enter a query. + $form['query'] = array( + '#type' => 'fieldset', + '#title' => t('Query'), + ); + $form['query']['dba_query'] = array( + '#type' => 'textarea', + '#title' => t('Database query'), + '#cols' => 70, '#rows' => 10, + '#description' => t('Enter the text of your database query. This will be executed directly in your database, so the action can not be undone. Do not wrap your tables in {}, as direct database queries do not support Drupal\'s database prefixing. If you are using a database prefix, you will need to manually include the prefix in your table name. Separate multiple queries with a ";". A sample query: \'SELECT COUNT(*) FROM accesslog;\''), + ); + $form['query']['actions'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + $form['query']['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Execute query'), + ); + $form['query']['actions']['cancel'] = array( + '#type' => 'markup', + '#value' => l(t('Cancel'), 'admin/build/database'), + ); + + // We use #pre_render, so that we can safely use validated form values + // to see if a query has been entered, and if so, run the query and + // dynamically generate other form elements to display the results. + $form['#pre_render'][] = 'dba_query_form_pre_render'; + + // We don't want to get redirected, which would run the queries twice. + $form['#redirect'] = false; + + return $form; +} + +function dba_query_form_pre_render($form_id, &$form) { + // If there are no validation errors and there's already a query, + // run it and display the results. + $dba_query = $form['query']['dba_query']['#value']; + if (!form_get_errors() && !empty($dba_query)) { + // Execute each sql statement individually. + $i = 0; + foreach (explode(';', $dba_query) as $sql) { $header = NULL; - if (trim($sql) == '') + if (trim($sql) == '') { break; + } $result = dba_execute_query($sql); if ($result && $result != 1 && db_num_rows($result)) { while ($row = db_fetch_array($result)) { if (!$header) { - $header = array_keys($row); + $header = array_map('check_plain', array_keys($row)); } - $rows[] = array_values($row); + $rows[] = array_map('check_plain', array_values($row)); } } - if (!is_null($rows)) { - $form['res'] = array('#type' => 'fieldset', '#title' => t('Result')); - $form['res']['result'] = array( - '#type' => 'markup', + if (!empty($rows)) { + $form['results'][$i] = array( + '#type' => 'fieldset', + '#title' => t('Result') .': '. theme_placeholder($sql), + ); + $form['results'][$i]['result'] = array( '#value' => theme('table', $header, $rows), ); + unset($rows); } - unset ($rows); + $i++; } + $form['results'] = form_builder('dba_query_form', $form['results']); } - - if (user_access('dba administer database')) { - $form['query'] = array('#type' => 'fieldset', '#title' => t('Query')); - $form['query']['dba_query'] = array( - '#type' => 'textarea', - '#title' => t('Database query'), - '#default_value' => $edit['dba_query'], - '#cols' => 70, '#rows' => 10, - '#description' => t('Enter the text of your database query. This will be executed directly in your database, so the action can not be undone. Do not wrap your tables in {}, as direct database queries do not support Drupal\'s database prefixing. If you are using a database prefix, you will need to manually include the prefix in your table name. Separate multiple queries with a ";". A sample query: \'SELECT COUNT(*) FROM accesslog;\''), - ); - $form['query']['actions'] = array( - '#prefix' => '
', - '#suffix' => '
', - ); - $form['query']['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Execute query'), - ); - $form['query']['actions']['cancel'] = array( - '#type' => 'markup', - '#value' => l(t('Cancel'), 'admin/database'), - ); - } - $form['#redirect'] = false; - $output .= drupal_get_form('dba_query', $form); - print theme('page', $output); } function dba_execute_query($sql) { if (user_access('dba administer database')) { - drupal_set_message($sql); return _db_query($sql); } } function dba_run_script() { - $edit = $_POST['edit']; drupal_set_title(t('Run script')); + if (user_access('dba administer database')) { + $form['script'] = array('#type' => 'fieldset', '#title' => 'Script'); + $form['script']['script_filename'] = array( + '#type' => 'file', '#title' => t('Select a script'), + '#description' => t('Click the "browse" button to select a database script from your local computer.') + ); + $form['script']['verbose'] = array( + '#type' => 'checkbox', + '#title' => t('Verbose'), + '#return_value' => 1, + '#default_value' => 0, + '#description' => t('Check this box if you wish to see all queries that are run.'), + ); + $form['script']['submit'] = array( + '#type' => 'submit', + '#value' => t('Run script'), + ); + $form['#attributes'] = array('enctype' => 'multipart/form-data'); + } + return $form; +} +function dba_run_script_submit($form_id, $form_values) { if ($file = file_save_upload('script_filename')) { - // file is now in temporary directory + // File is now in temporary directory if (file_exists($file->filepath)) { if ($fp = fopen($file->filepath, 'r')) { $query = NULL; @@ -774,11 +863,11 @@ if ($line && strncmp($line, '--', 2) && strncmp($line, '#', 1)) { $query .= $line; if (strpos($line, ';')) { - if (db_query($query, FALSE)) { - if ($edit['verbose']) { - drupal_set_message($query); + if (_db_query($query, FALSE)) { + if ($form_values['verbose']) { + drupal_set_message(check_plain($query)); } - $count++; + ++$count; } else { drupal_set_message(t('Query failed: %query', array('%query' => $query)), 'error'); @@ -788,71 +877,121 @@ } } fclose ($fp); - drupal_set_message(t('Succesfully ran %query from script "%filename".', array('%query' => format_plural($count, '1 query', '%count queries'), '%filename' => $file->filename))); + drupal_set_message(t('Succesfully ran !query from script %filename.', array('!query' => format_plural($count, '1 query', '@count queries'), '%filename' => $file->filename))); } else { - drupal_set_message(t('Unable to open script "%filename".', array('%filename' => $file->filename)), 'error'); + drupal_set_message(t('Unable to open script %filename.', array('%filename' => $file->filename)), 'error'); } file_delete($file->filepath); } else { - drupal_set_message(t('Script "%filename" does not exist.', array('%filename' => $file->filename)), 'error'); + drupal_set_message(t('Script %filename does not exist.', array('%filename' => $file->filename)), 'error'); } - // cleanup session + // Cleanup session unset($_SESSION['file_uploads'][$file->source]); } - - if (user_access('dba administer database')) { - $form['script'] = array('#type' => 'fieldset', '#title' => 'Script'); - $form['script']['script_filename'] = array( - '#type' => 'file', '#title' => t('Select a script'), - '#description' => t('Click the "browse" button to select a database script from your local computer.') - ); - $form['script']['verbose'] = array('#type' => 'checkbox', '#title' => t('Verbose'), '#return_value' => 1, '#default_value' => 0, '#description' => t('Check this box if you wish to see all queries that are run.')); - $form['script']['fileop'] = array('#type' => 'button', '#name' => 'fileop', '#value' => t('Run script')); - $form['#attributes'] = array('enctype' => 'multipart/form-data'); - } - - //$output .= form($form, 'post', 0, array('enctype' => 'multipart/form-data')); - $output .= drupal_get_form('dba_run_script', $form); - print theme('page', $output); } -function dba_check_tables($action = 'check') { - $edit = $_POST['edit']; - $tables = dba_get_active_tables($edit, 0); +function dba_check_tables() { + + // Setup a form value to remember what table(s) we're operating on. + $form['check_tables']['tables']['#type'] = 'hidden'; + // First, see what the active table is, based solely on + // $_SESSION and the URL. + $tables = dba_get_active_tables(0); unset($_SESSION['dba_tables']); - if (empty($tables)) { - if ($action == 'check') { - drupal_set_message(t('You must select the tables to check.'), 'error'); - } - else { - drupal_set_message(t('You must select the tables to repair.'), 'error'); + if (!empty($tables)) { + // We already know, so this is easy... + $form['check_tables']['tables']['#default_value'] = $tables; + } + else { + // It must be in the form values, then. In this case, we need to + // call form_builder() to safely grab the data out of $_POST. + $form['check_tables'] = form_builder('dba_check_tables', $form['check_tables']); + if (!empty($form['check_tables']['tables']['#value'])) { + $form_tables['tables'] = $form['check_tables']['tables']['#value']; + $tables = dba_get_active_tables(0, $form_tables); } - drupal_goto('admin/database'); } - $repair = array(); - if (!$edit['check_type']) { - $edit['check_type'] = variable_get('dba_default_check_type', 'MEDIUM'); + // Make sure we have something to do. + if (empty($tables)) { + drupal_set_message(t('You must select the tables to check.'), 'error'); + drupal_goto('admin/build/database'); } - $type = $edit['check_type']; - if ("$action" == 'check') { - drupal_set_title(t('Performing %type table check', array('%type' => $type))); - $result = dba_check_table($tables, $type); + + $form['check_options'] = array( + '#type' => 'fieldset', + '#title' => t('Actions'), + ); + $form['check_options']['check_type'] = array( + '#type' => 'radios', + '#title' => t('Check type'), + '#default_value' => variable_get('dba_default_check_type', 'MEDIUM'), + '#options' => array( + 'QUICK' => t('Quick'), + 'FAST' => t('Fast'), + 'CHANGED' => t('Changed'), + 'MEDIUM' => t('Medium'), + 'EXTENDED' => t('Extended') + ), + ); + $form['check_options']['check'] = array( + '#type' => 'submit', + '#value' => t('Check again') + ); + + // Most of the interesting stuff in this form has to be added via + // #pre_render, so that we can safely use validated form values to + // dynamically generate other form elements. In particular, we add + // a fieldset with the results of whatever operation we perform, and + // depending on the admin settings and the state of the tables, we + // might need to add a 'Repair' button, too. + $form['#pre_render'][] = 'dba_check_table_form_pre_render'; + + // We don't want to get redirected, which would run the queries twice. + $form['#redirect'] = false; + + return $form; +} + +function dba_check_table_form_pre_render($form_id, &$form) { + global $form_values; + if (form_get_errors()) { + // If there's a validation error (e.g. #token is wrong), return + // immediately since we can't trust $form_values. + // @todo $form_values seems to be empty at any time. 06/05/2007 sun + return; } - else { + + $action = isset($_POST['op']) ? $_POST['op'] : 'check'; + $type = $form['check_options']['check_type']['#post']['check_type']; + $tables = $form['check_tables']['tables']['#value']; + + if ($action == t('Repair')) { drupal_set_title(t('Performing table repair.')); $result = dba_repair_table($tables); } + else { + drupal_set_title(t('Performing %type table check', array('%type' => check_plain($type)))); + $result = dba_check_table($tables, $type); + } - $header = array(t('Table'), t('Operation'), t('Message type'), t('Message text')); + // Construct the output of the operation as a table, and see if any + // tables need to be repaired. + $repair = array(); + $header = array( + t('Table'), + t('Operation'), + t('Message type'), + t('Message text'), + ); while ($row = db_fetch_object($result)) { $rows[] = (array)($row); if ($row->Msg_type == 'status') { $status = $row->Msg_text; if ($status != 'OK' && $status != 'Table is already up to date') { // An error message will result if we use the database name when trying - // to repair a table and the database has '-' in the name, so to be + // to repair a table and the database has '-' in the name, so to be // safe we strip off the database name. $repair_table = explode('.', $row->Table); $repair[] = $repair_table[1]; @@ -860,63 +999,56 @@ } } $output .= theme('table', $header, $rows); - + if ($repair) { - $output .= '

'. t('One or more tables need repairs.'). '

'; + $output .= '

'. t('One or more tables need repairs.') .'

'; $to_repair = 1; } else { $output .= '

'. t('No repairs are required.') .'

'; $to_repair = 0; } - $form['tables'] = array('#type' => 'value', '#value' => $tables); - $form['check_results'] = array('#type' => 'fieldset', '#title' => t('Result')); + + $form['check_results'] = array( + '#type' => 'fieldset', + '#title' => t('Result'), + '#weight' => -5, + ); $form['check_results']['result'] = array( '#type' => 'markup', '#value' => $output, ); - $form['check_options'] = array('#type' => 'fieldset', '#title' => t('Actions')); - $form['check_options']['check_type'] = array( - '#type' => 'radios', - '#title' => t('Check type'), - '#default_value' => $edit['check_type'], - '#options' => array( - 'QUICK' => t('Quick'), - 'FAST' => t('Fast'), - 'CHANGED' => t('Changed'), - 'MEDIUM' => t('Medium'), - 'EXTENDED' => t('Extended') - ), - ); - $form['check_options']['check'] = array( - '#type' => 'submit', - '#value' => t('Check again') - ); - $repair_option = variable_get('dba_repair', 0); + // Since we just added these form elements, but we're already at the + // pre_render stage, we need to manually invoke form_builder() so that + // these elements are rendered in the final form shown to the user. + $form['check_results'] = form_builder('dba_check_tables', $form['check_results']); + if (user_access('dba administer database')) { + $repair_option = variable_get('dba_repair', 0); if (($repair_option == 0 && $to_repair) || $repair_option == 1) { $form['check_options']['repair'] = array( '#type' => 'submit', - '#value' => t('Repair') + '#value' => t('Repair'), + '#weight' => 20, ); if (!$repair_option) { $form['check_options']['repair_tables'] = array( '#type' => 'hidden', - '#value' => implode(',', $repair), + '#value' => implode(',', $repair_tables), ); } } } - $form['check_options']['tables'] = array( - '#type' => 'hidden', - '#value' => $tables, - ); - $form['#redirect'] = false; - return drupal_get_form('dba_check_tables', $form); + // Now that we know what type of check we used, we can set the default. + $form['check_options']['check_type']['#default_value'] = $type; + + // Rebuild the form elements we've modified here, so they're + // displayed on when the page is rendered. + $form['check_options'] = form_builder('dba_check_tables', $form['check_options']); } -function dba_tables_optimize($edit) { - $tables = dba_get_active_tables($edit, 0); +function dba_tables_optimize() { + $tables = dba_get_active_tables(0); if (empty($tables)) { $quantity = 0; } @@ -926,93 +1058,128 @@ drupal_set_title(t('Optimizing %table', array('%table' => format_plural($quantity, t('table'), t('tables'))))); if (!$quantity) { drupal_set_message(t('You must select the tables to optimize.'), 'error'); - drupal_goto('admin/database'); + drupal_goto('admin/build/database'); } else { $query = 'OPTIMIZE TABLE '. str_replace(',', ', ', $tables) .';'; - drupal_set_message($query); + drupal_set_message(check_plain($query)); $result = db_query($query); - + $header = array(t('Table'), t('Operation'), t('Message type'), t('Message text')); - $rows = array(); + $rows = array(); while ($row = db_fetch_object($result)) { - $rows[] = (array)($row); + $rows[] = array_map('check_plain', (array)($row)); } $output = theme('table', $header, $rows); } return $output; } -function dba_get_active_tables($edit, $default = 1) { +function dba_get_active_tables($default = 1, $edit = array()) { + static $all_tables = array(); + if (empty($all_tables)) { + $all_tables = dba_get_tables(); + } + $tables = array(); - if ($edit['tables']) { + if (!empty($edit['tables'])) { $tables = $edit['tables']; } - else if (arg(4) && arg(3)) { - $tables = arg(3); + else if (arg(5) && arg(4)) { + $tables = arg(4); } else { - foreach (dba_get_tables() as $table) { - if ($edit["$table"]) { + foreach ($all_tables as $table) { + if (!empty($edit[$table])) { $tables[] = $table; } } if (sizeof($tables)) { - $tables = implode(',', $tables); + return implode(',', $tables); } - // no tables were set, by default we will return a list of all tables + // No tables were set, by default we will return a list of all tables. else if ($default) { - $tables = dba_get_tables(); - $tables = implode(',', $tables); + return implode(',', $all_tables); } } if (empty($tables) && (isset($_SESSION['dba_tables']))) { $tables = $_SESSION['dba_tables']; } - return $tables; + // Final sanity/security check to prevent malicious table names. + $table_array = explode(',', $tables); + foreach ($table_array as $table) { + if (isset($all_tables[$table])) { + $safe_tables[] = $table; + } + } + return empty($safe_tables) ? '' : implode(',', $safe_tables); } function dba_verify($edit, $action) { - $tables = dba_get_active_tables($edit, 0); + $tables = dba_get_active_tables(0, $edit); $quantity = sizeof(explode(',', $tables)); if ($quantity) { - $form = array(); - $table_list = theme('placeholder', str_replace(',', ', ', $tables)); - $table_id = format_plural($quantity, t('table'), t('tables')); - $substitutions = array('%tables' => $table_list, '%table' => $table_id, '%this' => format_plural($quantity, t('this'), t('these')), '%itself' => format_plural($quantity, t('itself'), t('themselves')), '%its' => format_plural($quantity, t('its'), t('their'))); + $form = array(); + $table_list = theme('placeholder', str_replace(',', ', ', $tables)); + $table_id = format_plural($quantity, t('table'), t('tables')); + $substitutions = array( + '!tables' => $table_list, + '!table' => $table_id, + '!this' => format_plural($quantity, t('this'), t('these')), + '!itself' => format_plural($quantity, t('itself'), t('themselves')), + '!its' => format_plural($quantity, t('its'), t('their')), + ); $form['tables'] = array('#type' => 'hidden', '#value' => $tables); switch ($action) { case 'backup': $filename = ($quantity == 1 ? $tables .'.sql' : variable_get('dba_default_filename', 'backup.sql')); - $form['file_name'] = array('#type' => 'textfield', '#title' => t('Backup filename'), '#default_value' => $filename, '#size' => 40, '#maxlength' => 255, '#description' => t("Please specify the filename you wish to give your database backup. Once you click 'Backup %table' below your web browser will allow you to save the database backup to your local computer.", array('%table' => format_plural($quantity, 'table', 'tables')))); - $form['add_drop_table'] = array('#type' => 'checkbox', '#title' => t('Add DROP TABLE'), '#default_value' => 0, '#description' => t('Check this box if you wish to add DROP TABLE IF EXISTS before each table schema. This will allow you to quickly restore from a backup without having to manually drop all tables first.')); - $output = confirm_form('dba_verify_backup_form', $form, - t('Backup %table to local computer?', array('%table' => $table_id)), - 'admin/database', - t('By clicking "Backup %table" you will be prompted to save the following %table to your local computer: %tables', array('%tables' => $display, '%table' => $table_id)), - t('Backup %table', array('%table' => $table_id)), - t('Cancel')); - + $form['file_name'] = array( + '#type' => 'textfield', + '#title' => t('Backup filename'), + '#default_value' => $filename, + '#size' => 40, + '#maxlength' => 255, + '#description' => t("Please specify the filename you wish to give your database backup. Once you click 'Backup !table' below your web browser will allow you to save the database backup to your local computer.", + array('!table' => format_plural($quantity, 'table', 'tables')) + ), + ); + $form['add_drop_table'] = array( + '#type' => 'checkbox', + '#title' => t('Add DROP TABLE'), + '#default_value' => 0, + '#description' => t('Check this box if you wish to add DROP TABLE IF EXISTS before each table schema. This will allow you to quickly restore from a backup without having to manually drop all tables first.'), + ); + $form = confirm_form($form, + t('Backup !table to local computer?', array('!table' => $table_id)), + 'admin/build/database', + t('By clicking "Backup !table" you will be prompted to save the following !table to your local computer: !tables', array('!tables' => $display, '!table' => $table_id)), + t('Backup !table', array('!table' => $table_id)), + t('Cancel')); + $form['#base'] = 'dba_verify_backup_form'; break; + case 'empty': - $output = confirm_form('dba_verify_empty_form', $form, - t('Are you sure you want to delete all rows from the "%tables" %table?', $substitutions), - 'admin/database', - t('By clicking "Empty %table" you will completely remove all data from %this %table, though the %table %itself will not be dropped. This action cannot be undone.', $substitutions), - t('Empty %table', array('%table' => $table_id)), - t('Cancel')); + $form = confirm_form($form, + t('Are you sure you want to delete all rows from the "!tables" !table?', $substitutions), + 'admin/build/database', + t('By clicking "Empty !table" you will completely remove all data from !this !table, though the !table !itself will not be dropped. This action cannot be undone.', $substitutions), + t('Empty !table', array('!table' => $table_id)), + t('Cancel')); + $form['#base'] = 'dba_verify_empty_form'; break; + case 'drop': - $output = confirm_form('dba_verify_drop_form', $form, - t('Are you sure you want to drop the "%tables" %table?', $substitutions), - 'admin/database', - t('By clicking "Drop %table" you will be completely removing %this %table and all %its data from the database. This action cannot be undone.', $substitutions), - t('Drop %table', array('%table' => $table_id)), - t('Cancel')); + $form = confirm_form($form, + t('Are you sure you want to drop the "!tables" !table?', $substitutions), + 'admin/build/database', + t('By clicking "Drop !table" you will be completely removing !this !table and all !its data from the database. This action cannot be undone.', $substitutions), + t('Drop !table', array('!table' => $table_id)), + t('Cancel')); + $form['#base'] = 'dba_verify_drop_form'; break; } } - return $output; + return $form; } function dba_verify_backup_form_submit($form_id, $form_values) { @@ -1036,7 +1203,7 @@ } $quantity = sizeof($tables); $display = implode(', ', $tables); - drupal_set_message(t("Saved %tables to %filename.", array('%filename' => theme('placeholder', $file_name), '%tables' => theme('placeholder', $display)))); + drupal_set_message(t("Saved %tables to %filename.", array('%filename' => $file_name, '%tables' => $display))); exit(0); } } @@ -1054,9 +1221,9 @@ } } if (sizeof($tables) > 1) { - drupal_goto('admin/database'); + drupal_goto('admin/build/database'); } - drupal_goto("admin/database/table/$table/view"); + drupal_goto("admin/build/database/table/$table/view"); } function dba_verify_drop_form_submit($form_id, $form_values) { @@ -1071,61 +1238,65 @@ dba_drop_table($table); } } - drupal_goto('admin/database'); + drupal_goto('admin/build/database'); } +/** + * @defgroup dba_api Database Administrator API + * @{ + */ -/********** -* dba api * -**********/ - -// return as array all tables in active database +/** + * Return all tables in active database as array. + */ function dba_get_tables() { global $db_prefix; static $table_list = array(); if ($table_list) { - // cache copy so function can be called multiple times efficiently + // Cache copy so function can be called multiple times efficiently return $table_list; } - + if (_is_mysql()) { $result = db_query('show tables'); } else { $result = db_query('SELECT DISTINCT tabname as Table FROM {drupal_system_catalog}'); } - + while ($tables = db_fetch_object($result)) { foreach ($tables as $db => $table) { if (!$db_prefix) { - $table_list[] = $table; + $table_list[$table] = $table; } elseif (is_array($db_prefix)) { foreach ($db_prefix as $prefix) { $prefix = isset($db_prefix[$table]) ? $db_prefix[$table] : $db_prefix['default']; if (preg_match("/^($prefix)/", $table)) { - $table_list[] = $table; + $table_list[$table] = $table; break; } } - } + } elseif (preg_match("/^($db_prefix)/", $table)) { - $table_list[] = $table; + $table_list[$table] = $table; } } } - + return $table_list; } -// Return name of active database +/** + * Return name of active database. + */ function dba_get_database() { static $database = array(); if ($database) { - // cache copy so function can be called multiple times efficiently + // Cache copy so function can be called multiple times efficiently return $database; } - + if (_is_mysql()) { $database = array_keys(db_fetch_array(db_query('show tables'))); $database = preg_replace('/^Tables_in_/', '', $database[0]); @@ -1134,59 +1305,74 @@ $result = db_fetch_object(db_query('SELECT DISTINCT dbname FROM {drupal_system_catalog} LIMIT 1')); $database = $result->dbname; } - + return $database; } -// Return as array all fields in specified table -function dba_get_fields($table) { +/** + * Return all fields in specified table as array. + */ +function dba_get_fields($table, $describe = FALSE) { $fields = array(); - + if (_is_mysql()) { $result = db_query("DESCRIBE $table"); while ($row = db_fetch_object($result)) { - $fields[] = $row->Field; + if (!$describe) { + $fields[] = $row->Field; + } + else { + $fields[] = $row; + } } } else { - // lowercase of names of resulting columns seems to be important for postgresql - $result = db_query("SELECT colname as field FROM {drupal_system_catalog} WHERE tabname = '".$table."'"); + // Lowercase of names of resulting columns seems to be important for postgresql + $result = db_query("SELECT colname as field FROM {drupal_system_catalog} WHERE tabname = '". $table ."'"); while ($row = db_fetch_object($result)) { $fields[] = $row->field; } } - + return $fields; } -// Return the number of rows in the specified table +/** + * Return the number of rows in the specified table. + */ function dba_get_row_count($table) { $rows = db_fetch_object(db_query("SELECT COUNT(*) as count FROM {$table}")); return $rows->count; } -// Perform specified check type on specified table (MySQL specific) +/** + * Perform specified check type on specified table (MySQL specific). + */ function dba_check_table($table, $type = 'MEDIUM') { - $query = 'CHECK TABLE '. str_replace(',', ', ', $table) ." $type;"; - drupal_set_message($query); + $query = 'CHECK TABLE '. str_replace(',', ', ', $table) .' '. db_escape_string($type) . ';'; + drupal_set_message(check_plain($query)); return db_query($query); } -// Repair specified table (MySQL specific) +/** + * Repair specified table (MySQL specific) + */ function dba_repair_table($table) { - $query = "REPAIR TABLE $table;"; - drupal_set_message($query); + $query = 'REPAIR TABLE '. str_replace(',', ', ', $table) .';'; + drupal_set_message(check_plain($query)); return db_query($query); } -// Describe table +/** + * Describe table. + */ function dba_describe_table($table, $verbose = TRUE) { if (_is_mysql()) { - $query = "DESCRIBE $table;"; + $query = 'DESCRIBE '. db_escape_table($table) .';'; } else { - // lower case names of resulting colums are important for PostgreSQL - $query = "SELECT tabname as table, colname as field, coltype as type, colnull as null, coldefault as default, colextra as extra FROM {drupal_system_catalog} WHERE tabname = '{$table}' ORDER BY colextra;"; + // Lowercase names of resulting colums are important for PostgreSQL + $query = "SELECT tabname as table, colname as field, coltype as type, colnull as null, coldefault as default, colextra as extra FROM {drupal_system_catalog} WHERE tabname = '{". db_escape_table($table) ."}' ORDER BY colextra;"; } if ($verbose) { drupal_set_message($query); @@ -1194,50 +1380,68 @@ return db_query($query); } -// Backup table to file -function dba_backup_table($table, $add_drop_table, $live = TRUE, $data = TRUE) { +/** + * Backup table to file. + */ +function dba_backup_table($table, $add_drop_table, $verbose = TRUE, $data = TRUE) { $output = "--\n"; $output .= "-- Table structure for table '$table'\n"; $output .= "--\n\n"; - + if ($add_drop_table) { $output .= "DROP TABLE IF EXISTS $table;\n"; } - + $create = dba_show_create_table($table); $output .= $create['Create Table']; $output .= ";\n\n"; - - if (!$data) { - // backup schema only for this table + + if (!$data) { + // Backup schema only for this table return $output; } - + $output .= "--\n"; $output .= "-- Dumping data for table '$table'\n"; $output .= "--\n\n"; - - if ($live) { + + if ($verbose) { echo $output; $output = NULL; } - - $result = db_query("select * from $table"); - $numrow = db_num_rows($result); - $fields = dba_get_fields($table); + + $result = db_query("select * from $table"); + $numrow = db_num_rows($result); + $fields = dba_get_fields($table, TRUE); $num_fields = sizeof($fields); - + while ($row = db_fetch_array($result)) { $line = "INSERT INTO $table VALUES("; $i = 0; - foreach($row as $value) { - $value = addslashes($value); - $value = ereg_replace("\n","\\n",$value); - $line .= (isset($value)) ? "\"$value\"" : "\"\""; - $line .= (++$i < $num_fields) ? ',' : ");\n"; + foreach ($row as $value) { + if (!empty($value) && ($fields[$i]->Type == 'longblob' || $fields[$i]->Type == 'blob')) { + $line .= '0x'. bin2hex($value); + } + else if (is_numeric($value)) { + $line .= (!empty($value) ? $value : 0); + } + else { + // Value escaping, based on phpMyAdmin dumps + // Escape backslashes first + $value = str_replace('\\', '\\\\', $value); + // Escape newlines and carriage returns + $value = str_replace("\n", "\\n", $value); + $value = str_replace("\r", "\\r", $value); + // Escape single quotes + // This works in Oracle, PostGreSQL and MySQL for sure. + $value = str_replace("'", "''", $value); + + $line .= (!empty($value)) ? '\''. $value .'\'' : "''"; + } + $line .= (++$i < $num_fields) ? ', ' : ");\n"; } $output .= $line; - if ($live) { + if ($verbose) { echo $output; $output = NULL; } @@ -1252,11 +1456,13 @@ else { drupal_set_message(t('Support for showing the command used to create a table is not currently available in this module for PostgreSQL.'), 'error'); } - drupal_set_message($query); + drupal_set_message(check_plain($query)); return db_fetch_array(db_query($query)); } -// Delete table contents +/** + * Delete table contents. + */ function dba_delete_table($table) { if (_is_mysql()) { $query = "DELETE FROM $table;"; @@ -1265,14 +1471,16 @@ drupal_set_message(t('Support for deleting the contents of tables is not currently available in PostgreSQL.'), 'error'); return; } - drupal_set_message($query); + drupal_set_message(check_plain($query)); return db_query($query); } -// Drop table and all contents from current database +/** + * Drop table and all contents from current database. + */ function dba_drop_table($table) { $query = "DROP TABLE $table;"; - drupal_set_message($query); + drupal_set_message(check_plain($query)); return db_query($query); } @@ -1280,79 +1488,83 @@ return $GLOBALS['db_type'] == 'mysql' ? 1 : 0; } -// Adds the javascript for selecting all tables -function checkoff_head(){ -return ""; +/** + * Adds the javascript for selecting all tables. + * + * @todo Can be replaced by Checkall module. + */ +function checkoff_head() { + return ""; } -/*** Temporary mail handler class. ***/ - +/** + * Temporary mail handler class. + * + * Define a mail class to send a message with an attachment. Eventually Drupal + * core should provide this functionality, at which time this code will be + * removed. + * + * More info on sending email at . + */ function dba_mail_backup($attachment) { -// Define a mail class to send a message with an attachment. Eventually Drupal -// core should provide this functionality, at which time this code will be -// removed. -// -// More info on sending email at http://php.net/function.mail - -class mime_mail { - var $parts; - var $to; - var $from; - var $headers; - var $subject; - var $body; - - function mime_mail() { - $this->parts = array(); - $this->to = ""; - $this->from = ""; - $this->subject = ""; - $this->body = ""; - $this->headers = ""; - } - - function add_attachment($message, $name = "", $ctype = "application/octet-stream") { - $this->parts [] = array ( - "ctype" => $ctype, - "message" => $message, - "encode" => $encode, - "name" => $name - ); - } - - function build_message($part) { - $message = $part["message"]; - $message = chunk_split(base64_encode($message)); - $encoding = "base64"; - return "Content-Type: ".$part["ctype"].($part["name"]? "; name = \"".$part["name"]."\"" : "")."\nContent-Transfer-Encoding: $encoding\n\n$message\n"; - } - - function build_multipart() { - $boundary = "b".md5(uniqid(time())); - $multipart = "Content-Type: multipart/mixed; boundary = $boundary\n\nThis is a MIME encoded message.\n\n--$boundary"; - for($i = sizeof($this->parts)-1; $i>=0; $i--) $multipart .= "\n".$this->build_message($this->parts[$i]). "--$boundary"; - return $multipart.= "--\n"; - } - - function send() { - $mime = ""; - if (!empty($this->from)) $mime .= "From: ".$this->from. "\n"; - if (!empty($this->headers)) $mime .= $this->headers. "\n"; - if (!empty($this->body)) $this->add_attachment($this->body, "", "text/plain"); - $mime .= "MIME-Version: 1.0\n".$this->build_multipart(); - mail($this->to, $this->subject, "", $mime); + class mime_mail { + var$parts; + var$to; + var$from; + var$headers; + var$subject; + var$body; + function mime_mail() { + $this->parts = array(); + $this->to = ""; + $this->from = ""; + $this->subject = ""; + $this->body = ""; + $this->headers = ""; + } + + function add_attachment($message, $name = "", $ctype = "application/octet-stream") { + $this->parts[] = array( + "ctype" => $ctype, + "message" => $message, + "encode" => $encode, + "name" => $name + ); + } + + function build_message($part) { + $message = $part["message"]; + $message = chunk_split(base64_encode($message)); + $encoding = "base64"; + return "Content-Type: ". $part["ctype"] . ($part["name"] ? "; name = \"". $part["name"] ."\"" : "") ."\nContent-Transfer-Encoding: $encoding\n\n$message\n"; + } + + function build_multipart() { + $boundary = "b". md5(uniqid(time())); + $multipart = "Content-Type: multipart/mixed; boundary = $boundary\n\nThis is a MIME encoded message.\n\n--$boundary"; + for ($i = sizeof($this->parts) - 1; $i >= 0; $i--) $multipart .= "\n". $this->build_message($this->parts[$i]) ."--$boundary"; + return $multipart .= "--\n"; + } + + function send() { + $mime = ""; + if (!empty($this->from)) $mime .= "From: ". $this->from ."\n"; + if (!empty($this->headers)) $mime .= $this->headers ."\n"; + if (!empty($this->body)) $this->add_attachment($this->body, "", "text/plain"); + $mime .= "MIME-Version: 1.0\n". $this->build_multipart(); + mail($this->to, $this->subject, "", $mime); + } } -} - + // Send mail - $attach = fread(fopen($attachment->path, "r"), filesize($attachment->path)); - $mail = new mime_mail(); - $mail->from = variable_get('site_mail', ini_get('sendmail_from')); - $mail->headers = 'Errors-To: [EMAIL='. $mail->from. ']'. $mail->from. '[/EMAIL]'; - $admin = user_load(array('uid' => 1)); - $mail->to = $admin->mail; - $mail->subject = t('Database backup from %site: %file', array('%site' => variable_get('site_name', 'drupal'), '%file' => $attachment->filename)); - $mail->body = t('Database backup attached'); + $attach = fread(fopen($attachment->path, "r"), filesize($attachment->path)); + $mail = new mime_mail(); + $mail->from = variable_get('site_mail', ini_get('sendmail_from')); + $mail->headers = 'Errors-To: [EMAIL='. $mail->from .']'. $mail->from .'[/EMAIL]'; + $admin = user_load(array('uid' => 1)); + $mail->to = $admin->mail; + $mail->subject = t('Database backup from !site: !file', array('!site' => variable_get('site_name', 'drupal'), '!file' => $attachment->filename)); + $mail->body = t('Database backup attached'); $mail->add_attachment("$attach", $attachment->filename, "Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAgEASABIAAD/7QT+UGhvdG9zaG"); $mail->send(); @@ -1360,13 +1572,19 @@ function _dba_ops($op) { $ops = array( - 'drop' => t('drop'), - 'describe' => t('describe'), - 'optimize' => t('optimize'), - 'check' => t('check'), - 'backup' => t('backup'), - 'empty' => t('empty'), - 'view' => t('view'), + 'drop' => t('drop'), + 'describe' => t('describe'), + 'optimize' => t('optimize'), + 'check' => t('check'), + 'backup' => t('backup'), + 'empty' => t('empty'), + 'view' => t('view'), ); return $op ? $ops[$op] : $ops; } + +/** + * + * @} End of "defgroup dba_api". + */ + --- H:/unleashedmind/test/drupal-5.0/sites/all/modules/dba/dba.info +++ H:/unleashedmind/test/drupal-5.0/sites/all/modules/dba/dba.info @@ -0,0 +1,6 @@ +; $Id$ +name = Database Administration +description = "Directly administer your Drupal database." +version = "$Name$" +package = Development +