diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index dde62c7f2a511474610c5913d14446c6df87049d..6b1c6cd6e3ebe471ba32d3a9d803596e6a3407a1 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -793,32 +793,54 @@ class TicketsAjaxAPI extends AjaxController { || !$task->checkStaffPerm($thisstaff)) Http::response(404, 'Unknown task'); - $info=$errors=array(); - $note_form = new SimpleForm(array( + $info = $errors = array(); + $note_attachments_form = new SimpleForm(array( 'attachments' => new FileUploadField(array('id'=>'attach', - 'name'=>'attach:note', - 'configuration' => array('extensions'=>''))) - )); + 'name'=>'attach:note', + 'configuration' => array('extensions'=>''))) + )); + + $reply_attachments_form = new SimpleForm(array( + 'attachments' => new FileUploadField(array('id'=>'attach', + 'name'=>'attach:reply', + 'configuration' => array('extensions'=>''))) + )); if ($_POST) { + $vars = $_POST; switch ($_POST['a']) { case 'postnote': - $vars = $_POST; - $attachments = $note_form->getField('attachments')->getClean(); + $attachments = $note_attachments_form->getField('attachments')->getClean(); $vars['cannedattachments'] = array_merge( $vars['cannedattachments'] ?: array(), $attachments); - if(($note=$task->postNote($vars, $errors, $thisstaff))) { + if (($note=$task->postNote($vars, $errors, $thisstaff))) { $msg=__('Note posted successfully'); // Clear attachment list - $note_form->setSource(array()); - $note_form->getField('attachments')->reset(); + $note_attachments_form->setSource(array()); + $note_attachments_form->getField('attachments')->reset(); Draft::deleteForNamespace('task.note.'.$task->getId(), $thisstaff->getId()); } else { - if(!$errors['err']) + if (!$errors['err']) $errors['err'] = __('Unable to post the note - missing or invalid data.'); } break; + case 'postreply': + $attachments = $reply_attachments_form->getField('attachments')->getClean(); + $vars['cannedattachments'] = array_merge( + $vars['cannedattachments'] ?: array(), $attachments); + if (($response=$task->postReply($vars, $errors))) { + $msg=__('Update posted successfully'); + // Clear attachment list + $reply_attachments_form->setSource(array()); + $reply_attachments_form->getField('attachments')->reset(); + Draft::deleteForNamespace('task.reply.'.$task->getId(), + $thisstaff->getId()); + } else { + if (!$errors['err']) + $errors['err'] = __('Unable to post the reply - missing or invalid data.'); + } + break; default: $errors['err'] = __('Unknown action'); } diff --git a/include/class.config.php b/include/class.config.php index f9aec5d0f040a3bcf1e338a35dcdaece50ea586c..c9125aaa3a4be615079695d4d3668c43e35c911b 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -1181,7 +1181,6 @@ class OsticketConfig extends Config { function updateTasksSettings($vars, &$errors) { $f=array(); - $f['default_task_sla_id']=array('type'=>'int', 'required'=>1, 'error'=>__('Selection required')); $f['default_task_priority_id']=array('type'=>'int', 'required'=>1, 'error'=>__('Selection required')); if (!preg_match('`(?!<\\\)#`', $vars['task_number_format'])) diff --git a/include/class.thread_actions.php b/include/class.thread_actions.php index df4cf37e02e1d48cb087a47b293053d20ab17236..428793399807d245f86cf0592041606784cff557 100644 --- a/include/class.thread_actions.php +++ b/include/class.thread_actions.php @@ -277,10 +277,11 @@ class TEA_EditAndResendThreadEntry extends TEA_EditThreadEntry { function resend($response) { global $cfg, $thisstaff; - $vars = $_POST; - $ticket = $response->getThread()->getObject(); + if (!($object = $response->getThread()->getObject())) + return false; - $dept = $ticket->getDept(); + $vars = $_POST; + $dept = $object->getDept(); $poster = $response->getStaff(); if ($thisstaff && $vars['signature'] == 'mine') @@ -299,23 +300,25 @@ class TEA_EditAndResendThreadEntry extends TEA_EditThreadEntry { 'poster' => $response->getStaff()); $options = array('thread' => $response); - if (($email=$dept->getEmail()) - && ($tpl = $dept->getTemplate()) - && ($msg=$tpl->getReplyMsgTemplate()) - ) { - $msg = $ticket->replaceVars($msg->asArray(), - $variables + array('recipient' => $ticket->getOwner())); + // Resend response to collabs + if (($object instanceof Ticket) + && ($email=$dept->getEmail()) + && ($tpl = $dept->getTemplate()) + && ($msg=$tpl->getReplyMsgTemplate())) { + + $msg = $object->replaceVars($msg->asArray(), + $variables + array('recipient' => $object->getOwner())); $attachments = $cfg->emailAttachments() ? $response->getAttachments() : array(); - $email->send($ticket->getOwner(), $msg['subj'], $msg['body'], + $email->send($object->getOwner(), $msg['subj'], $msg['body'], $attachments, $options); } // TODO: Add an option to the dialog - $ticket->notifyCollaborators($response, array('signature' => $signature)); + $object->notifyCollaborators($response, array('signature' => $signature)); // Log an event that the item was resent - $ticket->logEvent('resent', array('entry' => $response->id)); + $object->logEvent('resent', array('entry' => $response->id)); // Flag the entry as resent $response->flags |= ThreadEntry::FLAG_RESENT; diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php index a72674066dda6442b29d01709e30c3d9b562823e..18ea9cd7a22f8eee3adf92a7ee667290c9a6b17b 100644 --- a/include/staff/templates/task-view.tmpl.php +++ b/include/staff/templates/task-view.tmpl.php @@ -4,6 +4,8 @@ if (!defined('OSTSCPINC') || !($role = $thisstaff->getRole($task->getDeptId()))) die('Invalid path'); +global $cfg; + $actions = array(); $actions += array( @@ -54,7 +56,10 @@ if ($role->hasPerm(Task::PERM_DELETE)) { $info=($_POST && $errors)?Format::input($_POST):array(); -$id = $task->getId(); +$id = $task->getId(); +$dept = $task->getDept(); +$thread = $task->getThread(); + if ($task->isOverdue()) $warn.=' <span class="Icon overdueTicket">'.__('Marked overdue!').'</span>'; @@ -67,7 +72,7 @@ if ($task->isOverdue()) <?php if ($ticket) { ?> <strong> - <a id="ticket-tasks" href="#"> All Tasks (<?php echo $ticket->getNumTasks(); ?>)</a> + <a id="all-ticket-tasks" href="#"> All Tasks (<?php echo $ticket->getNumTasks(); ?>)</a> / <?php echo $task->getTitle(); ?> — @@ -76,7 +81,9 @@ if ($task->isOverdue()) <?php echo ' class="preview" '; echo sprintf('data-preview="#tasks/%d/preview" ', $task->getId()); - echo sprintf('href="#tasks/%d" ', $task->getId()); + echo sprintf('href="#tickets/%s/tasks/%d/view" ', + $ticket->getId(), $task->getId() + ); ?> ><?php echo sprintf('#%s', $task->getNumber()); ?></a> @@ -98,6 +105,12 @@ if ($task->isOverdue()) <div class="flush-right"> <?php if ($ticket) { ?> + <a id="task-view" + target="_blank" + class="action-button" + href="tasks.php?id=<?php + echo $task->getId(); ?>"><i class="icon-share"></i> <?php + echo __('View Task'); ?></a> <span class="action-button" data-dropdown="#action-dropdown-task-options"> @@ -308,10 +321,104 @@ if ($ticket) else $action = 'tasks.php?id='.$task->getId(); ?> -<div id="response_options" class="sticky bar stop"> - <ul class="tabs"></ul> - <form id="<?php echo $ticket? 'ticket_task_note': 'task_note'; ?>" +<div id="task_response_options" class="<?php echo $ticket ? 'ticket_task_actions' : ''; ?> sticky bar stop"> + <ul class="tabs"> + <?php + if ($role->hasPerm(TaskModel::PERM_REPLY)) { ?> + <li class="active"><a href="#task_reply"><?php echo __('Post Update');?></a></li> + <li><a href="#task_note"><?php echo __('Post Internal Note');?></a></li> + <?php + }?> + </ul> + <?php + if ($role->hasPerm(TaskModel::PERM_REPLY)) { ?> + <form id="task_reply" class="tab_content spellcheck" + action="<?php echo $action; ?>" + name="task_reply" 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="postreply"> + <input type="hidden" name="lockCode" value="<?php echo ($mylock) ? $mylock->getCode() : ''; ?>"> + <span class="error"></span> + <table style="width:100%" border="0" cellspacing="0" cellpadding="3"> + <tbody id="collab_sec" style="display:table-row-group"> + <tr> + <td> + <input type='checkbox' value='1' name="emailcollab" id="emailcollab" + <?php echo ((!$info['emailcollab'] && !$errors) || isset($info['emailcollab']))?'checked="checked"':''; ?> + style="display:<?php echo $thread->getNumCollaborators() ? 'inline-block': 'none'; ?>;" + > + <?php + $recipients = __('Add Participants'); + if ($thread->getNumCollaborators()) + $recipients = sprintf(__('Recipients (%d of %d)'), + $thread->getNumActiveCollaborators(), + $thread->getNumCollaborators()); + + echo sprintf('<span><a class="collaborators preview" + href="#thread/%d/collaborators"><span id="t%d-recipients">%s</span></a></span>', + $thread->getId(), + $thread->getId(), + $recipients); + ?> + </td> + </tr> + </tbody> + <tbody id="update_sec"> + <tr> + <td> + <div class="error"><?php echo $errors['response']; ?></div> + <input type="hidden" name="draft_id" value=""/> + <textarea name="response" id="task-response" cols="50" + data-signature-field="signature" data-dept-id="<?php echo $dept->getId(); ?>" + data-signature="<?php + echo Format::htmlchars(Format::viewableImages($signature)); ?>" + placeholder="<?php echo __( 'Start writing your update here.'); ?>" + rows="9" wrap="soft" + class="<?php if ($cfg->isRichTextEnabled()) echo 'richtext'; + ?> draft draft-delete" <?php + list($draft, $attrs) = Draft::getDraftAndDataAttrs('task.response', $task->getId(), $info['task.response']); + echo $attrs; ?>><?php echo $draft ?: $info['task.response']; + ?></textarea> + <div id="task_response_form_attachments" class="attachments"> + <?php + if ($reply_attachments_form) + print $reply_attachments_form->getField('attachments')->render(); + ?> + </div> + </td> + </tr> + <tr> + <td> + <div><?php echo __('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 Update');?>"> + <input class="btn_sm" type="reset" value="<?php echo __('Reset');?>"> + </p> + </form> + <?php + } ?> + <form id="task_note" action="<?php echo $action; ?>" + class="tab_content spellcheck <?php + echo $role->hasPerm(TaskModel::PERM_REPLY) ? 'hidden' : ''; ?>" name="task_note" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> @@ -320,18 +427,9 @@ else <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'); ?>" + <div><span class='error'><?php echo $errors['note']; ?></span></div> + <textarea name="note" id="task-note" cols="80" + placeholder="<?php echo __('Internal 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 @@ -339,15 +437,15 @@ else ?></textarea> <div class="attachments"> <?php - if ($note_form) - print $note_form->getField('attachments')->render(); + if ($note_attachments_form) + print $note_attachments_form->getField('attachments')->render(); ?> </div> </td> </tr> <tr> <td> - <div><?php echo __('Task Status');?> + <div><?php echo __('Status');?> <span class="faded"> - </span> <select name="task_status"> <option value="1" <?php @@ -375,7 +473,7 @@ else <script type="text/javascript"> $(function() { $(document).off('.tasks-content'); - $(document).on('click.tasks-content', 'a#ticket-tasks', function(e) { + $(document).on('click.tasks-content', '#all-ticket-tasks, #ticket-tasks-tab', function(e) { e.preventDefault(); $('div#task_content').hide().empty(); $('div#tasks_content').show(); @@ -401,7 +499,7 @@ $(function() { }); $(document).off('.tf'); - $(document).on('submit.tf', 'form#ticket_task_note', function(e) { + $(document).on('submit.tf', '.ticket_task_actions form', function(e) { e.preventDefault(); var $form = $(this); var $container = $('div#task_content'); diff --git a/include/staff/templates/tasks-actions.tmpl.php b/include/staff/templates/tasks-actions.tmpl.php index 306cd6ea9ab29c390f4dd01c3bca570890b7a2fe..378b0eb39ef290255142f615e825a11c976f7ea7 100644 --- a/include/staff/templates/tasks-actions.tmpl.php +++ b/include/staff/templates/tasks-actions.tmpl.php @@ -120,8 +120,8 @@ if ($actions) { } ?> <script type="text/javascript"> $(function() { - $(document).off('.tasks'); - $(document).on('click.tasks', 'a.tasks-action', function(e) { + $(document).off('.tasks-actions'); + $(document).on('click.tasks-actions', 'a.tasks-action', function(e) { e.preventDefault(); var count = checkbox_checker($('form#tasks'), 1); if (count) { diff --git a/include/staff/templates/thread-entry-edit.tmpl.php b/include/staff/templates/thread-entry-edit.tmpl.php index 1440780061aa0831d3009522a54f501c63425ca4..3f2d0fe56235af4ab6e539da566dc6ba578ce1ee 100644 --- a/include/staff/templates/thread-entry-edit.tmpl.php +++ b/include/staff/templates/thread-entry-edit.tmpl.php @@ -12,8 +12,9 @@ <?php if ($poster && $this->entry->type == 'R') { $signature_type = $poster->getDefaultSignatureType(); $signature = ''; - if (($T = $this->entry->getThread()->getObject()) instanceof Ticket) + if (($T = $this->entry->getThread()->getObject())) $dept = $T->getDept(); + switch ($poster->getDefaultSignatureType()) { case 'dept': if ($dept && $dept->canAppendSignature()) @@ -24,7 +25,7 @@ $signature_type = 'theirs'; break; } ?> - data-dept-id="<?php echo $dept->getId(); ?>" + data-dept-id="<?php echo $dept ? $dept->getId() : 0; ?>" data-poster-id="<?php echo $this->entry->staff_id; ?>" data-signature-field="signature" data-signature="<?php echo Format::htmlchars(Format::viewableImages($signature)); ?>" diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php index 84300a10d52a1ebb1d416bfa97670a7cf8c2f215..6bab893e5abf68446f016f2df92a2d8621f36528 100644 --- a/include/staff/ticket-tasks.inc.php +++ b/include/staff/ticket-tasks.inc.php @@ -133,9 +133,10 @@ if ($count) { ?> <script type="text/javascript"> $(function() { - $(document).off('click.tasks'); - $(document).on('click.tasks', 'tbody.tasks a, a#reload-task', function(e) { + $(document).off('click.taskv'); + $(document).on('click.taskv', 'tbody.tasks a, a#reload-task', function(e) { e.preventDefault(); + e.stopImmediatePropagation(); var url = 'ajax.php/'+$(this).attr('href').substr(1); var $container = $('div#task_content'); var $stop = $('ul#ticket_tabs').offset().top; @@ -146,6 +147,7 @@ $(function() { $('.tip_box').remove(); $('div#tasks_content').hide(); }); + return false; }); // Ticket Tasks @@ -172,7 +174,5 @@ $(function() { }, $options); return false; }); - - }); </script> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 695ac4d745a312fbe8a23042c8ef4e254d549f20..d037930f342b6b0f0d4cd495056ba48073d01bdc 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -417,7 +417,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); ?> <ul class="tabs clean threads" id="ticket_tabs" > <li class="active"><a href="#ticket_thread"><?php echo sprintf(__('Ticket Thread (%d)'), $tcount); ?></a></li> - <li><a id="ticket_tasks" href="#tasks" + <li><a id="ticket-tasks-tab" href="#tasks" data-url="<?php echo sprintf('#tickets/%d/tasks', $ticket->getId()); ?>"><?php echo __('Tasks'); diff --git a/scp/js/scp.js b/scp/js/scp.js index fee30477431bd42982dc70250182d34af6c8f5bb..69df220315aa0b8bb03c55099231349ee28aefe1 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -756,7 +756,7 @@ $.orgLookup = function (url, cb) { $.uid = 1; // Tabs -$(document).on('click.tab', 'ul.tabs li a', function(e) { +$(document).on('click.tab', 'ul.tabs > li > a', function(e) { e.preventDefault(); var $this = $(this), $ul = $(this).closest('ul'), @@ -777,6 +777,7 @@ $(document).on('click.tab', 'ul.tabs li a', function(e) { // TODO: Add / hide loading spinner }) ); + $this.removeData('url'); } else { $tab.addClass('tab_content'); diff --git a/scp/tasks.php b/scp/tasks.php index c22fa5bdb39671a5e47e835d8a8447711b022f6a..757d507fdafed1caa75bf576a82b7af791800419 100644 --- a/scp/tasks.php +++ b/scp/tasks.php @@ -26,12 +26,18 @@ if ($_REQUEST['id']) { } // Configure form for file uploads -$note_form = new SimpleForm(array( +$note_attachments_form = new SimpleForm(array( 'attachments' => new FileUploadField(array('id'=>'attach', 'name'=>'attach:note', 'configuration' => array('extensions'=>''))) )); +$reply_attachments_form = new SimpleForm(array( + 'attachments' => new FileUploadField(array('id'=>'attach', + 'name'=>'attach:reply', + 'configuration' => array('extensions'=>''))) +)); + //At this stage we know the access status. we can process the post. if($_POST && !$errors): @@ -42,7 +48,7 @@ if($_POST && !$errors): switch(strtolower($_POST['a'])): case 'postnote': /* Post Internal Note */ $vars = $_POST; - $attachments = $note_form->getField('attachments')->getClean(); + $attachments = $note_attachments_form->getField('attachments')->getClean(); $vars['cannedattachments'] = array_merge( $vars['cannedattachments'] ?: array(), $attachments); @@ -51,24 +57,51 @@ if($_POST && !$errors): $msg=__('Internal note posted successfully'); // Clear attachment list - $note_form->setSource(array()); - $note_form->getField('attachments')->reset(); + $note_attachments_form->setSource(array()); + $note_attachments_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 + // Task 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; + case 'postreply': /* Post an update */ + $vars = $_POST; + $attachments = $reply_attachments_form->getField('attachments')->getClean(); + $vars['cannedattachments'] = array_merge( + $vars['cannedattachments'] ?: array(), $attachments); + + $wasOpen = ($task->isOpen()); + if (($response=$task->postReply($vars, $errors))) { + + $msg=__('Reply posted successfully'); + // Clear attachment list + $reply_attachments_form->setSource(array()); + $reply_attachments_form->getField('attachments')->reset(); + + if ($wasOpen && $task->isClosed()) + $task = null; //Going back to main listing. + else + // Task is still open -- clear draft for the note + Draft::deleteForNamespace('task.reply.'.$task->getId(), + $thisstaff->getId()); + + } else { + if (!$errors['err']) + $errors['err'] = __('Unable to post reply - missing or invalid data.'); + + $errors['postreply'] = __('Unable to post the reply. Correct the error(s) below and try again!'); + } + break; default: $errors['err']=__('Unknown action'); endswitch;