diff --git a/module/VuFind/src/VuFind/Export.php b/module/VuFind/src/VuFind/Export.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ce6edfe4357cc8b3769de2be5b33f2d3065223e
--- /dev/null
+++ b/module/VuFind/src/VuFind/Export.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Export support class
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Support_Classes
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org   Main Site
+ */
+namespace VuFind;
+use VuFind\Config\Reader as ConfigReader;
+
+/**
+ * Export support class
+ *
+ * @category VuFind2
+ * @package  Support_Classes
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org   Main Site
+ */
+class Export
+{
+    /**
+     * Get bulk export options.
+     *
+     * @return array
+     */
+    public static function getBulkOptions()
+    {
+        static $options = false;
+
+        if ($options === false) {
+            $options = array();
+            $config = ConfigReader::getConfig();
+            if (isset($config->BulkExport->enabled)
+                && isset($config->BulkExport->options)
+                && $config->BulkExport->enabled
+            ) {
+                foreach (explode(':', $config->BulkExport->options) as $option) {
+                    if (isset($config->Export->$option)
+                        && $config->Export->$option == true
+                    ) {
+                            $options[] = $option;
+                    }
+                }
+            }
+        }
+
+        return $options;
+    }
+
+    /**
+     * Get the URL for bulk export.
+     *
+     * @param Zend_View $view   View object (needed for URL generation)
+     * @param string    $format Export format being used
+     * @param array     $ids    Array of IDs to export (in source|id format)
+     *
+     * @return string
+     */
+    public static function getBulkUrl($view, $format, $ids)
+    {
+        $params = array();
+        $params[] = 'f=' . urlencode($format);
+        foreach ($ids as $id) {
+            $params[] = urlencode('i[]') . '=' . urlencode($id);
+        }
+        $url = $view->fullUrl(
+            $view->url(
+                array('controller' => 'Cart', 'action' => 'doExport'), 'default',
+                true
+            )
+        );
+        $url .= '?' . implode('&', $params);
+
+        return self::needsRedirect($format)
+            ? self::getRedirectUrl($format, $url) : $url;
+    }
+
+    /**
+     * Build callback URL for export.
+     *
+     * @param string $format   Export format being used
+     * @param string $callback Callback URL for retrieving record(s)
+     *
+     * @return string
+     */
+    public static function getRedirectUrl($format, $callback)
+    {
+        // Grab configuration, since we may need it to fill in template:
+        $config = ConfigReader::getConfig();
+
+        // Fill in special tokens in template:/*
+        $exportConfig = ConfigReader::getConfig('export');
+        $template = $exportConfig->$format->redirectUrl;
+        preg_match_all('/\{([^}]+)\}/', $template, $matches);
+        foreach ($matches[1] as $current) {
+            $parts = explode('|', $current);
+            switch ($parts[0]) {
+                
+            case 'config':
+            case 'encodedConfig':
+                if (isset($config->{$parts[1]}->{$parts[2]})) {
+                    $value = $config->{$parts[1]}->{$parts[2]};
+                } else {
+                    $value = $parts[3];
+                }
+                if ($parts[0] == 'encodedConfig') {
+                    $value = urlencode($value);
+                }
+                $template = str_replace('{' . $current . '}', $value, $template);
+                break;
+            case 'encodedCallback':
+                $template = str_replace(
+                    '{' . $current . '}', urlencode($callback), $template
+                );
+                break;
+            }
+        }
+        return $template;
+    }
+
+    /**
+     * Does the requested format require a redirect?
+     *
+     * @param string $format Format to check
+     *
+     * @return bool
+     */
+    public static function needsRedirect($format)
+    {
+        $exportConfig = ConfigReader::getConfig('export');
+        return isset($exportConfig->$format->redirectUrl);
+    }
+
+    /**
+     * Convert an array of individual records into a single string for display.
+     *
+     * @param string $format Format of records to process
+     * @param array  $parts  Multiple records to process
+     *
+     * @return string
+     */
+    public static function processGroup($format, $parts)
+    {
+        // Load export configuration:
+        $exportConfig = ConfigReader::getConfig('export');
+
+        // If we're in XML mode, we need to do some special processing:
+        if (isset($exportConfig->$format->combineXpath)) {
+            $ns = array();
+            if (isset($exportConfig->$format->combineNamespaces)) {
+                foreach ($exportConfig->$format->combineNamespaces as $current) {
+                    $ns[] = explode('|', $current, 2);
+                }
+            }
+            foreach ($parts as $part) {
+                // Convert text into XML object:
+                $current = simplexml_load_string($part);
+
+                // The first record gets kept as-is; subsequent records get merged
+                // in based on the configured XPath (currently only one level is
+                // supported)...
+                if (!isset($retVal)) {
+                    $retVal = $current;
+                } else {
+                    foreach ($ns as $n) {
+                        $current->registerXPathNamespace($n[0], $n[1]);
+                    }
+                    $matches = $current->xpath($exportConfig->$format->combineXpath);
+                    foreach ($matches as $match) {
+                        SimpleXML::appendElement($retVal, $match);
+                    }
+                }
+            }
+            return $retVal->asXML();
+        } else {
+            // Not in XML mode -- just concatenate everything together:
+            return implode('', $parts);
+        }
+    }
+
+    /**
+     * Set headers for the requested format.
+     *
+     * @param string                            $format   Selected export format
+     * @param Zend_Controller_Response_Abstract $response Response object to modify
+     *
+     * @return void
+     */
+    public static function setHeaders($format, $response)
+    {
+        $exportConfig = ConfigReader::getConfig('export');
+        if (isset($exportConfig->$format->headers)) {
+            $headerTypes = array();
+            foreach ($exportConfig->$format->headers as $header) {
+                // Keep track of which header keys we have already seen -- we
+                // want to allow duplicate values, so if we've previously encountered
+                // a particular key, we should set the "replace" parameter of the
+                // setHeader() method to false:
+                $parts = explode(':', $header, 2);
+                $key = strtolower($parts[0]);
+                $response->setHeader(
+                    trim($parts[0]), trim($parts[1]), !isset($headerTypes[$key])
+                );
+                $headerTypes[$key] = 1;
+            }
+        }
+    }
+}
diff --git a/module/VuFind/src/VuFind/SimpleXML.php b/module/VuFind/src/VuFind/SimpleXML.php
new file mode 100644
index 0000000000000000000000000000000000000000..ec08ff176ed57d9610e18170f22c863580629eb5
--- /dev/null
+++ b/module/VuFind/src/VuFind/SimpleXML.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * VuFind SimpleXML enhancement functionality
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2009.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Support_Classes
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/system_classes Wiki
+ */
+namespace VuFind;
+
+/**
+ * VuFind SimpleXML enhancement functionality
+ *
+ * @category VuFind2
+ * @package  Support_Classes
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/system_classes Wiki
+ */
+class SimpleXML
+{
+    /**
+     * Attach $child to $parent.  Adapted from function defined in PHP docs here:
+     *      http://www.php.net/manual/en/class.simplexmlelement.php#99071
+     *
+     * @param SimpleXMLElement $parent Parent element to modify
+     * @param SimpleXMLElement $child  Child element to attach
+     *
+     * @return void
+     */
+    public static function appendElement($parent, $child)
+    {
+        // get all namespaces for document
+        $namespaces = $child->getNamespaces(true);
+
+        // check if there is a default namespace for the current node
+        $currentNs = $child->getNamespaces();
+        $defaultNs = count($currentNs) > 0 ? current($currentNs) : null;
+        $prefix = (count($currentNs) > 0) ? current(array_keys($currentNs)) : '';
+        $childName = strlen($prefix) > 1
+            ? $prefix . ':' . $child->getName() : $child->getName();
+
+        // check if the value is string value / data
+        if (trim((string) $child) == '') {
+            $element = $parent->addChild($childName, null, $defaultNs);
+        } else {
+            $element = $parent->addChild(
+                $childName, htmlspecialchars((string)$child), $defaultNs
+            );
+        }
+
+        foreach ($child->attributes() as $attKey => $attValue) {
+            $element->addAttribute($attKey, $attValue);
+        }
+        foreach ($namespaces as $nskey => $nsurl) {
+            foreach ($child->attributes($nsurl) as $attKey => $attValue) {
+                $element->addAttribute($nskey . ':' . $attKey, $attValue, $nsurl);
+            }
+        }
+
+        // add children -- try with namespaces first, but default to all children
+        // if no namespaced children are found.
+        $children = 0;
+        foreach ($namespaces as $nskey => $nsurl) {
+            foreach ($child->children($nsurl) as $currChild) {
+                self::appendElement($element, $currChild);
+                $children++;
+            }
+        }
+        if ($children == 0) {
+            foreach ($child->children() as $currChild) {
+                self::appendElement($element, $currChild);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/themes/blueprint/templates/ajax/export-favorites.phtml b/themes/blueprint/templates/ajax/export-favorites.phtml
index 41f6aad941cf5480c67d543084a38f6745e8b020..cd5688f58ad699fbd98126ff4f637438239d8ec7 100644
--- a/themes/blueprint/templates/ajax/export-favorites.phtml
+++ b/themes/blueprint/templates/ajax/export-favorites.phtml
@@ -1,6 +1,6 @@
 <p>
-  <a class="save" onclick="hideLightbox();" href="<?=$this->escapeHtml($this->url)?>"<?=VF_Export::needsRedirect($this->format) ? ' target="_blank"' : ''?>><?=
-    VF_Export::needsRedirect($this->format)
+  <a class="save" onclick="hideLightbox();" href="<?=$this->escapeHtml($this->url)?>"<?=\VuFind\Export::needsRedirect($this->format) ? ' target="_blank"' : ''?>><?=
+    \VuFind\Export::needsRedirect($this->format)
         ? $this->transEsc('export_redirect', array('%%service%%' => $this->translate($this->format)))
         : $this->transEsc('export_download')
   ?></a>
diff --git a/themes/blueprint/templates/cart/cart.phtml b/themes/blueprint/templates/cart/cart.phtml
index 1df2f20c9aa1833066b930b25c4d66e9f6d50c56..677393dfbbc3c9ef0acc064431386ab2cb73e731 100644
--- a/themes/blueprint/templates/cart/cart.phtml
+++ b/themes/blueprint/templates/cart/cart.phtml
@@ -13,7 +13,7 @@
       <input type="checkbox" class="selectAllCheckboxes floatleft" name="selectAll" id="cartCheckboxSelectAll"/> <label for="cartCheckboxSelectAll" class="floatleft"><?=$this->transEsc('select_page')?></label>
       <input type="submit" class="fav floatleft button" name="saveCart" value="<?=$this->transEsc('bookbag_save_selected')?>" title="<?=$this->transEsc('bookbag_save')?>"/>
       <input type="submit" class="mail floatleft button" name="email" value="<?=$this->transEsc('bookbag_email_selected')?>" title="<?=$this->transEsc('bookbag_email')?>"/>
-      <? $exportOptions = VF_Export::getBulkOptions(); if (count($exportOptions) > 0): ?>
+      <? $exportOptions = \VuFind\Export::getBulkOptions(); if (count($exportOptions) > 0): ?>
         <input type="submit" class="export floatleft button" name="export" value="<?=$this->transEsc('bookbag_export_selected')?>" title="<?=$this->transEsc('bookbag_export')?>"/>
       <? endif; ?>
       <input type="submit" class="print floatleft button" name="print" value="<?=$this->transEsc('bookbag_print_selected')?>" title="<?=$this->transEsc('print_selected')?>"/>
diff --git a/themes/blueprint/templates/myresearch/bulk-action-buttons.phtml b/themes/blueprint/templates/myresearch/bulk-action-buttons.phtml
index bcc0a02337d836e4c37e1e416748e0483463a142..88d56a0449aacb6ffd2c095c505dd8d2b87427b3 100644
--- a/themes/blueprint/templates/myresearch/bulk-action-buttons.phtml
+++ b/themes/blueprint/templates/myresearch/bulk-action-buttons.phtml
@@ -6,7 +6,7 @@
   <? if ((!is_null($this->list) && $this->list->editAllowed()) || is_null($this->list) && $this->layout()->account->isLoggedIn()): ?>
     <input id="<?=$this->idPrefix?>delete_list_items_<?=!is_null($this->list) ? $this->escapeHtml($this->list->id) : ''?>" type="submit" class="delete floatleft button" name="delete" value="<?=$this->transEsc('Delete')?>" title="<?=$this->transEsc('delete_selected')?>"/>
   <? endif; ?>
-  <? $exportOptions = VF_Export::getBulkOptions(); if (count($exportOptions) > 0): ?>
+  <? $exportOptions = \VuFind\Export::getBulkOptions(); if (count($exportOptions) > 0): ?>
     <input type="submit" class="export floatleft button" name="export" value="<?=$this->transEsc('Export')?>" title="<?=$this->transEsc('export_selected')?>"/>
   <? endif; ?>
   <input type="submit" class="print floatleft button" name="print" value="<?=$this->transEsc('Print')?>" title="<?=$this->transEsc('print_selected')?>"/>
diff --git a/themes/blueprint/templates/record/view.phtml b/themes/blueprint/templates/record/view.phtml
index f81b8b6be4c5d8001e8379db946f59d194d755b4..1b7122799b1966b171a0ad0e3cca5978c6b551e3 100644
--- a/themes/blueprint/templates/record/view.phtml
+++ b/themes/blueprint/templates/record/view.phtml
@@ -37,7 +37,7 @@
         <a href="<?=$this->recordLink()->getUrl($this->driver, 'Export')?>" class="export exportMenu"><?=$this->transEsc('Export Record')?></a>
         <ul class="menu offscreen" id="exportMenu">
         <? foreach ($exportFormats as $exportFormat): ?>
-          <li><a <? if (VF_Export::needsRedirect($exportFormat)): ?>target="<?=$this->escapeHtml($exportFormat)?>Main" <? endif; ?>href="<?=$this->recordLink()->getUrl($this->driver, 'Export')?>?style=<?=$this->escapeHtml($exportFormat)?>"><?=$this->transEsc('Export to')?> <?=$this->escapeHtml($exportFormat)?></a></li>
+          <li><a <? if (\VuFind\Export::needsRedirect($exportFormat)): ?>target="<?=$this->escapeHtml($exportFormat)?>Main" <? endif; ?>href="<?=$this->recordLink()->getUrl($this->driver, 'Export')?>?style=<?=$this->escapeHtml($exportFormat)?>"><?=$this->transEsc('Export to')?> <?=$this->escapeHtml($exportFormat)?></a></li>
         <? endforeach; ?>
         </ul>
       </li>