Index: demo.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/demo/demo.admin.inc,v
retrieving revision 1.1.2.13
diff -u -p -r1.1.2.13 demo.admin.inc
--- demo.admin.inc	18 Jun 2009 16:10:02 -0000	1.1.2.13
+++ demo.admin.inc	6 Aug 2009 02:06:25 -0000
@@ -57,6 +57,37 @@ function demo_admin_settings() {
     '#size' => 30,
     '#description' => t('Enter a writable directory where dump files of this demonstration site are stored, f.e. %files. The name of this site (e.g. %confpath) is automatically appended to this directory, if required.<br /><br /><strong>Note: For security reasons you should store site dumps outside of the document root of your webspace!</strong>', array('%files' => file_directory_path() . '/demo', '%confpath' => $fileconfig['site'])),
   );
+
+  $form['dump']['compress'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Compress'),
+    '#default_value' => variable_get('demo_compress', 0),
+    '#description' => t('Compress demo snapshots.  <strong>Note:</strong> This depends on !zip_support_link being enabled in your site\'s php installation.', array('!zip_support_link' => l('zip support', 'http://www.php.net/manual/en/zip.installation.php'))),
+  );
+  $dv = variable_get('demo_include_files_path', '');
+  $dv = empty($dv) ? 0 : 1;
+  $form['dump']['include_files'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Include Files'),
+    '#disabled' => !variable_get('demo_compress', 0),
+    '#default_value' => $dv,
+    '#description' => t('Include files from the path below with demo snapshots.  <strong>Note:</strong> This depends on the compression option above being enabled.'),
+  );
+  $form['dump']['include_files_path'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Include Files'),
+    '#default_value' => variable_get('demo_include_files_path', 'sites/'),
+    '#size' => 30,
+    '#disabled' => !variable_get('demo_compress', 0),
+    '#description' => t('The path to site files to be included with demo snapshots.  <strong>Note:</strong> This depends on the compression option above being enabled.'),
+  );
+  $form['dump']['exclude_zip_files'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Exclude Zip Files'),
+    '#default_value' => variable_get('demo_exclude_zip_files', 1),
+    '#disabled' => !variable_get('demo_include_files_path', 0),
+    '#description' => t('This option will disable the collection of zip files from the file backups if configured above.  With this option off all files, including zip files (even if they are a part of previous backups), will be included in the archive.'),
+  );
   $form[] = array(
     '#type' => 'submit',
     '#value' => t('Save'),
@@ -74,6 +105,38 @@ function demo_admin_settings_submit($for
     variable_set('demo_dump_path', $form_state['values']['path']);
   }
   variable_set('demo_reset_interval', $form_state['values']['interval']);
+
+  // Check to make sure we have zip support.
+  // Is this a better way? if (!extension_loaded("zip"))
+  if ($form_state['values']['compress']) {
+    $path = variable_get('demo_dump_path', '') .'/demo.zip';
+    if (eval('zip_open(\''. $path .'\');') === FALSE) {
+      form_set_error('compress', t('ZIP support does not seem to be enabled.'));
+      return FALSE;
+    }
+    else {
+      variable_set('demo_compress', 1);
+    }
+  }
+
+  if ($form_state['values']['include_files'] && 
+    variable_get('demo_compress', 0)) {
+      if (!is_dir($form_state['values']['include_files_path'])) {
+        form_set_error('include_files_path', t('Invalid directory given.'));
+        return FALSE;
+      }
+      else {
+        variable_set('demo_include_files_path', 
+          $form_state['values']['include_files_path']);
+      }
+    }
+
+  if (isset($form_state['values']['exclude_zip_files']) && 
+    variable_get('demo_compress', 0) &&
+    strlen(variable_get('demo_include_files_path', ''))) {
+    variable_set('demo_exclude_zip_files', 
+      $form_state['values']['exclude_zip_files']);
+  }
   drupal_set_message(t('The configuration options have been saved.'));
 }
 
@@ -118,7 +181,23 @@ function demo_delete_confirm(&$form_stat
 
 function demo_delete_confirm_submit($form, &$form_state) {
   $files = demo_get_fileconfig($form_state['values']['filename']);
-  unlink($files['sqlfile']);
+  // We need to handle the different possible scenarios that adding 
+  // compression introduces.
+  if (is_file($files['sqlfile'])) {
+    unlink($files['sqlfile']);
+  }
+  else if (variable_get('demo_compress', 0) && 
+    !strlen(variable_get('demo_include_files_path', '')) &&
+    is_file($files['sqlfile'] .'.zip')
+  ) {
+    unlink($files['sqlfile'] .'.zip');
+  }
+  else if (variable_get('demo_compress', 0) && 
+    strlen(variable_get('demo_include_files_path', '')) &&
+    is_file($files['path'] .'/'. $form_state['values']['filename'] .'.zip')
+  ) {
+    unlink($files['path'] .'/'. $form_state['values']['filename'] .'.zip');
+  }
   unlink($files['infofile']);
   drupal_set_message(t('Snapshot %title has been deleted.', array('%title' => $form_state['values']['filename'])));
   $form_state['redirect'] = 'admin/build/demo/manage';
@@ -189,12 +268,41 @@ function demo_dump_submit($form, &$form_
     );
     $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'])));
   }
   else {
     drupal_set_message(t('@engine support not implemented yet.', array('@engine' => ucfirst($engine))), 'error');
   }
 
+  // Backup the files as well if we are configured to.
+  // IFP = include file path
+  $ifp = variable_get('demo_include_files_path', '');
+  if (strlen($ifp)) {
+    $files_zip = demo_dump_files($ifp, $info['filename']);
+  }
+
+  // Compress the dump if we are configured to.  
+  if (variable_get('demo_compress', 0)) {
+    if (empty($files_zip)) {
+      $backup_file = demo_compress_file($fileconfig['sqlfile']);
+      if ($backup_file) {
+        @unlink($fileconfig['sqlfile']);
+        drupal_set_message(t('Successfully created snapshot %filename.', array('%filename' => $backup_file)));
+      }
+    }
+    else {
+      // We will always add the sqlfile at the base of the archive.
+      $backup_file = demo_add_file_to_dump(
+        $files_zip, $fileconfig['sqlfile'], 0);
+      if ($backup_file) {
+        @unlink($fileconfig['sqlfile']);
+        drupal_set_message(t('Successfully created snapshot %filename.', array('%filename' => $files_zip)));
+      }
+    }
+  }
+  else {
+    drupal_set_message(t('Successfully created snapshot %filename.', array('%filename' => $fileconfig['sqlfile'])));
+  }
+
   $form_state['redirect'] = 'admin/build/demo/manage';
 }
 
@@ -222,6 +330,35 @@ function demo_reset_confirm_submit($form
 
 function demo_reset($filename = 'demo_site', $verbose = TRUE) {
   $fileconfig = demo_get_fileconfig($filename);
+  dpm($fileconfig);
+  dpm($filename);
+  return;
+
+  // If archiving is turned on, but not file archiving, we will have to unpack
+  // the sql dump first.
+  if (variable_get('demo_compress', 0) && 
+    !strlen(variable_get('demo_include_files_path', '')) &&
+    file_exists($fileconfig['sqlfile'] .'.zip')) {
+    $zip_file = $fileconfig['sqlfile'] .'.zip';
+    $zip = new ZipArchive();
+    if (!$zip->open($zip_file)) {
+      drupal_set_message(t('Unable to open archive: @archive.', array('@archive' => $zip_file)));
+      return false;
+    }
+
+    if (!$zip->extractTo($fileconfig['path'])) {
+      drupal_set_message(t('Unable to decompress: @archive.', array('@archive' => $zip_file)));
+      return false;
+    }
+  }
+  // If file archiving is turned on and we have an archive of the file proceed
+  // to restoring the site from file.
+  else if (
+    variable_get('demo_compress', 0) &&
+    strlen(variable_get('demo_include_files_path', ''))) {
+    ) {
+  }
+
   if (!file_exists($fileconfig['sqlfile']) || !($fp = fopen($fileconfig['sqlfile'], 'r'))) {
     if ($verbose) {
       drupal_set_message(t('Unable to open dump file %filename.', array('%filename' => $fileconfig['sqlfile'])), 'error');
@@ -349,8 +486,25 @@ function demo_get_dumps() {
   $files = file_scan_directory($fileconfig['dumppath'], '.info$');
 
   foreach ($files as $file => $object) {
+    $sql_file = substr($file, 0, -4) . 'sql';
+    if (is_file($sql_file)) {
+      $files[$file]->filesize = filesize($sql_file);
+    }
+    // If we have compression turned on, or if we have file archiving turned on
+    // the file may be embedded in a .zip.
+    else if (variable_get('demo_compress', 0) && 
+      !strlen(variable_get('demo_include_files_path', ''))) {
+      $files[$file]->filesize = filesize(substr($file, 0, -4) . 'sql.zip');
+    }
+    else if (variable_get('demo_compress', 0) && 
+      strlen(variable_get('demo_include_files_path', ''))) {
+      $files[$file]->filesize = 
+        demo_get_file_size_from_zip(substr($file, 0, -4) . 'zip', 
+          substr($file, 0, -4) . 'sql');
+    }
+    else {
+    }
     $files[$file]->filemtime = filemtime($file);
-    $files[$file]->filesize = filesize(substr($file, 0, -4) . 'sql');
   }
 
   // Sort snapshots by date (ascending file modification time)
@@ -389,6 +543,14 @@ function demo_get_dumps() {
       sort($info['modules']);
       $option['#description'] .= t('Modules: ') . implode(', ', $info['modules']);
     }
+    if (count($info['archived files']) > 1) {
+      $option['#description'] .= 
+        '<br /><br />'.
+        t('Archived Files (@file_archive): ', array(
+          '@file_archive' => $info['zip file'])
+        ) . 
+        implode(', ', $info['archived files']);
+    }
     $option['#attributes'] = array('onclick' => "$('.description', this.parentNode.parentNode).slideToggle();");
 
     $options['dump'][] = $option;
@@ -418,6 +580,17 @@ function demo_get_info($filename, $field
     }
   }
 
+  // If we have a file archive we would like to also pull the file names
+  // out.
+  $zip_file_name = substr($filename, 0, -4) . 'zip';
+  if (
+    strlen(variable_get('demo_include_files_path', '')) &&
+    is_file(substr($filename, 0, -4) . 'zip')
+  ) {
+    $info['zip file'] = $zip_file_name;
+    $info['archived files'] = demo_get_file_list_from_zip($zip_file_name);
+  }
+
   if (isset($field)) {
     return isset($info[$field]) ? $info[$field] : NULL;
   }
@@ -551,3 +724,184 @@ function demo_set_default($filename) {
   drupal_set_message(t('Snapshot %title will be used for upcoming cron runs.', array('%title' => $filename)));
 }
 
+/**
+ * Dumps the files to the dump directory.
+ *
+ * @param $ifp
+ *   The Include File Path.  This is the file path that we will be backing up.
+ * @see http://www.php.net/manual/en/function.ziparchive-addemptydir.php
+ */
+function demo_dump_files($ifp, $file_name = 'demo_site') {
+  if (!is_dir($ifp)) {
+    drupal_set_message(t('Attempted to backup an invalid directory.'));
+    return 0;
+  }
+
+  $path = variable_get('demo_dump_path', '');
+  if (empty($path) || !file_check_directory($path)) {
+    drupal_set_message(t('No valid backup directory is configured.'));
+    return 0;
+  }
+
+  // Supposedly you do not need to use different directory seperators to 
+  // write portable code in PHP.  But can you mix and match?
+  // @see http://old.alanhogan.com/tips/php/directory-separator-not-necessary
+  if (!preg_match('/'. DIRECTORY_SEPERATOR .'$/', $path)) {
+    $path .= '/';
+  }
+
+  $path .= $file_name .'.zip';
+  if (file_exists($path .'.zip')) {
+    @unlink($path);
+  }
+
+  $zip = new ZipArchive();
+  $zip->open($path, ZipArchive::CREATE);
+
+  $dirName = $ifp;
+
+  $dirName = realpath($dirName);
+  if (substr($dirName, -1) != '/') {
+      $dirName.= '/';
+  }
+
+  $dirStack = array($dirName);
+  //Find the index where the last dir starts
+  $cutFrom = strrpos(substr($dirName, 0, -1), '/')+1;
+
+  while (!empty($dirStack)) {
+      $currentDir = array_pop($dirStack);
+      $filesToAdd = array();
+
+      $dir = dir($currentDir);
+      while (false !== ($node = $dir->read())) {
+          if (($node == '..') || ($node == '.')) {
+              continue;
+          }
+          if (is_dir($currentDir . $node)) {
+              array_push($dirStack, $currentDir . $node . '/');
+          }
+          if (is_file($currentDir . $node)) {
+              $filesToAdd[] = $node;
+          }
+      }
+
+      $localDir = substr($currentDir, $cutFrom);
+      $zip->addEmptyDir($localDir);
+     
+      foreach ($filesToAdd as $file) {
+        if (variable_get('demo_exclude_zip_files', 1) &&
+          preg_match('/\.zip$/i', $file)) {
+          continue;
+        }
+        $zip->addFile($currentDir . $file, $localDir . $file);
+      }
+  }
+
+  $zip->close(); 
+
+  return $path;
+}
+
+/**
+ * Will compress a single file.
+ *
+ * @param $file = the file to compress.
+ *
+ * @return TRUE on success FALSE otherwise.
+ *
+ * @post $file.gz is created and is a compressed version of $file.
+ */
+function demo_compress_file($file) {
+  // Make sure the base path is writeable.
+  if (!file_check_directory(dirname($file))) {
+    drupal_set_message(t('Unable to compress @file.', array('@file' => $file)));
+    return false;
+  }
+
+  if (preg_match('/\.zip$/', $file)) {
+    drupal_set_message(t('Refusing to compress an existing compressed file: @file.', array('@file' => $file)));
+    return false;
+  }
+
+  $file_zip = $file .'.zip';
+
+  // Attempt to compress the file.
+  $zip = new ZipArchive();
+  $zip->open($file_zip, ZipArchive::CREATE);
+  $zip->addFile($file);
+  $zip->close();
+  return $file_zip;
+}
+
+/**
+ * Will add a file to an existing zip file.
+ *
+ * @param $zip The already existing zip file.
+ * @param $file The file to add to the zip file.
+ * @return True on Success False otherwise.
+ * @post The file $zip has $file added to it.
+ */
+function demo_add_file_to_dump($zip_file, $file, $preserve_path = 1) {
+  if (!is_file($zip_file) || !is_writeable($zip_file)) {
+    drupal_set_message(t('Recieved an invalid archive (@archive) for adding a file (@file) to.'), array('@archive' => $zip_file, '@file' => file));
+    return false;
+  }
+
+  // Attempt to compress the file.
+  $zip = new ZipArchive();
+  $zip->open($zip_file);
+  if (!$preserve_path) {
+    $zip->addFile($file, basename($file));
+  }
+  else {
+    $zip->addFile($file);
+  }
+  return $zip->close();
+}
+
+/**
+ * Reach into a zip archive and return the file size of $file.
+ *
+ * @param $zip The archive we are looking in.
+ * @param $file The filename of the file we are looking for.
+ */
+function demo_get_file_size_from_zip($zip_file, $file) {
+  if (!is_file($zip_file)) {
+    drupal_set_message(t('Trying to process an invalid archive: @archive.', array('@archive' => $zip_file)));
+    return false;
+  }
+
+  $zip = new ZipArchive();
+  if (!$zip->open($zip_file)) {
+    drupal_set_message(t('Unable to open archive: @archive.', array('@archive' => $zip_file)));
+    return false;
+  }
+  $info = $zip->statName(basename($file));
+  if (!$info) {
+    drupal_set_message(t('Unable to get file info for @file from archive: @archive.', array('@archive' => $zip_file, '@file' => $file)));
+    return false;
+  }
+  return $info['size'];
+}
+
+/**
+ * Return a list of files from a zip archive.
+ *
+ * @param $zip_file The zip file we are inspecting.
+ * @return An array of files archived in the zip file.
+ */
+function demo_get_file_list_from_zip($zip_file) {
+  $zip = new ZipArchive();
+  if (!$zip->open($zip_file)) {
+    drupal_set_message(t('Unable to open archive: @archive.', array('@archive' => $zip_file)));
+    return false;
+  }
+
+  $return = array();
+  for ($i = 0; $i < $zip->numFiles; $i++) {
+    array_push($return, $zip->getNameIndex($i));
+  }
+
+  return $return;
+}
