diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php index 31ce73c4835f1f8384c5fc9f6cc6f4f59459519e..d987b56786a9f603ebdd315405bdeca4bb32ba2d 100644 --- a/include/ajax.tasks.php +++ b/include/ajax.tasks.php @@ -175,7 +175,10 @@ class TasksAjaxAPI extends AjaxController { switch ($action) { case 'assign': - $inc = 'task-assign.tmpl.php'; + $inc = 'assign.tmpl.php'; + $info[':action'] = '#tasks/mass/assign'; + $info[':title'] = sprintf('Assign %s', + _N('selected task', 'selected tasks', $count)); $form = AssignmentForm::instantiate($_POST); if ($_POST && $form->isValid()) { foreach ($_POST['tids'] as $tid) { @@ -198,7 +201,10 @@ class TasksAjaxAPI extends AjaxController { } break; case 'transfer': - $inc = 'task-transfer.tmpl.php'; + $inc = 'transfer.tmpl.php'; + $info[':action'] = '#tasks/mass/transfer'; + $info[':title'] = sprintf('Transfer %s', + _N('selected task', 'selected tasks', $count)); $form = TransferForm::instantiate($_POST); if ($_POST && $form->isValid()) { foreach ($_POST['tids'] as $tid) { @@ -268,7 +274,10 @@ class TasksAjaxAPI extends AjaxController { } break; case 'delete': - $inc = 'task-delete.tmpl.php'; + $inc = 'delete.tmpl.php'; + $info[':action'] = '#tasks/mass/delete'; + $info[':title'] = sprintf('Delete %s', + _N('selected task', 'selected tasks', $count)); $info[':placeholder'] = sprintf(__( 'Optional reason for deleting %s'), _N('selected task', 'selected tasks', $count)); @@ -389,7 +398,7 @@ class TasksAjaxAPI extends AjaxController { $info['dept_id'] = $info['dept_id'] ?: $task->getDeptId(); - include STAFFINC_DIR . 'templates/task-transfer.tmpl.php'; + include STAFFINC_DIR . 'templates/transfer.tmpl.php'; } function assign($tid) { @@ -432,7 +441,7 @@ class TasksAjaxAPI extends AjaxController { $info['error'] = $errors['err'] ?: __('Unable to assign task'); } - include STAFFINC_DIR . 'templates/task-assign.tmpl.php'; + include STAFFINC_DIR . 'templates/assign.tmpl.php'; } function delete($tid) { @@ -475,7 +484,7 @@ class TasksAjaxAPI extends AjaxController { __('Deleted tasks CANNOT be recovered, including any associated attachments.') ); - include STAFFINC_DIR . 'templates/task-delete.tmpl.php'; + include STAFFINC_DIR . 'templates/delete.tmpl.php'; } diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 403b92244d1d3cf89397b96f3c43417e871b6952..8685edf5714bd98d2caeeff5c29611e93b32ffbe 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -369,6 +369,276 @@ class TicketsAjaxAPI extends AjaxController { return $canned->getFormattedResponse($format, $varReplacer); } + function transfer($tid) { + global $thisstaff; + + if (!($ticket=Ticket::lookup($tid))) + Http::response(404, __('No such ticket')); + + if (!$ticket->checkStaffPerm($thisstaff, Ticket::PERM_TRANSFER)) + Http::response(403, __('Permission Denied')); + + $errors = array(); + + $info = array( + ':title' => sprintf(__('Ticket #%s: %s'), + $ticket->getNumber(), + __('Transfer')), + ':action' => sprintf('#tickets/%d/transfer', + $ticket->getId()) + ); + + $form = $ticket->getTransferForm($_POST); + if ($_POST && $form->isValid()) { + if ($ticket->transfer($form, $errors)) { + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('%s successfully'), + sprintf( + __('%s transferred to %s department'), + __('Ticket'), + $ticket->getDept() + ) + ); + Http::response(201, $ticket->getId()); + } + + $form->addErrors($errors); + $info['error'] = $errors['err'] ?: __('Unable to transfer ticket'); + } + + $info['dept_id'] = $info['dept_id'] ?: $ticket->getDeptId(); + + include STAFFINC_DIR . 'templates/transfer.tmpl.php'; + } + + + function assign($tid, $to=null) { + global $thisstaff; + + if (!($ticket=Ticket::lookup($tid))) + Http::response(404, __('No such ticket')); + + if (!$ticket->checkStaffPerm($thisstaff, Ticket::PERM_ASSIGN)) + Http::response(403, __('Permission Denied')); + + $errors = array(); + $info = array( + ':title' => sprintf(__('Ticket #%s: %s'), + $ticket->getNumber(), + $ticket->isAssigned() ? __('Reassign') : __('Assign')), + ':action' => sprintf('#tickets/%d/assign%s', + $ticket->getId(), + ($to ? "/$to": '')), + ); + if ($ticket->isAssigned()) { + $info['notice'] = sprintf(__('%s is currently assigned to %s'), + __('Ticket'), + $ticket->getAssigned()); + } + + $form = $ticket->getAssignmentForm($_POST); + if ($_POST && $form->isValid()) { + if ($ticket->assign($form, $errors)) { + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('%s successfully'), + sprintf( + __('%s assigned to %s'), + __('Ticket'), + $form->getAssignee()) + ); + Http::response(201, $ticket->getId()); + } + + $form->addErrors($errors); + $info['error'] = $errors['err'] ?: __('Unable to assign ticket'); + } + + include STAFFINC_DIR . 'templates/assign.tmpl.php'; + } + + function massProcess($action) { + global $thisstaff; + + $actions = array( + 'transfer' => array( + 'verbed' => __('transferred'), + ), + 'assign' => array( + 'verbed' => __('assigned'), + ), + 'delete' => array( + 'verbed' => __('deleted'), + ), + 'reopen' => array( + 'verbed' => __('reopen'), + ), + 'close' => array( + 'verbed' => __('closed'), + ), + ); + + if (!isset($actions[$action])) + Http::response(404, __('Unknown action')); + + + $info = $errors = $e = array(); + $inc = null; + $i = $count = 0; + if ($_POST) { + if (!$_POST['tids'] || !($count=count($_POST['tids']))) + $errors['err'] = sprintf( + __('You must select at least %s.'), + __('one ticket')); + } else { + $count = $_REQUEST['count']; + } + switch ($action) { + case 'assign': + $inc = 'assign.tmpl.php'; + $info[':action'] = '#tickets/mass/assign'; + $info[':title'] = sprintf('Assign %s', + _N('selected ticket', 'selected tickets', $count)); + $form = AssignmentForm::instantiate($_POST); + if ($_POST && $form->isValid()) { + foreach ($_POST['tids'] as $tid) { + if (($t=Ticket::lookup($tid)) + // Make sure the agent is allowed to + // access and assign the task. + && $t->checkStaffPerm($thisstaff, Ticket::PERM_ASSIGN) + // Do the assignment + && $t->assign($form, $e) + ) + $i++; + } + + if (!$i) { + $info['error'] = sprintf( + __('Unable to %1$s %2$s'), + __('assign'), + _N('selected ticket', 'selected tickets', $count)); + } + } + break; + case 'transfer': + $inc = 'transfer.tmpl.php'; + $info[':action'] = '#tickets/mass/transfer'; + $info[':title'] = sprintf('Transfer %s', + _N('selected ticket', 'selected tickets', $count)); + $form = TransferForm::instantiate($_POST); + if ($_POST && $form->isValid()) { + foreach ($_POST['tids'] as $tid) { + if (($t=Ticket::lookup($tid)) + // Make sure the agent is allowed to + // access and transfer the task. + && $t->checkStaffPerm($thisstaff, Ticket::PERM_TRANSFER) + // Do the transfer + && $t->transfer($form, $e) + ) + $i++; + } + + if (!$i) { + $info['error'] = sprintf( + __('Unable to %1$s %2$s'), + __('transfer'), + _N('selected ticket', 'selected tickets', $count)); + } + } + break; + case 'delete': + $inc = 'delete.tmpl.php'; + $info[':action'] = '#tickets/mass/delete'; + $info[':title'] = sprintf('Delete %s', + _N('selected ticket', 'selected tickets', $count)); + + $info[':placeholder'] = sprintf(__( + 'Optional reason for deleting %s'), + _N('selected ticket', 'selected tickets', $count)); + $info['warn'] = sprintf(__( + 'Are you sure you want to DELETE %s?'), + _N('selected ticket', 'selected tickets', $count)); + $info[':extra'] = sprintf('<strong>%s</strong>', + __('Deleted tickets CANNOT be recovered, including any associated attachments.') + ); + + // Generic permission check. + if (!$thisstaff->hasPerm(Ticket::PERM_DELETE, false)) + $errors['err'] = sprintf( + __('You do not have permission to %s %s'), + __('delete'), + __('tickets')); + + + if ($_POST && !$errors) { + foreach ($_POST['tids'] as $tid) { + if (($t=Ticket::lookup($tid)) + && $t->getDeptId() != $_POST['dept_id'] + && $t->checkStaffPerm($thisstaff, Ticket::PERM_DELETE) + && $t->delete($_POST, $e) + ) + $i++; + } + + if (!$i) { + $info['error'] = sprintf( + __('Unable to %1$s %2$s'), + __('delete'), + _N('selected ticket', 'selected tickets', $count)); + } + } + break; + default: + Http::response(404, __('Unknown action')); + } + + if ($_POST && $i) { + + // Assume success + if ($i==$count) { + $msg = sprintf(__('Successfully %s %s.'), + $actions[$action]['verbed'], + sprintf(__('%1$d %2$s'), + $count, + _N('selected ticket', 'selected tickets', $count)) + ); + $_SESSION['::sysmsgs']['msg'] = $msg; + } else { + $warn = sprintf( + __('%1$d of %2$d %3$s %4$s'), $i, $count, + _N('selected ticket', 'selected tickets', + $count), + $actions[$action]['verbed']); + $_SESSION['::sysmsgs']['warn'] = $warn; + } + Http::response(201, 'processed'); + } elseif($_POST && !isset($info['error'])) { + $info['error'] = $errors['err'] ?: sprintf( + __('Unable to %1$s %2$s'), + __('process'), + _N('selected ticket', 'selected tickets', $count)); + } + + if ($_POST) + $info = array_merge($info, Format::htmlchars($_POST)); + + include STAFFINC_DIR . "templates/$inc"; + // Copy checked tickets to the form. + echo " + <script type=\"text/javascript\"> + $(function() { + $('form#tickets input[name=\"tids[]\"]:checkbox:checked') + .each(function() { + $('<input>') + .prop('type', 'hidden') + .attr('name', 'tids[]') + .val($(this).val()) + .appendTo('form.mass-action'); + }); + }); + </script>"; + + } + function changeTicketStatus($tid, $status, $id=0) { global $thisstaff; diff --git a/include/class.staff.php b/include/class.staff.php index b5d742bac71d659df99225bd96ec8e082fc95b48..8501cb7734abb2c852c4839c2be50de0f49196db 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -853,6 +853,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable { try { db_autocommit(false); + $errors = array(); $records = $importer->importCsv($form->getFields(), $defaults); foreach ($records as $data) { if (!isset($data['email']) || !isset($data['username'])) diff --git a/include/class.task.php b/include/class.task.php index 953bed9c0b391195d7b9100e5ae11cb14434ec97..edf0a0e87e3fef8df2048e4ce24b22b976c9549a 100644 --- a/include/class.task.php +++ b/include/class.task.php @@ -477,10 +477,10 @@ class Task extends TaskModel implements RestrictedAccess, Threadable { return $this->_entries ?: array(); } - function setStatus($status, $comments='') { + function setStatus($status, $comments='', &$errors=array()) { global $thisstaff; - $ecb = null; + $ecb = null; switch($status) { case 'open': if ($this->isOpen()) diff --git a/include/class.ticket.php b/include/class.ticket.php index 6a36a70ffe73807430c17ecd7d84453acc5db7f5..f964145877b2375755d4656f0d420a329fe97543 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -644,15 +644,32 @@ implements RestrictedAccess, Threadable { return $this->team; } + function getAssigneeId() { + + if (!($assignee=$this->getAssignee())) + return null; + + $id = ''; + if ($assignee instanceof Staff) + $id = 's'.$assignee->getId(); + elseif ($assignee instanceof Team) + $id = 't'.$assignee->getId(); + + return $id; + } + function getAssignee() { - if ($staff=$this->getStaff()) - return $staff->getName(); + if (!$this->isOpen() || !$this->isAssigned()) + return false; - if ($team=$this->getTeam()) - return $team->getName(); + if ($this->staff) + return $this->staff; - return ''; + if ($this->team) + return $this->team; + + return null; } function getAssignees() { @@ -818,6 +835,24 @@ implements RestrictedAccess, Threadable { return $this->recipients; } + function getAssignmentForm($source=null, $options=array()) { + + if (!$source) + $source = array('assignee' => array($this->getAssigneeId())); + + $options += array('dept' => $this->getDept()); + + return AssignmentForm::instantiate($source, $options); + } + + function getTransferForm($source=null) { + + if (!$source) + $source = array('dept' => array($this->getDeptId())); + + return TransferForm::instantiate($source); + } + function getDynamicFields($criteria=array()) { $fields = DynamicFormField::objects()->filter(array( @@ -1853,47 +1888,61 @@ implements RestrictedAccess, Threadable { } //Dept Tranfer...with alert.. done by staff - function transfer($deptId, $comments, $alert=true) { - global $cfg, $thisstaff; + function transfer(TransferForm $form, &$errors, $alert=true) { + global $thisstaff, $cfg; - if (!$this->checkStaffPerm($thisstaff, TicketModel::PERM_TRANSFER)) + // Check if staff can do the transfer + if (!$this->checkStaffPerm($thisstaff, Ticket::PERM_TRANSFER)) return false; - $currentDept = $this->getDeptName(); //Current department + $cdept = $this->getDept(); // Current department + $dept = $form->getDept(); // Target department + if (!$dept || !($dept instanceof Dept)) + $errors['dept'] = __('Department selection required'); + elseif ($dept->getid() == $this->getDeptId()) + $errors['dept'] = sprintf( + __('%s already in the department'), __('Ticket')); + else { + $this->dept_id = $dept->getId(); + + // Make sure the new department allows assignment to the + // currently assigned agent (if any) + if ($this->isAssigned() + && ($staff=$this->getStaff()) + && $dept->assignMembersOnly() + && !$dept->isMember($staff) + ) { + $this->staff_id = 0; + } + } - if (!$deptId || !$this->setDeptId($deptId)) + if ($errors || !$this->save(true)) return false; // Reopen ticket if closed if ($this->isClosed()) $this->reopen(); - $dept = $this->getDept(); - // Set SLA of the new department if (!$this->getSLAId() || $this->getSLA()->isTransient()) $this->selectSLAId(); - // Make sure the new department allows assignment to the - // currently assigned agent (if any) - if ($this->isAssigned() - && ($staff=$this->getStaff()) - && $dept->assignMembersOnly() - && !$dept->isMember($staff) - ) { - $this->setStaffId(0); - } + // Log transfer event + $this->logEvent('transferred'); - /*** log the transfer comments as internal note - with alerts disabled - ***/ - $title=sprintf(_S('Ticket transferred from %1$s to %2$s'), - $currentDept, $this->getDeptName()); + // Post internal note if any + $note = $form->getField('comments')->getClean(); + if ($note) { + $title = sprintf(__('%1$s transferred from %2$s to %3$s'), + __('Ticket'), + $cdept->getName(), + $dept->getName()); - if ($comments) { - $note = $this->logNote($title, $comments, $thisstaff, false); + $_errors = array(); + $note = $this->postNote( + array('note' => $note, 'title' => $title), + $_errors, $thisstaff, false); } - $comments = $comments ?: $title; - - $this->logEvent('transferred'); //Send out alerts if enabled AND requested if (!$alert || !$cfg->alertONTransfer()) @@ -1904,7 +1953,7 @@ implements RestrictedAccess, Threadable { && ($msg=$tpl->getTransferAlertMsgTemplate()) ) { $msg = $this->replaceVars($msg->asArray(), - array('comments' => $comments, 'staff' => $thisstaff)); + array('comments' => $note, 'staff' => $thisstaff)); // Recipients $recipients = array(); // Assigned staff or team... if any @@ -1950,6 +1999,7 @@ implements RestrictedAccess, Threadable { $sentlist[] = $staff->getEmail(); } } + return true; } @@ -2007,23 +2057,50 @@ implements RestrictedAccess, Threadable { return true; } - //Assign ticket to staff or team - overloaded ID. - function assign($assignId, $note, $alert=true) { + function assign(AssignmentForm $form, &$errors, $alert=true) { global $thisstaff; - $rv = 0; - $id = preg_replace("/[^0-9]/", "", $assignId); - if ($assignId[0] == 't') { - $rv = $this->assignToTeam($id, $note, $alert); - } - elseif ($assignId[0] == 's' || is_numeric($assignId)) { - $alert = ($alert && $thisstaff && $thisstaff->getId() == $id) - ? false : $alert; //No alerts on self assigned tickets!!! - // We don't care if a team is already assigned to the ticket - - // staff assignment takes precedence - $rv = $this->assignToStaff($id, $note, $alert); + $evd = array(); + $assignee = $form->getAssignee(); + if ($assignee instanceof Staff) { + if ($this->getStaffId() == $assignee->getId()) { + $errors['assignee'] = sprintf(__('%s already assigned to %s'), + __('Ticket'), + __('the agent') + ); + } elseif(!$assignee->isAvailable()) { + $errors['assignee'] = __('Agent is unavailable for assignment'); + } else { + $this->staff_id = $assignee->getId(); + if ($thisstaff && $thisstaff->getId() == $assignee->getId()) + $evd['claim'] = true; + else + $evd['staff'] = array($assignee->getId(), $assignee->getName()); + } + } elseif ($assignee instanceof Team) { + if ($this->getTeamId() == $assignee->getId()) { + $errors['assignee'] = sprintf(__('%s already assigned to %s'), + __('Ticket'), + __('the team') + ); + } else { + $this->team_id = $assignee->getId(); + $evd = array('team' => $assignee->getId()); + } + } else { + $errors['assignee'] = __('Unknown assignee'); } - return $rv; + + if ($errors || !$this->save(true)) + return false; + + $this->logEvent('assigned', $evd); + + $this->onAssign($assignee, + $form->getField('comments')->getClean(), + $alert); + + return true; } // Unassign primary assignee @@ -3431,5 +3508,11 @@ implements RestrictedAccess, Threadable { } } + static function agentActions($agent, $options=array()) { + if (!$agent) + return; + + require STAFFINC_DIR.'templates/tickets-actions.tmpl.php'; + } } ?> diff --git a/include/staff/templates/task-assign.tmpl.php b/include/staff/templates/assign.tmpl.php similarity index 90% rename from include/staff/templates/task-assign.tmpl.php rename to include/staff/templates/assign.tmpl.php index 1c0feca3a503dd433867579484d9751dbe16f846..639460ac42c045840f1ec279de9ec4467357f5cb 100644 --- a/include/staff/templates/task-assign.tmpl.php +++ b/include/staff/templates/assign.tmpl.php @@ -4,8 +4,7 @@ global $cfg; $form = $form ?: AssignmentForm::instantiate($info); if (!$info[':title']) - $info[':title'] = sprintf(__('%s Selected Tasks'), - __('Assign')); + $info[':title'] = __('Assign'); ?> <h3 class="drag-handle"><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> @@ -24,7 +23,7 @@ if ($info['error']) { } -$action = $info[':action'] ?: ('#tasks/mass/assign'); +$action = $info[':action'] ?: ('#'); ?> <div style="display:block; margin:5px;"> <form class="mass-action" method="post" @@ -60,7 +59,7 @@ $action = $info[':action'] ?: ('#tasks/mass/assign'); </span> <span class="buttons pull-right"> <input type="submit" value="<?php - echo $verb ?: __('Submit'); ?>"> + echo $verb ?: __('Assign'); ?>"> </span> </p> </form> diff --git a/include/staff/templates/task-delete.tmpl.php b/include/staff/templates/delete.tmpl.php similarity index 59% rename from include/staff/templates/task-delete.tmpl.php rename to include/staff/templates/delete.tmpl.php index 77cd9df4e6b98e9092be24aca5dadf4d0639fcaf..08d547799a9307d62ed55a41aed5d1f792de0c18 100644 --- a/include/staff/templates/task-delete.tmpl.php +++ b/include/staff/templates/delete.tmpl.php @@ -1,9 +1,8 @@ <?php global $cfg; + if (!$info[':title']) - $info[':title'] = sprintf(__('%s %s'), - __('Delete'), - _N('selected task', 'selected tasks', $count)); + $info[':title'] = __('Delete'); ?> <h3 class="drag-handle"><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> @@ -22,12 +21,13 @@ if ($info['error']) { } -$action = $info[':action'] ?: ('#tasks/mass/delete'); +$action = $info[':action'] ?: ('#'); ?> -<div id="ticket-status" style="display:block; margin:5px;"> -<form method="post" name="delete" id="delete" - action="<?php echo $action; ?>" - class="mass-action"> +<div style="display:block; margin:5px;"> +<form class="mass-action" method="post" + name="delete" + id="delete" + action="<?php echo $action; ?>"> <table width="100%"> <?php if ($info[':extra']) { @@ -42,15 +42,15 @@ $action = $info[':action'] ?: ('#tasks/mass/delete'); <tbody> <tr> <td colspan="2"> - <?php - $placeholder = $info[':placeholder'] ?: __('Optional reason for the deletion'); - ?> - <textarea name="comments" id="comments" - cols="50" rows="3" wrap="soft" style="width:100%" - class="<?php if ($cfg->isRichTextEnabled()) echo 'richtext'; - ?> no-bar" - placeholder="<?php echo $placeholder; ?>"><?php - echo $info['comments']; ?></textarea> + <?php + $placeholder = $info[':placeholder'] ?: __('Optional reason for the deletion'); + ?> + <textarea name="comments" id="comments" + cols="50" rows="3" wrap="soft" style="width:100%" + class="<?php if ($cfg->isRichTextEnabled()) echo 'richtext'; + ?> no-bar small" + placeholder="<?php echo $placeholder; ?>"><?php + echo $info['comments']; ?></textarea> </td> </tr> </tbody> @@ -63,8 +63,8 @@ $action = $info[':action'] ?: ('#tasks/mass/delete'); value="<?php echo __('Cancel'); ?>"> </span> <span class="buttons pull-right"> - <input type="submit" value="<?php - echo $verb ?: __('Submit'); ?>"> + <input type="submit" class="red button" value="<?php + echo $verb ?: __('Delete'); ?>"> </span> </p> </form> diff --git a/include/staff/templates/ticket-status.tmpl.php b/include/staff/templates/ticket-status.tmpl.php index 2caffaae69ea27a2d67548e2d86df093a0fe351f..9731af3b9bf6a75bb7cebac35b92bb0d61b96ec4 100644 --- a/include/staff/templates/ticket-status.tmpl.php +++ b/include/staff/templates/ticket-status.tmpl.php @@ -85,7 +85,7 @@ $action = $info['action'] ?: ('#tickets/status/'. $state); <textarea name="comments" id="comments" cols="50" rows="3" wrap="soft" style="width:100%" class="<?php if ($cfg->isRichTextEnabled()) echo 'richtext'; - ?> no-bar" + ?> no-bar small" placeholder="<?php echo $placeholder; ?>"><?php echo $info['comments']; ?></textarea> </td> diff --git a/include/staff/templates/tickets-actions.tmpl.php b/include/staff/templates/tickets-actions.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..cdc741db90d6adcc8eee43e76b137bc783d264ef --- /dev/null +++ b/include/staff/templates/tickets-actions.tmpl.php @@ -0,0 +1,88 @@ +<?php +// Tickets mass actions based on logged in agent + +if ($agent->canManageTickets()) + echo TicketStatus::status_options(); + +$actions = array(); +if ($agent->hasPerm(Ticket::PERM_ASSIGN, false)) { + $actions += array( + 'assign' => array( + 'icon' => 'icon-user', + 'action' => __('Assign') + )); +} + +if ($agent->hasPerm(Ticket::PERM_TRANSFER, false)) { + $actions += array( + 'transfer' => array( + 'icon' => 'icon-share', + 'action' => __('Transfer') + )); +} + +if ($agent->hasPerm(Ticket::PERM_DELETE, false)) { + $actions += array( + 'delete' => array( + 'class' => 'danger', + 'icon' => 'icon-trash', + 'action' => __('Delete') + )); +} +if ($actions) { + $more = $options['morelabel'] ?: __('More'); + ?> + <span + class="action-button" + data-dropdown="#action-dropdown-moreoptions"> + <i class="icon-caret-down pull-right"></i> + <a class="tickets-action" + href="#moreoptions"><i + class="icon-reorder"></i> <?php + echo $more; ?></a> + </span> + <div id="action-dropdown-moreoptions" + class="action-dropdown anchor-right"> + <ul> + <?php foreach ($actions as $a => $action) { ?> + <li <?php + if ($action['class']) + echo sprintf("class='%s'", $action['class']); ?> > + <a class="no-pjax tickets-action" + <?php + if ($action['dialog']) + echo sprintf("data-dialog-config='%s'", $action['dialog']); + if ($action['redirect']) + echo sprintf("data-redirect='%s'", $action['redirect']); + ?> + href="<?php + echo sprintf('#tickets/mass/%s', $a); ?>" + ><i class="icon-fixed-width <?php + echo $action['icon'] ?: 'icon-tag'; ?>"></i> <?php + echo $action['action']; ?></a> + </li> + <?php + } ?> + </ul> + </div> + <?php + } ?> +<script type="text/javascript"> +$(function() { + $(document).off('.tickets-actions'); + $(document).on('click.tickets-actions', 'a.tickets-action', function(e) { + e.preventDefault(); + var count = checkbox_checker($('form#tickets'), 1); + if (count) { + var url = 'ajax.php/' + +$(this).attr('href').substr(1) + +'?count='+count + +'&_uid='+new Date().getTime(); + $.dialog(url, [201], function (xhr) { + $.pjax.reload('#pjax-container'); + }); + } + return false; + }); +}); +</script> diff --git a/include/staff/templates/task-transfer.tmpl.php b/include/staff/templates/transfer.tmpl.php similarity index 89% rename from include/staff/templates/task-transfer.tmpl.php rename to include/staff/templates/transfer.tmpl.php index b199be14fcfcf90118081d4f4bb09d6861624e10..8472f2179f5a994bbb3f949b37a3664d0cf8813a 100644 --- a/include/staff/templates/task-transfer.tmpl.php +++ b/include/staff/templates/transfer.tmpl.php @@ -2,10 +2,6 @@ global $cfg; $form = $form ?: TransferForm::instantiate($info); - -if (!$info[':title']) - $info[':title'] = sprintf(__('%s Selected Tasks'), - __('Tranfer')); ?> <h3 class="drag-handle"><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> @@ -23,8 +19,7 @@ if ($info['error']) { $info['notice']); } - -$action = $info[':action'] ?: ('#tasks/mass/transfer'); +$action = $info[':action'] ?: ('#'); ?> <div style="display:block; margin:5px;"> <form method="post" name="transfer" id="transfer" @@ -59,7 +54,7 @@ $action = $info[':action'] ?: ('#tasks/mass/transfer'); </span> <span class="buttons pull-right"> <input type="submit" value="<?php - echo $verb ?: __('Submit'); ?>"> + echo $verb ?: __('Transfer'); ?>"> </span> </p> </form> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 967228946c4fcbb89b9b46ba7648fb67f5971dbf..4c6be3826de30dfab16099c45f2096361dc47b9d 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -71,16 +71,36 @@ if($ticket->isOverdue()) <a class="action-button pull-right" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=edit"><i class="icon-edit"></i> <?php echo __('Edit'); ?></a> <?php - } - if ($ticket->isOpen() - && !$ticket->isAssigned() - && $role->hasPerm(TicketModel::PERM_ASSIGN) - && $ticket->getDept()->isMember($thisstaff)) {?> - <a id="ticket-claim" class="action-button pull-right confirm-action" href="#claim"><i class="icon-user"></i> <?php - echo __('Claim'); ?></a> + } ?> + <?php + // Transfer + if ($role->hasPerm(TicketModel::PERM_TRANSFER)) {?> + <a class="ticket-action action-button pull-right" id="ticket-transfer" + href="#tickets/<?php echo $ticket->getId(); ?>/transfer"><i class="icon-share"></i> <?php + echo __('Transfer'); ?></a> + <?php + } ?> <?php - }?> + // Assign + if ($role->hasPerm(TicketModel::PERM_ASSIGN)) {?> + <span class="action-button pull-right" data-dropdown="#action-dropdown-assign"> + <i class="icon-caret-down pull-right"></i> + <a class="ticket-action" id="ticket-assign" href="#tickets/<?php echo $ticket->getId(); ?>/assign"><i class="icon-user"></i> <?php + echo $ticket->isAssigned() ? __('Assign') : __('Reassign'); ?></a> + </span> + <div id="action-dropdown-assign" class="action-dropdown anchor-right"> + <ul> + <li><a class="no-pjax ticket-action" href="#tickets/<?php echo $ticket->getId(); ?>/assign/<?php echo $thisstaff->getId(); ?>"><i + class="icon-chevron-sign-down"></i> <?php echo __('Claim'); ?></a> + <li><a class="no-pjax ticket-action" href="#tickets/<?php echo $ticket->getId(); ?>/assign/agents"><i + class="icon-user"></i> <?php echo __('Agent'); ?></a> + <li><a class="no-pjax ticket-action" href="#tickets/<?php echo $ticket->getId(); ?>/assign/teams"><i + class="icon-group"></i> <?php echo __('Team'); ?></a> + </ul> + </div> + <?php + } ?> <span class="action-button pull-right" data-dropdown="#action-dropdown-print"> <i class="icon-caret-down pull-right"></i> <a id="ticket-print" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print"><i class="icon-print"></i> <?php @@ -453,17 +473,6 @@ $tcount = $ticket->getThreadEntries($types)->count(); <?php } ?> <li><a href="#note"><?php echo __('Post Internal Note');?></a></li> - <?php - if ($role->hasPerm(TicketModel::PERM_TRANSFER)) { ?> - <li><a href="#transfer"><?php echo __('Department Transfer');?></a></li> - <?php - } - - if ($role->hasPerm(TicketModel::PERM_ASSIGN)) { ?> - <li><a href="#assign"><?php - echo $ticket->isAssigned()?__('Reassign Ticket'):__('Assign Ticket'); ?></a></li> - <?php - } ?> </ul> <?php if ($role->hasPerm(TicketModel::PERM_REPLY)) { ?> @@ -744,168 +753,6 @@ $tcount = $ticket->getThreadEntries($types)->count(); <input class="" type="reset" value="<?php echo __('Reset');?>"> </p> </form> - <?php - if ($role->hasPerm(TicketModel::PERM_TRANSFER)) { ?> - <form id="transfer" class="hidden tab_content spellcheck" action="tickets.php?id=<?php - echo $ticket->getId(); ?>#transfer" name="transfer" method="post" enctype="multipart/form-data"> - <?php csrf_token(); ?> - <input type="hidden" name="ticket_id" value="<?php echo $ticket->getId(); ?>"> - <input type="hidden" name="a" value="transfer"> - <table width="100%" border="0" cellspacing="0" cellpadding="3"> - <?php - if($errors['transfer']) { - ?> - <tr> - <td width="120"> </td> - <td class="error"><?php echo $errors['transfer']; ?></td> - </tr> - <?php - } ?> - <tr> - <td width="120"> - <label for="deptId"><strong><?php echo __('Department');?>:</strong></label> - </td> - <td> - <?php - echo sprintf('<span class="faded">'.__('Ticket is currently in <b>%s</b> department.').'</span>', $ticket->getDeptName()); - ?> - <br> - <select id="deptId" name="deptId" data-quick-add="department"> - <option value="0" selected="selected">— <?php echo __('Select Target Department');?> —</option> - <?php - if($depts=Dept::getDepartments()) { - foreach($depts as $id =>$name) { - if($id==$ticket->getDeptId()) continue; - echo sprintf('<option value="%d" %s>%s</option>', - $id, ($info['deptId']==$id)?'selected="selected"':'',$name); - } - } - ?> - <option value="0" data-quick-add>— <?php echo __('Add New'); ?> —</option> - </select> <span class='error'>* <?php echo $errors['deptId']; ?></span> - </td> - </tr> - <tr> - <td width="120" style="vertical-align:top"> - <label><strong><?php echo __('Comments'); ?>:</strong><span class='error'> *</span></label> - </td> - <td> - <textarea name="transfer_comments" id="transfer_comments" - placeholder="<?php echo __('Enter reasons for the transfer'); ?>" - class="<?php if ($cfg->isRichTextEnabled()) echo 'richtext'; - ?> no-bar" cols="80" rows="7" wrap="soft"><?php - echo $info['transfer_comments']; ?></textarea> - <span class="error"><?php echo $errors['transfer_comments']; ?></span> - </td> - </tr> - </table> - <p style="padding-left:165px;"> - <input class="save pending" type="submit" value="<?php echo __('Transfer');?>"> - <input class="" type="reset" value="<?php echo __('Reset');?>"> - </p> - </form> - <?php - } ?> - <?php - if ($role->hasPerm(TicketModel::PERM_ASSIGN)) { ?> - <form id="assign" class="hidden tab_content spellcheck" action="tickets.php?id=<?php - echo $ticket->getId(); ?>#assign" name="assign" method="post" enctype="multipart/form-data"> - <?php csrf_token(); ?> - <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> - <input type="hidden" name="a" value="assign"> - <table style="width:100%" border="0" cellspacing="0" cellpadding="3"> - - <?php - if($errors['assign']) { - ?> - <tr> - <td width="120"> </td> - <td class="error"><?php echo $errors['assign']; ?></td> - </tr> - <?php - } ?> - <tr> - <td width="120" style="vertical-align:top"> - <label for="assignId"><strong><?php echo __('Assignee');?>:</strong></label> - </td> - <td> - <select id="assignId" name="assignId"> - <option value="0" selected="selected">— <?php echo __('Select an Agent OR a Team');?> —</option> - <?php - if ($ticket->isOpen() - && !$ticket->isAssigned() - && $ticket->getDept()->isMember($thisstaff)) - echo sprintf('<option value="%d">'.__('Claim Ticket (comments optional)').'</option>', $thisstaff->getId()); - - $sid=$tid=0; - - if ($dept->assignMembersOnly()) - $users = $dept->getAvailableMembers(); - else - $users = Staff::getAvailableStaffMembers(); - - if ($users) { - echo '<OPTGROUP label="'.sprintf(__('Agents (%d)'), count($users)).'">'; - $staffId=$ticket->isAssigned()?$ticket->getStaffId():0; - foreach($users as $id => $name) { - if($staffId && $staffId==$id) - continue; - - if (!is_object($name)) - $name = new AgentsName($name); - - $k="s$id"; - echo sprintf('<option value="%s" %s>%s</option>', - $k,(($info['assignId']==$k)?'selected="selected"':''), $name); - } - echo '</OPTGROUP>'; - } - - if(($teams=Team::getActiveTeams())) { - echo '<OPTGROUP label="'.sprintf(__('Teams (%d)'), count($teams)).'">'; - $teamId=(!$sid && $ticket->isAssigned())?$ticket->getTeamId():0; - foreach($teams as $id => $name) { - if($teamId && $teamId==$id) - continue; - - $k="t$id"; - echo sprintf('<option value="%s" %s>%s</option>', - $k,(($info['assignId']==$k)?'selected="selected"':''),$name); - } - echo '</OPTGROUP>'; - } - ?> - </select> <span class='error'>* <?php echo $errors['assignId']; ?></span> - <?php - if ($ticket->isAssigned() && $ticket->isOpen()) { ?> - <div class="faded"><?php echo sprintf(__('Ticket is currently assigned to %s'), - sprintf('<b>%s</b>', $ticket->getAssignee())); ?></div> <?php - } elseif ($ticket->isClosed()) { ?> - <div class="faded"><?php echo __('Assigning a closed ticket will <b>reopen</b> it!'); ?></div> - <?php } ?> - </td> - </tr> - <tr> - <td width="120" style="vertical-align:top"> - <label><strong><?php echo __('Comments');?>:</strong><span class='error'> </span></label> - </td> - <td> - <textarea name="assign_comments" id="assign_comments" - cols="80" rows="7" wrap="soft" - placeholder="<?php echo __('Enter reasons for the assignment or instructions for assignee'); ?>" - class="<?php if ($cfg->isRichTextEnabled()) echo 'richtext'; - ?> no-bar"><?php echo $info['assign_comments']; ?></textarea> - <span class="error"><?php echo $errors['assign_comments']; ?></span><br> - </td> - </tr> - </table> - <p style="padding-left:165px;"> - <input class="save pending" type="submit" value="<?php echo $ticket->isAssigned()?__('Reassign'):__('Assign'); ?>"> - <input class="" type="reset" value="<?php echo __('Reset');?>"> - </p> - </form> - <?php - } ?> </div> </div> </div> diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index 73554b33a5ef91d3fb7ac7153335adcc5c9796ac..0978acfdd3444a135ab03a5c7cf0613b589b7b80 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -369,15 +369,7 @@ return false;"> <div class="pull-right flush-right"> <?php if ($count) { - if ($thisstaff->canManageTickets()) { - echo TicketStatus::status_options(); - } - if ($thisstaff->hasPerm(TicketModel::PERM_DELETE, false)) { ?> - <a id="tickets-delete" class="red button action-button tickets-action" - href="#tickets/status/delete"><i - class="icon-trash"></i> <?php echo __('Delete'); ?></a> - <?php - } + Ticket::agentActions($thisstaff, array('status' => $status)); }?> </div> </div> @@ -587,7 +579,7 @@ $(function() { +'?count='+count +'&_uid='+new Date().getTime(); $.dialog(url, [201], function (xhr) { - window.location.href = window.location.href; + $.pjax.reload('#pjax-container'); }); } return false; diff --git a/scp/ajax.php b/scp/ajax.php index c20995c61baabea70fe934dd849ee0f96d0b40b5..78682a36315846be8c87295a22d80d1ef521def6 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -161,6 +161,10 @@ $dispatcher = patterns('', url_get('^(?P<tid>\d+)/tasks/(?P<id>\d+)/view$', 'task'), url_post('^(?P<tid>\d+)/tasks/(?P<id>\d+)$', 'task'), url_get('^lookup', 'lookup'), + url_get('^mass/(?P<action>[\w.]+)', 'massProcess'), + url_post('^mass/(?P<action>[\w.]+)', 'massProcess'), + url('^(?P<tid>\d+)/transfer$', 'transfer'), + url('^(?P<tid>\d+)/assign(?:/(?P<to>\w+))?$', 'assign'), url('^search', patterns('ajax.search.php:SearchAjaxAPI', url_get('^$', 'getAdvancedSearchDialog'), url_post('^$', 'doSearch'), diff --git a/scp/tickets.php b/scp/tickets.php index ae4f15349c25064c85de275a402908741cc74ab6..bc04ec0c654134861c0842dbc0124e224a6feddc 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -123,83 +123,6 @@ if($_POST && !$errors): $errors['err']=__('Unable to post the reply. Correct the errors below and try again!'); } break; - case 'transfer': /** Transfer ticket **/ - //Check permission - if(!$role->hasPerm(TicketModel::PERM_TRANSFER)) - $errors['err']=$errors['transfer'] = __('Action Denied. You are not allowed to transfer tickets.'); - else { - - //Check target dept. - if(!$_POST['deptId']) - $errors['deptId'] = __('Select department'); - elseif($_POST['deptId']==$ticket->getDeptId()) - $errors['deptId'] = __('Ticket already in the department'); - elseif(!($dept=Dept::lookup($_POST['deptId']))) - $errors['deptId'] = __('Unknown or invalid department'); - - //Transfer message - required. - if(!$_POST['transfer_comments']) - $errors['transfer_comments'] = __('Transfer comments required'); - elseif(strlen($_POST['transfer_comments'])<5) - $errors['transfer_comments'] = __('Transfer comments too short!'); - - //If no errors - them attempt the transfer. - if(!$errors && $ticket->transfer($_POST['deptId'], $_POST['transfer_comments'])) { - $msg = sprintf(__('Ticket transferred successfully to %s'), - $ticket->getDept()->getFullName()); - //Check to make sure the staff still has access to the ticket - if(!$ticket->checkStaffPerm($thisstaff)) - $ticket=null; - - } elseif(!$errors['transfer']) { - $errors['err'] = __('Unable to complete the ticket transfer'); - $errors['transfer']=__('Correct the error(s) below and try again!'); - } - } - break; - case 'assign': - - if(!$role->hasPerm(TicketModel::PERM_ASSIGN)) - $errors['err']=$errors['assign'] = __('Action Denied. You are not allowed to assign/reassign tickets.'); - else { - - $id = preg_replace("/[^0-9]/", "",$_POST['assignId']); - $claim = (is_numeric($_POST['assignId']) && $_POST['assignId']==$thisstaff->getId()); - $dept = $ticket->getDept(); - - if (!$_POST['assignId'] || !$id) - $errors['assignId'] = __('Select assignee'); - elseif ($_POST['assignId'][0]!='s' && $_POST['assignId'][0]!='t' && !$claim) - $errors['assignId']= sprintf('%s - %s', - __('Invalid assignee'), - __('get technical support')); - elseif ($_POST['assignId'][0]=='s' - && $dept->assignMembersOnly() - && !$dept->isMember($id)) { - $errors['assignId'] = sprintf('%s. %s', - __('Invalid assignee'), - __('Must be department member')); - } elseif($ticket->isAssigned()) { - if($_POST['assignId'][0]=='s' && $id==$ticket->getStaffId()) - $errors['assignId']=__('Ticket already assigned to the agent.'); - elseif($_POST['assignId'][0]=='t' && $id==$ticket->getTeamId()) - $errors['assignId']=__('Ticket already assigned to the team.'); - } - - if(!$errors && $ticket->assign($_POST['assignId'], $_POST['assign_comments'], !$claim)) { - if($claim) { - $msg = __('Ticket is NOW assigned to you!'); - } else { - $msg=sprintf(__('Ticket assigned successfully to %s'), $ticket->getAssigned()); - $ticket->releaseLock($thisstaff->getId()); - $ticket=null; - } - } elseif(!$errors['assign']) { - $errors['err'] = __('Unable to complete the ticket assignment'); - $errors['assign'] = __('Correct the error(s) below and try again!'); - } - } - break; case 'postnote': /* Post Internal Note */ $vars = $_POST; $attachments = $note_form->getField('attachments')->getClean();