Skip to content
Snippets Groups Projects
Commit 29b37144 authored by Jared Hancock's avatar Jared Hancock
Browse files

Remove requirement of ticket id in subject line

This patch affords an administrator the ability to remove the
[#%{ticket.number}] from the email template subject line for the new ticket
autoresponse and the new message autoresponse. Previously, the ticket number
with a prefixed hash in brackets was used to identify which ticket thread an
email was in reference to.

With this patch, the email message-id (which was already kept on file) is
sent in the MIME "References" header. When a user responds to and
autoresponse email, the "References" will include this message-id in the
return email. The ticket thread is then matched up with the email based on
the message-id rather than the subject line.

Ticket numbers are still supported in the subject line, in the event that
non-compliant email clients do not properly include the References header.
parent acd4f06d
Branches
Tags
No related merge requests found
...@@ -113,6 +113,10 @@ class TicketApiController extends ApiController { ...@@ -113,6 +113,10 @@ class TicketApiController extends ApiController {
return $ticket; return $ticket;
} }
if (($thread = ThreadEntry::lookupByEmailHeaders($data))
&& $thread->postEmail($data)) {
return true;
}
return $this->createTicket($data); return $this->createTicket($data);
} }
......
...@@ -423,11 +423,6 @@ class ApiEmailDataParser extends EmailDataParser { ...@@ -423,11 +423,6 @@ class ApiEmailDataParser extends EmailDataParser {
if(!$data['emailId']) if(!$data['emailId'])
$data['emailId'] = $cfg->getDefaultEmailId(); $data['emailId'] = $cfg->getDefaultEmailId();
if($data['email'] && preg_match ('[[#][0-9]{1,10}]', $data['subject'], $matches)) {
if(($tid=Ticket::getIdByExtId(trim(preg_replace('/[^0-9]/', '', $matches[0])), $data['email'])))
$data['ticketId'] = $tid;
}
if(!$cfg->useEmailPriority()) if(!$cfg->useEmailPriority())
unset($data['priorityId']); unset($data['priorityId']);
......
...@@ -123,6 +123,18 @@ class Mailer { ...@@ -123,6 +123,18 @@ class Mailer {
$headers+= array('Precedence' => 'auto_reply'); $headers+= array('Precedence' => 'auto_reply');
} }
if ($options) {
if (isset($options['replyto']))
$headers += array('In-Reply-To' => $options['replyto']);
if (isset($options['references'])) {
if (is_array($options['references']))
$headers += array('References' =>
implode(' ', $options['references']));
else
$headers += array('References' => $options['references']);
}
}
$mime = new Mail_mime(); $mime = new Mail_mime();
$mime->setTXTBody($body); $mime->setTXTBody($body);
//XXX: Attachments //XXX: Attachments
......
...@@ -233,6 +233,8 @@ class MailFetcher { ...@@ -233,6 +233,8 @@ class MailFetcher {
'subject'=>@$headerinfo->subject, 'subject'=>@$headerinfo->subject,
'mid' => trim(@$headerinfo->message_id), 'mid' => trim(@$headerinfo->message_id),
'header' => $this->getHeader($mid), 'header' => $this->getHeader($mid),
'in-reply-to' => $headerinfo->in_reply_to,
'references' => $headerinfo->references,
); );
if ($replyto = $headerinfo->reply_to) { if ($replyto = $headerinfo->reply_to) {
...@@ -400,10 +402,6 @@ class MailFetcher { ...@@ -400,10 +402,6 @@ class MailFetcher {
return true; //Report success (moved or delete) return true; //Report success (moved or delete)
} }
//Make sure the email is NOT already fetched... (undeleted emails)
if($mailinfo['mid'] && ($id=Ticket::getIdByMessageId($mailinfo['mid'], $mailinfo['email'])))
return true; //Reporting success so the email can be moved or deleted.
$vars = $mailinfo; $vars = $mailinfo;
$vars['name']=$this->mime_decode($mailinfo['name']); $vars['name']=$this->mime_decode($mailinfo['name']);
$vars['subject']=$mailinfo['subject']?$this->mime_decode($mailinfo['subject']):'[No Subject]'; $vars['subject']=$mailinfo['subject']?$this->mime_decode($mailinfo['subject']):'[No Subject]';
...@@ -423,19 +421,15 @@ class MailFetcher { ...@@ -423,19 +421,15 @@ class MailFetcher {
$ticket=null; $ticket=null;
$newticket=true; $newticket=true;
//Check the subject line for possible ID.
if($vars['subject'] && preg_match ("[[#][0-9]{1,10}]", $vars['subject'], $regs)) {
$tid=trim(preg_replace("/[^0-9]/", "", $regs[0]));
//Allow mismatched emails?? For now NO.
if(!($ticket=Ticket::lookupByExtId($tid, $vars['email'])))
$ticket=null;
}
$errors=array(); $errors=array();
if($ticket) { if (($thread = ThreadEntry::lookupByEmailHeaders($vars))
if(!($message=$ticket->postMessage($vars, 'Email'))) && ($message = $thread->postEmail($vars))) {
return false; if ($message === true)
// Email has been processed previously
return true;
elseif ($message)
$ticket = $message->getTicket();
} elseif (($ticket=Ticket::create($vars, $errors, 'Email'))) { } elseif (($ticket=Ticket::create($vars, $errors, 'Email'))) {
$message = $ticket->getLastMessage(); $message = $ticket->getLastMessage();
} else { } else {
......
...@@ -189,6 +189,14 @@ class Thread { ...@@ -189,6 +189,14 @@ class Thread {
function delete() { function delete() {
/* XXX: Leave this out until TICKET_EMAIL_INFO_TABLE has a primary
* key
$sql = 'DELETE mid.* FROM '.TICKET_EMAIL_INFO_TABLE.' mid
INNER JOIN '.TICKET_THREAD_TABLE.' thread ON (thread.id = mid.message_id)
WHERE thread.ticket_id = '.db_input($this->getTicketId());
db_query($sql);
*/
$res=db_query('DELETE FROM '.TICKET_THREAD_TABLE.' WHERE ticket_id='.db_input($this->getTicketId())); $res=db_query('DELETE FROM '.TICKET_THREAD_TABLE.' WHERE ticket_id='.db_input($this->getTicketId()));
if(!$res || !db_affected_rows()) if(!$res || !db_affected_rows())
return false; return false;
...@@ -230,7 +238,7 @@ Class ThreadEntry { ...@@ -230,7 +238,7 @@ Class ThreadEntry {
if(!$id && !($id=$this->getId())) if(!$id && !($id=$this->getId()))
return false; return false;
$sql='SELECT thread.*, info.* ' $sql='SELECT thread.*, info.email_mid '
.' ,count(DISTINCT attach.attach_id) as attachments ' .' ,count(DISTINCT attach.attach_id) as attachments '
.' FROM '.TICKET_THREAD_TABLE.' thread ' .' FROM '.TICKET_THREAD_TABLE.' thread '
.' LEFT JOIN '.TICKET_EMAIL_INFO_TABLE.' info .' LEFT JOIN '.TICKET_EMAIL_INFO_TABLE.' info
...@@ -309,6 +317,10 @@ Class ThreadEntry { ...@@ -309,6 +317,10 @@ Class ThreadEntry {
return $this->ht['ticket_id']; return $this->ht['ticket_id'];
} }
function getEmailMessageId() {
return $this->ht['email_mid'];
}
function getTicket() { function getTicket() {
if(!$this->ticket && $this->getTicketId()) if(!$this->ticket && $this->getTicketId())
...@@ -471,7 +483,76 @@ Class ThreadEntry { ...@@ -471,7 +483,76 @@ Class ThreadEntry {
return $str; return $str;
} }
/**
* postEmail
*
* After some security and sanity checks, attaches the body and subject
* of the message in reply to this thread item
*
* Parameters:
* mailinfo - (array) of information about the email, with at least the
* following keys
* - mid - (string) email message-id
* - name - (string) personal name of email originator
* - email - (string<email>) originating email address
* - subject - (string) email subject line (decoded)
* - body - (string) email message body (decoded)
*/
function postEmail($mailinfo) {
// +==================+===================+=============+
// | Orig Thread-Type | Reply Thread-Type | Requires |
// +==================+===================+=============+
// | * | Message (M) | From: Owner |
// | * | Note (N) | From: Staff |
// | Response (R) | Message (M) | |
// | Message (M) | Response (R) | From: Staff |
// +------------------+-------------------+-------------+
if (!$ticket = $this->getTicket())
// Kind of hard to continue a discussion without a ticket ...
return false;
// Make sure the email is NOT already fetched... (undeleted emails)
elseif ($this->getEmailMessageId() == $mailinfo['mid'])
// Reporting success so the email can be moved or deleted.
return true;
$vars = array(
'mid' => $mailinfo['mid'],
'ticketId' => $ticket->getId(),
'poster' => $mailinfo['name'],
'origin' => 'Email',
'source' => 'Email',
'ip' => '',
'reply_to' => $this,
);
$body = $mailinfo['message'];
// Disambiguate if the user happens also to be a staff member of the
// system. The current ticket owner should _always_ post messages
// instead of notes or responses
if ($mailinfo['email'] == $ticket->getEmail()) {
$vars['message'] = $body;
return $ticket->postMessage($vars, 'Email');
}
elseif ($staff_id = Staff::getIdByEmail($mailinfo['email'])) {
$vars['staffId'] = $staff_id;
$poster = Staff::lookup($staff_id);
$errors = array();
$vars['note'] = $body;
return $ticket->postNote($vars, $errors, $poster);
}
// TODO: Consider security constraints
else {
$vars['message'] = sprintf("Received From: %s\n\n%s",
$mailinfo['email'], $body);
return $ticket->postMessage($vars, 'Email');
}
// Currently impossible, but indicate that this thread object could
// not append the incoming email.
return false;
}
/* Returns file names with id as key */ /* Returns file names with id as key */
function getFiles() { function getFiles() {
...@@ -495,8 +576,11 @@ Class ThreadEntry { ...@@ -495,8 +576,11 @@ Class ThreadEntry {
$sql='INSERT INTO '.TICKET_EMAIL_INFO_TABLE $sql='INSERT INTO '.TICKET_EMAIL_INFO_TABLE
.' SET message_id='.db_input($this->getId()) //TODO: change it to thread_id .' SET message_id='.db_input($this->getId()) //TODO: change it to thread_id
.', email_mid='.db_input($vars['mid']) //TODO: change it to mid. .', email_mid='.db_input($vars['mid']); //TODO: change it to mid.
.', headers='.db_input($vars['header']); if (isset($vars['header']))
$sql .= ', headers='.db_input($vars['header']);
$this->ht['email_mid'] = $vars['mid'];
return db_query($sql)?db_insert_id():0; return db_query($sql)?db_insert_id():0;
} }
...@@ -544,8 +628,56 @@ Class ThreadEntry { ...@@ -544,8 +628,56 @@ Class ThreadEntry {
)?$e:null; )?$e:null;
} }
/**
* Parameters:
* mailinfo (hash<String>) email header information. Must include keys
* - "mid" => Message-Id header of incoming mail
* - "in-reply-to" => Message-Id the email is a direct response to
* - "references" => List of Message-Id's the email is in response
* - "subject" => Find external ticket number in the subject line
*/
function lookupByEmailHeaders($mailinfo) {
// Search for messages using the References header, then the
// in-reply-to header
$search = 'SELECT message_id FROM '.TICKET_EMAIL_INFO_TABLE
. ' WHERE email_mid=%s ORDER BY message_id DESC';
if ($id = db_result(db_query(
sprintf($search, db_input($mailinfo['mid'])))))
return ThreadEntry::lookup($id);
foreach (array('mid', 'in-reply-to', 'references') as $header) {
$matches = array();
if (!isset($mailinfo[$header]) || !$mailinfo[$header])
continue;
// Header may have multiple entries (usually separated by
// semi-colons (;))
elseif (!preg_match_all('/<[^>@]+@[^>]+>/', $mailinfo[$header],
$matches))
continue;
foreach ($matches[0] as $mid) {
$res = db_query(sprintf($search, db_input($mid)));
while (list($id) = db_fetch_row($res)) {
if ($t = ThreadEntry::lookup($id))
return $t;
}
}
}
// Search for ticket by the [#123456] in the subject line
$subject = $mailinfo['subject'];
$match = array();
if ($subject && preg_match("/\[#([0-9]{1,10})\]/", $subject, $match))
// Return last message for the thread
return Message::lastByExtTicketId((int)$match[1]);
return null;
}
//new entry ... we're trusting the caller to check validity of the data. //new entry ... we're trusting the caller to check validity of the data.
function create($vars) { function create($vars) {
global $cfg;
//Must have... //Must have...
if(!$vars['ticketId'] || !$vars['type'] || !in_array($vars['type'], array('M','R','N'))) if(!$vars['ticketId'] || !$vars['type'] || !in_array($vars['type'], array('M','R','N')))
...@@ -562,6 +694,12 @@ Class ThreadEntry { ...@@ -562,6 +694,12 @@ Class ThreadEntry {
if(isset($vars['pid'])) if(isset($vars['pid']))
$sql.=' ,pid='.db_input($vars['pid']); $sql.=' ,pid='.db_input($vars['pid']);
// Check if 'reply_to' is in the $vars as the previous ThreadEntry
// instance. If the body of the previous message is found in the new
// body, strip it out.
elseif (isset($vars['reply_to'])
&& $vars['reply_to'] instanceof ThreadEntry)
$sql.=' ,pid='.db_input($vars['reply_to']->getId());
if($vars['ip_address']) if($vars['ip_address'])
$sql.=' ,ip_address='.db_input($vars['ip_address']); $sql.=' ,ip_address='.db_input($vars['ip_address']);
...@@ -584,6 +722,12 @@ Class ThreadEntry { ...@@ -584,6 +722,12 @@ Class ThreadEntry {
if($vars['cannedattachments'] && is_array($vars['cannedattachments'])) if($vars['cannedattachments'] && is_array($vars['cannedattachments']))
$entry->saveAttachments($vars['cannedattachments']); $entry->saveAttachments($vars['cannedattachments']);
// Email message id (required for all thread posts)
if (!isset($vars['mid']))
$vars['mid'] = sprintf('<%s@%s>', Misc::randCode(24),
substr(md5($cfg->getUrl()), -10));
$entry->saveEmailInfo($vars);
return $entry; return $entry;
} }
...@@ -630,6 +774,17 @@ class Message extends ThreadEntry { ...@@ -630,6 +774,17 @@ class Message extends ThreadEntry {
&& $m->getId()==$id && $m->getId()==$id
)?$m:null; )?$m:null;
} }
function lastByExtTicketId($ticketId) {
$sql = 'SELECT thread.id FROM '.TICKET_THREAD_TABLE
.' thread JOIN '.TICKET_TABLE.' ticket ON (ticket.ticket_id = thread.ticket_id)
WHERE thread_type=\'M\' AND ticket.ticketID = '.db_input($ticketId)
.' ORDER BY thread.id DESC LIMIT 1';
if (($res = db_query($sql)) && (list($id) = db_fetch_row($res)))
return Message::lookup($id);
else
return null;
}
} }
/* Response - Ticket thread entry of type response */ /* Response - Ticket thread entry of type response */
......
...@@ -733,6 +733,8 @@ class Ticket { ...@@ -733,6 +733,8 @@ class Ticket {
if(!$dept || !($email=$dept->getAutoRespEmail())) if(!$dept || !($email=$dept->getAutoRespEmail()))
$email =$cfg->getDefaultEmail(); $email =$cfg->getDefaultEmail();
$options = array('references'=>$message->getEmailMessageId());
//Send auto response - if enabled. //Send auto response - if enabled.
if($autorespond && $email && $cfg->autoRespONNewTicket() if($autorespond && $email && $cfg->autoRespONNewTicket()
&& $dept->autoRespONNewTicket() && $dept->autoRespONNewTicket()
...@@ -746,7 +748,8 @@ class Ticket { ...@@ -746,7 +748,8 @@ class Ticket {
if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator())) if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()))
$msg['body'] ="\n$tag\n\n".$msg['body']; $msg['body'] ="\n$tag\n\n".$msg['body'];
$email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body']); $email->sendAutoReply($this->getEmail(), $msg['subj'],
$msg['body'], null, $options);
} }
if(!($email=$cfg->getAlertEmail())) if(!($email=$cfg->getAlertEmail()))
...@@ -763,7 +766,8 @@ class Ticket { ...@@ -763,7 +766,8 @@ class Ticket {
//Alert admin?? //Alert admin??
if($cfg->alertAdminONNewTicket()) { if($cfg->alertAdminONNewTicket()) {
$alert = str_replace('%{recipient}', 'Admin', $msg['body']); $alert = str_replace('%{recipient}', 'Admin', $msg['body']);
$email->sendAlert($cfg->getAdminEmail(), $msg['subj'], $alert); $email->sendAlert($cfg->getAdminEmail(), $msg['subj'],
$alert, null, $options);
$sentlist[]=$cfg->getAdminEmail(); $sentlist[]=$cfg->getAdminEmail();
} }
...@@ -779,7 +783,8 @@ class Ticket { ...@@ -779,7 +783,8 @@ class Ticket {
foreach( $recipients as $k=>$staff) { foreach( $recipients as $k=>$staff) {
if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
$alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
$email->sendAlert($staff->getEmail(), $msg['subj'], $alert); $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
null, $options);
$sentlist[] = $staff->getEmail(); $sentlist[] = $staff->getEmail();
} }
...@@ -831,7 +836,7 @@ class Ticket { ...@@ -831,7 +836,7 @@ class Ticket {
db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1,lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId())); db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1,lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId()));
} }
function onMessage($autorespond=true, $alert=true) { function onMessage($autorespond=true, $message=null) {
global $cfg; global $cfg;
db_query('UPDATE '.TICKET_TABLE.' SET isanswered=0,lastmessage=NOW() WHERE ticket_id='.db_input($this->getId())); db_query('UPDATE '.TICKET_TABLE.' SET isanswered=0,lastmessage=NOW() WHERE ticket_id='.db_input($this->getId()));
...@@ -875,7 +880,11 @@ class Ticket { ...@@ -875,7 +880,11 @@ class Ticket {
if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator())) if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()))
$msg['body'] ="\n$tag\n\n".$msg['body']; $msg['body'] ="\n$tag\n\n".$msg['body'];
$email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body']); if (!$message)
$message = $this->getLastMessage();
$options = array('references' => $message->getEmailMessageId());
$email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'],
null, $options);
} }
} }
...@@ -893,7 +902,8 @@ class Ticket { ...@@ -893,7 +902,8 @@ class Ticket {
$assigner = $thisstaff?$thisstaff:'SYSTEM (Auto Assignment)'; $assigner = $thisstaff?$thisstaff:'SYSTEM (Auto Assignment)';
//Log an internal note - no alerts on the internal note. //Log an internal note - no alerts on the internal note.
$this->logNote('Ticket Assigned to '.$assignee->getName(), $comments, $assigner, false); $note = $this->logNote('Ticket Assigned to '.$assignee->getName(),
$comments, $assigner, false);
//See if we need to send alerts //See if we need to send alerts
if(!$alert || !$cfg->alertONAssignment()) return true; //No alerts! if(!$alert || !$cfg->alertONAssignment()) return true; //No alerts!
...@@ -931,10 +941,12 @@ class Ticket { ...@@ -931,10 +941,12 @@ class Ticket {
//Send the alerts. //Send the alerts.
$sentlist=array(); $sentlist=array();
$options = array('references' => $note->getEmailMessageId());
foreach( $recipients as $k=>$staff) { foreach( $recipients as $k=>$staff) {
if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
$alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
$email->sendAlert($staff->getEmail(), $msg['subj'], $alert); $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
null, $options);
$sentlist[] = $staff->getEmail(); $sentlist[] = $staff->getEmail();
} }
} }
...@@ -1140,7 +1152,7 @@ class Ticket { ...@@ -1140,7 +1152,7 @@ class Ticket {
/*** log the transfer comments as internal note - with alerts disabled - ***/ /*** log the transfer comments as internal note - with alerts disabled - ***/
$title='Ticket transfered from '.$currentDept.' to '.$this->getDeptName(); $title='Ticket transfered from '.$currentDept.' to '.$this->getDeptName();
$comments=$comments?$comments:$title; $comments=$comments?$comments:$title;
$this->logNote($title, $comments, $thisstaff, false); $note = $this->logNote($title, $comments, $thisstaff, false);
$this->logEvent('transferred'); $this->logEvent('transferred');
...@@ -1180,10 +1192,12 @@ class Ticket { ...@@ -1180,10 +1192,12 @@ class Ticket {
$recipients[]= $manager; $recipients[]= $manager;
$sentlist=array(); $sentlist=array();
$options = array('references' => $note->getEmailMessageId());
foreach( $recipients as $k=>$staff) { foreach( $recipients as $k=>$staff) {
if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
$alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
$email->sendAlert($staff->getEmail(), $msg['subj'], $alert); $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
null, $options);
$sentlist[] = $staff->getEmail(); $sentlist[] = $staff->getEmail();
} }
} }
...@@ -1279,7 +1293,7 @@ class Ticket { ...@@ -1279,7 +1293,7 @@ class Ticket {
if(list($msg) = split($tag, $vars['message'])) if(list($msg) = split($tag, $vars['message']))
$vars['message'] = $msg; $vars['message'] = $msg;
if($vars['ip']) if(isset($vars['ip']))
$vars['ip_address'] = $vars['ip']; $vars['ip_address'] = $vars['ip'];
elseif(!$vars['ip_address'] && $_SERVER['REMOTE_ADDR']) elseif(!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
$vars['ip_address'] = $_SERVER['REMOTE_ADDR']; $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
...@@ -1290,16 +1304,13 @@ class Ticket { ...@@ -1290,16 +1304,13 @@ class Ticket {
$this->setLastMsgId($message->getId()); $this->setLastMsgId($message->getId());
if (isset($vars['mid']))
$message->saveEmailInfo($vars);
if(!$alerts) return $message; //Our work is done... if(!$alerts) return $message; //Our work is done...
$autorespond = true; $autorespond = true;
if ($autorespond && $message->isAutoResponse()) if ($autorespond && $message->isAutoResponse())
$autorespond=false; $autorespond=false;
$this->onMessage($autorespond); //must be called b4 sending alerts to staff. $this->onMessage($autorespond, $message); //must be called b4 sending alerts to staff.
$dept = $this->getDept(); $dept = $this->getDept();
...@@ -1330,10 +1341,12 @@ class Ticket { ...@@ -1330,10 +1341,12 @@ class Ticket {
$recipients[]=$manager; $recipients[]=$manager;
$sentlist=array(); //I know it sucks...but..it works. $sentlist=array(); //I know it sucks...but..it works.
$options = array('references'=>$message->getEmailMessageId());
foreach( $recipients as $k=>$staff) { foreach( $recipients as $k=>$staff) {
if(!$staff || !$staff->getEmail() || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; if(!$staff || !$staff->getEmail() || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
$alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
$email->sendAlert($staff->getEmail(), $msg['subj'], $alert); $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
null, $options);
$sentlist[] = $staff->getEmail(); $sentlist[] = $staff->getEmail();
} }
} }
...@@ -1386,7 +1399,9 @@ class Ticket { ...@@ -1386,7 +1399,9 @@ class Ticket {
$msg['body'] ="\n$tag\n\n".$msg['body']; $msg['body'] ="\n$tag\n\n".$msg['body'];
$attachments =($cfg->emailAttachments() && $files)?$response->getAttachments():array(); $attachments =($cfg->emailAttachments() && $files)?$response->getAttachments():array();
$email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], $attachments); $options = array('references' => $response->getEmailMessageId());
$email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], $attachments,
$options);
} }
return $response; return $response;
...@@ -1441,8 +1456,10 @@ class Ticket { ...@@ -1441,8 +1456,10 @@ class Ticket {
//Set attachments if emailing. //Set attachments if emailing.
$attachments = $cfg->emailAttachments()?$response->getAttachments():array(); $attachments = $cfg->emailAttachments()?$response->getAttachments():array();
$options = array('references' => $response->getEmailMessageId());
//TODO: setup 5 param (options... e.g mid trackable on replies) //TODO: setup 5 param (options... e.g mid trackable on replies)
$email->send($this->getEmail(), $msg['subj'], $msg['body'], $attachments); $email->send($this->getEmail(), $msg['subj'], $msg['body'], $attachments,
$options);
} }
return $response; return $response;
...@@ -1551,6 +1568,7 @@ class Ticket { ...@@ -1551,6 +1568,7 @@ class Ticket {
$recipients[]=$dept->getManager(); $recipients[]=$dept->getManager();
$attachments = $note->getAttachments(); $attachments = $note->getAttachments();
$options = array('references' => $note->getEmailMessageId());
$sentlist=array(); $sentlist=array();
foreach( $recipients as $k=>$staff) { foreach( $recipients as $k=>$staff) {
if(!is_object($staff) if(!is_object($staff)
...@@ -1559,7 +1577,8 @@ class Ticket { ...@@ -1559,7 +1577,8 @@ class Ticket {
|| $note->getStaffId() == $staff->getId()) //No need to alert the poster! || $note->getStaffId() == $staff->getId()) //No need to alert the poster!
continue; continue;
$alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
$email->sendAlert($staff->getEmail(), $msg['subj'], $alert, $attachments); $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, $attachments,
$options);
$sentlist[] = $staff->getEmail(); $sentlist[] = $staff->getEmail();
} }
} }
...@@ -2128,7 +2147,12 @@ class Ticket { ...@@ -2128,7 +2147,12 @@ class Ticket {
$msg['body'] ="\n$tag\n\n".$msg['body']; $msg['body'] ="\n$tag\n\n".$msg['body'];
$attachments =($cfg->emailAttachments() && $response)?$response->getAttachments():array(); $attachments =($cfg->emailAttachments() && $response)?$response->getAttachments():array();
$email->send($ticket->getEmail(), $msg['subj'], $msg['body'], $attachments); $references = $ticket->getLastMessage()->getEmailMessageId();
if (isset($response))
$references = array($response->getEmailMessageId(), $references);
$options = array('references' => $references);
$email->send($ticket->getEmail(), $msg['subj'], $msg['body'], $attachments,
$options);
} }
return $ticket; return $ticket;
......
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html version="-//W3C//DTD XHTML 1.1//EN" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<link rel="stylesheet" type="text/css" href="/s/d16ebb.css" title="Default"/>
<title>xkcd: Monster</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<link rel="shortcut icon" href="/s/919f27.ico" type="image/x-icon"/>
<link rel="icon" href="/s/919f27.ico" type="image/x-icon"/>
<link rel="alternate" type="application/atom+xml" title="Atom 1.0" href="/atom.xml"/>
<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="/rss.xml"/>
<link rel="apple-touch-icon-precomposed" href="/s/d9522a.png" />
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25700708-7']);
_gaq.push(['_setDomainName', 'xkcd.com']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<div id="topContainer">
<div id="topLeft">
<ul>
<li><a href="/archive">Archive</a></li>
<li><a href="http://what-if.xkcd.com">What If?</a></li>
<li><a href="http://blag.xkcd.com">Blag</a></li>
<li><a href="http://store.xkcd.com/">Store</a></li>
<li><a rel="author" href="/about">About</a></li>
</ul>
</div>
<div id="topRight">
<div id="masthead">
<span><a href="/"><img src="http://imgs.xkcd.com/static/terrible_small_logo.png" alt="xkcd.com logo" height="83" width="185"/></a></span>
<span id="slogan">A webcomic of romance,<br/> sarcasm, math, and language.</span>
</div>
<div id="news">
You can get the Subways comic as a <a href="http://store-xkcd-com.myshopify.com/products/subways">poster</a>!
</div>
</div>
<div id="bgLeft" class="bg box"></div>
<div id="bgRight" class="bg box"></div>
</div>
<div id="middleContainer" class="box">
<div id="ctitle">Monster</div>
<ul class="comicNav">
<li><a href="/1/">|&lt;</a></li>
<li><a rel="prev" href="/1256/" accesskey="p">&lt; Prev</a></li>
<li><a href="http://dynamic.xkcd.com/random/comic/">Random</a></li>
<li><a rel="next" href="#" accesskey="n">Next &gt;</a></li>
<li><a href="/">&gt;|</a></li>
</ul>
<div id="comic">
<img src="http://imgs.xkcd.com/comics/monster.png" title="It was finally destroyed with a nuclear weapon carrying the destructive energy of the Hiroshima bomb." alt="Monster" />
</div>
<ul class="comicNav">
<li><a href="/1/">|&lt;</a></li>
<li><a rel="prev" href="/1256/" accesskey="p">&lt; Prev</a></li>
<li><a href="http://dynamic.xkcd.com/random/comic/">Random</a></li>
<li><a rel="next" href="#" accesskey="n">Next &gt;</a></li>
<li><a href="/">&gt;|</a></li>
</ul>
<br />
Permanent link to this comic: http://xkcd.com/1257/<br />
Image URL (for hotlinking/embedding): http://imgs.xkcd.com/comics/monster.png
<div id="transcript" style="display: none"></div>
</div>
<div id="bottom" class="box">
<img src="http://imgs.xkcd.com/s/a899e84.jpg" width="520" height="100" alt="Selected Comics" usemap="#comicmap"/>
<map id="comicmap" name="comicmap">
<!-- http://code.google.com/p/chromium/issues/detail?id=108489 Might be MIME dependent. -->
<area shape="rect" coords="0,0,100,100" href="/150/" alt="Grownups"/>
<area shape="rect" coords="104,0,204,100" href="/730/" alt="Circuit Diagram"/>
<area shape="rect" coords="208,0,308,100" href="/162/" alt="Angular Momentum"/>
<area shape="rect" coords="312,0,412,100" href="/688/" alt="Self-Description"/>
<area shape="rect" coords="416,0,520,100" href="/556/" alt="Alternative Energy Revolution"/>
</map>
<div>
Search comic titles and transcripts:
<script type="text/javascript" src="//www.google.com/jsapi"></script>
<script type="text/javascript">google.load('search', '1');google.setOnLoadCallback(function() {google.search.CustomSearchControl.attachAutoCompletion('012652707207066138651:zudjtuwe28q',document.getElementById('q'),'cse-search-box');});</script>
<form action="//www.google.com/cse" id="cse-search-box">
<div>
<input type="hidden" name="cx" value="012652707207066138651:zudjtuwe28q"/>
<input type="hidden" name="ie" value="UTF-8"/>
<input type="text" name="q" id="q" size="31"/>
<input type="submit" name="sa" value="Search"/>
</div>
</form>
<script type="text/javascript" src="//www.google.com/cse/brand?form=cse-search-box&amp;lang=en"></script>
<a href="/rss.xml">RSS Feed</a> - <a href="/atom.xml">Atom Feed</a>
</div>
<br />
<div id="comicLinks">
Comics I enjoy:<br/>
<a href="http://threewordphrase.com/">Three Word Phrase</a>,
<a href="http://oglaf.com/">Oglaf</a> (nsfw),
<a href="http://www.smbc-comics.com/">SMBC</a>,
<a href="http://www.qwantz.com">Dinosaur Comics</a>,
<a href="http://www.asofterworld.com">A Softer World</a>,
<a href="http://buttersafe.com/">Buttersafe</a>,
<a href="http://pbfcomics.com/">Perry Bible Fellowship</a>,
<a href="http://questionablecontent.net/">Questionable Content</a>,
<a href="http://www.buttercupfestival.com/">Buttercup Festival</a>
</div>
<p>Warning: this comic occasionally contains strong language (which may be unsuitable for children), unusual humor (which may be unsuitable for adults), and advanced mathematics (which may be unsuitable for liberal-arts majors).</p>
<div id="footnote">BTC 1NfBXWqseXc9rCBc3Cbbu6HjxYssFUgkH6<br />We did not invent the algorithm. The algorithm consistently finds Jesus. The algorithm killed Jeeves. <br/>The algorithm is banned in China. The algorithm is from Jersey. The algorithm constantly finds Jesus.<br/>This is not the algorithm. This is close.</div>
<div id="licenseText">
<p>
This work is licensed under a
<a href="http://creativecommons.org/licenses/by-nc/2.5/">Creative Commons Attribution-NonCommercial 2.5 License</a>.
</p><p>
This means you're free to copy and share these comics (but not to sell them). <a rel="license" href="/license.html">More details</a>.</p>
</div>
</div>
</body>
<!-- Layout by Ian Clasbey, davean, and chromakode -->
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment