Index: database_mysql_dump.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/demo/database_mysql_dump.inc,v
retrieving revision 1.5.2.6
diff -u -p -r1.5.2.6 database_mysql_dump.inc
--- database_mysql_dump.inc	17 May 2009 13:25:01 -0000	1.5.2.6
+++ database_mysql_dump.inc	30 Jul 2009 16:20:06 -0000
@@ -8,8 +8,13 @@ if (!defined('MYSQLI_BINARY_FLAG')) {
 
 /**
  * Dump active database.
+ *
+ * @param $filename
+ *   Output filename.
+ * @param $options
+ *   Snapshot options.
  */
-function demo_dump_db($filename, $exclude = array()) {
+function demo_dump_db($filename, $options = array()) {
   // Make sure we have permission to save our backup file.
   $directory = dirname($filename);
   if (!file_check_directory($directory, FILE_CREATE_DIRECTORY)) {
@@ -28,13 +33,12 @@ function demo_dump_db($filename, $exclud
     $header .= "SET FOREIGN_KEY_CHECKS = 0;\n";
     fwrite($fp, $header);
 
-    foreach (demo_enum_tables() as $table) {
-      // Always export structure to allow creating a new site
-      // from a database dump
-      fwrite($fp, _demo_dump_table_structure($table));
-
-      if (!in_array($table, $exclude)) {
-        fwrite($fp, _demo_dump_table_data($table));
+    foreach ($options['tables'] as $table => $dump_options) {
+      if ($dump_options['schema']) {
+        _demo_dump_table_schema($fp, $table);
+      }
+      if ($dump_options['data']) {
+        _demo_dump_table_data($fp, $table);
       }
     }
 
@@ -49,7 +53,7 @@ function demo_dump_db($filename, $exclud
 }
 
 /**
- * Returns the name of the active database.
+ * Return the name of the active database.
  */
 function _demo_get_database() {
   $database = array_keys(db_fetch_array(db_query('SHOW TABLES')));
@@ -58,9 +62,21 @@ function _demo_get_database() {
 }
 
 /**
- * Dump table structure.
+ * Enumerate database tables.
  */
-function _demo_dump_table_structure($table) {
+function _demo_enum_tables() {
+  return db_query("SHOW TABLES");
+}
+
+/**
+ * Dump table schema.
+ *
+ * @param $fp
+ *   Output file handle.
+ * @param $table
+ *   Table name to export schema for.
+ */
+function _demo_dump_table_schema($fp, $table) {
   $output = "\n";
   $output .= "--\n";
   $output .= "-- Table structure for table '$table'\n";
@@ -69,21 +85,26 @@ function _demo_dump_table_structure($tab
   $data = db_fetch_array(db_query("SHOW CREATE TABLE %s", $table));
   $output .= preg_replace('/^CREATE TABLE/', 'CREATE TABLE IF NOT EXISTS', $data['Create Table']) . ";\n";
 
-  return $output;
+  fwrite($fp, $output);
 }
 
 /**
  * Dump table data.
  *
  * This code has largely been stolen from the phpMyAdmin project.
+ *
+ * @param $fp
+ *   Output file handle.
+ * @param $table
+ *   Table name to export data for.
  */
-function _demo_dump_table_data($table) {
+function _demo_dump_table_data($fp, $table) {
   $output = "\n";
   $output .= "--\n";
   $output .= "-- Dumping data for table '$table'\n";
   $output .= "--\n\n";
 
-  // Dump table data
+  // Dump table data.
   $result = db_query("SELECT * FROM %s", $table);
 
   // Get table fields.
@@ -142,7 +163,9 @@ function _demo_dump_table_data($table) {
       else {
         $insert_buffer = '(' . implode(', ', $values) . ')';
         if ($query_size + strlen($insert_buffer) > 50000) {
-          $output .= ";\n";
+          // Flush buffer to disc.
+          fwrite($fp, $output . ";\n");
+          $output = '';
           $current_row = 1;
           $query_size = 0;
           $insert_buffer = $insert_cmd . $insert_buffer;
@@ -161,7 +184,7 @@ function _demo_dump_table_data($table) {
     $output .= "/*!40000 ALTER TABLE $table ENABLE KEYS */;\n";
   }
 
-  return $output;
+  fwrite($fp, $output);
 }
 
 /**
Index: demo.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/demo/demo.admin.inc,v
retrieving revision 1.1.2.15
diff -u -p -r1.1.2.15 demo.admin.inc
--- demo.admin.inc	17 Jul 2009 15:38:27 -0000	1.1.2.15
+++ demo.admin.inc	30 Jul 2009 16:22:58 -0000
@@ -77,7 +77,10 @@ function demo_admin_settings_submit($for
   drupal_set_message(t('The configuration options have been saved.'));
 }
 
-function demo_manage() {
+/**
+ * Menu callback to display manage snapshots form.
+ */
+function demo_manage_form() {
   $form['dump'] = array(
     '#type' => 'fieldset',
     '#title' => t('Available snapshots'),
@@ -95,7 +98,7 @@ function demo_manage() {
   return $form;
 }
 
-function demo_manage_submit($form, &$form_state) {
+function demo_manage_form_submit($form, &$form_state) {
   demo_set_default($form_state['values']['filename']);
 }
 
@@ -124,7 +127,10 @@ function demo_delete_confirm_submit($for
   $form_state['redirect'] = 'admin/build/demo/manage';
 }
 
-function demo_dump() {
+/**
+ * Menu callback to render the create snapshot form.
+ */
+function demo_dump_form() {
   $form = array();
   $form['dump']['filename'] = array(
     '#title' => t('File name'),
@@ -144,60 +150,62 @@ function demo_dump() {
     '#rows' => 2,
     '#description' => t('Optionally enter a description for this snapshot here. If no description is given and a snapshot with the same filename already exists, the previous description is used.'),
   );
+  $form['dump']['tables'] = array('#type' => 'value', '#value' => demo_enum_tables());
   return confirm_form($form, t('Are you sure you want to create a new snapshot?'), 'admin/build/demo', t('If the above filename already exists, creating a new snapshot will overwrite the existing snapshot. This action cannot be undone.'), t('Create'), t('Cancel'));
 }
 
-function demo_dump_submit($form, &$form_state) {
-  global $db_type;
+function demo_dump_form_submit($form, &$form_state) {
+  if ($fileconfig = demo_dump($form_state['values'])) {
+    drupal_set_message(t('Successfully created snapshot %filename.', array('%filename' => $fileconfig['sqlfile'])));
+  }
+  $form_state['redirect'] = 'admin/build/demo/manage';
+}
 
-  // Generate info file.
-  $info = demo_set_info($form_state['values']);
+/**
+ * Create a new snapshot.
+ *
+ * @param $options
+ *   A structured array of snapshot options:
+ *   - filename: The base output filename, without extension.
+ *   - default: Whether to set this dump as new default snapshot.
+ *   - description: A description for the snapshot. If a snapshot with the same
+ *     name already exists and this is left blank, the new snapshot will reuse
+ *     the existing description.
+ *   - tables: An array of tables to dump, keyed by table name (including table
+ *     prefix, if any). The value is an array of dump options:
+ *     - schema: Whether to dump the table schema.
+ *     - data: Whether to dump the table data.
+ */
+function demo_dump($options) {
+  // Increase PHP's max_execution_time for large dumps.
+  @set_time_limit(600);
+
+  // Allow other modules to alter the submitted option values.
+  drupal_alter('demo_dump', $options);
+
+  // Generate the info file.
+  $info = demo_set_info($options);
   if (!$info) {
     return FALSE;
   }
 
-  // Include database specific functions.
-  $engine = ($db_type == 'mysqli' ? 'mysql' : $db_type);
-  $inc_file = drupal_get_path('module', 'demo') . '/database_' . $engine . '_dump.inc';
-  if (file_exists($inc_file)) {
-    require_once $inc_file;
-
-    if (!empty($form_state['values']['default'])) {
-      // Set new default snapshot.
-      demo_set_default($info['filename']);
-    }
-
-    // Increase PHP's max_execution_time for large dumps.
-    @set_time_limit(600);
-
-    // Perform dump.
-    $fileconfig = demo_get_fileconfig($info['filename']);
-    $exclude = array(
-      '{cache}',
-      '{cache_admin_menu}',
-      '{cache_block}',
-      '{cache_content}',
-      '{cache_filter}',
-      '{cache_form}',
-      '{cache_menu}',
-      '{cache_page}',
-      '{cache_views}',
-      '{ctools_object_cache}',
-      '{panels_object_cache}',
-      '{views_object_cache}',
-      '{watchdog}',
-    );
-    $exclude = array_map('db_prefix_tables', $exclude);
-    demo_dump_db($fileconfig['sqlfile'], $exclude);
-    drupal_set_message(t('Successfully created snapshot %filename.', array('%filename' => $fileconfig['sqlfile'])));
+  // Set as new default snapshot.
+  if (!empty($options['default'])) {
+    demo_set_default($info['filename']);
   }
-  else {
-    drupal_set_message(t('@engine support not implemented yet.', array('@engine' => ucfirst($engine))), 'error');
+
+  // Perform database dump.
+  $fileconfig = demo_get_fileconfig($info['filename']);
+  if (!demo_dump_db($fileconfig['sqlfile'], $options)) {
+    return FALSE;
   }
 
-  $form_state['redirect'] = 'admin/build/demo/manage';
+  return $fileconfig;
 }
 
+/**
+ * Menu callback to display snapshot selection form.
+ */
 function demo_reset_confirm() {
   $form['dump'] = array(
     '#type' => 'fieldset',
@@ -209,18 +217,29 @@ function demo_reset_confirm() {
 }
 
 function demo_reset_confirm_submit($form, &$form_state) {
-  // Increase PHP's max_execution_time for large dumps.
-  @set_time_limit(600);
-
   // Reset site to chosen snapshot.
   demo_reset($form_state['values']['filename']);
-  // Save time of last reset.
-  variable_set('demo_reset_last', time());
 
   $form_state['redirect'] = isset($form_state['values']['redirect']) ? $form_state['values']['redirect'] : 'admin/build/demo';
 }
 
-function demo_reset($filename = 'demo_site', $verbose = TRUE) {
+/**
+ * Reset site using snapshot.
+ *
+ * @param $filename
+ *   Base snapshot filename, without extension.
+ * @param $verbose
+ *   Whether to output status messages.
+ */
+function demo_reset($filename, $verbose = TRUE) {
+  // Increase PHP's max_execution_time for large dumps.
+  @set_time_limit(600);
+
+  // Load database specific functions.
+  if (!demo_load_include()) {
+    return FALSE;
+  }
+
   $fileconfig = demo_get_fileconfig($filename);
   if (!file_exists($fileconfig['sqlfile']) || !($fp = fopen($fileconfig['sqlfile'], 'r'))) {
     if ($verbose) {
@@ -235,11 +254,11 @@ function demo_reset($filename = 'demo_si
   $version = demo_get_info($fileconfig['infofile'], 'version');
   $is_version_1_0_dump = version_compare($version, '1.1', '<');
 
-  // Drop tables
-  $dt_watchdog = db_prefix_tables('{watchdog}');
-  foreach (demo_enum_tables() as $table) {
+  // Drop tables.
+  $watchdog = db_prefix_tables('{watchdog}');
+  foreach (demo_enum_tables() as $table => $dump_options) {
     // Skip watchdog, except for legacy dumps that included the watchdog table
-    if ($table != $dt_watchdog || $is_version_1_0_dump) {
+    if ($table != $watchdog || $is_version_1_0_dump) {
       db_query("DROP TABLE %s", $table);
     }
   }
@@ -298,6 +317,8 @@ function demo_reset($filename = 'demo_si
 
   // Reset default dump to load on cron.
   variable_set('demo_dump_cron', $demo_dump_cron);
+  // Save time of last reset.
+  variable_set('demo_reset_last', $_SERVER['REQUEST_TIME']);
 
   return $success;
 }
@@ -342,6 +363,24 @@ function demo_get_fileconfig($filename =
   return $fileconfig;
 }
 
+/**
+ * Load database specific functions.
+ */
+function demo_load_include() {
+  global $db_type;
+
+  $engine = ($db_type == 'mysqli' ? 'mysql' : $db_type);
+  $inc_file = drupal_get_path('module', 'demo') . '/database_' . $engine . '_dump.inc';
+  if (!file_exists($inc_file)) {
+    drupal_set_message(t('@engine support not implemented yet.', array('@engine' => ucfirst($engine))), 'error');
+    return FALSE;
+  }
+
+  require_once $inc_file;
+
+  return TRUE;
+}
+
 function demo_get_dumps() {
   $fileconfig = demo_get_fileconfig();
 
@@ -484,25 +523,21 @@ function demo_enum_tables() {
 
   $tables = array();
 
+  // Load database specific functions.
+  if (!demo_load_include()) {
+    return FALSE;
+  }
+
+  // Create a regex that matches the table prefix(es).
   if (is_array($db_prefix)) {
-    // Create a regular expression for table prefix matching.
     $rx = '/^' . implode('|', array_filter($db_prefix)) . '/';
   }
   else if ($db_prefix != '') {
     $rx = '/^' . $db_prefix . '/';
   }
 
-  switch ($GLOBALS['db_type']) {
-    case 'mysql':
-    case 'mysqli':
-      $result = db_query("SHOW TABLES");
-      break;
-
-    case 'pgsql':
-      $result = db_query("SELECT table_name FROM information_schema.tables WHERE table_schema = '%s'", 'public');
-      break;
-  }
-
+  // Query the database engine for the table list.
+  $result = _demo_enum_tables();
   while ($table = db_fetch_array($result)) {
     $table = reset($table);
     if (is_array($db_prefix)) {
@@ -511,17 +546,46 @@ function demo_enum_tables() {
         $table_prefix = $matches[0];
         $plain_table = substr($table, strlen($table_prefix));
         if ($db_prefix[$plain_table] == $table_prefix || $db_prefix['default'] == $table_prefix) {
-          $tables[] = $table;
+          $tables[$table] = array('schema' => TRUE, 'data' => TRUE);
         }
       }
     }
     else if ($db_prefix != '') {
       if (preg_match($rx, $table)) {
-        $tables[] = $table;
+        $tables[$table] = array('schema' => TRUE, 'data' => TRUE);
       }
     }
     else {
-      $tables[] = $table;
+      $tables[$table] = array('schema' => TRUE, 'data' => TRUE);
+    }
+  }
+
+  // Apply default exclude list.
+  $excludes = array(
+    // Core
+    '{cache}',
+    '{cache_block}',
+    '{cache_content}',
+    '{cache_filter}',
+    '{cache_form}',
+    '{cache_menu}',
+    '{cache_page}',
+    '{cache_update}',
+    '{watchdog}',
+    // CTools
+    '{ctools_object_cache}',
+    // Drupal Administration Menu
+    '{cache_admin_menu}',
+    // Panels
+    '{panels_object_cache}',
+    // Views
+    '{cache_views}',
+    '{cache_views_data}',
+    '{views_object_cache}',
+  );
+  foreach (array_map('db_prefix_tables', $excludes) as $table) {
+    if (isset($tables[$table])) {
+      $tables[$table]['data'] = FALSE;
     }
   }
 
Index: demo.api.php
===================================================================
RCS file: demo.api.php
diff -N demo.api.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ demo.api.php	30 Jul 2009 16:10:33 -0000
@@ -0,0 +1,48 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Documentation for Demonstration Site API.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Hook implementation of hook_demo_dump_alter().
+ *
+ * Alter snapshot options. This function can change the value of $options since
+ * it is passed by reference.
+ *
+ * @param &$options
+ *   A structured array consisting of submitted form values:
+ *   - filename: The base output filename, without extension.
+ *   - default: Whether to set this dump as new default snapshot.
+ *   - description: A description for the snapshot. If a snapshot with the same
+ *     name already exists and this is left blank, the new snapshot will reuse
+ *     the existing description.
+ *   - tables: An array of tables to dump, keyed by table name (including table
+ *     prefix, if any). The value is an array of dump options:
+ *     - schema: Whether to dump the table schema.
+ *     - data: Whether to dump the table data.
+ */
+function hook_demo_dump_alter(&$options) {
+  // Only export the table schema of table cache_table, but not the data.
+  // Commonly used for cache tables.
+  $options['tables']['cache_table']['data'] = FALSE;
+
+  // Completely ignore tables starting with a certain prefix.
+  foreach ($options['tables'] as $table => $dump_options) {
+    // Test if the table name starts with 'unrelated_'.
+    if (strncmp($table, 'unrelated_', 10) == 0) {
+      unset($options['tables'][$table]);
+    }
+  }
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
Index: demo.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/demo/demo.module,v
retrieving revision 1.19.2.13
diff -u -p -r1.19.2.13 demo.module
--- demo.module	12 Jul 2009 00:09:48 -0000	1.19.2.13
+++ demo.module	30 Jul 2009 16:18:18 -0000
@@ -36,7 +36,7 @@ function demo_menu() {
   $items['admin/build/demo/manage'] = array(
     'title' => 'Manage snapshots',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('demo_manage'),
+    'page arguments' => array('demo_manage_form'),
     'access arguments' => $admin_access,
     'file' => 'demo.admin.inc',
     'type' => MENU_LOCAL_TASK,
@@ -45,7 +45,7 @@ function demo_menu() {
   $items['admin/build/demo/dump'] = array(
     'title' => 'Create snapshot',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('demo_dump'),
+    'page arguments' => array('demo_dump_form'),
     'access arguments' => $admin_access,
     'file' => 'demo.admin.inc',
     'type' => MENU_LOCAL_TASK,
@@ -137,7 +137,6 @@ function demo_cron() {
     if (($time - $interval) >= variable_get('demo_reset_last', 0)) {
       module_load_include('inc', 'demo', 'demo.admin');
       demo_reset(variable_get('demo_dump_cron', 'demo_site'), FALSE);
-      variable_set('demo_reset_last', $time);
     }
   }
 }
