diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php new file mode 100644 index 0000000000000000000000000000000000000000..e58507401e859115a20cc05458781227982f7454 --- /dev/null +++ b/include/ajax.tasks.php @@ -0,0 +1,78 @@ +<?php +/********************************************************************* + ajax.tasks.php + + AJAX interface for tasks + + Peter Rotich <peter@osticket.com> + Copyright (c) 20014 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: +**********************************************************************/ + +if(!defined('INCLUDE_DIR')) die('403'); + +include_once(INCLUDE_DIR.'class.ticket.php'); +require_once(INCLUDE_DIR.'class.ajax.php'); +require_once(INCLUDE_DIR.'class.task.php'); + +class TasksAjaxAPI extends AjaxController { + + function preview($tid) { + global $thisstaff; + + // TODO: check staff's access. + if(!$thisstaff || !($task=Task::lookup($tid))) + Http::response(404, __('No such task')); + + include STAFFINC_DIR . 'templates/task-preview.tmpl.php'; + } + + function task($tid) { + global $thisstaff; + + // TODO: check staff's access. + if (!$thisstaff || !($task=Task::lookup($tid))) + Http::response(404, __('No such task')); + + $info=$errors=array(); + $task_note_form = new Form(array( + 'attachments' => new FileUploadField(array('id'=>'attach', + 'name'=>'attach:note', + 'configuration' => array('extensions'=>''))) + )); + + if ($_POST) { + + switch ($_POST['a']) { + case 'postnote': + $vars = $_POST; + $attachments = $task_note_form->getField('attachments')->getClean(); + $vars['cannedattachments'] = array_merge( + $vars['cannedattachments'] ?: array(), $attachments); + + if(($note=$task->postNote($vars, $errors, $thisstaff))) { + $msg=__('Note posted successfully'); + // Clear attachment list + $task_note_form->setSource(array()); + $task_note_form->getField('attachments')->reset(); + Draft::deleteForNamespace('task.note.'.$task->getId(), + $thisstaff->getId()); + } else { + if(!$errors['err']) + $errors['err'] = __('Unable to post the note - missing or invalid data.'); + } + break; + default: + $errors['err'] = __('Unknown action'); + } + } + + include STAFFINC_DIR . 'templates/task-view.tmpl.php'; + } +} +?> diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 196bcc390e7147479bc5fd073dfaef3ec25e331b..28c97eaf8c777348649cce147f44c9bc3d14e65c 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -854,5 +854,47 @@ class TicketsAjaxAPI extends AjaxController { include(STAFFINC_DIR . 'templates/ticket-status.tmpl.php'); } + + function tasks($tid) { + global $thisstaff; + + if (!($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'Unknown ticket'); + + include STAFFINC_DIR . 'ticket-tasks.inc.php'; + } + + function addTask($tid) { + global $thisstaff; + + if (!($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'Unknown ticket'); + + $info = array(); + + if ($_POST) { + /* + Draft::deleteForNamespace( + sprintf('ticket.%d.task', $ticket->getId()), + $thisstaff->getId()); + */ + $form = TaskForm::getDefaultForm()->getForm($_POST); + if ($form && ($task = Task::create($form, $ticket))) + Http::response(201, $task->to_json()); + + $info['error'] = __('Error adding task - try again!'); + } + + $info['action'] = sprintf('#tickets/%d/add-task', $ticket->getId()); + $info['title'] = sprintf( + __( 'Ticket #%1$s: %2$s'), + $ticket->getNumber(), + _('Add New Task') + ); + + include STAFFINC_DIR . 'templates/task.tmpl.php'; + } } ?> diff --git a/include/staff/dynamic-forms.inc.php b/include/staff/dynamic-forms.inc.php index bfa399490f858aebbc46bdf25354d010be718a2e..55f1fc04fb0404c63618a19da932994774ef4f1c 100644 --- a/include/staff/dynamic-forms.inc.php +++ b/include/staff/dynamic-forms.inc.php @@ -31,6 +31,7 @@ $showing=$pageNav->showing().' '._N('form','forms',$count); $forms = array( 'U' => 'icon-user', 'T' => 'icon-ticket', + 'A' => 'icon-tasks', 'C' => 'icon-building', 'O' => 'icon-group', ); diff --git a/include/staff/templates/task-preview.tmpl.php b/include/staff/templates/task-preview.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..ab2b9f90cac48499b6dd01bfe20c07ee66d13d26 --- /dev/null +++ b/include/staff/templates/task-preview.tmpl.php @@ -0,0 +1,137 @@ +<?php +$error=$msg=$warn=null; + +if($lock && $lock->getStaffId()==$thisstaff->getId()) + $warn.=' <span class="Icon lockedTicket">' + .sprintf(__('Ticket is locked by %s'), $lock->getStaffName()).'</span>'; +elseif($task->isOverdue()) + $warn.=' <span class="Icon overdueTicket">'.__('Marked overdue!').'</span>'; + +echo sprintf( + '<div style="width:600px; padding: 2px 2px 0 5px;" id="t%s"> + <h2>'.__('Task #%s').': %s</h2><br>', + $task->getNumber(), + $task->getNumber(), + Format::htmlchars($task->getTitle())); + +if($error) + echo sprintf('<div id="msg_error">%s</div>',$error); +elseif($msg) + echo sprintf('<div id="msg_notice">%s</div>',$msg); +elseif($warn) + echo sprintf('<div id="msg_warning">%s</div>',$warn); + +echo '<ul class="tabs" id="ticket-preview">'; + +echo ' + <li><a id="preview_tab" href="#preview" class="active" + ><i class="icon-list-alt"></i> '.__('Task Summary').'</a></li>'; +if (0 && $task->getNumCollaborators()) { +echo sprintf(' + <li><a id="collab_tab" href="#collab" + ><i class="icon-fixed-width icon-group + faded"></i> '.__('Collaborators (%d)').'</a></li>', + $task->getNumCollaborators()); +} +echo '</ul>'; +echo '<div id="ticket-preview_container">'; +echo '<div class="tab_content" id="preview_tab_content">'; +echo '<table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">'; +$status=sprintf('<span>%s</span>',ucfirst($task->getStatus())); +echo sprintf(' + <tr> + <th width="100">'.__('Status').':</th> + <td>%s</td> + </tr> + <tr> + <th>'.__('Created').':</th> + <td>%s</td> + </tr>',$status, + Format::db_datetime($task->getCreateDate())); + +if (0 && $task->isOpen() && $task->getEstDueDate()) { + echo sprintf(' + <tr> + <th>'.__('Due Date').':</th> + <td>%s</td> + </tr>', + Format::db_datetime($task->getEstDueDate())); +} +echo '</table>'; + + +echo '<hr> + <table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">'; +if(0 && $ticket->isOpen()) { + echo sprintf(' + <tr> + <th width="100">'.__('Assigned To').':</th> + <td>%s</td> + </tr>',$ticket->isAssigned()?implode('/', $ticket->getAssignees()):' <span class="faded">— '.__('Unassigned').' —</span>'); +} +echo sprintf( + ' + <tr> + <th width="100">'.__('Department').':</th> + <td>%s</td> + </tr>', + Format::htmlchars('Dept. HERE') + ); + +echo ' + </table>'; +echo '</div>'; +?> +<div class="tab_content" id="collab_tab_content" style="display:none;"> + <table border="0" cellspacing="" cellpadding="1"> + <colgroup><col style="min-width: 250px;"></col></colgroup> + <?php + if (0 && ($collabs=$task->getCollaborators())) {?> + <?php + foreach($collabs as $collab) { + echo sprintf('<tr><td %s><i class="icon-%s"></i> + <a href="users.php?id=%d" class="no-pjax">%s</a> <em><%s></em></td></tr>', + ($collab->isActive()? '' : 'class="faded"'), + ($collab->isActive()? 'comments' : 'comment-alt'), + $collab->getUserId(), + $collab->getName(), + $collab->getEmail()); + } + } else { + echo __("Task doesn't have any collaborators."); + }?> + </table> + <br> + <?php + echo sprintf('<span><a class="collaborators" + href="#tasks/%d/collaborators">%s</a></span>', + $task->getId(), + 0 + ? __('Manage Collaborators') : __('Add Collaborator') + ); + ?> +</div> +</div> +<?php +$options = array(); +$options[]=array('action'=>sprintf(__('Thread (%d)'), + $task->getThread()->getNumEntries()), + 'url'=>"tickets.php?id=$tid"); +if ($thisstaff->canAssignTickets()) + $options[]=array('action'=>($task->isAssigned()?__('Reassign'):__('Assign')),'url'=>"tickets.php?id=$tid#assign"); + +if ($thisstaff->canTransferTickets()) + $options[]=array('action'=>'Transfer','url'=>"tickets.php?id=$tid#transfer"); + +if ($thisstaff->canEditTickets()) + $options[]=array('action'=>'Edit Task','url'=>"tickets.php?id=$tid&a=edit"); + +if ($options) { + echo '<ul class="tip_menu">'; + foreach($options as $option) + echo sprintf('<li><a href="%s">%s</a></li>',$option['url'],$option['action']); + echo '</ul>'; +} + +echo '</div>'; +?> diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..686ffa6fe3f4ee3fcca96629e220843ec36e22de --- /dev/null +++ b/include/staff/templates/task-view.tmpl.php @@ -0,0 +1,311 @@ +<?php +if (!defined('OSTSCPINC') || !$thisstaff || !is_object($task)) + die('Invalid path'); + +//Make sure the staff is allowed to access this task +/* + if (!@$thisstaff->isStaff() || !$task->checkStaffAccess($thisstaff)) + die('Access Denied'); +*/ + +//Re-use the post info on error...savekeyboards.org (Why keyboard? -> some people care about objects than users!!) +$info=($_POST && $errors)?Format::input($_POST):array(); + +/* +$dept = $task->getDept(); //Dept +$staff = $task->getStaff(); //Assigned or closed by.. +$team = $task->getTeam(); //Assigned team. +*/ +$id = $task->getId(); //Ticket ID. +if ($task->isOverdue()) + $warn.=' <span class="Icon overdueTicket">'.__('Marked overdue!').'</span>'; + +?> +<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 + echo sprintf(__('Task #%s'), $task->getNumber()); ?></a> + </h3> + </td> + <td width="auto" class="flush-right has_bottom_border"> + <span> + <i class="icon-list"></i> + <select name="task-action"> + <option value="" selected="selected"> + <?php echo _('Task Options'); ?> + </option> + </select> + </span> + </td> + </tr> +</table> +<table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0"> + <tr> + <td width="50%"> + <table border="0" cellspacing="" cellpadding="4" width="100%"> + <tr> + <th width="100"><?php echo __('Status');?>:</th> + <td><?php echo $task->getStatus(); ?></td> + </tr> + <tr> + <th><?php echo __('Department');?>:</th> + <td><?php echo Format::htmlchars( (string) $task->getDept()); ?></td> + </tr> + <tr> + <th><?php echo __('Create Date');?>:</th> + <td><?php echo Format::db_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> + <th width="100"><?php echo __('Assigned To');?>:</th> + <td> + <?php + if ( 0 && $ticket->isAssigned()) + echo Format::htmlchars(implode('/', $ticket->getAssignees())); + else + echo '<span class="faded">— '.__('Unassigned').' —</span>'; + ?> + </td> + </tr> + <?php + } else { ?> + <tr> + <th width="100"><?php echo __('Closed By');?>:</th> + <td> + <?php + if (0 && ($staff = $task->getStaff())) + echo Format::htmlchars($staff->getName()); + else + echo '<span class="faded">— '.__('Unknown').' —</span>'; + ?> + </td> + </tr> + <?php + } ?> + <tr> + <th><?php echo __('SLA Plan');?>:</th> + <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">— '.__('None').' —</span>'; ?></td> + </tr> + <?php + if($task->isOpen()){ ?> + <tr> + <th><?php echo __('Due Date');?>:</th> + <td><?php echo 'here'; //Format::db_datetime($ticket->getEstDueDate()); ?></td> + </tr> + <?php + }else { ?> + <tr> + <th><?php echo __('Close Date');?>:</th> + <td><?php echo 0 ? + Format::db_datetime($task->getCloseDate()) : ' recently! '; ?></td> + </tr> + <?php + } + ?> + </table> + </td> + </tr> +</table> +<br> +<br> +<table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0"> +<?php +$idx = 0; +foreach (DynamicFormEntry::forObject($task->getId(), + ObjectModel::OBJECT_TYPE_TASK) as $form) { + $answers = array_filter($form->getAnswers(), function ($a) { + return $a->getField()->isStorable(); + }); + if (count($answers) == 0) + continue; + ?> + <tr> + <td colspan="2"> + <table cellspacing="0" cellpadding="4" width="100%" border="0"> + <?php foreach($answers as $a) { + if (!($v = $a->display())) continue; ?> + <tr> + <th width="100"><?php + echo $a->getField()->get('label'); + ?>:</th> + <td><?php + echo $v; + ?></td> + </tr> + <?php + } ?> + </table> + </td> + </tr> + <?php + $idx++; +} ?> +</table> +<div class="clear"></div> +<div id="task_thread_container"> + <div id="task_thread_content"> + <?php + $threadTypes=array('M'=>'message','R'=>'response', 'N'=>'note'); + /* -------- Messages & Responses & Notes (if inline)-------------*/ + $types = array('M', 'R', 'N'); + if(($thread=$task->getThreadEntries($types))) { + foreach($thread as $entry) { ?> + <table class="thread-entry <?php echo $threadTypes[$entry['type']]; ?>" cellspacing="0" cellpadding="1" width="940" border="0"> + <tr> + <th colspan="4" width="100%"> + <div> + <span class="pull-left"> + <span style="display:inline-block"><?php + echo Format::db_datetime($entry['created']);?></span> + <span style="display:inline-block;padding:0 1em" class="faded title"><?php + echo Format::truncate($entry['title'], 100); ?></span> + </span> + <span class="pull-right" style="white-space:no-wrap;display:inline-block"> + <span style="vertical-align:middle;" class="textra"></span> + <span style="vertical-align:middle;" + class="tmeta faded title"><?php + echo Format::htmlchars($entry['name'] ?: $entry['poster']); ?></span> + </span> + </div> + </th> + </tr> + <tr><td colspan="4" class="thread-body" id="thread-id-<?php + echo $entry['id']; ?>"><div><?php + echo $entry['body']->toHtml(); ?></div></td></tr> + <?php + $urls = null; + if($entry['attachments'] + && ($tentry = $task->getThreadEntry($entry['id'])) + && ($urls = $tentry->getAttachmentUrls()) + && ($links = $tentry->getAttachmentsLinks())) {?> + <tr> + <td class="info" colspan="4"><?php echo $links; ?></td> + </tr> <?php + } + if ($urls) { ?> + <script type="text/javascript"> + $('#thread-id-<?php echo $entry['id']; ?>') + .data('urls', <?php + echo JsonDataEncoder::encode($urls); ?>) + .data('id', <?php echo $entry['id']; ?>); + </script> +<?php + } ?> + </table> + <?php + if ($entry['type'] == 'M') + $msgId = $entry['id']; + } + } else { + echo '<p>'.__('Error fetching thread - get technical help.').'</p>'; + }?> + </div> +</div> +<div class="clear" style="padding-bottom:10px;"></div> +<?php if($errors['err']) { ?> + <div id="msg_error"><?php echo $errors['err']; ?></div> +<?php }elseif($msg) { ?> + <div id="msg_notice"><?php echo $msg; ?></div> +<?php }elseif($warn) { ?> + <div id="msg_warning"><?php echo $warn; ?></div> +<?php } ?> +<div id="response_options"> + <ul class="tabs"></ul> + <form id="task_note" + action="#tasks/<? echo $task->getId(); ?>" + name="task_note" + method="post" enctype="multipart/form-data"> + <?php csrf_token(); ?> + <input type="hidden" name="id" value="<?php echo $task->getId(); ?>"> + <input type="hidden" name="a" value="postnote"> + <table width="100%" border="0" cellspacing="0" cellpadding="3"> + <tr> + <td> + <div> + <div class="faded" style="padding-left:0.15em"><?php + echo __('Note title - summary of the note (optional)'); ?></div> + <input type="text" name="title" id="title" size="60" value="<?php echo $info['title']; ?>" > + <br/> + <span class="error"> <?php echo $errors['title']; ?></span> + </div> + <div> + <label><strong><?php echo __('Internal Note'); ?></strong><span class='error'> * <?php echo $errors['note']; ?></span></label> + </div> + <textarea name="note" id="internal_note" cols="80" + placeholder="<?php echo __('Note details'); ?>" + rows="9" wrap="soft" data-draft-namespace="task.note" + data-draft-object-id="<?php echo $task->getId(); ?>" + class="richtext ifhtml draft draft-delete"><?php + echo $info['note']; + ?></textarea> + <div class="attachments"> + <?php + if ($task_note_form) + print $task_note_form->getField('attachments')->render(); + ?> + </div> + </td> + </tr> + <tr> + <td> + <div><?php echo __('Task Status');?> + <span class="faded"> - </span> + <select name="task_status"> + <option value="1" <?php + echo $task->isOpen() ? + 'selected="selected"': ''; ?>> <?php + echo _('Open'); ?></option> + <option value="0" <?php + echo $task->isClosed() ? + 'selected="selected"': ''; ?>> <?php + echo _('Closed'); ?></option> + </select> + <span class='error'><?php echo + $errors['task_status']; ?></span> + </div> + </td> + </tr> + </table> + <p style="padding-left:165px;"> + <input class="btn_sm" type="submit" value="<?php echo __('Post Note');?>"> + <input class="btn_sm" type="reset" value="<?php echo __('Reset');?>"> + </p> + </form> + </div> +<script type="text/javascript"> +$(function() { + $(document).on('click', 'a.active#ticket_tasks', function(e) { + e.preventDefault(); + $('div#task_content').hide().empty(); + $('div#tasks_content').show(); + 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/templates/task.tmpl.php b/include/staff/templates/task.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..959a80c73a8d52055b8954b9cbf9f6f96a973ba9 --- /dev/null +++ b/include/staff/templates/task.tmpl.php @@ -0,0 +1,46 @@ +<?php + +if (!$info['title']) + $info['title'] = __('New Task'); + +?> +<div id="task-form"> +<h3><?php echo $info['title']; ?></h3> +<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> +<hr/> +<?php + +if ($info['error']) { + echo sprintf('<p id="msg_error">%s</p>', $info['error']); +} elseif ($info['warning']) { + echo sprintf('<p id="msg_warning">%s</p>', $info['warning']); +} elseif ($info['msg']) { + echo sprintf('<p id="msg_notice">%s</p>', $info['msg']); +} ?> +<div id="new-task-form" style="display:block;"> +<form method="post" class="org" action="<?php echo $info['action'] ?: '#tasks/add'; ?>"> + <table width="100%" class="fixed"> + <?php + if (!$form) $form = TaskForm::getInstance(); + $form->render(true, + __('Create New Task'), + array( + 'draft-namespace' => sprintf('ticket.%d.task', + $ticket->getId())) + ); ?> + </table> + <hr> + <p class="full-width"> + <span class="buttons pull-left"> + <input type="reset" value="<?php echo __('Reset'); ?>"> + <input type="button" name="cancel" class="close" + value="<?php echo __('Cancel'); ?>"> + </span> + <span class="buttons pull-right"> + <input type="submit" value="<?php echo __('Create Task'); ?>"> + </span> + </p> +</form> +</div> +<div class="clear"></div> +</div> diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..24f111be56f2e238d055182b4e4ec2e092352b2e --- /dev/null +++ b/include/staff/ticket-tasks.inc.php @@ -0,0 +1,161 @@ +<?php + +//TODO: Make it ORM based once we marge other models. +$select ='SELECT task.*, dept.dept_name ' + .' ,CONCAT_WS(" ", staff.firstname, staff.lastname) as staff, team.name as team ' + .' ,IF(staff.staff_id IS NULL,team.name,CONCAT_WS(" ", staff.lastname, staff.firstname)) as assigned '; + +$from =' FROM '.TASK_TABLE.' task ' + .' LEFT JOIN '.DEPT_TABLE.' dept ON task.dept_id=dept.dept_id ' + .' LEFT JOIN '.STAFF_TABLE.' staff ON (task.staff_id=staff.staff_id) ' + .' LEFT JOIN '.TEAM_TABLE.' team ON (task.team_id=team.team_id) '; + +if ($ticket) + $where = 'WHERE task.object_type="T" AND task.object_id = '.db_input($ticket->getId()); + +$query ="$select $from $where ORDER BY task.created DESC"; + +// Fetch the results +$results = array(); +$res = db_query($query); +while ($row = db_fetch_array($res)) + $results[$row['id']] = $row; + +?> + +<div id="tasks_content" style="display:block;"> +<div style="width:700px; float:left;"> + <?php + if ($results) { + echo '<strong>'.sprintf(_N('Showing %d Task', 'Showing %d Tasks', + count($results)), count($results)).'</strong>'; + } else { + echo sprintf(__('%s does not have any tasks'), $ticket? 'Ticket' : + 'System'); + } + ?> +</div> +<div style="float:right;text-align:right;padding-right:5px;"> + <?php + if ($ticket) { ?> + <a + class="Icon newTicket ticket-action" + data-dialog='{"size":"large"}' + href="#tickets/<?php + echo $ticket->getId(); ?>/add-task"> <?php + print __('Add New Task'); ?></a> + <?php + } ?> +</div> +<br/> +<div> +<?php +if ($results) { ?> +<form action="tickets.php?id=<?php echo $ticket->getId(); ?>" method="POST" name='tasks' style="padding-top:10px;"> +<?php csrf_token(); ?> + <input type="hidden" name="a" value="mass_process" > + <input type="hidden" name="do" id="action" value="" > + <table class="list" border="0" cellspacing="1" cellpadding="2" width="940"> + <thead> + <tr> + <?php + if (0) {?> + <th width="8px"> </th> + <?php + } ?> + <th width="70"><?php echo __('Number'); ?></th> + <th width="100"><?php echo __('Date'); ?></th> + <th width="100"><?php echo __('Status'); ?></th> + <th width="300"><?php echo __('Title'); ?></th> + <th width="200"><?php echo __('Department'); ?></th> + <th width="200"><?php echo __('Assignee'); ?></th> + </tr> + </thead> + <tbody class="tasks"> + <?php + foreach($results as $row) { + if (!($task = Task::lookup($row['id']))) + continue; + + $flag=null; + if ($row['lock_id']) + $flag='locked'; + elseif ($row['isoverdue']) + $flag='overdue'; + + $assigned=''; + if ($row['staff_id']) + $assigned=sprintf('<span class="Icon staffAssigned">%s</span>',Format::truncate($row['staff'],40)); + elseif ($row['team_id']) + $assigned=sprintf('<span class="Icon teamAssigned">%s</span>',Format::truncate($row['team'],40)); + else + $assigned=' '; + + $status = $task->isOpen() ? '<strong>open</strong>': 'closed'; + + $tid=$row['number']; + $title = Format::htmlchars(Format::truncate($task->getTitle(),40)); + $threadcount= $task->getThread()->getNumEntries(); + ?> + <tr id="<?php echo $row['id']; ?>"> + <?php + //Implement mass action....if need be. + if (0) { ?> + <td align="center" class="nohover"> + <input class="ckb" type="checkbox" name="tids[]" + value="<?php echo $row['id']; ?>" <?php echo $sel?'checked="checked"':''; ?>> + </td> + <?php + } ?> + <td align="center" nowrap> + <a class="Icon no-pjax preview" + title="<?php echo __('Preview Task'); ?>" + href="#tasks/<?php echo $task->getId(); ?>/view" + data-preview="#tasks/<?php echo $task->getId(); ?>/preview" + ><?php echo $task->getNumber(); ?></a></td> + <td align="center" nowrap><?php echo + Format::db_datetime($row['created']); ?></td> + <td><?php echo $status; ?></td> + <td><a <?php if ($flag) { ?> class="no-pjax" + title="<?php echo ucfirst($flag); ?> Task" <?php } ?> + href="#tasks/<?php echo $task->getId(); ?>/view"><?php + echo $title; ?></a> + <?php + if ($threadcount>1) + echo "<small>($threadcount)</small> ".'<i + class="icon-fixed-width icon-comments-alt"></i> '; + if ($row['collaborators']) + echo '<i class="icon-fixed-width icon-group faded"></i> '; + if ($row['attachments']) + echo '<i class="icon-fixed-width icon-paperclip"></i> '; + ?> + </td> + <td><?php echo Format::truncate($row['dept_name'], 40); ?></td> + <td> <?php echo $assigned; ?></td> + </tr> + <?php + } + ?> + </tbody> +</table> +</form> +<?php + } ?> +</div> +</div> +<div id="task_content" style="display:none;"> +</div> +<script type="text/javascript"> +$(function() { + $(document).on('click.tasks', 'tbody.tasks a, a#reload-task', function(e) { + e.preventDefault(); + var url = 'ajax.php/'+$(this).attr('href').substr(1); + var $container = $('div#task_content'); + $container.load(url, function () { + $('.tip_box').remove(); + $('div#tasks_content').hide(); + }).show(); + return false; + }); +}); +</script> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 54095699b6015dee25b897cf47df82cb2b4ab717..d8d0e5b43b180dcb604540424e698344848c8daa 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -378,6 +378,12 @@ $tcount+= $ticket->getNumNotes(); ?> <ul class="tabs threads" id="ticket_tabs" > <li><a class="active" href="#ticket_thread"><?php echo sprintf(__('Ticket Thread (%d)'), $tcount); ?></a></li> + <li><a id="ticket_tasks" href="<?php + echo sprintf('#tickets/%d/tasks', $ticket->getId()); ?>"><?php + echo __('Tasks'); + if ($ticket->getNumTasks()) + echo sprintf(' (%d)', $ticket->getNumTasks()); + ?></a></li> </ul> <div id="ticket_tabs_container"> <div id="ticket_thread" class="tab_content"> diff --git a/scp/ajax.php b/scp/ajax.php index dc0d35e34821a50a57ff886ff52377c806781846..d0d6f409f4628e114db3833635b848e993dd570a 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -144,6 +144,8 @@ $dispatcher = patterns('', url_post('^(?P<tid>\d+)/status$', 'setTicketStatus'), url_get('^status/(?P<status>\w+)(?:/(?P<sid>\d+))?$', 'changeSelectedTicketsStatus'), url_post('^status/(?P<state>\w+)$', 'setSelectedTicketsStatus'), + url_get('^(?P<tid>\d+)/tasks$', 'tasks'), + url('^(?P<tid>\d+)/add-task$', 'addTask'), url_get('^lookup', 'lookup'), url('^search', patterns('ajax.search.php:SearchAjaxAPI', url_get('^$', 'getAdvancedSearchDialog'), @@ -156,6 +158,11 @@ $dispatcher = patterns('', url_get('^/field/(?P<id>[\w_!:]+)$', 'addField') )) )), + url('^/tasks/', patterns('ajax.tasks.php:TasksAjaxAPI', + url_get('^(?P<tid>\d+)/preview$', 'preview'), + url_get('^(?P<tid>\d+)/view$', 'task'), + url_post('^(?P<tid>\d+)$', 'task') + )), url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI', url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'), url_post('^(?P<cid>\d+)$', 'updateCollaborator') diff --git a/scp/css/scp.css b/scp/css/scp.css index e797c6083c7d134b349d94b230960d02ade136d2..0d76bf2ba467afd9ff21f10e99c9f039bdc3fb2e 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -459,7 +459,7 @@ table.list thead th { color:#000; text-align:left; vertical-align:top; - padding: 2px 4px; + padding: 4px 5px; } table.list th a { @@ -468,7 +468,7 @@ table.list th a { color:#000; } -table.list thead th a { padding: 3px; padding-right: 15px; display: block; white-space: nowrap; color: #000; background: url('../images/asc_desc.gif') 100% 50% no-repeat; } +table.list thead th a { padding-right: 15px; display: block; white-space: nowrap; color: #000; background: url('../images/asc_desc.gif') 100% 50% no-repeat; } table.list thead th a.asc { background: url('../images/asc.gif') 100% 50% no-repeat #cfe6ff; } table.list thead th a.desc { background: url('../images/desc.gif') 100% 50% no-repeat #cfe6ff; }