Index: export/views-bonus-export-xml.tpl.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views_bonus/export/views-bonus-export-xml.tpl.php,v
retrieving revision 1.2
diff -u -p -r1.2 views-bonus-export-xml.tpl.php
--- export/views-bonus-export-xml.tpl.php	24 Jun 2009 17:27:53 -0000	1.2
+++ export/views-bonus-export-xml.tpl.php	20 Nov 2009 10:04:15 -0000
@@ -12,17 +12,23 @@
  * @ingroup views_templates
  */
 
+// If we've got a SimpleXML version of the document, print that instead:
+if ($xml) {
+  print $xml->asXML();
+  return;
+}
+
 // Short tags act bad below in the html so we print it here.
 print '<?xml version="1.0" encoding="UTF-8" ?>';
 ?>
-<xml>
+<<?php print $root_node_tag; ?>>
 <?php foreach ($themed_rows as $count => $row): ?>
-  <node>
+  <<?php print $item_tag; ?>>
 <?php foreach ($row as $field => $content):
     $label = $header[$field] ? $header[$field] : $field;
 ?>
     <<?php print $label; ?>><?php print $content; ?></<?php print $label; ?>>
 <?php endforeach; ?>
-  </node>
+  </<?php print $item_tag; ?>>
 <?php endforeach; ?>
-</xml>
+</<?php print $root_node_tag; ?>>
Index: export/views_bonus_export.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views_bonus/export/views_bonus_export.module,v
retrieving revision 1.12
diff -u -p -r1.12 views_bonus_export.module
--- export/views_bonus_export.module	1 Oct 2009 17:45:40 -0000	1.12
+++ export/views_bonus_export.module	20 Nov 2009 10:04:15 -0000
@@ -109,17 +109,85 @@ function template_preprocess_views_bonus
   }
   _views_bonus_export_shared_preprocess($vars);
 
-  foreach ($vars['themed_rows'] as $num => $row) {
-    foreach ($row as $field => $content) {
-      $vars['themed_rows'][$num][$field] = str_replace(
-        array('&', '<', '>'),
-        array('&amp;', '&lt;', '&gt;'),
-        $content);
+  // Clean up the base table to give the root tag:
+  $vars['root_node_tag'] = views_bonus_export_make_xml_valid_tag($vars['view']->base_table);
+
+  // Compute the item xml tag, removing the last 's' from the root node:
+  $vars['item_tag'] = $vars['root_node_tag'];
+  if (strpos($vars['item_tag'], 's', strlen($vars['item_tag']) - 1) !== FALSE) {
+    $vars['item_tag'] = substr($vars['item_tag'], 0, strlen($vars['item_tag']) - 1);
+  }
+
+  // Further process the Header tags to make them Valid XML:
+  foreach ($vars['header'] as $field_name => $header) {
+    $vars['header'][$field_name] = views_bonus_export_make_xml_valid_tag($header);
+  }
+
+  // If we've got SimpleXML around, use that:
+  if (class_exists('SimpleXMLElement')) {
+    // Create a root element with the base table name:
+    $xml = new SimpleXMLElement('<' . $vars['root_node_tag'] . '/>');
+
+    foreach ($vars['themed_rows'] as $num => $row) {
+      // Create a node for each row:
+      $item = $xml->addChild($vars['item_tag']);
+
+      foreach ($row as $field => $content) {
+        // Create a node for each field:
+        $label = !empty($vars['header'][$field]) ? $vars['header'][$field] : views_bonus_export_make_xml_valid_tag($field);
+        $field = $item->addChild($label);
+
+        // Add the field value as the contents of the field node, SimpleXML will
+        // do escaping for us, nice!
+        $field[0] = $content;
+
+      }
+    }
+
+  // Put the SimpleXML root element in the $vars array for other modules to alter
+  $vars['xml'] = $xml;
+  }
+  // Fallback to not using SimpleXML if we haven't got it:
+  else {
+    $vars['xml'] = FALSE;
+    foreach ($vars['themed_rows'] as $num => $row) {
+      foreach ($row as $field => $content) {
+        $vars['themed_rows'][$num][$field] = str_replace(
+          array('&', '<', '>'),
+          array('&amp;', '&lt;', '&gt;'),
+          $content);
+      }
     }
   }
 }
 
 /**
+ * Returns a valid XML tag formed from the given input.
+ *
+ * @param $tag The string that should be made into a valid XML tag.
+ * @return The valid XML tag or an empty string if the string contained no valid
+ * XML tag characters.
+ */
+function views_bonus_export_make_xml_valid_tag($tag) {
+
+  // This regex matches characters that are not valid in XML tags, and the
+  // unicode ones that are. We don't bother with unicode, because it would so
+  // the preg_replace down a lot.
+  static $invalid_tag_chars_regex = '#[^\:A-Za-z_\-.0-9]+#';
+
+  // These characters are not valid at the start of an XML tag:
+  static $invalid_start_chars = '-.0123456789';
+
+  // Convert invalid chars to '-':
+  $tag = preg_replace($invalid_tag_chars_regex, '-', $tag);
+
+  // Need to trim invalid characters from the start of the string:
+  $tag = ltrim($tag, $invalid_start_chars);
+
+  return $tag;
+}
+
+/**
  * Shared helper function for export preprocess functions.
  */
 function _views_bonus_export_shared_preprocess(&$vars) {
