diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 2864efda74a09e4c5ceed50e699f15edbff6f5ef..3338ea4c536f334dcf73df470ecb62d2182b6e37 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -1256,6 +1256,70 @@ function refer($tid, $target=null) { return self::_changeSelectedTicketsStatus($state, $info, $errors); } + function markAs($tid, $action='') { + global $thisstaff; + + // Standard validation + if (!($ticket=Ticket::lookup($tid))) + Http::response(404, __('No such ticket')); + + if (!$ticket->checkStaffPerm($thisstaff, Ticket::PERM_REPLY) && !$thisstaff->isManager()) + Http::response(403, __('Permission denied')); + + $errors = array(); + $info = array(':title' => __('Please Confirm')); + + // Instantiate form for comment field + $form = MarkAsForm::instantiate($_POST); + + // Mark as answered or unanswered + if ($_POST) { + switch($action) { + case 'answered': + if($ticket->isAnswered()) + $errors['err'] = __('Ticket is already marked as answered'); + elseif (!$ticket->markAnswered()) + $errors['err'] = __('Cannot mark ticket as answered'); + break; + + case 'unanswered': + if(!$ticket->isAnswered()) + $errors['err'] = __('Ticket is already marked as unanswered'); + elseif (!$ticket->markUnanswered()) + $errors['err'] - __('Cannot mark ticket as unanswered'); + break; + + default: + Http::response(404, __('Unknown action')); + } + + // Retrun errors to form (if any) + if($errors) { + $info['error'] = $errors['err'] ?: sprintf(__('Unable to mark ticket as %s'), $action); + $form->addErrors($errors); + } else { + // Add comment (if provided) + $comments = $form->getComments(); + if ($comments) { + $title = __(sprintf('Ticket Marked %s', ucfirst($action))); + $_errors = array(); + + $ticket->postNote( + array('note' => $comments, 'title' => $title), + $_errors, $thisstaff, false); + } + + // Add success messages and log activity + $_SESSION['::sysmsgs']['msg'] = sprintf(__('Ticket marked as %s successfully'), $action); + $msg = sprintf(__('Ticket flagged as %s by %s'), $action, $thisstaff->getName()); + $ticket->logActivity(sprintf(__('Ticket Marked %s'), ucfirst($action)), $msg); + Http::response(201, $ticket->getId()); + } + } + + include STAFFINC_DIR . 'templates/mark-as.tmpl.php'; + } + function triggerThreadAction($ticket_id, $thread_id, $action) { $thread = ThreadEntry::lookup($thread_id); if (!$thread) diff --git a/include/class.file.php b/include/class.file.php index 5bbd3d8859b1303648bc6464a9075040b8a1e3c9..419a2820922f6405f83d236b8cc47934f566dbc5 100644 --- a/include/class.file.php +++ b/include/class.file.php @@ -197,6 +197,18 @@ class AttachmentFile extends VerySimpleModel { $options); } + // Generates full download URL for external sources. + // e.g. https://domain.tld/file.php?args=123 + function getExternalDownloadUrl($options=array()) { + global $cfg; + + $download = self::getDownloadUrl($options); + // Separate URL handle and args + list($handle, $args) = explode('file.php?', $download); + + return (string) rtrim($cfg->getBaseUrl(), '/').'/file.php?'.$args; + } + static function generateDownloadUrl($id, $key, $hash, $options = array()) { // Expire at the nearest midnight, allow at least12 hrs access diff --git a/include/class.forms.php b/include/class.forms.php index 3976cd8894b6d8f342766fb4165d74ff4ebf524b..2c3ec0da755cbfac1bd14b502c09c3ffa224a31f 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -5104,6 +5104,49 @@ class ReleaseForm extends Form { } } +class MarkAsForm extends Form { + static $id = 'markAs'; + + function getFields() { + if ($this->fields) + return $this->fields; + + $fields = array( + 'comments' => new TextareaField(array( + 'id' => 1, 'label'=> '', 'required'=>false, 'default'=>'', + 'configuration' => array( + 'html' => true, + 'size' => 'small', + 'placeholder' => __('Optional reason for marking ticket as (un)answered'), + ), + ) + ), + ); + + + $this->setFields($fields); + + return $this->fields; + } + + function getField($name) { + if (($fields = $this->getFields()) + && isset($fields[$name])) + return $fields[$name]; + } + + function isValid($include=false) { + if (!parent::isValid($include)) + return false; + + return !$this->errors(); + } + + function getComments() { + return $this->getField('comments')->getClean(); + } +} + class ReferralForm extends Form { static $id = 'refer'; diff --git a/include/class.thread.php b/include/class.thread.php index 9488835a3d80c68f1a8a949f2d21094f4b414368..343b3e9005689e01a11ef5611e3a00179b776f82 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -2964,6 +2964,14 @@ implements TemplateVariable { return $resp; } + function __toString() { + return $this->asVar(); + } + + function asVar() { + return $this->getVar('complete'); + } + function getVar($name) { switch ($name) { case 'original': @@ -2983,12 +2991,23 @@ implements TemplateVariable { if ($entry) return $entry->getBody(); + break; + case 'complete': + $content = ''; + $thread = $this; + ob_start(); + include INCLUDE_DIR.'client/templates/thread-export.tmpl.php'; + $content = ob_get_contents(); + ob_end_clean(); + return $content; + break; } } static function getVarScope() { return array( + 'complete' => __('Thread Correspondence'), 'original' => array('class' => 'MessageThreadEntry', 'desc' => __('Original Message')), 'lastmessage' => array('class' => 'MessageThreadEntry', 'desc' => __('Last Message')), ); diff --git a/include/client/templates/thread-export.tmpl.php b/include/client/templates/thread-export.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..5914a88da94a48a5da28c31dd1685a4e2f438ae4 --- /dev/null +++ b/include/client/templates/thread-export.tmpl.php @@ -0,0 +1,93 @@ +<?php +global $cfg; + +$entryTypes = array( + 'M' => array('color' => '#0088cc'), + 'R' => array('color' => '#e65524'), + ); + +AttachmentFile::objects()->filter(array( + 'attachments__thread_entry__thread__id' => $thread->getId(), + 'attachments__thread_entry__type__in' => array_keys($entryTypes) + ))->all(); + +$entries = $thread->getEntries(); +$entries->filter(array('type__in' => array_keys($entryTypes))); +?> +<style type="text/css"> + div {font-family: sans-serif;} +</style> +<div style="width: 100%; margin: 0; padding: 0;"> + <div style="padding:10px;"> + <p style="font-family: sans-serif; font-size:12px; color:#999;"> </p> + </div> + <table width="100%" cellpadding="0" cellspacing="0" border="0"> + <tbody> + <tr> + <td></td> + </tr> + <?php + foreach ($entries as $entry) { + $user = $entry->getUser() ?: $entry->getStaff(); + $name = $user ? $user->getName() : $entry->poster; + $color = $entryTypes[$entry->type]['color']; + ?> + <tr> + <td style=" border-top: 1px dashed #999;"> + <div style="background-color:#f7f7f7; padding:10px 20px;"> + <p style="font-family: sans-serif; padding:0; margin:0; color:<?php echo $color; ?>;"> + <strong><?php echo $name; ?></strong> + <span style="color:#888; font-size:12px; padding-left: 20px;"><?php + echo $entry->title; + ?> + </span> + </p> + <p style="font-family: sans-serif; padding:0; margin:0; color:#888; font-size:12px;"> + <?php + echo Format::daydatetime($entry->created); + ?> + </p> + </div> + <div style="padding:2px 20px;"> + <p style="font-family: sans-serif; font-size:14px; color:#555;"> + <?php + echo $entry->getBody()->display('email'); + ?> + </p> + <?php + if ($entry->has_attachments) { ?> + <p style="font-family: sans-serif; font-size:12px; line-height:20px; color:#888;"> + <?php echo __('Attachments'); ?> + <br /> + <?php + foreach ($entry->attachments as $a) { + if ($a->inline) continue; + $size = ''; + if ($a->file->size) + $size = sprintf('<small style="color:#ccc;"> (%s)</small>', + Format::file_size($a->file->size)); + + $filename = Format::htmlchars($a->getFilename()); + echo sprintf('<a href="%s" download="%s" + style="font-size:11px; color:#0088cc;" + target="_blank">%s</a> %s<br/>', + $a->file->getExternalDownloadUrl(), + $filename, + $filename, + $size); + } + ?> + </p> + <?php + } ?> + </div> + </td> + </tr> + <?php + } ?> + </tbody> + </table> + <div style="font-family: sans-serif; margin: 2px 0 14px 0; padding: 10px ; border-top: 1px solid #999; font-size:12px; color:#888;"> + + </div> +</div> diff --git a/include/staff/templates/mark-as.tmpl.php b/include/staff/templates/mark-as.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..49c2d2c8664467dcf3ba6dfac34f575f8a471604 --- /dev/null +++ b/include/staff/templates/mark-as.tmpl.php @@ -0,0 +1,57 @@ +<?php +global $cfg; + +$form = MarkAsForm::instantiate($_POST); +?> +<h3 class="drag-handle"><?php echo $info[':title'] ?: __('Please Confirm'); ?></h3> +<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> +<div class="clear"></div> +<hr/> +<?php +if ($info['error']) { + echo sprintf('<p id="msg_error">%s</p>', $info['error']); +} elseif ($info['warn']) { + echo sprintf('<p id="msg_warning">%s</p>', $info['warn']); +} elseif ($info['msg']) { + echo sprintf('<p id="msg_notice">%s</p>', $info['msg']); +} elseif ($info['notice']) { + echo sprintf('<p id="msg_info"><i class="icon-info-sign"></i> %s</p>', + $info['notice']); +} +?> +<form class="mass-action" method="post" + action="#tickets/<?php echo $ticket->getId(); ?>/mark/<?php echo $action; ?>" + name="markAs"> + <table width="100%"> + <tbody> + <tr><td> + <p> + <?php echo sprintf( + __('Are you sure you want to mark ticket as <b>%s</b>?'), + $action); ?> + </p> + <p> + <?php echo __('Please confirm to continue.'); ?> + </p> + </td></tr> + <tr><td> + <p> + <?php print $form->getField('comments')->render(); ?> + </p> + </td></tr> + </tbody> + </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 __('OK'); ?>"> + </span> + </p> +</form> +<div class="clear"></div> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index b1edfb999d04744e673705617233d0b32c3028e3..bc1573f354b5358d458d6962c8bca8b27d583680 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -22,6 +22,7 @@ $mylock = ($lock && $lock->getStaffId() == $thisstaff->getId()) ? $lock : null; $id = $ticket->getId(); //Ticket ID. $isManager = $dept->isManager($thisstaff); //Check if Agent is Manager $canRelease = ($isManager || $role->hasPerm(Ticket::PERM_RELEASE)); //Check if Agent can release tickets +$canAnswer = ($isManager || $role->hasPerm(Ticket::PERM_REPLY)); //Check if Agent can mark as answered/unanswered //Useful warnings and errors the user might want to know! if ($ticket->isClosed() && !$ticket->isReopenable()) @@ -161,13 +162,20 @@ if($ticket->isOverdue()) echo __('Mark as Overdue'); ?></a></li> <?php } + } elseif($ticket->isOpen() && $canAnswer) { if($ticket->isAnswered()) { ?> - <li><a class="confirm-action" id="ticket-unanswered" href="#unanswered"><i class="icon-circle-arrow-left"></i> <?php + <li><a href="#tickets/<?php echo $ticket->getId(); + ?>/mark/unanswered" class="ticket-action" + data-redirect="tickets.php?id=<?php echo $ticket->getId(); ?>"> + <i class="icon-circle-arrow-left"></i> <?php echo __('Mark as Unanswered'); ?></a></li> <?php } else { ?> - <li><a class="confirm-action" id="ticket-answered" href="#answered"><i class="icon-circle-arrow-right"></i> <?php + <li><a href="#tickets/<?php echo $ticket->getId(); + ?>/mark/answered" class="ticket-action" + data-redirect="tickets.php?id=<?php echo $ticket->getId(); ?>"> + <i class="icon-circle-arrow-right"></i> <?php echo __('Mark as Answered'); ?></a></li> <?php } diff --git a/scp/ajax.php b/scp/ajax.php index dfdb7499e7ae093c1a55e20f8937772d6447002e..304fab05b0956ca0c0169d502a3c8d44faa90b6d 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -168,6 +168,7 @@ $dispatcher = patterns('', url('^(?P<tid>\d+)/field/(?P<field>\w+)/edit$', 'editField'), url('^(?P<tid>\d+)/assign(?:/(?P<to>\w+))?$', 'assign'), url('^(?P<tid>\d+)/release$', 'release'), + url('^(?P<tid>\d+)/mark/(?P<action>\w+)$', 'markAs'), url('^(?P<tid>\d+)/refer(?:/(?P<to>\w+))?$', 'refer'), url('^(?P<tid>\d+)/referrals$', 'referrals'), url('^(?P<tid>\d+)/claim$', 'claim'), diff --git a/scp/tickets.php b/scp/tickets.php index f09184143d8e193d199127bcb6348dbc9f69d5ba..8881874e897217bbea32e02ba8735e482ee937ab 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -155,6 +155,8 @@ if($_POST && !$errors): $errors=array(); $lock = $ticket->getLock(); //Ticket lock if any $role = $ticket->getRole($thisstaff); + $dept = $ticket->getDept(); + $isManager = $dept->isManager($thisstaff); //Check if Agent is Manager switch(strtolower($_POST['a'])): case 'reply': if (!$role || !$role->hasPerm(Ticket::PERM_REPLY)) { @@ -310,8 +312,7 @@ if($_POST && !$errors): } break; case 'overdue': - $dept = $ticket->getDept(); - if(!$dept || !$dept->isManager($thisstaff)) { + if(!$dept || !$isManager) { $errors['err']=__('Permission Denied. You are not allowed to flag tickets overdue'); } elseif($ticket->markOverdue()) { $msg=sprintf(__('Ticket flagged as overdue by %s'),$thisstaff->getName()); @@ -320,28 +321,6 @@ if($_POST && !$errors): $errors['err']=sprintf('%s %s', __('Problems marking the the ticket overdue.'), __('Please try again!')); } break; - case 'answered': - $dept = $ticket->getDept(); - if(!$dept || !$dept->isManager($thisstaff)) { - $errors['err']=__('Permission Denied. You are not allowed to flag tickets'); - } elseif($ticket->markAnswered()) { - $msg=sprintf(__('Ticket flagged as answered by %s'),$thisstaff->getName()); - $ticket->logActivity(__('Ticket Marked Answered'),$msg); - } else { - $errors['err']=sprintf('%s %s', __('Problems marking the ticket answered.'), __('Please try again!')); - } - break; - case 'unanswered': - $dept = $ticket->getDept(); - if(!$dept || !$dept->isManager($thisstaff)) { - $errors['err']=__('Permission Denied. You are not allowed to flag tickets'); - } elseif($ticket->markUnAnswered()) { - $msg=sprintf(__('Ticket flagged as unanswered by %s'),$thisstaff->getName()); - $ticket->logActivity(__('Ticket Marked Unanswered'),$msg); - } else { - $errors['err']=sprintf('%s %s', __('Problems marking the ticket unanswered.'), __('Please try again!')); - } - break; case 'banemail': if (!$thisstaff->hasPerm(Email::PERM_BANLIST)) { $errors['err']=__('Permission Denied. You are not allowed to ban emails');