diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php index d987b56786a9f603ebdd315405bdeca4bb32ba2d..6c4c159244ab892151e2d385742fce5896dd9485 100644 --- a/include/ajax.tasks.php +++ b/include/ajax.tasks.php @@ -136,8 +136,8 @@ class TasksAjaxAPI extends AjaxController { include STAFFINC_DIR . 'templates/task-edit.tmpl.php'; } - function massProcess($action) { - global $thisstaff; + function massProcess($action, $w=null) { + global $thisstaff, $cfg; $actions = array( 'transfer' => array( @@ -146,6 +146,9 @@ class TasksAjaxAPI extends AjaxController { 'assign' => array( 'verbed' => __('assigned'), ), + 'claim' => array( + 'verbed' => __('claimed'), + ), 'delete' => array( 'verbed' => __('deleted'), ), @@ -174,20 +177,112 @@ class TasksAjaxAPI extends AjaxController { } switch ($action) { + case 'claim': + $w = 'me'; case 'assign': $inc = 'assign.tmpl.php'; - $info[':action'] = '#tasks/mass/assign'; + $info[':action'] = "#tasks/mass/assign/$w"; $info[':title'] = sprintf('Assign %s', _N('selected task', 'selected tasks', $count)); + $form = AssignmentForm::instantiate($_POST); - if ($_POST && $form->isValid()) { + + $assignCB = function($t, $f, $e) { + return $t->assign($f, $e); + }; + + $assignees = null; + switch ($w) { + case 'agents': + $depts = array(); + $tids = $_POST['tids'] ?: array_filter( + explode(',', @$_REQUEST['tids'] ?: '')); + if ($tids) { + $tasks = Task::objects() + ->distinct('dept_id') + ->filter(array('id__in' => $tids)); + $depts = $tasks->values_flat('dept_id'); + } + + $members = Staff::objects() + ->distinct('staff_id') + ->filter(array( + 'onvacation' => 0, + 'isactive' => 1, + ) + ); + + if ($depts) { + $members->filter(Q::any( array( + 'dept_id__in' => $depts, + Q::all(array( + 'dept_access__dept__id__in' => $depts, + Q::not(array('dept_access__dept__flags__hasbit' + => Dept::FLAG_ASSIGN_MEMBERS_ONLY)) + )) + ))); + } + + switch ($cfg->getAgentNameFormat()) { + case 'last': + case 'lastfirst': + case 'legal': + $members->order_by('lastname', 'firstname'); + break; + + default: + $members->order_by('firstname', 'lastname'); + } + + $prompt = __('Select an Agent'); + $assignees = array(); + foreach ($members as $member) + $assignees['s'.$member->getId()] = $member->getName(); + + if (!$assignees) + $info['warn'] = __('No agents available for assignment'); + break; + case 'teams': + $assignees = array(); + $prompt = __('Select a Team'); + foreach (Team::getActiveTeams() as $id => $name) + $assignees['t'.$id] = $name; + + if (!$assignees) + $info['warn'] = __('No teams available for assignment'); + break; + case 'me': + $info[':action'] = '#tasks/mass/claim'; + $info[':title'] = sprintf('Claim %s', + _N('selected task', 'selected tasks', $count)); + $info['warn'] = sprintf( + __('Are you sure you want to CLAIM %s?'), + _N('selected task', 'selected tasks', $count)); + $verb = sprintf('%s, %s', __('Yes'), __('Claim')); + $id = sprintf('s%s', $thisstaff->getId()); + $assignees = array($id => $thisstaff->getName()); + $vars = $_POST ?: array('assignee' => array($id)); + $form = ClaimForm::instantiate($vars); + $assignCB = function($t, $f, $e) { + return $t->claim($f, $e); + }; + break; + } + + if ($assignees != null) + $form->setAssignees($assignees); + + if ($prompt && ($f=$form->getField('assignee'))) + $f->configure('prompt', $prompt); + + if ($_POST && $form->isValid() && !$errors) { foreach ($_POST['tids'] as $tid) { if (($t=Task::lookup($tid)) // Make sure the agent is allowed to // access and assign the task. && $t->checkStaffPerm($thisstaff, Task::PERM_ASSIGN) - // Do the transfer - && $t->assign($form, $e) + // Do the assignment + && $assignCB($t, $form, $e) ) $i++; } @@ -230,19 +325,34 @@ class TasksAjaxAPI extends AjaxController { $info['status'] = 'open'; case 'close': $inc = 'task-status.tmpl.php'; + $info[':action'] = "#tasks/mass/$action"; $info['status'] = $info['status'] ?: 'closed'; - $perm = ''; + $perm = $action = ''; switch ($info['status']) { case 'open': // If an agent can create a task then they're allowed to // reopen closed ones. $perm = Task::PERM_CREATE; + $info[':title'] = sprintf('Reopen %s', + _N('selected task', 'selected tasks', $count)); + + $info['warn'] = sprintf(__('Are you sure you want to %s?'), + sprintf(__('REOPEN %s'), + _N('selected task', 'selected tasks', $count) + )); break; case 'closed': $perm = Task::PERM_CLOSE; + $info[':title'] = sprintf('Close %s', + _N('selected task', 'selected tasks', $count)); + + $info['warn'] = sprintf(__('Are you sure you want to %s?'), + sprintf(__('CLOSE %s'), + _N('selected task', 'selected tasks', $count) + )); break; default: - $errors['err'] = __('Unknown action'); + Http::response(404, __('Unknown action')); } // Check generic permissions -- department specific permissions // will be checked below. @@ -401,13 +511,15 @@ class TasksAjaxAPI extends AjaxController { include STAFFINC_DIR . 'templates/transfer.tmpl.php'; } - function assign($tid) { + function assign($tid, $target=null) { global $thisstaff; if (!($task=Task::lookup($tid))) Http::response(404, __('No such task')); - if (!$task->checkStaffPerm($thisstaff, Task::PERM_ASSIGN)) + if (!$task->checkStaffPerm($thisstaff, Task::PERM_ASSIGN) + || !($form=$task->getAssignmentForm($_POST, array( + 'target' => $target)))) Http::response(403, __('Permission Denied')); $errors = array(); @@ -415,8 +527,9 @@ class TasksAjaxAPI extends AjaxController { ':title' => sprintf(__('Task #%s: %s'), $task->getNumber(), $task->isAssigned() ? __('Reassign') : __('Assign')), - ':action' => sprintf('#tasks/%d/assign', - $task->getId()), + ':action' => sprintf('#tasks/%d/assign%s', + $task->getId(), + $target ? "/$target" : ''), ); if ($task->isAssigned()) { $info['notice'] = sprintf(__('%s is currently assigned to %s'), @@ -424,7 +537,6 @@ class TasksAjaxAPI extends AjaxController { $task->getAssigned()); } - $form = $task->getAssignmentForm($_POST); if ($_POST && $form->isValid()) { if ($task->assign($form, $errors)) { $_SESSION['::sysmsgs']['msg'] = sprintf( @@ -444,6 +556,64 @@ class TasksAjaxAPI extends AjaxController { include STAFFINC_DIR . 'templates/assign.tmpl.php'; } + function claim($tid) { + + global $thisstaff; + + if (!($task=Task::lookup($tid))) + Http::response(404, __('No such task')); + + // Check for premissions and such + if (!$task->checkStaffPerm($thisstaff, Task::PERM_ASSIGN) + || !($form = $task->getClaimForm($_POST))) + Http::response(403, __('Permission Denied')); + + $errors = array(); + $info = array( + ':title' => sprintf(__('Task #%s: %s'), + $task->getNumber(), + __('Claim')), + ':action' => sprintf('#tasks/%d/claim', + $task->getId()), + + ); + + if ($task->isAssigned()) { + if ($task->getStaffId() == $thisstaff->getId()) + $assigned = __('you'); + else + $assigneed = $task->getAssigned(); + + $info['error'] = sprintf(__('%s is currently assigned to <b>%s</b>'), + __('This task'), + $assigned); + } else { + $info['warn'] = sprintf(__('Are you sure you want to CLAIM %s?'), + __('this task')); + } + + if ($_POST && $form->isValid()) { + if ($task->claim($form, $errors)) { + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('%s successfully'), + sprintf( + __('%s assigned to %s'), + __('Task'), + __('you')) + ); + Http::response(201, $task->getId()); + } + + $form->addErrors($errors); + $info['error'] = $errors['err'] ?: __('Unable to claim task'); + } + + $verb = sprintf('%s, %s', __('Yes'), __('Claim')); + + include STAFFINC_DIR . 'templates/assign.tmpl.php'; + + } + function delete($tid) { global $thisstaff; @@ -487,6 +657,71 @@ class TasksAjaxAPI extends AjaxController { include STAFFINC_DIR . 'templates/delete.tmpl.php'; } + function changeStatus($tid, $status) { + global $thisstaff; + $statuses = array( + 'open' => __('Reopen'), + 'closed' => __('Close'), + ); + + if(!($task=Task::lookup($tid)) || !$task->checkStaffPerm($thisstaff)) + Http::response(404, __('No such task')); + + $perm = null; + $info = $errors = array(); + switch ($status) { + case 'open': + $perm = Task::PERM_CREATE; + $info = array( + ':title' => sprintf(__('Reopen Task #%s'), + $task->getNumber()), + ':action' => sprintf('#tasks/%d/reopen', + $task->getId()) + ); + break; + case 'closed': + $perm = Task::PERM_CLOSE; + $info = array( + ':title' => sprintf(__('Close Task #%s'), + $task->getNumber()), + ':action' => sprintf('#tasks/%d/close', + $task->getId()) + ); + + if (($m=$task->isCloseable()) !== true) + $errors['err'] = $info['error'] = $m; + else + $info['warn'] = sprintf(__('Are you sure you want to %s?'), + sprintf(__('change status of %s'), __('this task'))); + break; + default: + Http::response(404, __('Unknown status')); + } + + if (!$errors && (!$perm || !$task->checkStaffPerm($thisstaff, $perm))) + $errors['err'] = sprintf( + __('You do not have permission to %s %s'), + $statuses[$status], __('tasks')); + + if ($_POST && !$errors) { + if ($task->setStatus($status, $_POST['comments'], $errors)) + Http::response(201, 0); + + $info['error'] = $errors['err'] ?: __('Unable to change status of the task'); + } + + $info['status'] = $status; + + include STAFFINC_DIR . 'templates/task-status.tmpl.php'; + } + + function reopen($tid) { + return $this->changeStatus($tid, 'open'); + } + + function close($tid) { + return $this->changeStatus($tid, 'closed'); + } function task($tid) { global $thisstaff; diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index fbb8425bdaab9f65da2b833974ab2f8f62e10bd8..95e124a1fcd5dad3c4d008c2b14340a3c7660eba 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -496,7 +496,7 @@ class TicketsAjaxAPI extends AjaxController { __('This ticket'), $assigned); } else { - $info['warn'] = sprintf(__('Are you sure you want to claim %s?'), + $info['warn'] = sprintf(__('Are you sure you want to CLAIM %s?'), __('this ticket')); } @@ -639,8 +639,9 @@ class TicketsAjaxAPI extends AjaxController { $info[':action'] = '#tickets/mass/claim'; $info[':title'] = sprintf('Claim %s', _N('selected ticket', 'selected tickets', $count)); - $info['warn'] = sprintf(__('Are you sure you want to claim %s?'), - _N('selected ticket', 'selected tickets', $count)); + $info['warn'] = sprintf( + __('Are you sure you want to CLAIM %s?'), + _N('selected ticket', 'selected tickets', $count)); $verb = sprintf('%s, %s', __('Yes'), __('Claim')); $id = sprintf('s%s', $thisstaff->getId()); $assignees = array($id => $thisstaff->getName()); @@ -1084,9 +1085,10 @@ class TicketsAjaxAPI extends AjaxController { $count = $_REQUEST['count'] ?: ($_REQUEST['tids'] ? count($_REQUEST['tids']) : 0); - $info['title'] = sprintf(__('%1$s Tickets — %2$d selected'), - TicketStateField::getVerb($state), - $count); + $info['title'] = sprintf(__('Change Status — %1$d %2$s selected'), + $count, + _N('ticket', 'tickets', $count) + ); if (!strcasecmp($state, 'deleted')) { diff --git a/include/class.forms.php b/include/class.forms.php index d642209565a79a0cdc468e9e41b8c448608ebd1f..3b6d129643a8baf1b9d992964b1036df898ab10a 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -2215,7 +2215,7 @@ FormField::addFieldTypes(/*@trans*/ 'Dynamic Fields', function() { class AssigneeField extends ChoiceField { - var $_choices = array(); + var $_choices = null; var $_criteria = null; function getWidget() { diff --git a/include/class.task.php b/include/class.task.php index c3b23bb694c92aea9da74c3e48ac7d81069929ae..3de800bce36b9322b197c567af6825f74970e6b0 100644 --- a/include/class.task.php +++ b/include/class.task.php @@ -433,15 +433,62 @@ class Task extends TaskModel implements RestrictedAccess, Threadable { return $this->form; } - function getAssignmentForm($source=null) { + function getAssignmentForm($source=null, $options=array()) { + $prompt = $assignee = ''; + // Possible assignees + $assignees = array(); + switch (strtolower($options['target'])) { + case 'agents': + $dept = $this->getDept(); + foreach ($dept->getAssignees() as $member) + $assignees['s'.$member->getId()] = $member; + + if (!$source && $this->isOpen() && $this->staff) + $assignee = sprintf('s%d', $this->staff->getId()); + $prompt = __('Select an Agent'); + break; + case 'teams': + if (($teams = Team::getActiveTeams())) + foreach ($teams as $id => $name) + $assignees['t'.$id] = $name; + + if (!$source && $this->isOpen() && $this->team) + $assignee = sprintf('t%d', $this->team->getId()); + $prompt = __('Select a Team'); + break; + } + // Default to current assignee if source is not set if (!$source) - $source = array('assignee' => array($this->getAssigneeId())); + $source = array('assignee' => array($assignee)); + + $form = AssignmentForm::instantiate($source, $options); + + if ($assignees) + $form->setAssignees($assignees); + + if ($prompt && ($f=$form->getField('assignee'))) + $f->configure('prompt', $prompt); + + + return $form; + } + + function getClaimForm($source=null, $options=array()) { + global $thisstaff; + + $id = sprintf('s%d', $thisstaff->getId()); + if(!$source) + $source = array('assignee' => array($id)); + + $form = ClaimForm::instantiate($source, $options); + $form->setAssignees(array($id => $thisstaff->getName())); + + return $form; - return AssignmentForm::instantiate($source, - array('dept' => $this->getDept())); } + function getTransferForm($source=null) { if (!$source) @@ -571,6 +618,55 @@ class Task extends TaskModel implements RestrictedAccess, Threadable { $this->getThread()->getEvents()->log($this, $state, $data, $user, $annul); } + function claim(ClaimForm $form, &$errors) { + global $thisstaff; + + $dept = $this->getDept(); + $assignee = $form->getAssignee(); + if (!($assignee instanceof Staff) + || !$thisstaff + || $thisstaff->getId() != $assignee->getId()) { + $errors['err'] = __('Unknown assignee'); + } elseif (!$assignee->isAvailable()) { + $errors['err'] = __('Agent is unavailable for assignment'); + } elseif ($dept->assignMembersOnly() && !$dept->isMember($assignee)) { + $errors['err'] = __('Permission denied'); + } + + if ($errors) + return false; + + return $this->assignToStaff($assignee, $form->getComments(), false); + } + + function assignToStaff($staff, $note, $alert=true) { + + if(!is_object($staff) && !($staff = Staff::lookup($staff))) + return false; + + if (!$staff->isAvailable()) + return false; + + $this->staff_id = $staff->getId(); + + if (!$this->save()) + return false; + + $this->onAssignment($staff, $note, $alert); + + global $thisstaff; + $data = array(); + if ($thisstaff && $staff->getId() == $thisstaff->getId()) + $data['claim'] = true; + else + $data['staff'] = $staff->getId(); + + $this->logEvent('assigned', $data); + + return true; + } + + function assign(AssignmentForm $form, &$errors, $alert=true) { global $thisstaff; @@ -706,7 +802,7 @@ class Task extends TaskModel implements RestrictedAccess, Threadable { else $this->dept_id = $dept->getId(); - if ($errors || !$this->save()) + if ($errors || !$this->save(true)) return false; // Log transfer event @@ -1430,7 +1526,7 @@ extends AbstractForm { $mode = @$this->options['mode']; if ($mode && $mode == 'edit') { unset($fields['dept_id']); - unset($fields['staff_id']); + unset($fields['assignee']); } return $fields; diff --git a/include/class.ticket.php b/include/class.ticket.php index 61da381c5b641bf1895dc4d69e5462bd84eb4f67..f1aeef7e9fe2c39a5586a663e6d4e386ce2747e9 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -885,7 +885,7 @@ implements RestrictedAccess, Threadable { $assignees['t'.$id] = $name; if (!$source && $this->isOpen() && $this->team) - $assignee = sprintf('s%d', $this->team->getId()); + $assignee = sprintf('t%d', $this->team->getId()); $prompt = __('Select a Team'); break; } diff --git a/include/staff/tasks.inc.php b/include/staff/tasks.inc.php index 9c96ad5bb3ac752dfc173482fbf9dfa71bfdc43f..f144ab189819531c524684fbf37f9f30b32d68f4 100644 --- a/include/staff/tasks.inc.php +++ b/include/staff/tasks.inc.php @@ -383,7 +383,7 @@ if ($thisstaff->hasPerm(Task::PERM_DELETE, false)) { </tfoot> </table> <?php - if ($total>0) { //if we actually had any tickets returned. + if ($total>0) { //if we actually had any tasks returned. echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' '; echo sprintf('<a class="export-csv no-pjax" href="?%s">%s</a>', Http::build_query(array( @@ -400,7 +400,7 @@ if ($thisstaff->hasPerm(Task::PERM_DELETE, false)) { <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> <p class="confirm-action" style="display:none;" id="mark_overdue-confirm"> - <?php echo __('Are you sure want to flag the selected tickets as <font color="red"><b>overdue</b></font>?');?> + <?php echo __('Are you sure want to flag the selected tasks as <font color="red"><b>overdue</b></font>?');?> </p> <div><?php echo __('Please confirm to continue.');?></div> <hr style="margin-top:1em"/> @@ -416,38 +416,21 @@ if ($thisstaff->hasPerm(Task::PERM_DELETE, false)) { </div> <script type="text/javascript"> $(function() { - $(document).off('.tasks'); - $(document).on('click.tasks', 'a.tasks-action', function(e) { - e.preventDefault(); - var count = checkbox_checker($('form#tasks'), 1); - if (count) { - var url = 'ajax.php/' - +$(this).attr('href').substr(1) - +'?count='+count - +'&_uid='+new Date().getTime(); - var $redirect = $(this).data('redirect'); - $.dialog(url, [201], function (xhr) { - if (!!$redirect) - $.pjax({url: $redirect, container:'#pjax-container'}); - else - $.pjax.reload('#pjax-container'); - }); - } - return false; - }); - $(document).off('.task-action'); - $(document).on('click.task-action', 'a.task-action', function(e) { + + $(document).off('.new-task'); + $(document).on('click.new-task', 'a.new-task', function(e) { e.preventDefault(); var url = 'ajax.php/' +$(this).attr('href').substr(1) +'?_uid='+new Date().getTime(); var $options = $(this).data('dialogConfig'); - var $redirect = $(this).data('redirect'); $.dialog(url, [201], function (xhr) { - if (!!$redirect) - window.location.href = $redirect; - else + var tid = parseInt(xhr.responseText); + if (tid) { + window.location.href = 'tasks.php?id='+tid; + } else { $.pjax.reload('#pjax-container'); + } }, $options); return false; diff --git a/include/staff/templates/task-status.tmpl.php b/include/staff/templates/task-status.tmpl.php index 8aba2d100c0fdaa56b4b77d3961e27bb15f231c7..a6cf6594bb6a4abac698f379a987485c9b22b7e0 100644 --- a/include/staff/templates/task-status.tmpl.php +++ b/include/staff/templates/task-status.tmpl.php @@ -27,6 +27,7 @@ $action = $info[':action'] ?: ('#tasks/mass/'. $action); <form method="post" name="status" id="status" action="<?php echo $action; ?>" class="mass-action"> + <input type="hidden" name="status" value="<?php echo $info['status']; ?>" > <table width="100%"> <?php if ($info[':extra']) { @@ -38,36 +39,6 @@ $action = $info[':action'] ?: ('#tasks/mass/'. $action); <?php } ?> - <tbody> - <tr> - <td colspan=2> - <span> - <strong><?php echo __('Status') ?>: </strong> - <select name="status"> - <?php - $statuses = array( - 'open' => __('Open'), - 'closed' => __('Closed')); - - if (!$info['status']) - echo '<option value=""> '. __('Select One') - .' </option>'; - foreach ($statuses as $k => $status) { - echo sprintf('<option value="%s" %s>%s</option>', - $k, - ($info['status'] == $k) - ? 'selected="selected"' : '', - $status - ); - } - ?> - </select> - <font class="error">* <?php echo - $errors['status']; ?></font> - </span> - </td> - </tr> - </tbody> <tbody> <tr> <td colspan="2"> diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php index a2a95ff4ebd01632f320c40a97fa89d4fbfac89e..f53a410ef722f9928630a6bdce9948a6c644989b 100644 --- a/include/staff/templates/task-view.tmpl.php +++ b/include/staff/templates/task-view.tmpl.php @@ -6,33 +6,41 @@ if (!defined('OSTSCPINC') global $cfg; +$id = $task->getId(); +$dept = $task->getDept(); +$thread = $task->getThread(); + $iscloseable = $task->isCloseable(); $canClose = ($role->hasPerm(TaskModel::PERM_CLOSE) && $iscloseable === true); $actions = array(); -$actions += array( - 'print' => array( - 'href' => sprintf('tasks.php?id=%d&a=print', $task->getId()), - 'class' => 'no-pjax', - 'icon' => 'icon-print', - 'label' => __('Print') - )); -if ($role->hasPerm(Task::PERM_EDIT)) { +if ($task->isOpen() && $role->hasPerm(Task::PERM_ASSIGN)) { + + if ($task->getStaffId() != $thisstaff->getId() + && (!$dept->assignMembersOnly() + || $dept->isMember($thisstaff))) { + $actions += array( + 'claim' => array( + 'href' => sprintf('#tasks/%d/claim', $task->getId()), + 'icon' => 'icon-user', + 'label' => __('Claim'), + 'redirect' => 'tasks.php' + )); + } + $actions += array( - 'edit' => array( - 'href' => sprintf('#tasks/%d/edit', $task->getId()), - 'icon' => 'icon-edit', - 'dialog' => '{"size":"large"}', - 'label' => __('Edit') + 'assign/agents' => array( + 'href' => sprintf('#tasks/%d/assign/agents', $task->getId()), + 'icon' => 'icon-user', + 'label' => __('Assign to Agent'), + 'redirect' => 'tasks.php' )); -} -if ($role->hasPerm(Task::PERM_ASSIGN)) { $actions += array( - 'assign' => array( - 'href' => sprintf('#tasks/%d/assign', $task->getId()), + 'assign/teams' => array( + 'href' => sprintf('#tasks/%d/assign/teams', $task->getId()), 'icon' => 'icon-user', - 'label' => $task->isAssigned() ? __('Reassign') : __('Assign'), + 'label' => __('Assign to Team'), 'redirect' => 'tasks.php' )); } @@ -47,13 +55,30 @@ if ($role->hasPerm(Task::PERM_TRANSFER)) { )); } +$actions += array( + 'print' => array( + 'href' => sprintf('tasks.php?id=%d&a=print', $task->getId()), + 'class' => 'no-pjax', + 'icon' => 'icon-print', + 'label' => __('Print') + )); + +if ($role->hasPerm(Task::PERM_EDIT)) { + $actions += array( + 'edit' => array( + 'href' => sprintf('#tasks/%d/edit', $task->getId()), + 'icon' => 'icon-edit', + 'dialog' => '{"size":"large"}', + 'label' => __('Edit') + )); +} if ($role->hasPerm(Task::PERM_DELETE)) { $actions += array( 'delete' => array( 'href' => sprintf('#tasks/%d/delete', $task->getId()), 'icon' => 'icon-trash', - 'class' => 'danger', + 'class' => 'red button', 'label' => __('Delete'), 'redirect' => 'tasks.php' )); @@ -61,50 +86,40 @@ if ($role->hasPerm(Task::PERM_DELETE)) { $info=($_POST && $errors)?Format::input($_POST):array(); -$id = $task->getId(); -$dept = $task->getDept(); -$thread = $task->getThread(); - if ($task->isOverdue()) $warn.=' <span class="Icon overdueTicket">'.__('Marked overdue!').'</span>'; ?> - -<div class="has_bottom_border" class="sticky bar stop"> +<div> <div class="sticky bar"> <div class="content"> <div class="pull-left flush-left"> <?php if ($ticket) { ?> <strong> - <a id="all-ticket-tasks" href="#"> All Tasks (<?php echo $ticket->getNumTasks(); ?>)</a> + <a id="all-ticket-tasks" href="#"> + <?php + echo sprintf(__('All Tasks (%s)'), + $ticket->getNumTasks()); + ?></a> / - <?php echo $task->getTitle(); ?> - — - <a - id="reload-task" class="preview" + <a id="reload-task" class="preview" <?php echo ' class="preview" '; echo sprintf('data-preview="#tasks/%d/preview" ', $task->getId()); echo sprintf('href="#tickets/%s/tasks/%d/view" ', $ticket->getId(), $task->getId() ); - ?> - ><?php - echo sprintf('#%s', $task->getNumber()); ?></a> + ?>><?php echo sprintf(__('Task #%s'), $task->getNumber()); ?></a> </strong> <?php } else { ?> <h2> - <a - id="reload-task" - href="tasks.php?id=<?php echo $task->getId(); ?>" - href="tasks.php?id=<?php echo $task->getId(); ?>" - ><i class="icon-refresh"></i> <?php - echo sprintf(__('Task #%s'), $task->getNumber()); ?></a> - <?php if ($task) { ?> – <small><span class="ltr"><?php echo $task->getTitle(); ?></span></small> - <?php } ?> - </h2> + <a id="reload-task" + href="tasks.php?id=<?php echo $task->getId(); ?>"><i + class="icon-refresh"></i> <?php + echo sprintf(__('Task #%s'), $task->getNumber()); ?></a> + </h2> <?php } ?> </div> @@ -129,7 +144,28 @@ if ($task->isOverdue()) <div id="action-dropdown-task-options" class="action-dropdown anchor-right"> <ul> - <?php foreach ($actions as $a => $action) { ?> + + <?php + if ($task->isOpen()) { ?> + <li> + <a class="no-pjax task-action" + href="#tasks/<?php echo $task->getId(); ?>/reopen"><i + class="icon-fixed-width icon-undo"></i> <?php + echo __('Reopen');?> </a> + </li> + <?php + } else { + ?> + <li> + <a class="no-pjax task-action" + href="#tasks/<?php echo $task->getId(); ?>/close"><i + class="icon-fixed-width icon-ok-circle"></i> <?php + echo __('Close');?> </a> + </li> + <?php + } ?> + <?php + foreach ($actions as $a => $action) { ?> <li <?php if ($action['class']) echo sprintf("class='%s'", $action['class']); ?> > <a class="no-pjax task-action" <?php if ($action['dialog']) @@ -152,21 +188,101 @@ if ($task->isOverdue()) </ul> </div> <?php - } else { - foreach ($actions as $action) {?> - <a class="action-button <?php - echo $action['class'] ?: 'task-action'; ?>" + } else { ?> + <span + class="action-button" + data-dropdown="#action-dropdown-tasks-status"> + <i class="icon-caret-down pull-right"></i> + <a class="tasks-status-action" + href="#statuses" + data-placement="bottom" + data-toggle="tooltip" + title="<?php echo __('Change Status'); ?>"><i + class="icon-flag"></i></a> + </span> + <div id="action-dropdown-tasks-status" + class="action-dropdown anchor-right"> + <ul> + <?php + if ($task->isClosed()) { ?> + <li> + <a class="no-pjax task-action" + href="#tasks/<?php echo $task->getId(); ?>/reopen"><i + class="icon-fixed-width icon-undo"></i> <?php + echo __('Reopen');?> </a> + </li> + <?php + } else { + ?> + <li> + <a class="no-pjax task-action" + href="#tasks/<?php echo $task->getId(); ?>/close"><i + class="icon-fixed-width icon-ok-circle"></i> <?php + echo __('Close');?> </a> + </li> + <?php + } ?> + </ul> + </div> + <?php + // Assign + unset($actions['claim'], $actions['assign/agents'], $actions['assign/teams']); + if ($task->isOpen() && $role->hasPerm(Task::PERM_ASSIGN)) {?> + <span class="action-button" + data-dropdown="#action-dropdown-assign" + data-placement="bottom" + data-toggle="tooltip" + title=" <?php echo $task->isAssigned() ? __('Reassign') : __('Assign'); ?>" + > + <i class="icon-caret-down pull-right"></i> + <a class="task-action" id="task-assign" + data-redirect="tasks.php" + href="#tasks/<?php echo $task->getId(); ?>/assign"><i class="icon-user"></i></a> + </span> + <div id="action-dropdown-assign" class="action-dropdown anchor-right"> + <ul> <?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 $action['href']; ?>"><i - class="<?php - echo $action['icon'] ?: 'icon-tag'; ?>"></i> <?php - echo $action['label']; - ?></a> + // Agent can claim team assigned ticket + if ($task->getStaffId() != $thisstaff->getId() + && (!$dept->assignMembersOnly() + || $dept->isMember($thisstaff)) + ) { ?> + <li><a class="no-pjax task-action" + data-redirect="tasks.php" + href="#tasks/<?php echo $task->getId(); ?>/claim"><i + class="icon-chevron-sign-down"></i> <?php echo __('Claim'); ?></a> + <?php + } ?> + <li><a class="no-pjax task-action" + data-redirect="tasks.php" + href="#tasks/<?php echo $task->getId(); ?>/assign/agents"><i + class="icon-user"></i> <?php echo __('Agent'); ?></a> + <li><a class="no-pjax task-action" + data-redirect="tasks.php" + href="#tasks/<?php echo $task->getId(); ?>/assign/teams"><i + class="icon-group"></i> <?php echo __('Team'); ?></a> + </ul> + </div> + <?php + } ?> + <?php + foreach ($actions as $action) {?> + <span class="action-button <?php echo $action['class'] ?: ''; ?>"> + <a class="task-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 $action['href']; ?>" + data-placement="bottom" + data-toggle="tooltip" + title="<?php echo $action['label']; ?>"> + <i class="<?php + echo $action['icon'] ?: 'icon-tag'; ?>"></i> + </a> + </span> <?php } } ?> @@ -175,6 +291,14 @@ if ($task->isOverdue()) </div> </div> +<div class="clear tixTitle has_bottom_border"> + <h3> + <?php + $title = TaskForm::getInstance()->getField('title'); + echo $title->display($task->getTitle()); + ?> + </h3> +</div> <?php if (!$ticket) { ?> <table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0"> @@ -274,7 +398,8 @@ if (!$ticket) { ?> foreach (DynamicFormEntry::forObject($task->getId(), ObjectModel::OBJECT_TYPE_TASK) as $form) { $answers = $form->getAnswers()->exclude(Q::any(array( - 'field__flags__hasbit' => DynamicFormField::FLAG_EXT_STORED + 'field__flags__hasbit' => DynamicFormField::FLAG_EXT_STORED, + 'field__name__in' => array('title') ))); if (!$answers || count($answers) == 0) continue; @@ -543,5 +668,10 @@ $(function() { .done(function() { }) .fail(function() { }); }); + <?php + if ($ticket) { ?> + $('#ticket-tasks-count').html(<?php echo $ticket->getNumTasks(); ?>); + <?php + } ?> }); </script> diff --git a/include/staff/templates/tasks-actions.tmpl.php b/include/staff/templates/tasks-actions.tmpl.php index 04696a7714499e87b20aa35f0e3140d79921d17e..dc59eae4fc5827a7231c39dbdbb4a7fc2f9c0d2e 100644 --- a/include/staff/templates/tasks-actions.tmpl.php +++ b/include/staff/templates/tasks-actions.tmpl.php @@ -13,9 +13,11 @@ if ($agent->hasPerm(Task::PERM_CLOSE, false)) { data-dropdown="#action-dropdown-tasks-status"> <i class="icon-caret-down pull-right"></i> <a class="tasks-status-action" - href="#statuses"><i - class="icon-flag"></i> <?php - echo __('Change Status'); ?></a> + href="#statuses" + data-placement="bottom" + data-toggle="tooltip" + title="<?php echo __('Change Status'); ?>"><i + class="icon-flag"></i></a> </span> <div id="action-dropdown-tasks-status" class="action-dropdown anchor-right"> @@ -61,9 +63,19 @@ if ($agent->hasPerm(Task::PERM_CLOSE, false)) { if ($agent->hasPerm(Task::PERM_ASSIGN, false)) { $actions += array( - 'assign' => array( + 'claim' => array( 'icon' => 'icon-user', - 'action' => __('Assign') + 'action' => __('Claim') + )); + $actions += array( + 'assign/agents' => array( + 'icon' => 'icon-user', + 'action' => __('Assign to Agent') + )); + $actions += array( + 'assign/teams' => array( + 'icon' => 'icon-group', + 'action' => __('Assign to Team') )); } @@ -71,7 +83,6 @@ if ($agent->hasPerm(Task::PERM_TRANSFER, false)) { $actions += array( 'transfer' => array( 'icon' => 'icon-share', - 'redirect' => 'tickets.php', 'action' => __('Transfer') )); } @@ -84,7 +95,7 @@ if ($agent->hasPerm(Task::PERM_DELETE, false)) { 'action' => __('Delete') )); } -if ($actions) { +if ($actions && !isset($options['status'])) { $more = $options['morelabel'] ?: __('More'); ?> <span @@ -121,20 +132,87 @@ if ($actions) { </ul> </div> <?php - } ?> + } else { + // Mass Claim/Assignment + if ($agent->hasPerm(Task::PERM_ASSIGN, false)) {?> + <span + class="action-button" data-placement="bottom" + data-dropdown="#action-dropdown-assign" data-toggle="tooltip" title=" <?php + echo __('Assign'); ?>"> + <i class="icon-caret-down pull-right"></i> + <a class="tasks-action" id="tasks-assign" + href="#tasks/mass/assign"><i class="icon-user"></i></a> + </span> + <div id="action-dropdown-assign" class="action-dropdown anchor-right"> + <ul> + <li><a class="no-pjax tasks-action" + href="#tasks/mass/claim"><i + class="icon-chevron-sign-down"></i> <?php echo __('Claim'); ?></a> + <li><a class="no-pjax tasks-action" + href="#tasks/mass/assign/agents"><i + class="icon-user"></i> <?php echo __('Agent'); ?></a> + <li><a class="no-pjax tasks-action" + href="#tasks/mass/assign/teams"><i + class="icon-group"></i> <?php echo __('Team'); ?></a> + </ul> + </div> + <?php + } + + // Mass Transfer + if ($agent->hasPerm(Task::PERM_TRANSFER, false)) {?> + <span class="action-button"> + <a class="tasks-action" id="tasks-transfer" data-placement="bottom" + data-toggle="tooltip" title="<?php echo __('Transfer'); ?>" + href="#tasks/mass/transfer"><i class="icon-share"></i></a> + </span> + <?php + } + + + // Mass Delete + if ($agent->hasPerm(Task::PERM_DELETE, false)) {?> + <span class="red button action-button"> + <a class="tasks-action" id="tasks-delete" data-placement="bottom" + data-toggle="tooltip" title="<?php echo __('Delete'); ?>" + href="#tasks/mass/delete"><i class="icon-trash"></i></a> + </span> +<?php + } +} ?> + + <script type="text/javascript"> $(function() { $(document).off('.tasks-actions'); $(document).on('click.tasks-actions', 'a.tasks-action', function(e) { e.preventDefault(); - var count = checkbox_checker($('form#tasks'), 1); + var $form = $('form#tasks'); + var count = checkbox_checker($form, 1); if (count) { + var tids = $('.ckb:checked', $form).map(function() { + return this.value; + }).get(); var url = 'ajax.php/' +$(this).attr('href').substr(1) +'?count='+count + +'&tids='+tids.join(',') +'&_uid='+new Date().getTime(); + var $redirect = $(this).data('redirect'); $.dialog(url, [201], function (xhr) { - $.pjax.reload('#pjax-container'); + if (!!$redirect) + $.pjax({url: $redirect, container:'#pjax-container'}); + else + <?php + if (isset($options['callback_url'])) + echo sprintf("$.pjax({url: '%s', container: '%s', push: false});", + $options['callback_url'], + @$options['container'] ?: '#pjax-container' + ); + else + echo sprintf("$.pjax.reload('%s');", + @$options['container'] ?: '#pjax-container'); + ?> }); } return false; diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php index cf6c7a0c7561f1a11dfd114807a21cf081c5f6db..a260f0f05b07e2427b973ccfd68f44ea9ce0e30c 100644 --- a/include/staff/ticket-tasks.inc.php +++ b/include/staff/ticket-tasks.inc.php @@ -41,7 +41,11 @@ $showing = $pageNav->showing().' '._N('task', 'tasks', $count); <?php } if ($count) - Task::getAgentActions($thisstaff, array('morelabel' => __('Options'))); + Task::getAgentActions($thisstaff, array( + 'container' => '#tasks_content', + 'callback_url' => sprintf('ajax.php/tickets/%d/tasks', + $ticket->getId()), + 'morelabel' => __('Options'))); ?> </div> <div class="clear"></div> @@ -177,11 +181,12 @@ $(function() { var tid = parseInt(xhr.responseText); if (tid) { var url = 'ajax.php/tickets/'+<?php echo $ticket->getId(); - ?>+'/tasks/'+tid+'/view'; + ?>+'/tasks'; var $container = $('div#task_content'); - $container.load(url, function () { + $container.load(url+'/'+tid+'/view', function () { $('.tip_box').remove(); $('div#tasks_content').hide(); + $.pjax({url: url, container: '#tasks_content', push: false}); }).show(); } else { window.location.href = $redirect ? $redirect : window.location.href; @@ -189,5 +194,7 @@ $(function() { }, $options); return false; }); + + $('#ticket-tasks-count').html(<?php echo $count; ?>); }); </script> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index ca3ba69cdbdc3092cd8ded141dd72130f7015ecd..2b39913a5483cc892a0a0ce202e9b63b21fb6e96 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -485,7 +485,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); echo sprintf('#tickets/%d/tasks', $ticket->getId()); ?>"><?php echo __('Tasks'); if ($ticket->getNumTasks()) - echo sprintf(' (%d)', $ticket->getNumTasks()); + echo sprintf(' (<span id="ticket-tasks-count">%d</span>)', $ticket->getNumTasks()); ?></a></li> </ul> diff --git a/scp/ajax.php b/scp/ajax.php index 8fcb6146f2f9e6d97bdc2567ac6a22c6259f2e6a..f01cf70b4bb00f0b4b4a6f17bab1c0d5d92e5a2c 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -182,16 +182,17 @@ $dispatcher = patterns('', url_post('^(?P<tid>\d+)/edit$', 'edit'), url_get('^(?P<tid>\d+)/transfer', 'transfer'), url_post('^(?P<tid>\d+)/transfer$', 'transfer'), - url_get('^(?P<tid>\d+)/assign', 'assign'), - url_post('^(?P<tid>\d+)/assign$', 'assign'), + url('^(?P<tid>\d+)/assign(?:/(?P<to>\w+))?$', 'assign'), + url('^(?P<tid>\d+)/claim$', 'claim'), url_get('^(?P<tid>\d+)/delete', 'delete'), url_post('^(?P<tid>\d+)/delete$', 'delete'), + url('^(?P<tid>\d+)/close', 'close'), + url('^(?P<tid>\d+)/reopen', 'reopen'), url_get('^(?P<tid>\d+)/view$', 'task'), url_post('^(?P<tid>\d+)$', 'task'), url('^add$', 'add'), url('^lookup', 'lookup'), - url_get('^mass/(?P<action>[\w.]+)', 'massProcess'), - url_post('^mass/(?P<action>[\w.]+)', 'massProcess') + url('^mass/(?P<action>\w+)(?:/(?P<what>\w+))?', 'massProcess') )), url('^/thread/', patterns('ajax.thread.php:ThreadAjaxAPI', url_get('^(?P<tid>\d+)/collaborators/preview$', 'previewCollaborators'), diff --git a/scp/tasks.php b/scp/tasks.php index f33018f3252569cd9ace6f0de58115d1e7ee7a26..616a1720adff44c09e86b72c66d6864f4a3c7fb8 100644 --- a/scp/tasks.php +++ b/scp/tasks.php @@ -195,7 +195,7 @@ if ($thisstaff->hasPerm(TaskModel::PERM_CREATE, false)) { $nav->addSubMenu(array('desc'=>__('New Task'), 'title'=> __('Open a New Task'), 'href'=>'#tasks/add', - 'iconclass'=>'newTicket task-action', + 'iconclass'=>'newTicket new-task', 'id' => 'new-task', 'attr' => array( 'data-dialog-config' => '{"size":"large"}'