diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index ca09066889eb2d6d6fae72b8ca916bdad2a929ea..fe55cd32cc6ebd69b2df889066cdcba11fa159ca 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -904,6 +904,10 @@ class SelectionField extends FormField {
         return ($item) ? $item : $value;
     }
 
+    function hasIdValue() {
+        return true;
+    }
+
     function to_database($item) {
         if ($item instanceof DynamicListItem)
             return array($item->value, $item->id);
diff --git a/include/class.export.php b/include/class.export.php
index 8c16501ecc8da623d237e0c59b51481cf4da71ae..b5c5a44396eb07aec4c6a20c87d34a0a7c9eeff5 100644
--- a/include/class.export.php
+++ b/include/class.export.php
@@ -16,12 +16,12 @@
 
 class Export {
 
-    /* static */ function dumpQuery($sql, $headers, $how='csv', $filter=false) {
+    static function dumpQuery($sql, $headers, $how='csv', $options=array()) {
         $exporters = array(
             'csv' => CsvResultsExporter,
             'json' => JsonResultsExporter
         );
-        $exp = new $exporters[$how]($sql, $headers, $filter);
+        $exp = new $exporters[$how]($sql, $headers, $options);
         return $exp->dump();
     }
 
@@ -32,13 +32,36 @@ class Export {
     #      SQL is exported, but for something like tickets, we will need to
     #      export attached messages, reponses, and notes, as well as
     #      attachments associated with each, ...
-    /* static */ function dumpTickets($sql, $how='csv') {
+    static function dumpTickets($sql, $how='csv') {
+        // Add custom fields to the $sql statement
+        $cdata = $fields = $select = array();
+        foreach (TicketForm::getInstance()->getFields() as $f) {
+            // Ignore core fields
+            if (in_array($f->get('name'), array('subject','priority')))
+                continue;
+            // Ignore non-data fields
+            elseif (!$f->hasData() || $f->isPresentationOnly())
+                continue;
+
+            $name = $f->get('name') ? $f->get('name') : 'field_'.$f->get('id');
+            $key = '__field_'.$f->get('id');
+            // Fetch ID values for ID-based data
+            if ($f->hasIdValue()) {
+                $name .= '_id';
+            }
+            $cdata[$key] = $f->get('label');
+            $fields[$key] = $f;
+            $select[] = "cdata.`$name` AS __field_".$f->get('id');
+        }
+        if ($select)
+            $sql = str_replace(' FROM ', ',' . implode(',', $select) . ' FROM ', $sql);
         return self::dumpQuery($sql,
             array(
                 'ticketID' =>       'Ticket Id',
                 'created' =>        'Date',
                 'subject' =>        'Subject',
                 'name' =>           'From',
+                'email' =>          'From Email',
                 'priority_desc' =>  'Priority',
                 'dept_name' =>      'Department',
                 'helptopic' =>      'Help Topic',
@@ -53,8 +76,17 @@ class Export {
                 'team' =>           'Team Assigned',
                 'thread_count' =>   'Thread Count',
                 'attachments' =>    'Attachment Count',
-            ),
-            $how);
+            ) + $cdata,
+            $how,
+            array('modify' => function(&$record, $keys) use ($fields) {
+                foreach ($fields as $k=>$f) {
+                    if (($i = array_search($k, $keys)) !== false) {
+                        $record[$i] = $f->export($f->to_php($record[$i]));
+                    }
+                }
+                return $record;
+            })
+            );
     }
 
     /* static */ function saveTickets($sql, $filename, $how='csv') {
@@ -70,11 +102,12 @@ class Export {
 }
 
 class ResultSetExporter {
-    function ResultSetExporter($sql, $headers, $filter=false) {
+    function ResultSetExporter($sql, $headers, $options=array()) {
         $this->headers = array_values($headers);
         if ($s = strpos(strtoupper($sql), ' LIMIT '))
             $sql = substr($sql, 0, $s);
         # TODO: If $filter, add different LIMIT clause to query
+        $this->options = $options;
         $this->_res = db_query($sql);
         if ($row = db_fetch_array($this->_res)) {
             $query_fields = array_keys($row);
@@ -106,6 +139,10 @@ class ResultSetExporter {
         $record = array();
         foreach ($this->lookups as $idx)
             $record[] = $row[$idx];
+
+        if (isset($this->options['modify']) && is_callable($this->options['modify']))
+            $record = $this->options['modify']($record, $this->keys);
+
         return $record;
     }
 
diff --git a/include/class.forms.php b/include/class.forms.php
index daf67fa72cc50747aab1478f50df6adff075c688..c7810b10a6fb5cd442428f8b8394782502bddca0 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -300,6 +300,15 @@ class FormField {
         return Format::htmlchars($this->toString($value));
     }
 
+    /**
+     * Returns a value suitable for exporting to a foreign system. Mostly
+     * useful for things like dates and phone numbers which should be
+     * formatted using a standard when exported
+     */
+    function export($value) {
+        return $this->toString($value);
+    }
+
     function getLabel() { return $this->get('label'); }
 
     /**
@@ -734,6 +743,16 @@ class DatetimeField extends FormField {
             return Format::date($format, $value);
     }
 
+    function export($value) {
+        $config = $this->getConfiguration();
+        if (!$value)
+            return '';
+        elseif ($config['gmt'])
+            return Format::userdate('Y-m-d H:i:s', $value);
+        else
+            return Format::date('Y-m-d H:i:s', $value);
+    }
+
     function getConfigurationOptions() {
         return array(
             'time' => new BooleanField(array(