Skip to content
Snippets Groups Projects
Commit cc7f20b9 authored by Jared Hancock's avatar Jared Hancock
Browse files

Add export support for ticket search page

Adds a link to the scp/tickets.php page (Tickets) allowing for the export of
the data to CSV file. This is preliminary and still needs a few items:
  * An icon + button CSS definition
  * Support for alternate columns (staff assigned, etc.)

Also add beginnings of complex export support (PDF, JSON  and other
formats
to follow). CSV is fully supported for now.

And for brevity, add a Http::download method to allow for consistent and
browser-independent forced page content downloads
parent bec8de7e
No related branches found
No related tags found
No related merge requests found
<?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);
}
}
...@@ -50,5 +50,27 @@ class Http { ...@@ -50,5 +50,27 @@ class Http {
} }
exit; 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;
}
}
} }
?> ?>
...@@ -237,6 +237,8 @@ $qfrom.=' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (ticket.priority_id=pri.pri ...@@ -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(); $query="$qselect $qfrom $qwhere $qgroup ORDER BY $order_by $order LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
//echo $query; //echo $query;
$hash = md5($query);
$_SESSION['search_'.$hash] = $query;
$res = db_query($query); $res = db_query($query);
$showing=db_num_rows($res)?$pageNav->showing():""; $showing=db_num_rows($res)?$pageNav->showing():"";
if(!$results_type) { if(!$results_type) {
...@@ -486,7 +488,9 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false; ...@@ -486,7 +488,9 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false;
</table> </table>
<?php <?php
if($num>0){ //if we actually had any tickets returned. if($num>0){ //if we actually had any tickets returned.
echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>'; echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;';
echo '<a class="export-csv" href="?a=export&h='
.$hash.'&status='.$_REQUEST['status'] .'">Export</a></div>';
?> ?>
<?php <?php
if($thisstaff->canManageTickets()) { ?> if($thisstaff->canManageTickets()) { ?>
......
...@@ -460,6 +460,16 @@ if($ticket) { ...@@ -460,6 +460,16 @@ if($ticket) {
$inc = 'tickets.inc.php'; $inc = 'tickets.inc.php';
if($_REQUEST['a']=='open' && $thisstaff->canCreateTickets()) if($_REQUEST['a']=='open' && $thisstaff->canCreateTickets())
$inc = 'ticket-open.inc.php'; $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())) elseif(!$_POST && $_REQUEST['a']!='search' && ($min=$thisstaff->getRefreshRate()))
define('AUTO_REFRESH',1); //set refresh rate if the user has it configured define('AUTO_REFRESH',1); //set refresh rate if the user has it configured
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment