diff --git a/include/class.orm.php b/include/class.orm.php index 0cfaf64222da759eb56d156b69b1a32397c5bcdd..1638d8b97de68423fd079d3ee8733705e3582aa0 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -1292,6 +1292,10 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl function asView() { $unique = spl_object_hash($this); $classname = "QueryView{$unique}"; + + if (class_exists($classname)) + return $classname; + $class = <<<EOF class {$classname} extends VerySimpleModel { static \$meta = array( diff --git a/include/class.ticket.php b/include/class.ticket.php index f849340c57ebf4d9a849d2da055e0a5124be5af8..9710199056df4585a2604d6d853d99671a8cbd0d 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -22,6 +22,7 @@ include_once(INCLUDE_DIR.'class.dept.php'); include_once(INCLUDE_DIR.'class.topic.php'); include_once(INCLUDE_DIR.'class.lock.php'); include_once(INCLUDE_DIR.'class.file.php'); +include_once(INCLUDE_DIR.'class.export.php'); include_once(INCLUDE_DIR.'class.attachment.php'); include_once(INCLUDE_DIR.'class.banlist.php'); include_once(INCLUDE_DIR.'class.template.php'); diff --git a/include/staff/templates/tickets.tmpl.php b/include/staff/templates/tickets.tmpl.php index c140f689011ea81412d674e6398797056795b484..71dc0470a6524ed0a4f094c0affebeca37f6ea68 100644 --- a/include/staff/templates/tickets.tmpl.php +++ b/include/staff/templates/tickets.tmpl.php @@ -1,14 +1,29 @@ <?php +$args = array(); +parse_str($_SERVER['QUERY_STRING'], $args); +$args['t'] = 'tickets'; +unset($args['p'], $args['_pjax']); $tickets = TicketModel::objects(); if ($user) { - $tickets->filter(array('user_id' => $user->getId())); -} -elseif ($org) { - $tickets->filter(array('user__org' => $org)); + $filter = $tickets->copy() + ->values_flat('ticket_id') + ->filter(array('user_id' => $user->getId())) + ->union($tickets->copy() + ->values_flat('ticket_id') + ->filter(array('thread__collaborators__user_id' => $user->getId())) + , false); +} elseif ($org) { + $filter = $tickets->copy() + ->values_flat('ticket_id') + ->filter(array('user__org' => $org)); } +// Apply filter +$tickets->filter(array('ticket_id__in' => $filter)); + +// Apply staff visibility if (!$thisstaff->hasPerm(SearchBackend::PERM_EVERYTHING)) { // -- Open and assigned to me $visibility = array( @@ -25,10 +40,22 @@ if (!$thisstaff->hasPerm(SearchBackend::PERM_EVERYTHING)) { $tickets->filter(Q::any($visibility)); } - $tickets->constrain(array('lock' => array( 'lock__expire__gt' => SqlFunction::NOW()))); +// Group by ticket_id. +$tickets->distinct('ticket_id'); + +// Save the query to the session for exporting +$queue = sprintf(':%s:tickets', $user ? 'U' : 'O'); +$_SESSION[$queue] = $tickets; + +// Apply pagination +$total = $tickets->count(); +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$pageNav = new Pagenate($total, $page, PAGE_LIMIT); +$pageNav->setURL(($user ? 'users.php' : 'orgs.php'), $args); +$tickets = $pageNav->paginate($tickets); $tickets->annotate(array( 'collab_count' => SqlAggregate::COUNT('thread__collaborators', true), @@ -48,16 +75,15 @@ $tickets->annotate(array( $tickets->values('staff_id', 'staff__firstname', 'staff__lastname', 'team__name', 'team_id', 'lock__lock_id', 'lock__staff_id', 'isoverdue', 'status_id', 'status__name', 'status__state', 'number', 'cdata__subject', 'ticket_id', 'source', 'dept_id', 'dept__name', 'user_id', 'user__default_email__address', 'user__name', 'lastupdate'); -TicketForm::ensureDynamicDataView(); +$tickets->order_by('-created'); +TicketForm::ensureDynamicDataView(); // Fetch the results -$results = count($tickets); ?> <div class="pull-left" style="margin-top:5px;"> <?php - if ($results) { - echo '<strong>'.sprintf(_N('Showing %d ticket', 'Showing %d tickets', - count($results)), count($results)).'</strong>'; + if ($total) { + echo '<strong>'.$pageNav->showing().'</strong>'; } else { echo sprintf(__('%s does not have any tickets'), $user? 'User' : 'Organization'); } @@ -76,7 +102,7 @@ $results = count($tickets); <br/> <div> <?php -if ($results) { ?> +if ($total) { ?> <form action="users.php" method="POST" name='tickets' style="padding-top:10px;"> <?php csrf_token(); ?> <input type="hidden" name="a" value="mass_process" > @@ -107,6 +133,7 @@ if ($results) { ?> <tbody> <?php $subject_field = TicketForm::objects()->one()->getField('subject'); + $user_id = $user ? $user->getId() : 0; foreach($tickets as $T) { $flag=null; if ($T['lock__lock_id'] && $T['lock__staff_id'] != $thisstaff->getId()) @@ -139,13 +166,19 @@ if ($results) { ?> </td> <?php } ?> - <td align="center" nowrap> + <td nowrap> <a class="Icon <?php echo strtolower($T['source']); ?>Ticket preview" title="<?php echo __('Preview Ticket'); ?>" href="tickets.php?id=<?php echo $T['ticket_id']; ?>" - data-preview="#tickets/<?php echo $T['ticket_id']; ?>/preview"><?php echo $tid; ?></a></td> - <td align="center" nowrap><?php echo Format::datetime($T['lastupdate']); ?></td> + data-preview="#tickets/<?php echo $T['ticket_id']; ?>/preview"><?php + echo $tid; ?></a> + <?php + if ($user_id && $user_id != $T['user_id']) + echo '<span class="pull-right faded-more" data-toggle="tooltip" title="' + .__('Collaborator').'"><i class="icon-eye-open"></i></span>'; + ?></td> + <td nowrap><?php echo Format::datetime($T['lastupdate']); ?></td> <td><?php echo $status; ?></td> <td><a class="truncate <?php if ($flag) { ?> Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket<?php } ?>" style="max-width: 230px;" @@ -187,6 +220,18 @@ if ($results) { ?> ?> </tbody> </table> +<?php +if ($total>0) { + echo '<div>'; + echo __('Page').':'.$pageNav->getPageLinks('tickets', '#tickets').' '; + echo sprintf('<a class="export-csv no-pjax" href="?%s">%s</a>', + Http::build_query(array( + 'id' => $user ? $user->getId(): $org->getId(), + 'a' => 'export', + 't' => 'tickets')), + __('Export')); + echo '</div>'; +} ?> </form> <?php } ?> diff --git a/include/staff/user-view.inc.php b/include/staff/user-view.inc.php index 9299e4e83c20719c547f91fc404fe1983494f17a..837d8a1cda8e34a86fa228c67a53e5832740f8b9 100644 --- a/include/staff/user-view.inc.php +++ b/include/staff/user-view.inc.php @@ -148,7 +148,7 @@ if ($thisstaff->hasPerm(User::PERM_EDIT)) { ?> <div class="clear"></div> <ul class="clean tabs" id="user-view-tabs"> <li class="active"><a href="#tickets"><i - class="icon-list-alt"></i> <?php echo __('User Tickets'); ?></a></li> + class="icon-list-alt"></i> <?php echo __('Tickets'); ?></a></li> <li><a href="#notes"><i class="icon-pushpin"></i> <?php echo __('Notes'); ?></a></li> </ul> diff --git a/scp/orgs.php b/scp/orgs.php index fb224a5047c58d21ee85d931b4092836f086d03e..ad9624b9492a1ece928c29e43cc8032a47c21deb 100644 --- a/scp/orgs.php +++ b/scp/orgs.php @@ -95,7 +95,7 @@ if ($_POST) { default: $errors['err'] = __('Unknown action'); } -} elseif ($_REQUEST['a'] == 'export') { +} elseif (!$org && $_REQUEST['a'] == 'export') { require_once(INCLUDE_DIR.'class.export.php'); $ts = strftime('%Y%m%d'); if (!($token=$_REQUEST['qh'])) @@ -106,7 +106,26 @@ if ($_POST) { $errors['err'] = __('Internal error: Unable to export results'); } -$page = $org? 'org-view.inc.php' : 'orgs.inc.php'; +$page = 'orgs.inc.php'; +if ($org) { + $page = 'org-view.inc.php'; + switch (strtolower($_REQUEST['t'])) { + case 'tickets': + if (isset($_SERVER['HTTP_X_PJAX'])) { + $page='templates/tickets.tmpl.php'; + $pjax_container = @$_SERVER['HTTP_X_PJAX_CONTAINER']; + require(STAFFINC_DIR.$page); + return; + } elseif ($_REQUEST['a'] == 'export' && ($query=$_SESSION[':O:tickets'])) { + $filename = sprintf('%s-tickets-%s.csv', + $org->getName(), strftime('%Y%m%d')); + if (!Export::saveTickets($query, $filename, 'csv')) + $errors['err'] = __('Internal error: Unable to dump query results'); + } + break; + } +} + $nav->setTabActive('users'); require(STAFFINC_DIR.'header.inc.php'); require(STAFFINC_DIR.$page); diff --git a/scp/users.php b/scp/users.php index 8e88d72a1f00e3debded552b1bb3b98918bffd7b..edc6b52fb7a1289670e6f782280f1fb6d91a0ba8 100644 --- a/scp/users.php +++ b/scp/users.php @@ -159,7 +159,7 @@ if ($_POST) { $errors['err'] = __('Unknown action'); break; } -} elseif($_REQUEST['a'] == 'export') { +} elseif(!$user && $_REQUEST['a'] == 'export') { require_once(INCLUDE_DIR.'class.export.php'); $ts = strftime('%Y%m%d'); if (!($token=$_REQUEST['qh'])) @@ -170,7 +170,25 @@ if ($_POST) { $errors['err'] = __('Internal error: Unable to dump query results'); } -$page = $user? 'user-view.inc.php' : 'users.inc.php'; +$page = 'users.inc.php'; +if ($user ) { + $page = 'user-view.inc.php'; + switch (strtolower($_REQUEST['t'])) { + case 'tickets': + if (isset($_SERVER['HTTP_X_PJAX'])) { + $page='templates/tickets.tmpl.php'; + $pjax_container = @$_SERVER['HTTP_X_PJAX_CONTAINER']; + require(STAFFINC_DIR.$page); + return; + } elseif ($_REQUEST['a'] == 'export' && ($query=$_SESSION[':U:tickets'])) { + $filename = sprintf('%s-tickets-%s.csv', + $user->getName(), strftime('%Y%m%d')); + if (!Export::saveTickets($query, $filename, 'csv')) + $errors['err'] = __('Internal error: Unable to dump query results'); + } + break; + } +} $nav->setTabActive('users'); require(STAFFINC_DIR.'header.inc.php');