From db7daa24b03e449228441fbaef4df25acc237ffe Mon Sep 17 00:00:00 2001 From: Peter Rotich <peter@enhancesoft.com> Date: Fri, 13 Jul 2018 17:39:54 +0000 Subject: [PATCH] Mailer: Add concept of multi-recipients to mailer This commit adds ability to send and email to multiple recipients with auto-detection of TO/CC/BCC (based on user class) --- include/class.client.php | 14 ++++---- include/class.mailer.php | 53 +++++++++++++++++---------- include/class.ticket.php | 78 ++++++++++++++++++++-------------------- include/class.user.php | 49 +++++-------------------- include/class.util.php | 60 +++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 103 deletions(-) diff --git a/include/class.client.php b/include/class.client.php index 36fa13746..1e82ebe41 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -82,6 +82,9 @@ implements EmailContact, ITicketUser, TemplateVariable { function getId() { return ($this->user) ? $this->user->getId() : null; } function getEmail() { return ($this->user) ? $this->user->getEmail() : null; } + function getName() { + return ($this->user) ? $this->user->getName() : null; + } static function lookupByToken($token) { @@ -158,6 +161,11 @@ class TicketOwner extends TicketUser { $this->ticket = $ticket; } + function __toString() { + return (string) $this->getName(); + } + + function getTicket() { return $this->ticket; } @@ -470,12 +478,6 @@ class ClientAccount extends UserAccount { } } -// Used by the email system -interface EmailContact { - // function getId() - // function getName() - // function getEmail() -} interface ITicketUser { /* PHP 5.3 < 5.3.8 will crash with some abstract inheritance issue diff --git a/include/class.mailer.php b/include/class.mailer.php index ed1ef55df..51cbab842 100644 --- a/include/class.mailer.php +++ b/include/class.mailer.php @@ -165,8 +165,11 @@ class Mailer { case $recipient instanceof Collaborator: $utype = 'C'; break; + case $recipient instanceof MailingList: + $utype = 'M'; + break; default: - $utype = $options['utype'] ?: '?'; + $utype = $options['utype'] ?: is_array($recipient) ? 'M' : '?'; } @@ -209,6 +212,7 @@ class Mailer { * 'U' - TicketOwner * 'S' - Staff * 'C' - Collborator + * 'M' - Multiple * '?' - Something else */ static function decodeMessageId($mid) { @@ -294,14 +298,16 @@ class Mailer { 0, 6); } - function send($recipient, $subject, $message, $options=null, $collabs=array()) { + function send($recipients, $subject, $message, $options=null) { global $ost, $cfg; //Get the goodies require_once (PEAR_DIR.'Mail.php'); // PEAR Mail package require_once (PEAR_DIR.'Mail/mime.php'); // PEAR Mail_Mime packge - $messageId = $this->getMessageId($recipient, $options); + + $messageId = $this->getMessageId($recipients, $options); + if (is_object($recipient) && is_callable(array($recipient, 'getEmail'))) { // Add personal name if available @@ -323,7 +329,6 @@ class Mailer { $headers = array ( 'From' => $this->getFromAddress($options), - 'To' => $to, 'Subject' => $subject, 'Date'=> date('D, d M Y H:i:s O'), 'Message-ID' => "<{$messageId}>", @@ -413,13 +418,35 @@ class Mailer { // Use general failsafe default initially $eol = "\n"; - // MAIL_EOL setting can be defined in `ost-config.php` if (defined('MAIL_EOL') && is_string(MAIL_EOL)) { $eol = MAIL_EOL; } + $mime = new Mail_mime($eol); + // Add recipients + if (!is_array($recipients) && (!$recipients instanceof MailingList)) + $recipients = array($recipients); + foreach ($recipients as $recipient) { + switch (true) { + case $recipient instanceof TicketOwner: + case $recipient instanceof Staff: + $mime->addTo(sprintf('%s <%s>', + $recipient->getName(), + $recipient->getEmail())); + break; + case $recipient instanceof Collaborator: + $mime->addCc(sprintf('%s <%s>', + $recipient->getName(), + $recipient->getEmail())); + break; + default: + // Assuming email address. + $mime->addTo($recipient); + } + } + // Add in extra attachments, if any from template variables if ($message instanceof TextWithExtras && ($files = $message->getFiles()) @@ -508,19 +535,6 @@ class Mailer { } } - $cc = array(); - if($collabs) { - if($collabs['cc']) { - foreach ($collabs['cc'] as $email) { - $mime->addCc($email); - $email = preg_replace("/(\r\n|\r|\n)/s",'', trim($email)); - $cc[] = $email; - } - $to = $to.', '.implode(', ',$cc); - } - } - $to = ltrim($to, ', '); - //Desired encodings... $encodings=array( 'head_encoding' => 'quoted-printable', @@ -534,7 +548,8 @@ class Mailer { $body = $mime->get($encodings); //encode the headers. $headers = $mime->headers($headers, true); - + $to = implode(',', array_filter(array($headers['To'], $headers['Cc'], + $headers['Bcc']))); // Cache smtp connections made during this request static $smtp_connections = array(); if(($smtp=$this->getSMTPInfo())) { //Send via SMTP diff --git a/include/class.ticket.php b/include/class.ticket.php index 5078b566e..d50180eef 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -829,17 +829,17 @@ implements RestrictedAccess, Threadable, Searchable { return $entries; } - //UserList of recipients (owner + collaborators) + // MailingList of recipients (owner + active collaborators) function getRecipients() { - $list = new UserList(); - $list->add($this->getOwner()); - if ($collabs = $this->getThread()->getActiveCollaborators()) { - foreach ($collabs as $c) { - $list->add($c); - } - } - $this->recipients = $list; - + if (!isset($this->recipients)) { + $list = new MailingList(); + $list->add($this->getOwner()); + if ($collabs = $this->getActiveCollaborators()) { + foreach ($collabs as $c) + $list->add($c); + } + $this->recipients = $list; + } return $this->recipients; } @@ -1585,6 +1585,7 @@ implements RestrictedAccess, Threadable, Searchable { $poster = User::lookup($entry->user_id); $posterEmail = $poster->getEmail()->address; + $recipients = array(); if($vars['ccs']) { foreach ($vars['ccs'] as $cc) { $collab = Collaborator::getIdByUserId($cc, $this->getThread()->getId()); @@ -2163,8 +2164,6 @@ implements RestrictedAccess, Threadable, Searchable { function replaceVars($input, $vars = array()) { global $ost; - $recipients = $this->getRecipients(); - $vars = array_merge($vars, array('ticket' => $this)); return $ost->replaceTemplateVariables($input, $vars); } @@ -2843,20 +2842,16 @@ implements RestrictedAccess, Threadable, Searchable { function postReply($vars, &$errors, $alert=true, $claim=true) { global $thisstaff, $cfg; - if ($collabs = $this->getRecipients()) { - $collabIds = array(); - foreach ($collabs as $collab) - $collabIds[] = $collab->user_id; - } - - $ticket = Ticket::lookup($vars['id']); + // Add new collabs if any. + $collabs = $this->getCollaborators(); if (isset($vars['ccs'])) { - foreach ($vars['ccs'] as $uid) { - $user = User::lookup($uid); - if (!in_array($uid, $collabIds)) - if (($c2=$ticket->getThread()->addCollaborator($user,array(), $errors))) - $c2->setCc(); - } + foreach ($vars['ccs'] as $uid) { + if ($collabs->findFirst(array('user_id' => $uid))) + continue; + + if ($user=User::lookup($uid)) + $this->addCollaborator($user, array(), $errors); + } } if (!$vars['poster'] && $thisstaff) @@ -2898,7 +2893,9 @@ implements RestrictedAccess, Threadable, Searchable { return $response; //allow agent to send from different dept email - $vars['from_name'] ? $email = Email::lookup($vars['from_name']) : $email = $dept->getEmail(); + if (!$vars['from_email_id'] + || !($email = Email::lookup($vars['from_email_id']))) + $email = $dept->getEmail(); $options = array('thread'=>$response); $signature = $from_name = ''; @@ -2932,26 +2929,31 @@ implements RestrictedAccess, Threadable, Searchable { 'poster' => $thisstaff ); - $user = $this->getOwner(); - if (($email=$email) + if ($email && ($tpl = $dept->getTemplate()) && ($msg=$tpl->getReplyMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), - $variables + array('recipient' => $user) + $variables + array('recipient' => $this->getOwner()) ); - $attachments = $cfg->emailAttachments() ? $response->getAttachments() : array(); - //Cc collaborators - $collabsCc = array(); - if ($vars['ccs']) { - $collabsCc[] = Collaborator::getCollabList($vars['ccs']); - $collabsCc['cc'] = $collabsCc[0]; + // Attachments + $attachments = $cfg->emailAttachments() ? + $response->getAttachments() : array(); + + // Get active recipients + $recipients = new MailingList(); + $recioients->add($this->getOwner()); + if ($collabs = $this->getActiveCollaborators()) { + foreach ($collabs as $c) + if ($vars['ccs'] && in_array($c->getUserId(), + $vars['ccs'])) + $recipients->add($c); } - $email->send($user, $msg['subj'], $msg['body'], $attachments, - $options, $collabsCc); - + //Send email to recepients + $email->send($recipients, $msg['subj'], $msg['body'], + $attachments, $options); } return $response; diff --git a/include/class.user.php b/include/class.user.php index 083e0d5bf..fee13c2cc 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -210,9 +210,12 @@ class UserCdata extends VerySimpleModel { class User extends UserModel implements TemplateVariable, Searchable { + var $_email; var $_entries; var $_forms; + + static function fromVars($vars, $create=true, $update=false) { // Try and lookup by email address $user = static::lookupByEmail($vars['email']); @@ -1310,51 +1313,17 @@ class UserAccountStatus { } } - /* * Generic user list. */ -class UserList extends ListObject -implements TemplateVariable { +class UserList extends MailingList { - function __toString() { - return $this->getNames(); - } + function add($user) { + if (!$user instanceof ITicketUser) + throw new InvalidArgumentException('User expected'); - function getNames() { - $list = array(); - foreach($this->storage as $user) { - if (is_object($user)) - $list [] = $user->getName(); - } - return $list ? implode(', ', $list) : ''; - } - - function getFull() { - $list = array(); - foreach($this->storage as $user) { - if (is_object($user)) - $list[] = sprintf("%s <%s>", $user->getName(), $user->getEmail()); - } - - return $list ? implode(', ', $list) : ''; - } - - function getEmails() { - $list = array(); - foreach($this->storage as $user) { - if (is_object($user)) - $list[] = $user->getEmail(); - } - return $list ? implode(', ', $list) : ''; - } - - static function getVarScope() { - return array( - 'names' => __('List of names'), - 'emails' => __('List of email addresses'), - 'full' => __('List of names and email addresses'), - ); + return parent::add($user); } } + ?> diff --git a/include/class.util.php b/include/class.util.php index a56f23b2c..52035bcb2 100644 --- a/include/class.util.php +++ b/include/class.util.php @@ -1,5 +1,14 @@ <?php +require_once INCLUDE_DIR . 'class.variable.php'; + +// Used by the email system +interface EmailContact { + function getId(); + function getName(); + function getEmail(); +} + abstract class BaseList implements IteratorAggregate, Countable { protected $storage = array(); @@ -177,3 +186,54 @@ implements ArrayAccess, Serializable { $this->storage = unserialize($what); } } + +class MailingList extends ListObject +implements TemplateVariable { + + function add($contact) { + if (!$contact instanceof EmailContact) + throw new InvalidArgumentException('Email Contact expected'); + + return parent::add($contact); + } + + function __toString() { + return $this->getNames(); + } + + function getNames() { + $list = array(); + foreach($this->storage as $user) { + if (is_object($user)) + $list [] = $user->getName(); + } + return $list ? implode(', ', $list) : ''; + } + + function getFull() { + $list = array(); + foreach($this->storage as $user) { + if (is_object($user)) + $list[] = sprintf("%s <%s>", $user->getName(), $user->getEmail()); + } + + return $list ? implode(', ', $list) : ''; + } + + function getEmails() { + $list = array(); + foreach($this->storage as $user) { + if (is_object($user)) + $list[] = $user->getEmail(); + } + return $list ? implode(', ', $list) : ''; + } + + static function getVarScope() { + return array( + 'names' => __('List of names'), + 'emails' => __('List of email addresses'), + 'full' => __('List of names and email addresses'), + ); + } +} -- GitLab