diff --git a/include/class.export.php b/include/class.export.php
index 521eb7cefbdb89605ebe87e04ea2d644f359d60c..6f8ec713f3437a2cb21a264a80aa4637966ddd7e 100644
--- a/include/class.export.php
+++ b/include/class.export.php
@@ -42,48 +42,40 @@ class Export {
     #      attachments associated with each, ...
     static function dumpTickets($sql, $how='csv') {
         // Add custom fields to the $sql statement
-        $cdata = $fields = $select = array();
+        $cdata = $fields = array();
         foreach (TicketForm::getInstance()->getFields() as $f) {
             // Ignore core fields
-            if (in_array($f->get('name'), array('subject','priority')))
+            if (in_array($f->get('name'), array('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');
+            $name = $f->get('name') ?: 'field_'.$f->get('id');
+            $key = 'cdata.'.$name;
             $fields[$key] = $f;
-            $select[] = "cdata.`$name` AS __field_".$f->get('id');
+            $cdata[$key] = $f->getLocal('label');
         }
-        if ($select)
-            $sql = str_replace(' FROM ', ',' . implode(',', $select) . ' FROM ', $sql);
         return self::dumpQuery($sql,
             array(
                 'number' =>         __('Ticket Number'),
-                'ticket_created' => __('Date'),
-                'subject' =>        __('Subject'),
-                'name' =>           __('From'),
-                'email' =>          __('From Email'),
-                'priority_desc' =>  __('Priority'),
-                'dept_name' =>      __('Department'),
-                'helptopic' =>      __('Help Topic'),
+                'created' =>        __('Date'),
+                'cdata.subject' =>  __('Subject'),
+                'user.name' =>      __('From'),
+                'user.default_email.address' => __('From Email'),
+                'cdata.:priority.priority_desc' => __('Priority'),
+                'dept::getLocalName' => __('Department'),
+                'topic::getName' => __('Help Topic'),
                 'source' =>         __('Source'),
-                'status' =>         __('Current Status'),
-                'effective_date' => __('Last Updated'),
+                'status::getName' =>__('Current Status'),
+                '::getEffectiveDate' => __('Last Updated'),
                 'duedate' =>        __('Due Date'),
                 'isoverdue' =>      __('Overdue'),
                 'isanswered' =>     __('Answered'),
-                'assigned' =>       __('Assigned To'),
-                'staff' =>          __('Agent Assigned'),
-                'team' =>           __('Team Assigned'),
-                'thread_count' =>   __('Thread Count'),
-                'attachments' =>    __('Attachment Count'),
+                'staff::getName' => __('Agent Assigned'),
+                'team::getName' =>  __('Team Assigned'),
+                #'thread_count' =>   __('Thread Count'),
+                #'attachments' =>    __('Attachment Count'),
             ) + $cdata,
             $how,
             array('modify' => function(&$record, $keys) use ($fields) {
@@ -213,32 +205,22 @@ class Export {
 class ResultSetExporter {
     var $output;
 
-    function ResultSetExporter($sql, $headers, $options=array()) {
+    function __construct($sql, $headers, $options=array()) {
         $this->headers = array_values($headers);
-        if ($s = strpos(strtoupper($sql), ' LIMIT '))
-            $sql = substr($sql, 0, $s);
+        // Remove limit and offset
+        $sql->limit(null)->offset(null);
         # TODO: If $filter, add different LIMIT clause to query
         $this->options = $options;
         $this->output = $options['output'] ?: fopen('php://output', 'w');
 
-        $this->_res = db_query($sql, true, true);
-        if ($row = db_fetch_array($this->_res)) {
-            $query_fields = array_keys($row);
-            $this->headers = array();
-            $this->keys = array();
-            $this->lookups = array();
-            foreach ($headers as $field=>$name) {
-                if (array_key_exists($field, $row)) {
-                    $this->headers[] = $name;
-                    $this->keys[] = $field;
-                    # Remember the location of this header in the query results
-                    # (column-wise) so we don't have to do hashtable lookups for every
-                    # column of every row.
-                    $this->lookups[] = array_search($field, $query_fields);
-                }
-            }
-            db_data_reset($this->_res);
+        $this->headers = array();
+        $this->keys = array();
+        foreach ($headers as $field=>$name) {
+            $this->headers[] = $name;
+            $this->keys[] = $field;
         }
+        $this->_res = $sql->getIterator();
+        $this->_res->rewind();
     }
 
     function getHeaders() {
@@ -246,12 +228,30 @@ class ResultSetExporter {
     }
 
     function next() {
-        if (!($row = db_fetch_row($this->_res)))
+        if (!$this->_res->valid())
             return false;
 
+        $object = $this->_res->current();
+        $this->_res->next();
+
         $record = array();
-        foreach ($this->lookups as $idx)
-            $record[] = $row[$idx];
+
+        foreach ($this->keys as $field) {
+            list($field, $func) = explode('::', $field);
+            $path = explode('.', $field);
+            $current = $object;
+            // Evaluate dotted ORM path
+            if ($field) {
+                foreach ($path as $P) {
+                    $current = $current->{$P};
+                }
+            }
+            // Evalutate :: function call on target current
+            if ($func && method_exists($current, $func)) {
+                $current = $current->{$func}();
+            }
+            $record[] = (string) $current;
+        }
 
         if (isset($this->options['modify']) && is_callable($this->options['modify']))
             $record = $this->options['modify']($record, $this->keys);
diff --git a/include/class.staff.php b/include/class.staff.php
index 231717bef71bf8aa720b5f67c7f22a19fadc1f5b..6fcaf873fb1b299610fbf06faa4a7ac817922b2e 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -32,6 +32,10 @@ class StaffModel extends VerySimpleModel {
             ),
         ),
     );
+
+    function getName() {
+        return $this->firstname . ' ' . $this->lastname;
+    }
 }
 
 class Staff extends AuthenticatedUser {
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 81201705984c97f9c26b0a00cfa30d1f46762045..4ca2477a3b8d39652ca5ec086f9180aea85f2935 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -77,12 +77,12 @@ class TicketModel extends VerySimpleModel {
     }
 
     function getEffectiveDate() {
-         return max(
+         return Format::datetime(max(
              strtotime($this->lastmessage),
              strtotime($this->closed),
              strtotime($this->reopened),
              strtotime($this->created)
-         );
+         ));
     }
 
     function delete() {
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index b0dca99dd0d197f0a1d7fb235f381e8936eabe0d..cb0c1011c644a881a8f187bfd8887b1f6b20a8b4 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -237,7 +237,7 @@ $_SESSION[':Q:tickets'] = $tickets;
                 <td title="<?php echo $T->user->getDefaultEmailAddress(); ?>" nowrap>
                   <a class="Icon <?php echo strtolower($T->source); ?>Ticket ticketPreview" title="Preview Ticket"
                     href="tickets.php?id=<?php echo $T->ticket_id; ?>"><?php echo $tid; ?></a></td>
-                <td align="center" nowrap><?php echo Format::datetime($T->getEffectiveDate()); ?></td>
+                <td align="center" nowrap><?php echo $T->getEffectiveDate(); ?></td>
                 <td><a <?php if ($flag) { ?> class="Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket" <?php } ?>
                     href="tickets.php?id=<?php echo $T->ticket_id; ?>"><?php echo $subject; ?></a>
                      <?php
@@ -292,8 +292,8 @@ $_SESSION[':Q:tickets'] = $tickets;
     <?php
     if($total>0){ //if we actually had any tickets returned.
         echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;';
-        echo '<a class="export-csv no-pjax" href="?a=export&h='
-            .$hash.'&status='.$_REQUEST['status'] .'">'.__('Export').'</a>&nbsp;<i class="help-tip icon-question-sign" href="#export"></i></div>';
+        echo '<a class="export-csv no-pjax" href="?a=export&status='
+            .$_REQUEST['status'] .'">'.__('Export').'</a>&nbsp;<i class="help-tip icon-question-sign" href="#export"></i></div>';
     } ?>
     </form>
 </div>
diff --git a/scp/tickets.php b/scp/tickets.php
index 74fa99bff5df98e7fcc711d74bd8f398e0564872..4a84530660188770bbf136d41b6132994ab47482 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -465,9 +465,7 @@ if($ticket) {
         $inc = 'ticket-open.inc.php';
     elseif($_REQUEST['a'] == 'export') {
         $ts = strftime('%Y%m%d');
-        if (!($token=$_REQUEST['h']))
-            $errors['err'] = __('Query token required');
-        elseif (!($query=$_SESSION['search_'.$token]))
+        if (!($query=$_SESSION[':Q:tickets']))
             $errors['err'] = __('Query token not found');
         elseif (!Export::saveTickets($query, "tickets-$ts.csv", 'csv'))
             $errors['err'] = __('Internal error: Unable to dump query results');