Skip to content
Permalink
Browse files

ticket-view: Add ZIP export option

This adds the ability to dowload a ticket thread PDF in a ZIP file with all of
the attachments included. And, optionally, also include the associated tasks,
again with a PDF of the thread and all attachments included.

Conflicts:

	include/staff/ticket-view.inc.php
	scp/tickets.php
  • Loading branch information...
protich committed Nov 11, 2019
1 parent 7a7304b commit 5e5b6b849514829136286e52b2a267733a718037
Showing with 119 additions and 0 deletions.
  1. +100 −0 include/class.export.php
  2. +6 −0 include/class.ticket.php
  3. +4 −0 include/staff/system.inc.php
  4. +6 −0 include/staff/ticket-view.inc.php
  5. +3 −0 scp/tickets.php
@@ -864,3 +864,103 @@ function transfer_array($destination, $array, $keys, $options=array()) {
$this->write_block(array('end-table'));
}
}
class TicketZipExporter {
var $ticket;
var $tmpfiles;
function __construct(Ticket $ticket) {
$this->ticket = $ticket;
$this->tmpfiles = array();
}
function addTicket($ticket, $zip, $prefix, $notes=true, $psize=null) {
require_once(INCLUDE_DIR.'class.pdf.php');
$pdf_file = $this->tmpfiles[] = tempnam(sys_get_temp_dir(), 'zip');
$pdf = new Ticket2PDF($ticket, $psize, $notes);
$pdf->output($pdf_file, 'F');
$zip->addFile($pdf_file, "{$ticket->getNumber()}.pdf");
// Include all the (non-inline) attachments
// XXX: Handle attachments with duplicate filenames between entry posts
$attachments = Attachment::objects()
->filter([
'thread_entry__thread' => $ticket->getThread(),
'inline' => 0
])
->order_by('thread_entry__created')
->select_related('file');
foreach ($attachments as $att) {
$zip->addFromString("{$prefix}/{$att->getFilename()}",
$att->getFile()->getData());
}
}
function addTask($task, $zip, $prefix, $notes=true, $psize=null) {
require_once(INCLUDE_DIR.'class.pdf.php');
$pdf_file = $this->tmpfiles[] = tempnam(sys_get_temp_dir(), 'zip');
$pdf = new Task2PDF($task, ['psize' => $psize]);
$pdf->output($pdf_file, 'F');
$zip->addFile($pdf_file, "{$prefix}/{$task->getNumber()}.pdf");
// Include all the (non-inline) attachments
// XXX: Handle attachments with duplicate filenames between entry posts
$attachments = Attachment::objects()
->filter([
'thread_entry__thread' => $task->getThread(),
'inline' => 0
])
->order_by('thread_entry__created')
->select_related('file');
foreach ($attachments as $att) {
$zip->addFromString("{$prefix}/{$task->getNumber()}/{$att->getFilename()}",
$att->getFile()->getData());
}
}
function download($options = array()) {
global $thisstaff;
$notes = @$options['notes'] ?? false;
$tasks = @$options['tasks'] ?? false;
// TODO: Use a streaming ZIP library
$zipfile = tempnam(sys_get_temp_dir(), 'zip');
try {
$zip = new ZipArchive();
if (!$zip->open($zipfile, ZipArchive::CREATE))
return;
$prefix = "{$this->ticket->getNumber()}";
// Include a PDF of the ticket thread (with optional notes)
if (!$thisstaff || !($psize = $thisstaff->getDefaultPaperSize()))
$psize = 'Letter';
$this->addTicket($this->ticket, $zip, $prefix, $notes, $psize);
if ($tasks) {
foreach ($this->ticket->tasks as $task)
$this->addTask($task, $zip, "{$prefix}/tasks", $notes, $psize);
}
$zip->close();
Http::download("ticket-{$this->ticket->getNumber()}.zip", "application/zip",
null, 'attachment');
$fp = fopen($zipfile, 'r');
fpassthru($fp);
fclose($fp);
}
finally {
foreach ($this->tmpfiles as $T)
@unlink($T);
unlink($zipfile);
}
}
}
@@ -3504,6 +3504,12 @@ function pdfExport($psize='Letter', $notes=false, $events=false) {
exit;
}
function zipExport($notes=true, $tasks=false) {
$exporter = new TicketZipExporter($this);
$exporter->download(['notes'=>$notes, 'tasks'=>$tasks]);
exit;
}
function delete($comments='') {
global $ost, $thisstaff;
@@ -41,6 +41,10 @@
'name' => 'fileinfo',
'desc' => __('Used to detect file types for uploads')
),
'zip' => array(
'name' => 'zip',
'desc' => __('Used for ticket and task exporting')
),
'apcu' => array(
'name' => 'APCu',
'desc' => __('Improves overall performance')
@@ -95,6 +95,12 @@ class="icon-file-alt"></i> <?php echo __('Ticket Thread'); ?></a>
class="icon-file-text-alt"></i> <?php echo __('Thread + Internal Notes'); ?></a>
<li><a class="no-pjax" target="_blank" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print&notes=1&events=1"><i
class="icon-list-alt"></i> <?php echo __('Thread + Internal Notes + Events'); ?></a>
<?php if (extension_loaded('zip')) { ?>
<li><a class="no-pjax" target="_blank" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=zip&notes=1"><i
class="icon-download-alt"></i> <?php echo __('Export with Notes + Attachments'); ?></a>
<li><a class="no-pjax" target="_blank" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=zip&notes=1&tasks=1"><i
class="icon-download"></i> <?php echo __('Export with Notes + Attachments + Tasks'); ?></a>
<?php } ?>
</ul>
</div>
<?php
@@ -504,6 +504,9 @@
elseif (!$ticket->pdfExport($_REQUEST['psize'], $_REQUEST['notes'], $_REQUEST['events']))
$errors['err'] = __('Unable to export the ticket to PDF for print.')
.' '.__('Internal error occurred');
} elseif ($_GET['a'] == 'zip' && !$ticket->zipExport($_REQUEST['notes'], $_REQUEST['tasks'])) {
$errors['err'] = __('Unable to export the ticket to ZIP.')
.' '.__('Internal error occurred');
} elseif (PluginManager::auditPlugin() && $_REQUEST['a'] == 'export' && strtolower($_REQUEST['t']) == 'audits') {
require_once(sprintf('phar:///%s/plugins/audit.phar/class.audit.php', INCLUDE_DIR));
$show = AuditEntry::$show_view_audits;

0 comments on commit 5e5b6b8

Please sign in to comment.
You can’t perform that action at this time.