diff --git a/include/class.export.php b/include/class.export.php new file mode 100644 index 0000000000000000000000000000000000000000..7d6a7e7ec87b5939d8a7657370ea61626c06700c --- /dev/null +++ b/include/class.export.php @@ -0,0 +1,135 @@ +<?php +/************************************************************************* + class.export.php + + Exports stuff (details to follow) + + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2012 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +class Export { + + /* static */ function dumpQuery($sql, $headers, $how='csv') { + $exporters = array( + 'csv' => CsvResultsExporter, + 'json' => JsonResultsExporter + ); + $exp = new $exporters[$how]($sql, $headers); + return $exp->dump(); + } + + # XXX: Think about facilitated exporting. For instance, we might have a + # TicketExporter, which will know how to formulate or lookup a + # formatl query (SQL), and cooperate with the output process to add + # extra (recursive) information. In this funciton, the top-level + # 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') { + return self::dumpQuery($sql, + array( + 'ticketID' => 'Ticket Id', + 'created' => 'Date', + 'subject' => 'Subject', + 'name' => 'From', + 'priority_desc' => 'Priority', + 'dept_name' => 'Department', + 'source' => 'Source', + 'status' => 'Current Status' + ), + $how); + } + + /* static */ function saveTickets($sql, $filename, $how='csv') { + ob_start(); + self::dumpTickets($sql, $how); + $stuff = ob_get_contents(); + ob_end_clean(); + if ($stuff) + Http::download($filename, "text/$how", $stuff); + + return false; + } +} + +class ResultSetExporter { + function ResultSetExporter($sql, $headers) { + $this->headers = array_values($headers); + $this->_res = db_query($sql); + 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 (isset($row[$field])) { + $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); + } + } + + function getHeaders() { + return $this->headers; + } + + function next() { + if (!($row = db_fetch_row($this->_res))) + return false; + + $record = array(); + foreach ($this->lookups as $idx) + $record[] = $row[$idx]; + return $record; + } + + function nextArray() { + if (!($row = $this->next())) + return false; + return array_combine($this->keys, $row); + } + + function dump() { + # Useful for debug output + while ($row=$this->nextArray()) { + var_dump($row); + } + } +} + +class CsvResultsExporter extends ResultSetExporter { + function dump() { + echo '"' . implode('","', $this->getHeaders()) . "\"\n"; + while ($row=$this->next()) { + foreach ($row as &$val) + # Escape enclosed double-quotes + $val = str_replace('"','""',$val); + echo '"' . implode('","', $row) . "\"\n"; + } + } +} + +class JsonResultsExporter extends ResultSetExporter { + function dump() { + require_once(INCLUDE_DIR.'class.json.php'); + $exp = new JsonDataEncoder(); + $rows = array(); + while ($row=$this->nextArray()) { + $rows[] = $row; + } + echo $exp->encode($rows); + } +} diff --git a/include/class.http.php b/include/class.http.php index d134e6b8f3aef21f50440b175d8aa7249dc08676..37ad0302719cd8c96586dd03ed864c0dd07bc3ee 100644 --- a/include/class.http.php +++ b/include/class.http.php @@ -50,5 +50,27 @@ class Http { } exit; } + + function download($filename, $type, $data=null) { + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Cache-Control: public'); + header('Content-Type: '.$type); + $user_agent = strtolower ($_SERVER['HTTP_USER_AGENT']); + if (strpos($user_agent,'msie') !== false + && strpos($user_agent,'win') !== false) { + header('Content-Disposition: filename="'.basename($filename).'";'); + } else { + header('Content-Disposition: attachment; filename="' + .basename($filename).'"'); + } + header('Content-Transfer-Encoding: binary'); + if ($data !== null) { + header('Content-Length: '.strlen($data)); + print $data; + exit; + } + } } ?> diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index 3bfc8156ace212268d53e34be15f6ccaafb4b4c3..14d0514ecb43f7b42eed6b5740c2ce51d423b2fa 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -237,6 +237,8 @@ $qfrom.=' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (ticket.priority_id=pri.pri $query="$qselect $qfrom $qwhere $qgroup ORDER BY $order_by $order LIMIT ".$pageNav->getStart().",".$pageNav->getLimit(); //echo $query; +$hash = md5($query); +$_SESSION['search_'.$hash] = $query; $res = db_query($query); $showing=db_num_rows($res)?$pageNav->showing():""; if(!$results_type) { @@ -486,7 +488,9 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false; </table> <?php if($num>0){ //if we actually had any tickets returned. - echo '<div> Page:'.$pageNav->getPageLinks().' </div>'; + echo '<div> Page:'.$pageNav->getPageLinks().' '; + echo '<a class="export-csv" href="?a=export&h=' + .$hash.'&status='.$_REQUEST['status'] .'">Export</a></div>'; ?> <?php if($thisstaff->canManageTickets()) { ?> diff --git a/scp/tickets.php b/scp/tickets.php index da9dfa97e1ea8f37df2fcc718a950da6027d12c7..3d271e123d2e1e8ebd2e2353b6635a6c830d5f21 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -460,6 +460,16 @@ if($ticket) { $inc = 'tickets.inc.php'; if($_REQUEST['a']=='open' && $thisstaff->canCreateTickets()) $inc = 'ticket-open.inc.php'; + elseif($_REQUEST['a'] == 'export') { + require_once(INCLUDE_DIR.'class.export.php'); + $ts = strftime('%Y%m%d'); + if (!($token=$_REQUEST['h'])) + $errors['err'] = 'Query token required'; + elseif (!($query=$_SESSION['search_'.$token])) + $errors['err'] = 'Query token not found'; + elseif (!Export::saveTickets($query, "tickets-$ts.csv", 'csv')) + $errors['err'] = 'Internal error: Unable to dump query results'; + } elseif(!$_POST && $_REQUEST['a']!='search' && ($min=$thisstaff->getRefreshRate())) define('AUTO_REFRESH',1); //set refresh rate if the user has it configured }