=== added directory 'ca/ca_export'
=== added file 'ca/ca_export/ca_export.info'
--- ca/ca_export/ca_export.info	1970-01-01 00:00:00 +0000
+++ ca/ca_export/ca_export.info	2009-02-10 21:51:14 +0000
@@ -0,0 +1,6 @@
+; $Id$
+name = Export conditional actions
+description = "Export conditional actions"
+package = Ubercart - extra
+core = 6.x
+php = 5.0

=== added file 'ca/ca_export/ca_export.module'
--- ca/ca_export/ca_export.module	1970-01-01 00:00:00 +0000
+++ ca/ca_export/ca_export.module	2009-02-13 00:06:29 +0000
@@ -0,0 +1,419 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * This module imports/exports conditional actions from/to XML using
+ * the SimpleXML library.
+ *
+ * There's a lot of weird code dealing with casts to arrays and strings
+ * when dealing with SimpleXML. It looks weird, but it's necessary... 
+ * strange problems arise otherwise.
+ */
+
+/*******************************************************************************
+ * Drupal Hooks
+ ******************************************************************************/
+
+/**
+ * Implementation of hook_menu().
+ */
+function ca_export_menu() {
+  $items['ca-export'] = array(
+    'title' => 'Export conditional actions',
+    'description' => 'Export conditional actions to XML, by module, CA item type, or name.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ca_export_export_form'),
+    'access arguments' => array('administer conditional actions'),
+  );
+  $items['ca-import'] = array(
+    'title' => 'Import conditional actions',
+    'description' => 'Import conditional actions from an XML file.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ca_export_import_form'),
+    'access arguments' => array('administer conditional actions'),
+  );
+  return $items;
+}
+
+/**
+ * Export conditional actions to XML.
+ *
+ * @param $subject
+ *   Either the name of a module who's conditional actions to export, a
+ *   specific conditional action name, or if unset, all of a site's 
+ *   conditional actions.
+ */
+function ca_export_export_form($form_state, $subject = '') {
+  
+  // Export the requested conditional actions to a string of XML.
+  $xml = ca_export($subject)->asXML();
+  
+  $form['export'] = array(
+    '#type' => 'textarea',
+    '#rows' => 15,
+    // Make the XML pretty. =)
+    '#default_value' => _ca_export_format_xml($xml),
+  );
+  
+  $form['download'] = array(
+    '#type' => 'submit',
+    '#value' => t('Download as XML'),
+    '#prefix' => '<div class="container-inline">',
+  );
+  
+  $form['gzip'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('GZIP it first.'),
+    '#suffix' => '</div>',
+  );
+  
+  return $form;
+}
+
+/**
+ * Push a [gzipped] XML to the user who requested it.
+ */
+function ca_export_export_form_submit($form, &$form_state) {
+  // The text of the XML.
+  $xml = $form_state['values']['export'];
+  // It's an XML for now...
+  $filename = 'ca.xml';
+  // User requested GZIPping.
+  if ($form_state['values']['gzip']) {
+    // Encode and rename it to .gz.
+    $xml = gzencode($xml);
+    $filename .= '.gz';
+    drupal_set_header('Content-Type: application/x-gzip; charset=utf-8');
+  }
+  else {
+    drupal_set_header('Content-Type: text/xml; charset=utf-8');
+  }
+  drupal_set_header('Content-Disposition: attachment; filename="'. $filename .'"');
+  drupal_set_header('Content-Length: '. strlen($xml));
+  
+  // Push the data to the client.
+  print($xml);
+  flush();
+  ob_flush();
+}
+
+/**
+ * Import conditional actions from XML.
+ */
+function ca_export_import_form($form_state) {
+  $form['import'] = array(
+    '#type' => 'textarea',
+    '#rows' => 15,
+  );
+  
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => 'Import conditional actions',
+  );
+  
+  return $form;
+}
+
+function ca_export_import_form_validate($form, &$form_state) {
+  // We do a little work here in validation. We parse the XML and store it.
+  // It isn't possible to store $xml here, because it's an object reference,
+  // so, we ca_import() it first, then store it.
+  if (FALSE !== ($xml = simplexml_load_string($form_state['values']['import']))) {
+    $form_state['storage']['xml'] = ca_import($xml);
+  }
+  else {
+    form_set_error('import', t('The XML could not be parsed.'));
+  }
+}
+
+function ca_export_import_form_submit($form, &$form_state) {
+  global $user;
+  
+  $predicates = $form_state['storage']['xml'];
+  foreach ($predicates as $pid => $predicate) {
+    // A numeric predicate ID can not reliably be imported verbatim.
+    // We use the global predicate ID variable to assign the next available
+    // ID, also incrementing it.
+    if (is_numeric($pid)) {
+      $predicate['#pid'] = variable_get('ca_predicates_pid', 1);
+      variable_set('ca_predicates_pid', $predicate['#pid'] + 1);
+    }
+    else {
+      $predicate['#pid'] = $pid;
+    }
+    // Owned by the importing user.
+    $predicate['#uid'] = $user->uid;
+    
+    ca_save_predicate($predicate);
+  }
+  drupal_set_message(t('Conditional actions imported successfully'));
+}
+
+/**
+ * Format our exported XML.
+ */
+function _ca_export_format_xml($xml) {
+  $pretty = new DOMDocument('1.0');
+  $pretty->preserveWhiteSpace = FALSE;
+  $pretty->loadXML($xml);
+  $pretty->formatOutput = TRUE;
+  
+  return $pretty->saveXML();
+}
+
+/**
+ * Export the selected conditional actions.
+ */
+function ca_export($subject) {
+  $ca_data = array();
+  // Is $subject a module?
+  if (module_exists($subject)) {
+    $is_module = TRUE;
+  }
+  // If so, that limits our scope to that module.
+  if ($is_module) {
+    $modules = array($subject);
+  }
+  // Otherwise, we'll pull from all modules.
+  else {
+    $modules = module_implements('ca_predicate');
+  }
+  // Get selected predicates.
+  foreach ($modules as $module) {
+    $ca_data = array_merge($ca_data, (array) module_invoke($module, 'ca_predicate'));
+  }
+  
+  // Get user-defined/imported predicates...
+  $result = db_query("SELECT * FROM {ca_predicates}");
+  while ($data = db_fetch_array($result)) {
+    // Module defined predicates have string IDs.  When a user modifies one of
+    // these, we unset the module defined predicate and reconsider adding it in.
+    if (!is_numeric($data['pid'])) {
+      unset($ca_data[$data['pid']]);
+    }
+    // Fix it up and store it.
+    $predicate = ca_prepare_db_predicate($data);
+    $ca_data[$data['pid']] = $predicate;
+  }
+  // If our subject was a specific predicate name, only return that one...
+  // and only if it exists.
+  if (!$is_module) {
+    if ($subject && array_key_exists($subject, $ca_data)) {
+      $ca_data = array($subject => $ca_data[$subject]);
+    }
+    else {
+      $ca_data = array();
+    }
+  }
+  
+  // Create an XML object, and save the predicates to it.
+  $xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><conditional_actions></conditional_actions>');
+  _ca_export_predicates($xml, $ca_data);
+  
+  return $xml;
+}
+  
+/**
+ * Export conditional actions to XML.
+ *
+ * @param $parent
+ *   SimpleXML object, the root of the exported conditional actions.
+ * @param $predicates
+ *   An array of predicates to export.
+ */
+function _ca_export_predicates(&$parent, $predicates) {
+  foreach ($predicates as $key => $predicate) {
+    $child = $parent->addChild('predicate');
+    
+    $child->addChild('title', $predicate['#title']);
+    $child->addChild('description', $predicate['#description']);
+    $child->addChild('class', $predicate['#class']);
+    $child->addChild('status', $predicate['#status']);
+    $child->addChild('trigger', $predicate['#trigger']);
+    $child->addChild('weight', $predicate['#weight']);
+    // Export the pid as an element, not the tag name, to accomodate numeric 
+    // predicate IDs. (<69> is illegal, for instance)
+    $child->addChild('pid', $key);
+    
+    if (is_array($predicate['#conditions'])) {
+      // Export the predicate conditions.
+      _ca_export_predicate_conditions($child->addChild('conditions'), $predicate['#conditions']);
+    }
+    
+    if (is_array($predicate['#actions'])) {
+      $actions = $child->addChild('actions');
+      foreach ($predicate['#actions'] as $action) {
+        // Export the predicate actions.
+        _ca_export_predicate_element_template($actions->addChild('action'), $action);
+      }
+    }
+  }
+}
+
+/**
+ * Recursively export predicate conditions.
+ *
+ * @param $parent
+ *   SimpleXML object, the root of the exported conditional actions.
+ * @param $conditions
+ *   An array of predicate conditions to export.
+ */
+function _ca_export_predicate_conditions(&$parent, $conditions) {
+  // If there's an operator, we're not at the bottom level yet.
+  if (array_key_exists('#operator', $conditions)) {
+    // So, create another tag and recur.
+    $parent->addChild('operator', $conditions['#operator']);
+    _ca_export_predicate_conditions($parent->addChild('conditions'), $conditions['#conditions']);
+  }
+  else {
+    foreach ($conditions as $condition) {
+      // Export the predicate conditions.
+      _ca_export_predicate_element_template($parent->addChild('condition'), $condition);
+    }
+  }
+}
+
+/**
+ * Export a predicate element (condition, action)
+ *
+ * @param $parent
+ *   SimpleXML object, the root of the exported conditional actions.
+ * @param $element
+ *   A predicate element (a condition, or action) to export.
+ */
+function _ca_export_predicate_element_template(&$parent, $element) {
+  $parent->addChild('name', $element['#name']);
+  $parent->addChild('title', $element['#title']);
+  $parent->addChild('description', $element['#description']);
+  
+  // Export the argument map if it exists.
+  if (is_array($element['#argument_map'])) {
+    $map = $parent->addChild('argument_map');
+    foreach ($element['#argument_map'] as $key => $value) {
+      $map->addChild($key, $value);
+    }
+  }
+  
+  // Export the settings of they exist.
+  if (is_array($element['#settings'])) {
+    $settings = $parent->addChild('settings');
+    foreach ($element['#settings'] as $key => $value) {
+      $settings->addChild($key, _ca_export_setting($value));
+    }
+  }
+}
+
+/**
+ * Export a predicate element setting.
+ *
+ * @param $setting
+ *   A predicate element setting, can be an array or a "scalar" value.
+ */
+function _ca_export_setting($setting) {
+  if (is_array($setting)) {
+    return 'array('. implode(', ', $setting) .')';
+  }
+  else {
+    return $setting;
+  }
+}
+
+/**
+ * Import a predicate element setting.
+ *
+ * @param $xml
+ *   A SimpleXML object; the XML to import.
+ */
+function ca_import($xml) {
+  return _ca_import_predicates($xml);
+}
+
+/**
+ * Import a predicate element setting.
+ *
+ * @param $predicates
+ *   A SimpleXML root that contains predicates to import.
+ */
+function _ca_import_predicates($predicates) {
+  $result = array();
+  foreach ($predicates as $predicate) {
+    $predicate = (array) $predicate;
+    
+    // Import the 'bottom level' elements...
+    foreach (array('title', 'description', 'class', 'status', 'trigger', 'weight') as $key) {
+      $result[$predicate['pid']]['#'. $key] = (string) $predicate[$key];
+    }
+    
+    // Import the predicate conditions.
+    $result[$predicate['pid']]['#conditions'] = _ca_import_predicate_conditions((array) $predicate['conditions']);
+    // Import the predicate actions.
+    foreach ((array) $predicate['actions'] as $action) {
+      $result[$predicate['pid']]['#actions'] = _ca_import_predicate_element_template((array) $action);
+    }
+  }
+  return $result;
+}
+
+/**
+ * Recursively import predicate conditions.
+ *
+ * @param $predicates
+ *   A SimpleXML root that contains predicates to import.
+ */
+function _ca_import_predicate_conditions($conditions) {
+  $result = array();
+  // If there's an 'operator' key, we need to recur.
+  if (array_key_exists('operator', $conditions)) {
+    $result['#operator'] = (string) $conditions['operator'];
+    $result['#conditions'] = _ca_import_predicate_conditions($conditions['conditions']);
+  }
+  else {
+    foreach ($conditions as $condition) {
+      // Store the condition.
+      $result[] = _ca_import_predicate_element_template((array) $condition);
+    }
+  }
+  return $result;
+}
+
+/**
+ * Import a predicate element (condition, action)
+ *
+ * @param $element
+ *   A predicate element (a condition, or action) to import.
+ */
+function _ca_import_predicate_element_template($element) {
+  $result = array();
+  
+  $result['#name'] = (string) $element['name'];
+  $result['#title'] = (string) $element['title'];
+  $result['#description'] = (string) $element['description'];
+  
+  foreach ((array) $element['argument_map'] as $key => $value) {
+    $result['#argument_map'][$key] = $value;
+  }
+  
+  foreach ((array) $element['settings'] as $key => $value) {
+    $result['#settings'][$key] = _ca_import_setting((string) $value);
+  }
+  return $result;
+}
+
+/**
+ * Import a predicate element setting.
+ *
+ * @param $setting
+ *   A predicate element setting, can be an array or a "scalar" value.
+ */
+function _ca_import_setting($setting) {
+  $matches = array();
+  if (preg_match('/array\((.*)\)/', $setting, $matches)) {
+    // array(foo, bar, baz) => $matches[1] = foo, bar, baz
+    return explode(', ', $matches[1]);
+  }
+  else {
+    return $setting;
+  }
+}
+

