diff --git a/bootstrap.php b/bootstrap.php index cae6611cd72c486129c81d70afa70205cae921ee..a49d8dcd98530af5842123898776108b9411c606 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -103,7 +103,8 @@ class Bootstrap { define('TICKET_STATUS_TABLE', $prefix.'ticket_status'); define('TICKET_PRIORITY_TABLE',$prefix.'ticket_priority'); - define('TASK_TABLE',$prefix.'task'); + define('TASK_TABLE', $prefix.'task'); + define('TASK_CDATA_TABLE', $prefix.'task__cdata'); define('PRIORITY_TABLE',TICKET_PRIORITY_TABLE); diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php index cb6728a2dbd0eee8245c2050184e4cb1c795cffe..31671f9e11043976f9a3113446b5efa65bbf5884 100644 --- a/include/ajax.tasks.php +++ b/include/ajax.tasks.php @@ -54,6 +54,175 @@ class TasksAjaxAPI extends AjaxController { include STAFFINC_DIR . 'templates/task-edit.tmpl.php'; } + function massProcess($action) { + global $thisstaff; + + $actions = array( + 'transfer' => array( + 'verbed' => __('transferred'), + ), + 'assign' => array( + 'verbed' => __('assigned'), + ), + 'delete' => array( + 'verbed' => __('deleted'), + ), + ); + + if (!isset($actions[$action])) + Http::response(404, __('Unknown action')); + + + $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 task')); + } else { + $count = $_REQUEST['count']; + } + + switch ($action) { + case 'assign': + $inc = 'task-assign.tmpl.php'; + if ($_POST && !$errors) { + if (!isset($_POST['staff_id']) || !is_numeric($_POST['staff_id'])) + $errors['staff_id'] = __('Assignee selection required'); + else { + foreach ($_POST['tids'] as $tid) { + if (($t=Task::lookup($tid)) + && $t->getDeptId() != $_POST['dept_id'] + // Make sure the agent is allowed to + // access and assign the task. + && $t->checkStaffPerm($thisstaff, Task::PERM_ASSIGN) + // Do the transfer + && $t->assign($_POST, $e) + ) + $i++; + } + + if (!$i) { + $info['error'] = sprintf( + __('Unable to %1$s %2$s'), + __('assign'), + _N('selected task', 'selected tasks', $count)); + } + } + } + break; + case 'transfer': + $inc = 'task-transfer.tmpl.php'; + if ($_POST && !$errors) { + if (!isset($_POST['dept_id']) || !is_numeric($_POST['dept_id'])) + $errors['dept_id'] = __('Department selection required'); + else { + foreach ($_POST['tids'] as $tid) { + if (($t=Task::lookup($tid)) + && $t->getDeptId() != $_POST['dept_id'] + // Make sure the agent is allowed to + // access and transfer the task. + && $t->checkStaffPerm($thisstaff, Task::PERM_TRANSFER) + // Do the transfer + && $t->transfer($_POST, $e) + ) + $i++; + } + + if (!$i) { + $info['error'] = sprintf( + __('Unable to %1$s %2$s'), + __('transfer'), + _N('selected task', 'selected tasks', $count)); + } + } + } + break; + case 'delete': + $inc = 'task-delete.tmpl.php'; + $info[':placeholder'] = sprintf(__( + 'Optional reason for deleting %s'), + _N('selected task', 'selected tasks', $count)); + $info['warn'] = sprintf(__( + 'Are you sure you want to DELETE %s?'), + _N('selected task', 'selected tasks', $count)); + $info[':extra'] = sprintf('<strong>%s</strong>', + __('Deleted tasks CANNOT be recovered, including any associated attachments.') + ); + + if ($_POST && !$errors) { + foreach ($_POST['tids'] as $tid) { + if (($t=Task::lookup($tid)) + && $t->getDeptId() != $_POST['dept_id'] + && $t->checkStaffPerm($thisstaff, Task::PERM_DELETE) + && $t->delete($_POST, $e) + ) + $i++; + } + + if (!$i) { + $info['error'] = sprintf( + __('Unable to %1$s %2$s'), + __('delete'), + _N('selected task', 'selected tasks', $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 task', 'selected tasks', $count)) + ); + $_SESSION['::sysmsgs']['msg'] = $msg; + } else { + $warn = sprintf( + __('%1$d of %2$d %3$s %4$s'), $i, $count, + _N('selected task', 'selected tasks', + $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'), + $actions[$action]['verbed'], + _N('selected task', 'selected tasks', $count)); + } + + if ($_POST) + $info = array_merge($info, Format::htmlchars($_POST)); + + + include STAFFINC_DIR . "templates/$inc"; + // Copy checked tasks to the form. + echo " + <script type=\"text/javascript\"> + $(function() { + $('form#tasks input[name=\"tids[]\"]:checkbox:checked') + .each(function() { + $('<input>') + .prop('type', 'hidden') + .attr('name', 'tids[]') + .val($(this).val()) + .appendTo('form.mass-action'); + }); + }); + </script>"; + } + function transfer($tid) { global $thisstaff; @@ -63,40 +232,75 @@ class TasksAjaxAPI extends AjaxController { if (!$task->checkStaffPerm($thisstaff, Task::PERM_TRANSFER)) Http::response(403, __('Permission Denied')); - $info = $errors = array(); + $errors = array(); + + $info = array( + ':title' => sprintf(__('Task #%s: %s'), + $task->getNumber(), + __('Tranfer')), + ':action' => sprintf('#tasks/%d/transfer', + $task->getId()) + ); + if ($_POST) { - if ($task->transfer($_POST, $errors)) { + if ($task->transfer($_POST, $errors)) { + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('%s successfully'), + sprintf( + __('%s transferred to %s department'), + __('Task'), + $task->getDept() + ) + ); Http::response(201, $task->getId()); - } - $info = Format::htmlchars($_POST); + $info = array_merge($info, Format::htmlchars($_POST)); $info['error'] = $errors['err'] ?: __('Unable to transfer task'); } + $info['dept_id'] = $info['dept_id'] ?: $task->getDeptId(); + include STAFFINC_DIR . 'templates/task-transfer.tmpl.php'; } function assign($tid) { global $thisstaff; - if(!($task=Task::lookup($tid))) + if (!($task=Task::lookup($tid))) Http::response(404, __('No such task')); if (!$task->checkStaffPerm($thisstaff, Task::PERM_ASSIGN)) Http::response(403, __('Permission Denied')); - $info = $errors = array(); + $errors = array(); + $info = array( + ':title' => sprintf(__('Task #%s: %s'), + $task->getNumber(), + $task->isAssigned() ? __('Reassign') : __('Assign')), + ':action' => sprintf('#tasks/%d/assign', + $task->getId()), + ); if ($_POST) { if ($task->assign($_POST, $errors)) { - Http::response(201, $task->getId()); + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('%s successfully'), + sprintf( + __('%s assigned to %s'), + __('Task'), + $task->getStaff() + ) + ); + Http::response(201, $task->getId()); } - $info = Format::htmlchars($_POST); + $info = array_merge($info, Format::htmlchars($_POST)); $info['error'] = $errors['err'] ?: __('Unable to assign task'); } + $info['staff_id'] = $info['staff_id'] ?: $task->getStaffId(); + include STAFFINC_DIR . 'templates/task-assign.tmpl.php'; } @@ -109,23 +313,34 @@ class TasksAjaxAPI extends AjaxController { if (!$task->checkStaffPerm($thisstaff, Task::PERM_DELETE)) Http::response(403, __('Permission Denied')); - $info = $errors = array(); + $errors = array(); + $info = array( + ':title' => sprintf(__('Task #%s: %s'), + $task->getNumber(), + __('Delete')), + ':action' => sprintf('#tasks/%d/delete', + $task->getId()), + ); + if ($_POST) { if ($task->delete($_POST, $errors)) { + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('%s #%s deleted successfully'), + __('Task'), + $task->getNumber(), + $task->getDept()); Http::response(201, 0); - } - - $info = Format::htmlchars($_POST); + $info = array_merge($info, Format::htmlchars($_POST)); $info['error'] = $errors['err'] ?: __('Unable to delete task'); } - $info['placeholder'] = sprintf(__( + $info[':placeholder'] = sprintf(__( 'Optional reason for deleting %s'), __('this task')); $info['warn'] = sprintf(__( 'Are you sure you want to DELETE %s?'), __('this task')); - $info['extra'] = sprintf('<strong>%s</strong>', + $info[':extra'] = sprintf('<strong>%s</strong>', __('Deleted tasks CANNOT be recovered, including any associated attachments.') ); diff --git a/include/class.nav.php b/include/class.nav.php index 6dee08171b05fbf4c7fb7c6eb7658980b89c641a..c5c296d4733cfb17ec4cde98e795e1ecbe4ce547 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -123,7 +123,9 @@ class StaffNav { 'desc' => __('Users'), 'href' => 'users.php', 'title' => __('User Directory') ); } + $this->tabs['tasks'] = array('desc'=>__('Tasks'), 'href'=>'tasks.php', 'title'=>__('Task Queue')); $this->tabs['tickets'] = array('desc'=>__('Tickets'),'href'=>'tickets.php','title'=>__('Ticket Queue')); + $this->tabs['kbase'] = array('desc'=>__('Knowledgebase'),'href'=>'kb.php','title'=>__('Knowledgebase')); if (count($this->getRegisteredApps())) $this->tabs['apps']=array('desc'=>__('Applications'),'href'=>'apps.php','title'=>__('Applications')); @@ -140,6 +142,9 @@ class StaffNav { foreach($this->getTabs() as $k=>$tab){ $subnav=array(); switch(strtolower($k)){ + case 'tasks': + $subnav[]=array('desc'=>__('Tasks'), 'href'=>'tasks.php', 'iconclass'=>'Ticket', 'droponly'=>true); + break; case 'tickets': $subnav[]=array('desc'=>__('Tickets'),'href'=>'tickets.php','iconclass'=>'Ticket', 'droponly'=>true); if($staff) { diff --git a/include/class.staff.php b/include/class.staff.php index 9e093d7fcc93dcf349b1761f1bacd1a77ef2d839..bcd3ed16f3f2c111d033a6ccd1df2cf39d674795 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -490,6 +490,22 @@ implements AuthenticatedUser, EmailContact, TemplateVariable { return ($stats=$this->getTicketsStats())?$stats['closed']:0; } + function getTasksStats() { + + if (!$this->stats['tasks']) + $this->stats['tasks'] = Task::getStaffStats($this); + + return $this->stats['tasks']; + } + + function getNumAssignedTasks() { + return ($stats=$this->getTasksStats()) ? $stats['assigned'] : 0; + } + + function getNumClosedTasks() { + return ($stats=$this->getTasksStats()) ? $stats['closed'] : 0; + } + function getExtraAttr($attr=false, $default=null) { if (!isset($this->_extra) && isset($this->extra)) $this->_extra = JsonDataParser::decode($this->extra); diff --git a/include/class.task.php b/include/class.task.php index a39c28a00ceecbc306086a94bf0afe3dba85c4df..599be600983e6d6109d37b9bbf9724e88200c85e 100644 --- a/include/class.task.php +++ b/include/class.task.php @@ -33,6 +33,22 @@ class TaskModel extends VerySimpleModel { 'constraint' => array('staff_id' => 'Staff.staff_id'), 'null' => true, ), + 'team' => array( + 'constraint' => array('team_id' => 'Team.team_id'), + 'null' => true, + ), + 'thread' => array( + 'constraint' => array( + 'id' => 'ThreadModel.object_id', + "'A'" => 'ThreadModel.object_type', + ), + 'list' => false, + 'null' => false, + ), + 'cdata' => array( + 'constraint' => array('id' => 'TaskCData.task_id'), + 'list' => false, + ), ), ); @@ -104,14 +120,26 @@ class TaskModel extends VerySimpleModel { return $this->staff_id; } + function getStaff() { + return $this->staff; + } + function getTeamId() { return $this->team_id; } + function getTeam() { + return $this->team; + } + function getDeptId() { return $this->dept_id; } + function getDept() { + return $this->dept; + } + function getCreateDate() { return $this->created; } @@ -156,11 +184,10 @@ RolePermission::register(/* @trans */ 'Tasks', TaskModel::getPermissions()); class Task extends TaskModel { var $form; var $entry; - var $thread; + var $_thread; var $_entries; - function getStatus() { return $this->isOpen() ? _('Open') : _('Closed'); } @@ -202,7 +229,7 @@ class Task extends TaskModel { $assignees[] = $this->staff->getName(); //Add team assignment - if (isset($this->team)) + if ($this->team) $assignees[] = $this->team->getName(); return $assignees; @@ -216,13 +243,14 @@ class Task extends TaskModel { function getThread() { - if (!$this->thread) - $this->thread = TaskThread::lookup(array( + //FIXME: use $this->thread once thread classes get ORMed. + if (!$this->_thread) + $this->_thread = TaskThread::lookup(array( 'object_id' => $this->getId(), 'object_type' => ObjectModel::OBJECT_TYPE_TASK) ); - return $this->thread; + return $this->_thread; } function getThreadEntry($id) { @@ -360,7 +388,8 @@ class Task extends TaskModel { return false; // Transfer completed... post internal note. - $title = sprintf(__('Task transfered to %s department'), + $title = sprintf(__('%s transferred to %s department'), + __('Task'), $dept->getName()); if ($vars['comments']) { $note = $vars['comments']; @@ -496,8 +525,79 @@ class Task extends TaskModel { } } } + + /* Quick staff's stats */ + static function getStaffStats($staff) { + global $cfg; + + /* Unknown or invalid staff */ + if (!$staff + || (!is_object($staff) && !($staff=Staff::lookup($staff))) + || !$staff->isStaff()) + return null; + + $where = array('(task.staff_id='.db_input($staff->getId()) + .sprintf(' AND task.flags & %d != 0 ', TaskModel::ISOPEN) + .') '); + $where2 = ''; + + if(($teams=$staff->getTeams())) + $where[] = ' ( flags.team_id IN('.implode(',', db_input(array_filter($teams))) + .') AND ' + .sprintf('task.flags & %d != 0 ', TaskModel::ISOPEN) + .')'; + + if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tickets. + $where[] = 'task.dept_id IN('.implode(',', db_input($depts)).') '; + + $where = implode(' OR ', $where); + if ($where) $where = 'AND ( '.$where.' ) '; + + $sql = 'SELECT \'open\', count(task.id ) AS tasks ' + .'FROM ' . TASK_TABLE . ' task ' + . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN) + . $where . $where2 + + .'UNION SELECT \'overdue\', count( task.id ) AS tasks ' + .'FROM ' . TASK_TABLE . ' task ' + . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN) + . sprintf(' AND task.flags & %d != 0 ', TaskModel::ISOVERDUE) + . $where + + .'UNION SELECT \'assigned\', count( task.id ) AS tasks ' + .'FROM ' . TASK_TABLE . ' task ' + . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN) + .'AND task.staff_id = ' . db_input($staff->getId()) . ' ' + . $where + + .'UNION SELECT \'closed\', count( task.id ) AS tasks ' + .'FROM ' . TASK_TABLE . ' task ' + . sprintf(' WHERE task.flags & %d = 0 ', TaskModel::ISOPEN) + . $where; + + $res = db_query($sql); + $stats = array(); + while ($row = db_fetch_row($res)) + $stats[$row[0]] = $row[1]; + + return $stats; + } +} + + +class TaskCData extends VerySimpleModel { + static $meta = array( + 'pk' => array('task_id'), + 'table' => TASK_CDATA_TABLE, + 'joins' => array( + 'task' => array( + 'constraint' => array('task_id' => 'TaskModel.task_id'), + ), + ), + ); } + class TaskForm extends DynamicForm { static $instance; static $defaultForm; @@ -505,6 +605,12 @@ class TaskForm extends DynamicForm { static $forms; + static $cdata = array( + 'table' => TASK_CDATA_TABLE, + 'object_id' => 'task_id', + 'object_type' => 'A', + ); + static function objects() { $os = parent::objects(); return $os->filter(array('type'=>ObjectModel::OBJECT_TYPE_TASK)); diff --git a/include/staff/tasks.inc.php b/include/staff/tasks.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..74381d7a914349a11c87277b560976a2395729be --- /dev/null +++ b/include/staff/tasks.inc.php @@ -0,0 +1,433 @@ +<?php +$tasks = TaskModel::objects(); +$date_header = $date_col = false; + +// Figure out REFRESH url — which might not be accurate after posting a +// response +list($path,) = explode('?', $_SERVER['REQUEST_URI'], 2); +$args = array(); +parse_str($_SERVER['QUERY_STRING'], $args); + +// Remove commands from query +unset($args['id']); +unset($args['a']); + +$refresh_url = $path . '?' . http_build_query($args); + +$queue_name = strtolower($_GET['status'] ?: $_GET['a']); //Status is overloaded +switch ($queue_name) { +case 'closed': + $status='closed'; + $results_type=__('Closed Tasks'); + $showassigned=true; //closed by. + break; +case 'overdue': + $status='open'; + $results_type=__('Overdue Tasks'); + $tasks->filter(array('isoverdue'=>1)); + break; +case 'assigned': + $status='open'; + $staffId=$thisstaff->getId(); + $results_type=__('My Tasks'); + $tasks->filter(array('staff_id'=>$thisstaff->getId())); + break; +default: +case 'search': + // Consider basic search + if ($_REQUEST['query']) { + $results_type=__('Search Results'); + // Use an index if possible + if (Validator::is_email($_REQUEST['query'])) { + $tickets = $tickets->filter(array( + 'user__emails__address' => $_REQUEST['query'], + )); + } + else { + $tickets = $tickets->filter(Q::any(array( + 'number__startswith' => $_REQUEST['query'], + 'user__emails__address__contains' => $_REQUEST['query'], + ))); + } + break; + } + elseif (isset($_GET['uid'])) { + // Apply user filter + $tickets->filter(array('user__id'=>$_GET['uid'])); + } + elseif (isset($_SESSION['advsearch'])) { + // XXX: De-duplicate and simplify this code + $form = $search->getFormFromSession('advsearch'); + $form->loadState($_SESSION['advsearch']); + $tasks = $search->mangleQuerySet($tasks, $form); + $results_type=__('Advanced Search') + . '<a class="action-button" href="?clear_filter"><i class="icon-ban-circle"></i> <em>' . __('clear') . '</em></a>'; + break; + } + // Fall-through and show open tickets +case 'open': + $status='open'; + $results_type=__('Open Tasks'); + break; +} + +// Apply filters +$filters = array(); +$SQ = new Q(array('flags__hasbit' => TaskModel::ISOPEN)); +if ($status && !strcasecmp($status, 'closed')) + $SQ->negate(); + +$filters[] = $SQ; +$tasks->filter($filters); + +// Impose visibility constraints +// ------------------------------------------------------------ +// -- Open and assigned to me +$visibility = array( + new Q(array('flags__hasbit' => TaskModel::ISOPEN, 'staff_id' => $thisstaff->getId())) +); +// -- Routed to a department of mine +if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts())) + $visibility[] = new Q(array('dept_id__in' => $depts)); +// -- Open and assigned to a team of mine +if (($teams = $thisstaff->getTeams()) && count(array_filter($teams))) + $visibility[] = new Q(array( + 'team_id__in' => array_filter($teams), + 'flags__hasbit' => TaskModel::ISOPEN + )); +$tasks->filter(Q::any($visibility)); + +// Add in annotations +$tasks->annotate(array( + //'collab_count' => SqlAggregate::COUNT('collaborators'), + 'attachment_count' => SqlAggregate::COUNT('thread__entries__attachments'), + 'thread_count' => SqlAggregate::COUNT('thread__entries'), + // 'isopen' => new SqlExpr(array('flags__hasbit' => TaskModel::ISOPEN)), +)); + +$tasks->values('id', 'number', 'created', 'staff_id', 'team_id', + 'staff__firstname', 'staff__lastname', 'team__name', + 'dept__name', 'cdata__title', 'flags'); +// Apply requested quick filter + +// Apply requested sorting +$queue_sort_key = sprintf(':Q:%s:sort', $queue_name); + +if (isset($_GET['sort'])) + $_SESSION[$queue_sort_key] = $_GET['sort']; +switch ($_SESSION[$queue_sort_key]) { +case 'number': + $tasks->extra(array( + 'order_by'=>array(SqlExpression::times(new SqlField('number'), 1)) + )); + break; +case 'priority,due': + $tasks->order_by('cdata__:priority__priority_urgency'); + // Fall through to add in due date filter +case 'due': + $date_header = __('Due Date'); + $date_col = 'est_duedate'; + $tasks->values('est_duedate'); + $tasks->filter(array('est_duedate__isnull'=>false)); + $tasks->order_by('est_duedate'); + break; +case 'updated': + $tasks->order_by('cdata__:priority__priority_urgency', '-lastupdate'); + break; +case 'created': +default: + $tasks->order_by('-created'); + break; +} + +// Apply requested pagination +$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; +$pageNav=new Pagenate($tasks->count(), $page, PAGE_LIMIT); +$pageNav->setURL('tasks.php', $args); +$tasks = $pageNav->paginate($tasks); + +TaskForm::ensureDynamicDataView(); + +// Save the query to the session for exporting +$_SESSION[':Q:tasks'] = $tasks; + +// Mass actions +$actions = array(); + +if ($thisstaff->hasPerm(Task::PERM_ASSIGN)) { + $actions += array( + 'assign' => array( + 'icon' => 'icon-user', + 'action' => __('Assign Tasks') + )); +} + +if ($thisstaff->hasPerm(Task::PERM_TRANSFER)) { + $actions += array( + 'transfer' => array( + 'icon' => 'icon-share', + 'action' => __('Transfer Tasks') + )); +} + +if ($thisstaff->hasPerm(Task::PERM_DELETE)) { + $actions += array( + 'delete' => array( + 'icon' => 'icon-trash', + 'action' => __('Delete Tasks') + )); +} + + +?> +<!-- SEARCH FORM START --> +<div id='basic_search'> + <form action="tasks.php" method="get"> + <input type="hidden" name="a" value="search"> + <table> + <tr> + <td><input type="text" id="basic-ticket-search" name="query" + size=30 value="<?php echo Format::htmlchars($_REQUEST['query'], + true); ?>" + autocomplete="off" autocorrect="off" autocapitalize="off"></td> + <td><input type="submit" class="button" value="<?php echo __('Search'); ?>"></td> + <td> <a href="#" onclick="javascript: + $.dialog('ajax.php/tasks/search', 201);" + >[<?php echo __('advanced'); ?>]</a> <i class="help-tip icon-question-sign" href="#advanced"></i></td> + </tr> + </table> + </form> +</div> +<!-- SEARCH FORM END --> +<div class="clear"></div> +<div style="margin-bottom:20px; padding-top:10px;"> +<div> + <div class="pull-left flush-left"> + <h2><a href="<?php echo $refresh_url; ?>" + title="<?php echo __('Refresh'); ?>"><i class="icon-refresh"></i> <?php echo + $results_type.$showing; ?></a></h2> + </div> + <div class="pull-right flush-right"> + <span style="display:inline-block"> + <span style="vertical-align: baseline">Sort:</span> + <select name="sort" onchange="javascript:addSearchParam('sort', $(this).val());"> +<?php foreach (array( + 'created' => __('Most Recently Created'), + 'updated' => __('Most Recently Updated'), + 'due' => __('Due Soon'), + 'priority,due' => __('Priority + Due Soon'), + 'number' => __('Task Number'), +) as $mode => $desc) { ?> + <option value="<?php echo $mode; ?>" <?php if ($mode == $_SESSION[$queue_sort_key]) echo 'selected="selected"'; ?>><?php echo $desc; ?></option> +<?php } ?> + </select> + </span> + <?php + if ($thisstaff->canManageTickets()) { + echo TicketStatus::status_options(); + } + if ($actions) { ?> + <span + class="action-button" + data-dropdown="#action-dropdown-moreoptions"> + <i class="icon-caret-down pull-right"></i> + <a class="tasks-action" + href="#moreoptions"><i + class="icon-reorder"></i> <?php + echo __('Options'); ?></a> + </span> + <div id="action-dropdown-moreoptions" + class="action-dropdown anchor-right"> + <ul> + <?php foreach ($actions as $a => $action) { ?> + <li> + <a class="no-pjax tasks-action" + <?php + if ($action['dialog']) + echo sprintf("data-dialog='%s'", $action['dialog']); + if ($action['redirect']) + echo sprintf("data-redirect='%s'", $action['redirect']); + ?> + href="<?php + echo sprintf('#tasks/mass/%s', $a); ?>" + ><i class="<?php + echo $action['icon'] ?: 'icon-tag'; ?>"></i> <?php + echo $action['action']; ?></a> + </li> + <?php + } ?> + </ul> + </div> + <?php + } ?> + </div> +</div> +<div class="clear" style="margin-bottom:10px;"></div> +<form action="tasks.php" method="POST" name='tasks' id="tasks"> +<?php csrf_token(); ?> + <input type="hidden" name="a" value="mass_process" > + <input type="hidden" name="do" id="action" value="" > + <input type="hidden" name="status" value="<?php echo + Format::htmlchars($_REQUEST['status'], true); ?>" > + <table class="list" border="0" cellspacing="1" cellpadding="2" width="940"> + <thead> + <tr> + <?php if ($thisstaff->canManageTickets()) { ?> + <th width="8px"> </th> + <?php } ?> + <th width="70"> + <?php echo __('Number'); ?></th> + <th width="70"> + <?php echo $date_header ?: __('Date'); ?></th> + <th width="280"> + <?php echo __('Title'); ?></th> + <th width="250"> + <?php echo __('Department');?></th> + <th width="250"> + <?php echo __('Assignee');?></th> + </tr> + </thead> + <tbody> + <?php + // Setup Subject field for display + $total=0; + $title_field = TaskForm::objects()->one()->getField('title'); + $ids=($errors && $_POST['tids'] && is_array($_POST['tids']))?$_POST['tids']:null; + foreach ($tasks as $T) { + $T['isopen'] = ($T['flags'] & TaskModel::ISOPEN != 0); //XXX: + $total += 1; + $tag=$T['staff_id']?'assigned':'openticket'; + $flag=null; + if($T['lock__staff_id'] && $T['lock__staff_id'] != $thisstaff->getId()) + $flag='locked'; + elseif($T['isoverdue']) + $flag='overdue'; + + $assignee = ''; + $dept = Dept::getLocalById($T['dept_id'], 'name', $T['dept__name']); + $assinee =''; + if ($T['staff_id']) { + $staff = new PersonsName($T['staff__firstname'].' '.$T['staff__lastname']); + $assignee = sprintf('<span class="Icon staffAssigned">%s</span>', + Format::truncate((string) $staff, 40)); + } elseif($T['team_id']) { + $assignee = sprintf('<span class="Icon teamAssigned">%s</span>', + Format::truncate(Team::getLocalById($T['team_id'], 'name', $T['team__name']),40)); + } + + $threadcount=$T['thread_count']; + $number = $T['number']; + if ($T['isopen']) + $number = sprintf('<b>%s</b>', $number); + + $title = Format::truncate($title_field->display($title_field->to_php($T['cdata__title'])), 40); + ?> + <tr id="<?php echo $T['id']; ?>"> + <?php + if ($thisstaff->canManageTickets()) { + $sel = false; + if ($ids && in_array($T['id'], $ids)) + $sel = true; + ?> + <td align="center" class="nohover"> + <input class="ckb" type="checkbox" name="tids[]" + value="<?php echo $T['id']; ?>" <?php echo $sel?'checked="checked"':''; ?>> + </td> + <?php } ?> + <td nowrap> + <a class="preview" + href="tasks.php?id=<?php echo $T['id']; ?>" + data-preview="#tasks/<?php echo $T['id']; ?>/preview" + ><?php echo $number; ?></a></td> + <td align="center" nowrap><?php echo + Format::datetime($T[$date_col ?: 'created']); ?></td> + <td><a <?php if ($flag) { ?> class="Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket" <?php } ?> + href="tasks.php?id=<?php echo $T['id']; ?>"><?php + echo $title; ?></a> + <?php + if ($threadcount>1) + echo "<small>($threadcount)</small> ".'<i + class="icon-fixed-width icon-comments-alt"></i> '; + if ($T['collab_count']) + echo '<i class="icon-fixed-width icon-group faded"></i> '; + if ($T['attachment_count']) + echo '<i class="icon-fixed-width icon-paperclip"></i> '; + ?> + </td> + <td nowrap> <?php echo Format::truncate($dept, 40); ?></td> + <td nowrap> <?php echo $assignee; ?></td> + </tr> + <?php + } //end of foreach + if (!$total) + $ferror=__('There are no tickets matching your criteria.'); + ?> + </tbody> + <tfoot> + <tr> + <td colspan="6"> + <?php if($total && $thisstaff->canManageTickets()){ ?> + <?php echo __('Select');?>: + <a id="selectAll" href="#ckb"><?php echo __('All');?></a> + <a id="selectNone" href="#ckb"><?php echo __('None');?></a> + <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a> + <?php }else{ + echo '<i>'; + echo $ferror?Format::htmlchars($ferror):__('Query returned 0 results.'); + echo '</i>'; + } ?> + </td> + </tr> + </tfoot> + </table> + <?php + if ($total>0) { //if we actually had any tickets returned. + echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' '; + echo sprintf('<a class="export-csv no-pjax" href="?%s">%s</a>', + Http::build_query(array( + 'a' => 'export', 'h' => $hash, + 'status' => $_REQUEST['status'])), + __('Export')); + echo ' <i class="help-tip icon-question-sign" href="#export"></i></div>'; + } ?> + </form> +</div> + +<div style="display:none;" class="dialog" id="confirm-action"> + <h3><?php echo __('Please Confirm');?></h3> + <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>?');?> + </p> + <div><?php echo __('Please confirm to continue.');?></div> + <hr style="margin-top:1em"/> + <p class="full-width"> + <span class="buttons pull-left"> + <input type="button" value="<?php echo __('No, Cancel');?>" class="close"> + </span> + <span class="buttons pull-right"> + <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm"> + </span> + </p> + <div class="clear"></div> +</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(); + $.dialog(url, [201], function (xhr) { + $.pjax.reload('#pjax-container'); + }); + } + return false; + }); +}); +</script> diff --git a/include/staff/templates/task-assign.tmpl.php b/include/staff/templates/task-assign.tmpl.php index 0c5dd7a5eac411e9f78ab99e0096a0dd671187a8..48d389b933d8311526e8850864d2cbbd345e3ecb 100644 --- a/include/staff/templates/task-assign.tmpl.php +++ b/include/staff/templates/task-assign.tmpl.php @@ -1,14 +1,11 @@ <?php global $cfg; -if (!$info['title']) - $info['title'] = sprintf(__('%s Tasks #%s'), - $task->isAssigned() ? __('Reassign') : __('Assign'), - $task->getNumber() - ); - +if (!$info[':title']) + $info[':title'] = sprintf(__('%s Selected Tasks'), + __('Assign')); ?> -<h3><?php echo $info['title']; ?></h3> +<h3><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> @@ -25,10 +22,10 @@ if ($info['error']) { } -$action = $info['action'] ?: ('#tasks/'.$task->getId().'/assign'); +$action = $info[':action'] ?: ('#tasks/mass/assign'); ?> <div id="ticket-status" style="display:block; margin:5px;"> -<form method="post" name="transfer" id="transfer" +<form class="mass-action" method="post" name="transfer" id="transfer" action="<?php echo $action; ?>"> <table width="100%"> <?php @@ -46,8 +43,13 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/assign'); <span> <strong><?php echo __('Agent') ?>: </strong> <select name="staff_id"> + <option value=""><?php + echo __('Select Assignee'); ?></option> <?php foreach (Staff::getAvailableStaffMembers() as $id => $name) { + if ($task && $task->getStaffId() == $id) + $name .= sprintf(' (%s) ', __('current')); + echo sprintf('<option value="%d" %s>%s</option>', $id, ($info['staff_id'] == $id) @@ -58,7 +60,7 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/assign'); ?> </select> <font class="error">* <?php echo - $errors['dept_id']; ?></font> + $errors['staff_id']; ?></font> </span> </td> </tr> </tbody> diff --git a/include/staff/templates/task-delete.tmpl.php b/include/staff/templates/task-delete.tmpl.php index 6014bd88aa6be37d1027d01cbb083ba6b4d03b1b..9a9191e4cc5134849fee8d60a5b4e0aa5d82c21a 100644 --- a/include/staff/templates/task-delete.tmpl.php +++ b/include/staff/templates/task-delete.tmpl.php @@ -1,14 +1,11 @@ <?php global $cfg; - -if (!$info['title']) - $info['title'] = sprintf(__('%s Tasks #%s'), +if (!$info[':title']) + $info[':title'] = sprintf(__('%s %s'), __('Delete'), - $task->getNumber() - ); - + _N('selected task', 'selected tasks', $count)); ?> -<h3><?php echo $info['title']; ?></h3> +<h3><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> @@ -25,17 +22,18 @@ if ($info['error']) { } -$action = $info['action'] ?: ('#tasks/'.$task->getId().'/delete'); +$action = $info[':action'] ?: ('#tasks/mass/delete'); ?> <div id="ticket-status" style="display:block; margin:5px;"> <form method="post" name="delete" id="delete" - action="<?php echo $action; ?>"> + action="<?php echo $action; ?>" + class="mass-action"> <table width="100%"> <?php - if ($info['extra']) { + if ($info[':extra']) { ?> <tbody> - <tr><td colspan="2"><strong><?php echo $info['extra']; + <tr><td colspan="2"><strong><?php echo $info[':extra']; ?></strong></td> </tr> </tbody> <?php @@ -45,7 +43,7 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/delete'); <tr> <td colspan="2"> <?php - $placeholder = $info['placeholder'] ?: __('Optional reason for the deletion'); + $placeholder = $info[':placeholder'] ?: __('Optional reason for the deletion'); ?> <textarea name="comments" id="comments" cols="50" rows="3" wrap="soft" style="width:100%" diff --git a/include/staff/templates/task-transfer.tmpl.php b/include/staff/templates/task-transfer.tmpl.php index 8a872f43845ec328c92554f32125e10682173b89..462714bb8a431ad8236d42b580464ee3f7129c12 100644 --- a/include/staff/templates/task-transfer.tmpl.php +++ b/include/staff/templates/task-transfer.tmpl.php @@ -1,12 +1,11 @@ <?php global $cfg; -if (!$info['title']) - $info['title'] = sprintf(__('%s Tasks #%s'), - __('Tranfer'), $task->getNumber()); - +if (!$info[':title']) + $info[':title'] = sprintf(__('%s Selected Tasks'), + __('Tranfer')); ?> -<h3><?php echo $info['title']; ?></h3> +<h3><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> @@ -23,17 +22,18 @@ if ($info['error']) { } -$action = $info['action'] ?: ('#tasks/'.$task->getId().'/transfer'); +$action = $info[':action'] ?: ('#tasks/mass/transfer'); ?> <div style="display:block; margin:5px;"> <form method="post" name="transfer" id="transfer" + class="mass-action" action="<?php echo $action; ?>"> <table width="100%"> <?php - if ($info['extra']) { + if ($info[':extra']) { ?> <tbody> - <tr><td colspan="2"><strong><?php echo $info['extra']; + <tr><td colspan="2"><strong><?php echo $info[':extra']; ?></strong></td> </tr> </tbody> <?php @@ -46,6 +46,9 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/transfer'); <select name="dept_id"> <?php foreach (Dept::getDepartments() as $id => $name) { + if ($task && $task->getDeptId() == $id) + $name .= sprintf(' (%s) ', __('current')); + echo sprintf('<option value="%d" %s>%s</option>', $id, ($info['dept_id'] == $id) @@ -64,7 +67,7 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/transfer'); <tr> <td colspan="2"> <?php - $placeholder = $info['placeholder'] ?: __('Optional reason for the transfer'); + $placeholder = $info[':placeholder'] ?: __('Optional reason for the transfer'); ?> <textarea name="comments" id="comments" cols="50" rows="3" wrap="soft" style="width:100%" diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php index a9a4c72170780e61cd2edd7ada25a4ab30a6ab3b..8369a396b0a763d31cb8ed199910faa8fa3fe20e 100644 --- a/include/staff/templates/task-view.tmpl.php +++ b/include/staff/templates/task-view.tmpl.php @@ -1,50 +1,22 @@ <?php if (!defined('OSTSCPINC') - || !$thisstaff - || !$task - || !($role = $thisstaff->getRole($task->getId()))) + || !$thisstaff || !$task + || !($role = $thisstaff->getRole($task->getDeptId()))) die('Invalid path'); - -$actions = array(); - -if ($role->hasPerm(Task::PERM_EDIT)) { - $actions += array( - 'edit' => array( - 'icon' => 'icon-edit', - 'dialog' => '{"size":"large"}', - 'action' => __('Edit') - )); -} - -if ($role->hasPerm(Task::PERM_ASSIGN)) { - $actions += array( - 'assign' => array( - 'icon' => 'icon-user', - 'action' => $task->isAssigned() ? __('Reassign') : __('Assign') - )); -} - -if ($role->hasPerm(Task::PERM_TRANSFER)) { - $actions += array( - 'transfer' => array( - 'icon' => 'icon-share', - 'action' => __('Transfer') - )); -} - +$more = array(); if ($role->hasPerm(Task::PERM_DELETE)) { - $actions += array( + $more += array( 'delete' => array( 'icon' => 'icon-trash', - 'action' => __('Delete') + 'redirect' => 'tasks.php', + 'action' => __('Delete Task') )); } - $info=($_POST && $errors)?Format::input($_POST):array(); -$id = $task->getId(); //Ticket ID. +$id = $task->getId(); if ($task->isOverdue()) $warn.=' <span class="Icon overdueTicket">'.__('Marked overdue!').'</span>'; @@ -52,32 +24,58 @@ if ($task->isOverdue()) <table width="940" cellpadding="2" cellspacing="0" border="0"> <tr> <td width="20%" class="has_bottom_border"> - <h3><a href="#tasks/<?php echo $task->getId(); ?>/view" - id="reload-task"><i class="icon-refresh"></i> <?php + <h2><a href="tasks.php?id=<?php echo $task->getId(); ?>"><i class="icon-refresh"></i> <?php echo sprintf(__('Task #%s'), $task->getNumber()); ?></a> - </h3> + </h2> </td> <td width="auto" class="flush-right has_bottom_border"> - <?php - if ($actions) { ?> + <a class="action-button" + href="tasks.php?id=<?php echo $task->getId(); ?>&a=print"><i class="icon-print"></i> <?php + echo __('Print'); ?></a> + <?php + if ($role->hasPerm(Task::PERM_EDIT)) { ?> + <a class="action-button task-action" + href="tasks.php?id=<?php echo $task->getId(); ?>&a=edit"><i class="icon-edit"></i> <?php + echo __('Edit'); ?></a> + <?php + } + if ($role->hasPerm(Task::PERM_ASSIGN)) { ?> + <a class="action-button task-action" + href="#tasks/<?php echo $task->getId(); ?>/assign"><i + class="icon-user"></i> <?php + echo $task->isAssigned() ? __('Reassign') : __('Assign'); + ?></a> + <?php + } + if ($role->hasPerm(Task::PERM_TRANSFER)) { ?> + <a class="action-button task-action" + href="#tasks/<?php echo $task->getId(); ?>/transfer"><i + class="icon-share"></i> <?php + echo __('Transfer'); + ?></a> + <?php + } + if ($more) { ?> <span class="action-button" - data-dropdown="#action-dropdown-taskoptions"> + data-dropdown="#action-dropdown-moreoptions"> <i class="icon-caret-down pull-right"></i> <a class="task-action" - href="#taskoptions"><i + href="#moreoptions"><i class="icon-reorder"></i> <?php - echo __('Task Options'); ?></a> + echo __('More'); ?></a> </span> - <div id="action-dropdown-taskoptions" + <div id="action-dropdown-moreoptions" class="action-dropdown anchor-right"> <ul> - <?php foreach ($actions as $a => $action) { ?> + <?php foreach ($more as $a => $action) { ?> <li> <a class="no-pjax task-action" <?php if ($action['dialog']) echo sprintf("data-dialog='%s'", $action['dialog']); + if ($action['redirect']) + echo sprintf("data-redirect='%s'", $action['redirect']); ?> href="<?php echo sprintf('#tasks/%d/%s', $task->getId(), $a); ?>" @@ -106,14 +104,6 @@ if ($task->isOverdue()) <th><?php echo __('Department');?>:</th> <td><?php echo Format::htmlchars($task->dept->getName()); ?></td> </tr> - <tr> - <th><?php echo __('Create Date');?>:</th> - <td><?php echo Format::datetime($task->getCreateDate()); ?></td> - </tr> - </table> - </td> - <td width="50%" style="vertical-align:top"> - <table cellspacing="0" cellpadding="4" width="100%" border="0"> <?php if ($task->isOpen()) { ?> <tr> @@ -142,10 +132,18 @@ if ($task->isOverdue()) </tr> <?php } ?> + </table> + </td> + <td width="50%" style="vertical-align:top"> + <table cellspacing="0" cellpadding="4" width="100%" border="0"> <tr> <th><?php echo __('SLA Plan');?>:</th> <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">— '.__('None').' —</span>'; ?></td> </tr> + <tr> + <th><?php echo __('Create Date');?>:</th> + <td><?php echo Format::datetime($task->getCreateDate()); ?></td> + </tr> <?php if($task->isOpen()){ ?> <tr> @@ -273,7 +271,7 @@ foreach (DynamicFormEntry::forObject($task->getId(), <div id="response_options"> <ul class="tabs"></ul> <form id="task_note" - action="#tasks/<?php echo $task->getId(); ?>" + action="tasks.php?id=<?php echo $task->getId(); ?>" name="task_note" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> @@ -301,8 +299,8 @@ foreach (DynamicFormEntry::forObject($task->getId(), ?></textarea> <div class="attachments"> <?php - if ($task_note_form) - print $task_note_form->getField('attachments')->render(); + if ($note_form) + print $note_form->getField('attachments')->render(); ?> </div> </td> @@ -333,33 +331,25 @@ foreach (DynamicFormEntry::forObject($task->getId(), </p> </form> </div> + <script type="text/javascript"> $(function() { - $(document).on('click', 'li.active a#ticket_tasks', function(e) { + $(document).off('.task-action'); + $(document).on('click.task-action', 'a.task-action', function(e) { e.preventDefault(); - $('div#task_content').hide().empty(); - $('div#tasks_content').show(); + var url = 'ajax.php/' + +$(this).attr('href').substr(1) + +'?_uid='+new Date().getTime(); + var $options = $(this).data('dialog'); + var $redirect = $(this).data('redirect'); + $.dialog(url, [201], function (xhr) { + if ($redirect) + window.location.href = $redirect; + else + $.pjax.reload('#pjax-container'); + }, $options); + return false; - }); - $(document).off('.tf'); - $(document).on('submit.tf', 'form#task_note', function(e) { - e.preventDefault(); - var $form = $(this); - var $container = $('div#task_content'); - $.ajax({ - type: $form.attr('method'), - url: 'ajax.php/'+$form.attr('action').substr(1), - data: $form.serialize(), - cache: false, - success: function(resp, status, xhr) { - $container.html(resp); - $('#msg_notice, #msg_error',$container) - .delay(5000) - .slideUp(); - } - }) - .done(function() { }) - .fail(function() { }); }); }); </script> diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php index 8b8f3fb9d7c13f601ad6c101b4d2f7a32dd0bb43..3dec2c3867c99f6db91ef277667ddc9219450160 100644 --- a/include/staff/ticket-tasks.inc.php +++ b/include/staff/ticket-tasks.inc.php @@ -137,8 +137,9 @@ $(function() { }).show(); return false; }); - $(document).off('.task-action'); - $(document).on('click.task-action', 'a.task-action', function(e) { + // Ticket Tasks + $(document).off('.ticket-task-action'); + $(document).on('click.ticket-task-action', 'a.ticket-task-action', function(e) { e.preventDefault(); var url = 'ajax.php/' +$(this).attr('href').substr(1) diff --git a/scp/ajax.php b/scp/ajax.php index f34bcfb930f912ea70e6b4592324924ec62fd4a7..70c92db226f885958efe0d5c9c3bd9a4a2b2388d 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -182,7 +182,9 @@ $dispatcher = patterns('', url_get('^(?P<tid>\d+)/delete', 'delete'), url_post('^(?P<tid>\d+)/delete$', 'delete'), url_get('^(?P<tid>\d+)/view$', 'task'), - url_post('^(?P<tid>\d+)$', 'task') + url_post('^(?P<tid>\d+)$', 'task'), + url_get('^mass/(?P<action>[\w.]+)', 'massProcess'), + url_post('^mass/(?P<action>[\w.]+)', 'massProcess') )), url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI', url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'), diff --git a/scp/tasks.php b/scp/tasks.php new file mode 100644 index 0000000000000000000000000000000000000000..21ec96fd162769e56f63216bdb26f74d397b2fc3 --- /dev/null +++ b/scp/tasks.php @@ -0,0 +1,197 @@ +<?php +/************************************************************************* + tasks.php + + Copyright (c) 2006-2013 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: +**********************************************************************/ + +require('staff.inc.php'); +require_once(INCLUDE_DIR.'class.task.php'); + +$page = ''; +$task = null; //clean start. +if ($_REQUEST['id']) { + if (!($task=Task::lookup($_REQUEST['id']))) + $errors['err'] = sprintf(__('%s: Unknown or invalid ID.'), __('task')); + elseif (!$task->checkStaffPerm($thisstaff)) { + $errors['err'] = __('Access denied. Contact admin if you believe this is in error'); + $task = null; + } +} + +// Configure form for file uploads +$note_form = new Form(array( + 'attachments' => new FileUploadField(array('id'=>'attach', + 'name'=>'attach:note', + 'configuration' => array('extensions'=>''))) +)); + +//At this stage we know the access status. we can process the post. +if($_POST && !$errors): + + if ($task) { + //More coffee please. + $errors=array(); + $role = $thisstaff->getRole($task->getDeptId()); + switch(strtolower($_POST['a'])): + case 'postnote': /* Post Internal Note */ + $vars = $_POST; + $attachments = $note_form->getField('attachments')->getClean(); + $vars['cannedattachments'] = array_merge( + $vars['cannedattachments'] ?: array(), $attachments); + + $wasOpen = ($task->isOpen()); + if(($note=$task->postNote($vars, $errors, $thisstaff))) { + + $msg=__('Internal note posted successfully'); + // Clear attachment list + $note_form->setSource(array()); + $note_form->getField('attachments')->reset(); + + if($wasOpen && $task->isClosed()) + $task = null; //Going back to main listing. + else + // Ticket is still open -- clear draft for the note + Draft::deleteForNamespace('task.note.'.$task->getId(), + $thisstaff->getId()); + + } else { + + if(!$errors['err']) + $errors['err'] = __('Unable to post internal note - missing or invalid data.'); + + $errors['postnote'] = __('Unable to post the note. Correct the error(s) below and try again!'); + } + break; + default: + $errors['err']=__('Unknown action'); + endswitch; + } + if(!$errors) + $thisstaff->resetStats(); //We'll need to reflect any changes just made! +endif; + +/*... Quick stats ...*/ +$stats= $thisstaff->getTasksStats(); + +// Clear advanced search upon request +if (isset($_GET['clear_filter'])) + unset($_SESSION['advsearch']); + +//Navigation +$nav->setTabActive('tasks'); +$open_name = _P('queue-name', + /* This is the name of the open tasks queue */ + 'Open'); + +$nav->addSubMenu(array('desc'=>$open_name.' ('.number_format($stats['open']).')', + 'title'=>__('Open Tasks'), + 'href'=>'tasks.php?status=open', + 'iconclass'=>'Ticket'), + ((!$_REQUEST['status'] && !isset($_SESSION['advsearch'])) || $_REQUEST['status']=='open')); + +if ($stats['assigned']) { + + $nav->addSubMenu(array('desc'=>__('My Tasks').' ('.number_format($stats['assigned']).')', + 'title'=>__('Assigned Tasks'), + 'href'=>'tasks.php?status=assigned', + 'iconclass'=>'assignedTickets'), + ($_REQUEST['status']=='assigned')); +} + +if ($stats['overdue']) { + $nav->addSubMenu(array('desc'=>__('Overdue').' ('.number_format($stats['overdue']).')', + 'title'=>__('Stale Tasks'), + 'href'=>'tasks.php?status=overdue', + 'iconclass'=>'overdueTickets'), + ($_REQUEST['status']=='overdue')); + + if(!$sysnotice && $stats['overdue']>10) + $sysnotice=sprintf(__('%d overdue tasks!'), $stats['overdue']); +} + +if ($stats['closed']) { + $nav->addSubMenu(array('desc' => __('Closed').' ('.number_format($stats['closed']).')', + 'title'=>__('Closed Tasks'), + 'href'=>'tasks.php?status=closed', + 'iconclass'=>'closedTickets'), + ($_REQUEST['status']=='closed')); +} + +if (isset($_SESSION['advsearch'])) { + // XXX: De-duplicate and simplify this code + $search = SavedSearch::create(); + $form = $search->getFormFromSession('advsearch'); + $form->loadState($_SESSION['advsearch']); + $tickets = TicketModel::objects(); + $tickets = $search->mangleQuerySet($tickets, $form); + $count = $tickets->count(); + $nav->addSubMenu(array('desc' => __('Search').' ('.number_format($count).')', + 'title'=>__('Advanced Ticket Search'), + 'href'=>'tickets.php?status=search', + 'iconclass'=>'Ticket'), + (!$_REQUEST['status'] || $_REQUEST['status']=='search')); +} + +if ($thisstaff->hasPerm(TaskModel::PERM_CREATE)) { + $nav->addSubMenu(array('desc'=>__('New Task'), + 'title'=> __('Open a New Task'), + 'href'=>'tasks.php?a=open', + 'iconclass'=>'newTicket', + 'id' => 'new-task'), + ($_REQUEST['a']=='open')); +} + + +$ost->addExtraHeader('<script type="text/javascript" src="js/ticket.js"></script>'); +$ost->addExtraHeader('<meta name="tip-namespace" content="tasks.queue" />', + "$('#content').data('tipNamespace', 'tasks.queue');"); + +if($task) { + $ost->setPageTitle(sprintf(__('Task #%s'),$task->getNumber())); + $nav->setActiveSubMenu(-1); + $inc = 'templates/task-view.tmpl.php'; + if ($_REQUEST['a']=='edit' + && $task->checkStaffPerm($thisstaff, TaskModel::PERM_EDIT)) { + $inc = 'task-edit.inc.php'; + if (!$forms) $forms=DynamicFormEntry::forObject($task->getId(), 'A'); + // Auto add new fields to the entries + foreach ($forms as $f) $f->addMissingFields(); + } elseif($_REQUEST['a'] == 'print' && !$task->pdfExport($_REQUEST['psize'])) + $errors['err'] = __('Internal error: Unable to export the task to PDF for print.'); +} else { + $inc = 'tasks.inc.php'; + if ($_REQUEST['a']=='open' && + $thisstaff->hasPerm(Task::PERM_CREATE)) + $inc = 'task-open.inc.php'; + elseif($_REQUEST['a'] == 'export') { + $ts = strftime('%Y%m%d'); + if (!($query=$_SESSION[':Q:tasks'])) + $errors['err'] = __('Query token not found'); + elseif (!Export::saveTasks($query, "tasks-$ts.csv", 'csv')) + $errors['err'] = __('Internal error: Unable to dump query results'); + } + + //Clear active submenu on search with no status + if($_REQUEST['a']=='search' && !$_REQUEST['status']) + $nav->setActiveSubMenu(-1); + + //set refresh rate if the user has it configured + if(!$_POST && !$_REQUEST['a'] && ($min=$thisstaff->getRefreshRate())) { + $js = "clearTimeout(window.task_refresh); + window.task_refresh = setTimeout($.refreshTaskView," + .($min*60000).");"; + $ost->addExtraHeader('<script type="text/javascript">'.$js.'</script>', + $js); + } +} + +require_once(STAFFINC_DIR.'header.inc.php'); +require_once(STAFFINC_DIR.$inc); +require_once(STAFFINC_DIR.'footer.inc.php');