function showConversationAction() { @($id = DevblocksPlatform::importGPC($_REQUEST['ticket_id'], 'integer')); @($expand_all = DevblocksPlatform::importGPC($_REQUEST['expand_all'], 'integer', '0')); @($active_worker = CerberusApplication::getActiveWorker()); $tpl = DevblocksPlatform::getTemplateService(); $tpl->assign('path', $this->_TPL_PATH); $tpl->assign('expand_all', $expand_all); $ticket = DAO_Ticket::getTicket($id); $tpl->assign('ticket', $ticket); $tpl->assign('requesters', $ticket->getRequesters()); $messages = $ticket->getMessages(); arsort($messages); $tpl->assign('latest_message_id', key($messages)); $tpl->assign('messages', $messages); // Thread comments and messages on the same level $convo_timeline = array(); // Track senders and their orgs $message_senders = array(); $message_sender_orgs = array(); // Loop messages foreach ($messages as $message_id => $message) { /* @var $message CerberusMessage */ $key = $message->created_date . '_m' . $message_id; // build a chrono index of messages $convo_timeline[$key] = array('m', $message_id); // If we haven't cached this sender address yet if (!isset($message_senders[$message->address_id])) { if (null != ($sender_addy = DAO_Address::get($message->address_id))) { $message_senders[$sender_addy->id] = $sender_addy; // If we haven't cached this sender org yet if (!isset($message_sender_orgs[$sender_addy->contact_org_id])) { if (null != ($sender_org = DAO_ContactOrg::get($sender_addy->contact_org_id))) { $message_sender_orgs[$sender_org->id] = $sender_org; } } } } } $tpl->assign('message_senders', $message_senders); $tpl->assign('message_sender_orgs', $message_sender_orgs); @($mail_inline_comments = DAO_WorkerPref::get($active_worker->id, 'mail_inline_comments', 1)); if ($mail_inline_comments) { // if inline comments are enabled $comments = DAO_TicketComment::getByTicketId($id); arsort($comments); $tpl->assign('comments', $comments); // build a chrono index of comments foreach ($comments as $comment_id => $comment) { /* @var $comment Model_TicketComment */ $key = $comment->created . '_c' . $comment_id; $convo_timeline[$key] = array('c', $comment_id); } } // sort the timeline if (!$expand_all) { krsort($convo_timeline); } else { ksort($convo_timeline); } $tpl->assign('convo_timeline', $convo_timeline); // Message Notes $notes = DAO_MessageNote::getByTicketId($id); $message_notes = array(); // Index notes by message id if (is_array($notes)) { foreach ($notes as $note) { if (!isset($message_notes[$note->message_id])) { $message_notes[$note->message_id] = array(); } $message_notes[$note->message_id][$note->id] = $note; } } $tpl->assign('message_notes', $message_notes); // Message toolbar items $messageToolbarItems = DevblocksPlatform::getExtensions('cerberusweb.message.toolbaritem', true); if (!empty($messageToolbarItems)) { $tpl->assign('message_toolbaritems', $messageToolbarItems); } // Workers $workers = DAO_Worker::getAll(); $tpl->assign('workers', $workers); $tpl->register_modifier('makehrefs', array('CerberusUtils', 'smarty_modifier_makehrefs')); $tpl->display('file:' . $this->_TPL_PATH . 'display/modules/conversation/index.tpl'); }
private function _sendForwards($event, $is_inbound) { @($ticket_id = $event->params['ticket_id']); @($send_worker_id = $event->params['worker_id']); $url_writer = DevblocksPlatform::getUrlService(); $ticket = DAO_Ticket::get($ticket_id); // (Action) Forward Email To: // Sanitize and combine all the destination addresses $context_workers = CerberusContexts::getWorkers(CerberusContexts::CONTEXT_TICKET, $ticket->id); if (!is_array($context_workers)) { return; } foreach ($context_workers as $next_worker) { $notify_emails = $next_worker->email; if (empty($notify_emails)) { continue; } } // [TODO] This could be more efficient $messages = DAO_Message::getMessagesByTicket($ticket_id); $message = end($messages); // last message unset($messages); $headers = $message->getHeaders(); // The whole flipping Swift section needs wrapped to catch exceptions try { $settings = DevblocksPlatform::getPluginSettingsService(); $reply_to = $settings->get('cerberusweb.core', CerberusSettings::DEFAULT_REPLY_FROM, CerberusSettingsDefaults::DEFAULT_REPLY_FROM); // See if we need a group-specific reply-to if (!empty($ticket->team_id)) { @($group_from = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_FROM, '')); if (!empty($group_from)) { $reply_to = $group_from; } } $sender = DAO_Address::get($message->address_id); $sender_email = strtolower($sender->email); $sender_split = explode('@', $sender_email); if (!is_array($sender_split) || count($sender_split) != 2) { return; } // If return-path is blank if (isset($headers['return-path']) && $headers['return-path'] == '<>') { return; } // Ignore bounces if ($sender_split[0] == "postmaster" || $sender_split[0] == "mailer-daemon") { return; } // Ignore autoresponses autoresponses if (isset($headers['auto-submitted']) && $headers['auto-submitted'] != 'no') { return; } // Attachments $attachments = $message->getAttachments(); $mime_attachments = array(); if (is_array($attachments)) { foreach ($attachments as $attachment) { if (0 == strcasecmp($attachment->display_name, 'original_message.html')) { continue; } $attachment_path = APP_STORAGE_PATH . '/attachments/'; // [TODO] This is highly redundant in the codebase if (!file_exists($attachment_path . $attachment->filepath)) { continue; } $attach = Swift_Attachment::fromPath($attachment_path . $attachment->filepath); if (!empty($attachment->display_name)) { $attach->setFilename($attachment->display_name); } $mime_attachments[] = $attach; } } // Send copies $mail_service = DevblocksPlatform::getMailService(); $mailer = $mail_service->getMailer(CerberusMail::getMailerDefaults()); $mail = $mail_service->createMessage(); /* @var $mail Swift_Message */ $mail->setTo(array($notify_emails)); $mail->setFrom(array($sender->email)); $mail->setReplyTo($reply_to); $mail->setReturnPath($reply_to); $mail->setSubject(sprintf("[RW: %s #%s]: %s", $is_inbound ? 'inbound' : 'outbound', $ticket->mask, $ticket->subject)); $hdrs = $mail->getHeaders(); if (null !== @($msgid = $headers['message-id'])) { $hdrs->addTextHeader('Message-Id', $msgid); } if (null !== @($in_reply_to = $headers['in-reply-to'])) { $hdrs->addTextHeader('References', $in_reply_to); $hdrs->addTextHeader('In-Reply-To', $in_reply_to); } $hdrs->addTextHeader('X-Mailer', 'Cerberus Helpdesk (Build ' . APP_BUILD . ')'); $hdrs->addTextHeader('Precedence', 'List'); $hdrs->addTextHeader('Auto-Submitted', 'auto-generated'); $mail->setBody($message->getContent()); // Send message attachments with watcher if (is_array($mime_attachments)) { foreach ($mime_attachments as $mime_attachment) { $mail->attach($mime_attachment); } } $result = $mailer->send($mail); } catch (Exception $e) { if (!empty($message_id)) { $fields = array(DAO_MessageNote::MESSAGE_ID => $message_id, DAO_MessageNote::CREATED => time(), DAO_MessageNote::WORKER_ID => 0, DAO_MessageNote::CONTENT => 'Exception thrown while sending watcher email: ' . $e->getMessage(), DAO_MessageNote::TYPE => Model_MessageNote::TYPE_ERROR); DAO_MessageNote::create($fields); } } }
function replyAction() { @($ticket_id = DevblocksPlatform::importGPC($_REQUEST['ticket_id'], 'integer', 0)); @($message_id = DevblocksPlatform::importGPC($_REQUEST['message_id'], 'integer')); @($content = DevblocksPlatform::importGPC($_REQUEST['content'], 'content')); @($to = DevblocksPlatform::importGPC($_REQUEST['to'], 'string')); // used by forward @($page_type = DevblocksPlatform::importGPC($_REQUEST['page_type'], 'string')); $worker = CerberusApplication::getActiveWorker(); if ($page_type == 'comment') { $properties = array(DAO_MessageNote::MESSAGE_ID => $message_id, DAO_MessageNote::CREATED => time(), DAO_MessageNote::WORKER_ID => @$worker->id, DAO_MessageNote::CONTENT => $content); $note_id = DAO_MessageNote::create($properties); } else { $properties = array('message_id' => $message_id, 'content' => $content, 'agent_id' => @$worker->id, 'to' => $to); CerberusMail::sendTicketMessage($properties); } DevblocksPlatform::redirect(new DevblocksHttpResponse(array('mobile', 'display', $ticket_id))); }
function handleRequest(DevblocksHttpRequest $request) { $worker = CerberusApplication::getActiveWorker(); if (empty($worker)) { return; } $stack = $request->path; array_shift($stack); // print @($object = strtolower(array_shift($stack))); // ticket|message|etc $tpl = DevblocksPlatform::getTemplateService(); $tpl->assign('path', $this->_TPL_PATH); $settings = DevblocksPlatform::getPluginSettingsService(); $tpl->assign('settings', $settings); $translate = DevblocksPlatform::getTranslationService(); $tpl->assign('translate', $translate); $teams = DAO_Group::getAll(); $tpl->assign('teams', $teams); $buckets = DAO_Bucket::getAll(); $tpl->assign('buckets', $buckets); $workers = DAO_Worker::getAll(); $tpl->assign('workers', $workers); // Security $active_worker = CerberusApplication::getActiveWorker(); $active_worker_memberships = $active_worker->getMemberships(); // [TODO] Make this pluggable // Subcontroller switch ($object) { case 'ticket': @($id = array_shift($stack)); @($ticket = is_numeric($id) ? DAO_Ticket::getTicket($id) : DAO_Ticket::getTicketByMask($id)); $convo_timeline = array(); $messages = $ticket->getMessages(); foreach ($messages as $message_id => $message) { /* @var $message CerberusMessage */ $key = $message->created_date . '_m' . $message_id; // build a chrono index of messages $convo_timeline[$key] = array('m', $message_id); } @($mail_inline_comments = DAO_WorkerPref::get($active_worker->id, 'mail_inline_comments', 1)); if ($mail_inline_comments) { // if inline comments are enabled $comments = DAO_TicketComment::getByTicketId($ticket->id); arsort($comments); $tpl->assign('comments', $comments); // build a chrono index of comments foreach ($comments as $comment_id => $comment) { /* @var $comment Model_TicketComment */ $key = $comment->created . '_c' . $comment_id; $convo_timeline[$key] = array('c', $comment_id); } } ksort($convo_timeline); $tpl->assign('convo_timeline', $convo_timeline); // Comment parent addresses $comment_addresses = array(); foreach ($comments as $comment) { /* @var $comment Model_TicketComment */ $address_id = intval($comment->address_id); if (!isset($comment_addresses[$address_id])) { $address = DAO_Address::get($address_id); $comment_addresses[$address_id] = $address; } } $tpl->assign('comment_addresses', $comment_addresses); // Message Notes $notes = DAO_MessageNote::getByTicketId($ticket->id); $message_notes = array(); // Index notes by message id if (is_array($notes)) { foreach ($notes as $note) { if (!isset($message_notes[$note->message_id])) { $message_notes[$note->message_id] = array(); } $message_notes[$note->message_id][$note->id] = $note; } } $tpl->assign('message_notes', $message_notes); // Make sure we're allowed to view this ticket or message if (!isset($active_worker_memberships[$ticket->team_id])) { echo "<H1>" . $translate->_('common.access_denied') . "</H1>"; return; } $tpl->assign('ticket', $ticket); $tpl->display('file:' . $this->_TPL_PATH . 'print/ticket.tpl'); break; case 'message': @($id = array_shift($stack)); @($message = DAO_Ticket::getMessage($id)); @($ticket = DAO_Ticket::getTicket($message->ticket_id)); // Make sure we're allowed to view this ticket or message if (!isset($active_worker_memberships[$ticket->team_id])) { echo "<H1>" . $translate->_('common.access_denied') . "</H1>"; return; } // Message Notes $notes = DAO_MessageNote::getByTicketId($ticket->id); $message_notes = array(); // Index notes by message id if (is_array($notes)) { foreach ($notes as $note) { if (!isset($message_notes[$note->message_id])) { $message_notes[$note->message_id] = array(); } $message_notes[$note->message_id][$note->id] = $note; } } $tpl->assign('message_notes', $message_notes); $tpl->assign('message', $message); $tpl->assign('ticket', $ticket); $tpl->display('file:' . $this->_TPL_PATH . 'print/message.tpl'); break; } }
private function _deleteIdAction($path) { $in_id = array_shift($path); if (empty($in_id)) { $this->_error("ID was not provided."); } if (null == ($note = DAO_MessageNote::get($in_id))) { $this->_error("ID is not valid."); } DAO_MessageNote::delete($note->id); $out_xml = new SimpleXMLElement('<success></success>'); $this->_render($out_xml->asXML()); }
private function _sendForwards($event, $is_inbound) { @($ticket_id = $event->params['ticket_id']); @($message_id = $event->params['message_id']); @($send_worker_id = $event->params['worker_id']); $ticket = DAO_Ticket::getTicket($ticket_id); $helpdesk_senders = CerberusApplication::getHelpdeskSenders(); $workers = DAO_Worker::getAllActive(); // [JAS]: Don't send obvious spam to watchers. if ($ticket->spam_score >= 0.9) { return true; } @($notifications = DAO_WorkerMailForward::getWhere(sprintf("%s = %d", DAO_WorkerMailForward::GROUP_ID, $ticket->team_id))); // Bail out early if we have no forwards for this group if (empty($notifications)) { return; } $message = DAO_Ticket::getMessage($message_id); $headers = $message->getHeaders(); // The whole flipping Swift section needs wrapped to catch exceptions try { $settings = CerberusSettings::getInstance(); $reply_to = $settings->get(CerberusSettings::DEFAULT_REPLY_FROM, ''); // See if we need a group-specific reply-to if (!empty($ticket->team_id)) { @($group_from = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_FROM, '')); if (!empty($group_from)) { $reply_to = $group_from; } } $sender = DAO_Address::get($message->address_id); $sender_email = strtolower($sender->email); $sender_split = explode('@', $sender_email); if (!is_array($sender_split) || count($sender_split) != 2) { return; } // If return-path is blank if (isset($headers['return-path']) && $headers['return-path'] == '<>') { return; } // Ignore bounces if ($sender_split[1] == "postmaster" || $sender_split[1] == "mailer-daemon") { return; } // Ignore autoresponses autoresponses if (isset($headers['auto-submitted']) && $headers['auto-submitted'] != 'no') { return; } // Headers //========== // Build mailing list $send_to = array(); foreach ($notifications as $n) { /* @var $n Model_WorkerMailForward */ if (!isset($n->group_id) || !isset($n->bucket_id)) { continue; } // if worker no longer exists or is disabled if (!isset($workers[$n->worker_id])) { continue; } // Don't allow a worker to usurp a helpdesk address if (isset($helpdesk_senders[$n->email])) { continue; } if ($n->group_id == $ticket->team_id && ($n->bucket_id == -1 || $n->bucket_id == $ticket->category_id)) { // Event checking if ($is_inbound && ($n->event == 'i' || $n->event == 'io') || !$is_inbound && ($n->event == 'o' || $n->event == 'io') || $is_inbound && $n->event == 'r' && $ticket->next_worker_id == $n->worker_id) { $send_to[$n->email] = true; } } } // Attachments $attachments = $message->getAttachments(); $mime_attachments = array(); if (is_array($attachments)) { foreach ($attachments as $attachment) { if (0 == strcasecmp($attachment->display_name, 'original_message.html')) { continue; } $attachment_path = APP_STORAGE_PATH . '/attachments/'; // [TODO] This is highly redundant in the codebase if (!file_exists($attachment_path . $attachment->filepath)) { continue; } $file =& new Swift_File($attachment_path . $attachment->filepath); $mime_attachments[] =& new Swift_Message_Attachment($file, $attachment->display_name, $attachment->mime_type); } } // Send copies if (is_array($send_to) && !empty($send_to)) { $mail_service = DevblocksPlatform::getMailService(); $mailer = $mail_service->getMailer(CerberusMail::getMailerDefaults()); foreach ($send_to as $to => $bool) { // Proxy the message $rcpt_to = new Swift_RecipientList(); $a_rcpt_to = array(); $mail_from = new Swift_Address($sender->email); $rcpt_to->addTo($to); $a_rcpt_to = new Swift_Address($to); $mail = $mail_service->createMessage(); /* @var $mail Swift_Message */ $mail->setTo($a_rcpt_to); $mail->setFrom($mail_from); $mail->setReplyTo($reply_to); $mail->setReturnPath($reply_to); $mail->setSubject(sprintf("[%s #%s]: %s", $is_inbound ? 'inbound' : 'outbound', $ticket->mask, $ticket->subject)); if (false !== @($msgid = $headers['message-id'])) { $mail->headers->set('Message-Id', $msgid); } if (false !== @($in_reply_to = $headers['in-reply-to'])) { $mail->headers->set('References', $in_reply_to); $mail->headers->set('In-Reply-To', $in_reply_to); } $mail->headers->set('X-Mailer', 'Cerberus Helpdesk (Build ' . APP_BUILD . ')'); $mail->headers->set('Precedence', 'List'); $mail->headers->set('Auto-Submitted', 'auto-generated'); $mail->attach(new Swift_Message_Part($message->getContent(), 'text/plain', 'base64', LANG_CHARSET_CODE)); // Send message attachments with watcher if (is_array($mime_attachments)) { foreach ($mime_attachments as $mime_attachment) { $mail->attach($mime_attachment); } } $mailer->send($mail, $rcpt_to, $mail_from); } } } catch (Exception $e) { $fields = array(DAO_MessageNote::MESSAGE_ID => $message_id, DAO_MessageNote::CREATED => time(), DAO_MessageNote::WORKER_ID => 0, DAO_MessageNote::CONTENT => 'Exception thrown while sending watcher email: ' . $e->getMessage(), DAO_MessageNote::TYPE => Model_MessageNote::TYPE_ERROR); DAO_MessageNote::create($fields); } }
static function sendTicketMessage($properties = array()) { $settings = DevblocksPlatform::getPluginSettingsService(); $helpdesk_senders = CerberusApplication::getHelpdeskSenders(); @($from_addy = $settings->get('cerberusweb.core', CerberusSettings::DEFAULT_REPLY_FROM, $_SERVER['SERVER_ADMIN'])); @($from_personal = $settings->get('cerberusweb.core', CerberusSettings::DEFAULT_REPLY_PERSONAL, '')); // [TODO] If we still don't have a $from_addy we need a graceful failure. /* * [TODO] Move these into constants? 'message_id' -----'ticket_id' 'subject' 'to' 'cc' 'bcc' 'content' 'files' 'closed' 'ticket_reopen' 'unlock_date' 'bucket_id' 'agent_id', 'is_autoreply', 'dont_send', 'dont_save_copy' */ $mail_succeeded = true; try { // objects $mail_service = DevblocksPlatform::getMailService(); $mailer = $mail_service->getMailer(CerberusMail::getMailerDefaults()); $mail = $mail_service->createMessage(); // properties @($reply_message_id = $properties['message_id']); @($content = $properties['content']); @($files = $properties['files']); @($forward_files = $properties['forward_files']); @($worker_id = $properties['agent_id']); @($subject = $properties['subject']); $message = DAO_Ticket::getMessage($reply_message_id); $message_headers = DAO_MessageHeader::getAll($reply_message_id); $ticket_id = $message->ticket_id; $ticket = DAO_Ticket::getTicket($ticket_id); // [TODO] Check that message|ticket isn't NULL // If this ticket isn't spam trained and our outgoing message isn't an autoreply if ($ticket->spam_training == CerberusTicketSpamTraining::BLANK && (!isset($properties['is_autoreply']) || !$properties['is_autoreply'])) { CerberusBayes::markTicketAsNotSpam($ticket_id); } // Allow teams to override the default from/personal @($group_reply = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_FROM, '')); @($group_personal = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_PERSONAL, '')); @($group_personal_with_worker = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_PERSONAL_WITH_WORKER, 0)); if (!empty($group_reply)) { $from_addy = $group_reply; } if (!empty($group_personal)) { $from_personal = $group_personal; } // Prefix the worker name on the personal line? if (!empty($group_personal_with_worker) && null != ($reply_worker = DAO_Worker::getAgent($worker_id))) { $from_personal = $reply_worker->getName() . (!empty($from_personal) ? ', ' . $from_personal : ""); } // Headers $mail->setFrom(array($from_addy => $from_personal)); $mail->generateId(); $headers = $mail->getHeaders(); $headers->addTextHeader('X-Mailer', 'Cerberus Helpdesk (Build ' . APP_BUILD . ')'); // Subject if (empty($subject)) { $subject = $ticket->subject; } if (!empty($properties['to'])) { // forward $mail->setSubject($subject); } else { // reply @($group_has_subject = intval(DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_SUBJECT_HAS_MASK, 0))); @($group_subject_prefix = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_SUBJECT_PREFIX, '')); $prefix = sprintf("[%s#%s] ", !empty($group_subject_prefix) ? $group_subject_prefix . ' ' : '', $ticket->mask); $mail->setSubject(sprintf('Re: %s%s', $group_has_subject ? $prefix : '', $subject)); } // References if (!empty($message) && false !== @($in_reply_to = $message_headers['message-id'])) { $headers->addTextHeader('References', $in_reply_to); $headers->addTextHeader('In-Reply-To', $in_reply_to); } // Auto-reply handling (RFC-3834 compliant) if (isset($properties['is_autoreply']) && $properties['is_autoreply']) { $headers->addTextHeader('Auto-Submitted', 'auto-replied'); if (null == ($first_address = DAO_Address::get($ticket->first_wrote_address_id))) { return; } // Don't send e-mail to ourselves if (isset($helpdesk_senders[$first_address->email])) { return; } // Make sure we haven't mailed this address an autoreply within 5 minutes if ($first_address->last_autoreply > 0 && $first_address->last_autoreply > time() - 300) { return; } $first_email = strtolower($first_address->email); $first_split = explode('@', $first_email); if (!is_array($first_split) || count($first_split) != 2) { return; } // If return-path is blank if (isset($message_headers['return-path']) && $message_headers['return-path'] == '<>') { return; } // Ignore bounces if ($first_split[0] == "postmaster" || $first_split[0] == "mailer-daemon") { return; } // Ignore autoresponses to autoresponses if (isset($message_headers['auto-submitted']) && $message_headers['auto-submitted'] != 'no') { return; } if (isset($message_headers['precedence']) && ($message_headers['precedence'] == 'list' || $message_headers['precedence'] == 'junk' || ($message_headers['precedence'] = 'bulk'))) { return; } // Set the auto-reply date for this address to right now DAO_Address::update($ticket->first_wrote_address_id, array(DAO_Address::LAST_AUTOREPLY => time())); // Auto-reply just to the initial requester $mail->addTo($first_address->email); // Not an auto-reply } else { // Forwards if (!empty($properties['to'])) { $aTo = DevblocksPlatform::parseCsvString(str_replace(';', ',', $properties['to'])); if (is_array($aTo)) { foreach ($aTo as $to_addy) { $mail->addTo($to_addy); } } // Replies } else { // Recipients $requesters = DAO_Ticket::getRequestersByTicket($ticket_id); if (is_array($requesters)) { foreach ($requesters as $requester) { /* @var $requester Model_Address */ $mail->addTo($requester->email); } } } // Ccs if (!empty($properties['cc'])) { $aCc = DevblocksPlatform::parseCsvString(str_replace(';', ',', $properties['cc'])); $mail->setCc($aCc); } // Bccs if (!empty($properties['bcc'])) { $aBcc = DevblocksPlatform::parseCsvString(str_replace(';', ',', $properties['bcc'])); $mail->setBcc($aBcc); } } /* * [IMPORTANT -- Yes, this is simply a line in the sand.] * You're welcome to modify the code to meet your needs, but please respect * our licensing. Buy a legitimate copy to help support the project! * http://www.cerberusweb.com/ */ $license = CerberusLicense::getInstance(); if (empty($license) || @empty($license['serial'])) { $content .= base64_decode("DQoNCi0tLQ0KQ29tYmF0IHNwYW0gYW5kIGltcHJvdmUgcmVzc" . "G9uc2UgdGltZXMgd2l0aCBDZXJiZXJ1cyBIZWxwZGVzayA0LjAhDQpodHRwOi8vd3d3LmNlc" . "mJlcnVzd2ViLmNvbS8NCg"); } // Body $mail->setBody($content); // Mime Attachments if (is_array($files) && !empty($files)) { foreach ($files['tmp_name'] as $idx => $file) { if (empty($file) || empty($files['name'][$idx])) { continue; } $mail->attach(Swift_Attachment::fromPath($file)->setFilename($files['name'][$idx])); } } // Forward Attachments if (!empty($forward_files) && is_array($forward_files)) { $attachments_path = APP_STORAGE_PATH . '/attachments/'; foreach ($forward_files as $file_id) { $attachment = DAO_Attachment::get($file_id); $attachment_path = $attachments_path . $attachment->filepath; $mail->attach(Swift_Attachment::fromPath($attachment_path)->setFilename($attachment->display_name)); } } if (!DEMO_MODE) { // If we're not supposed to send if (isset($properties['dont_send']) && $properties['dont_send']) { // ...do nothing } else { // otherwise send if (!$mailer->send($mail)) { $mail_succeeded = false; throw new Exception('Mail not sent.'); } } } } catch (Exception $e) { // tag failure, so we can add a note to the message later $mail_succeeded = false; } // Handle post-mail actions $change_fields = array(); $fromAddressInst = CerberusApplication::hashLookupAddress($from_addy, true); $fromAddressId = $fromAddressInst->id; if ((!isset($properties['dont_keep_copy']) || !$properties['dont_keep_copy']) && (!isset($properties['is_autoreply']) || !$properties['is_autoreply'])) { $change_fields[DAO_Ticket::LAST_WROTE_ID] = $fromAddressId; $change_fields[DAO_Ticket::UPDATED_DATE] = time(); if (!empty($worker_id)) { $change_fields[DAO_Ticket::LAST_WORKER_ID] = $worker_id; $change_fields[DAO_Ticket::LAST_ACTION_CODE] = CerberusTicketActionCode::TICKET_WORKER_REPLY; } // Only change the subject if not forwarding if (!empty($subject) && empty($properties['to'])) { $change_fields[DAO_Ticket::SUBJECT] = $subject; } $fields = array(DAO_Message::TICKET_ID => $ticket_id, DAO_Message::CREATED_DATE => time(), DAO_Message::ADDRESS_ID => $fromAddressId, DAO_Message::IS_OUTGOING => 1, DAO_Message::WORKER_ID => !empty($worker_id) ? $worker_id : 0); $message_id = DAO_Message::create($fields); // Content DAO_MessageContent::create($message_id, $content); $headers = $mail->getHeaders(); // Headers foreach ($headers->getAll() as $hdr) { if (null != ($hdr_val = $hdr->getFieldBody())) { if (!empty($hdr_val)) { DAO_MessageHeader::create($message_id, $hdr->getFieldName(), CerberusParser::fixQuotePrintableString($hdr_val)); } } } // Attachments if (is_array($files) && !empty($files)) { $attachment_path = APP_STORAGE_PATH . '/attachments/'; reset($files); foreach ($files['tmp_name'] as $idx => $file) { if (empty($file) || empty($files['name'][$idx]) || !file_exists($file)) { continue; } $fields = array(DAO_Attachment::MESSAGE_ID => $message_id, DAO_Attachment::DISPLAY_NAME => $files['name'][$idx], DAO_Attachment::MIME_TYPE => $files['type'][$idx], DAO_Attachment::FILE_SIZE => filesize($file)); $file_id = DAO_Attachment::create($fields); $attachment_bucket = sprintf("%03d/", mt_rand(1, 100)); $attachment_file = $file_id; if (!file_exists($attachment_path . $attachment_bucket)) { mkdir($attachment_path . $attachment_bucket, 0775, true); } if (!is_writeable($attachment_path . $attachment_bucket)) { echo "Can't write to bucket " . $attachment_path . $attachment_bucket . "<BR>"; } copy($file, $attachment_path . $attachment_bucket . $attachment_file); @unlink($file); DAO_Attachment::update($file_id, array(DAO_Attachment::FILEPATH => $attachment_bucket . $attachment_file)); } } // add note to message if email failed if ($mail_succeeded === false) { $fields = array(DAO_MessageNote::MESSAGE_ID => $message_id, DAO_MessageNote::CREATED => time(), DAO_MessageNote::WORKER_ID => 0, DAO_MessageNote::CONTENT => 'Exception thrown while sending email: ' . $e->getMessage(), DAO_MessageNote::TYPE => Model_MessageNote::TYPE_ERROR); DAO_MessageNote::create($fields); } } // Post-Reply Change Properties if (isset($properties['closed'])) { switch ($properties['closed']) { case 0: // open $change_fields[DAO_Ticket::IS_WAITING] = 0; $change_fields[DAO_Ticket::IS_CLOSED] = 0; $change_fields[DAO_Ticket::IS_DELETED] = 0; $change_fields[DAO_Ticket::DUE_DATE] = 0; break; case 1: // closed $change_fields[DAO_Ticket::IS_WAITING] = 0; $change_fields[DAO_Ticket::IS_CLOSED] = 1; $change_fields[DAO_Ticket::IS_DELETED] = 0; if (isset($properties['ticket_reopen'])) { @($time = intval(strtotime($properties['ticket_reopen']))); $change_fields[DAO_Ticket::DUE_DATE] = $time; } break; case 2: // waiting $change_fields[DAO_Ticket::IS_WAITING] = 1; $change_fields[DAO_Ticket::IS_CLOSED] = 0; $change_fields[DAO_Ticket::IS_DELETED] = 0; if (isset($properties['ticket_reopen'])) { @($time = intval(strtotime($properties['ticket_reopen']))); $change_fields[DAO_Ticket::DUE_DATE] = $time; } break; } } // Who should handle the followup? if (isset($properties['next_worker_id'])) { $change_fields[DAO_Ticket::NEXT_WORKER_ID] = $properties['next_worker_id']; } // Allow anybody to reply after if (isset($properties['unlock_date']) && !empty($properties['unlock_date'])) { $unlock = strtotime($properties['unlock_date']); if (intval($unlock) > 0) { $change_fields[DAO_Ticket::UNLOCK_DATE] = $unlock; } } // Move if (!empty($properties['bucket_id'])) { // [TODO] Use API to move, or fire event // [TODO] Ensure team/bucket exist list($team_id, $bucket_id) = CerberusApplication::translateTeamCategoryCode($properties['bucket_id']); $change_fields[DAO_Ticket::TEAM_ID] = $team_id; $change_fields[DAO_Ticket::CATEGORY_ID] = $bucket_id; } if (!empty($ticket_id) && !empty($change_fields)) { DAO_Ticket::updateTicket($ticket_id, $change_fields); } // Outbound Reply Event (not automated reply, etc.) if (!empty($worker_id)) { $eventMgr = DevblocksPlatform::getEventService(); $eventMgr->trigger(new Model_DevblocksEvent('ticket.reply.outbound', array('ticket_id' => $ticket_id, 'worker_id' => $worker_id))); } }
private function _sendForwards($event, $is_inbound) { @($ticket_id = $event->params['ticket_id']); @($send_worker_id = $event->params['worker_id']); $ticket = DAO_Ticket::getTicket($ticket_id); // Find all our matching filters if (false == ($matches = Model_WatcherMailFilter::getMatches($ticket, $is_inbound ? 'mail_incoming' : 'mail_outgoing'))) { return; } // Sanitize and combine all the destination addresses $notify_emails = $this->_getMailingListFromMatches($matches); if (empty($notify_emails)) { return; } // [TODO] This could be more efficient $messages = DAO_Ticket::getMessagesByTicket($ticket_id); $message = end($messages); // last message unset($messages); $headers = $message->getHeaders(); // The whole flipping Swift section needs wrapped to catch exceptions try { $settings = CerberusSettings::getInstance(); $reply_to = $settings->get(CerberusSettings::DEFAULT_REPLY_FROM, ''); // See if we need a group-specific reply-to if (!empty($ticket->team_id)) { @($group_from = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_FROM, '')); if (!empty($group_from)) { $reply_to = $group_from; } } $sender = DAO_Address::get($message->address_id); $sender_email = strtolower($sender->email); $sender_split = explode('@', $sender_email); if (!is_array($sender_split) || count($sender_split) != 2) { return; } // If return-path is blank if (isset($headers['return-path']) && $headers['return-path'] == '<>') { return; } // Ignore bounces if ($sender_split[0] == "postmaster" || $sender_split[0] == "mailer-daemon") { return; } // Ignore autoresponses autoresponses if (isset($headers['auto-submitted']) && $headers['auto-submitted'] != 'no') { return; } // Attachments $attachments = $message->getAttachments(); $mime_attachments = array(); if (is_array($attachments)) { foreach ($attachments as $attachment) { if (0 == strcasecmp($attachment->display_name, 'original_message.html')) { continue; } $attachment_path = APP_STORAGE_PATH . '/attachments/'; // [TODO] This is highly redundant in the codebase if (!file_exists($attachment_path . $attachment->filepath)) { continue; } $file =& new Swift_File($attachment_path . $attachment->filepath); $mime_attachments[] =& new Swift_Message_Attachment($file, $attachment->display_name, $attachment->mime_type); } } // Send copies if (is_array($notify_emails) && !empty($notify_emails)) { $mail_service = DevblocksPlatform::getMailService(); $mailer = $mail_service->getMailer(CerberusMail::getMailerDefaults()); foreach ($notify_emails as $to) { // Proxy the message $rcpt_to = new Swift_RecipientList(); $a_rcpt_to = array(); $mail_from = new Swift_Address($sender->email); $rcpt_to->addTo($to); $a_rcpt_to = new Swift_Address($to); $mail = $mail_service->createMessage(); /* @var $mail Swift_Message */ $mail->setTo($a_rcpt_to); $mail->setFrom($mail_from); $mail->setReplyTo($reply_to); $mail->setReturnPath($reply_to); $mail->setSubject(sprintf("[%s #%s]: %s", $is_inbound ? 'inbound' : 'outbound', $ticket->mask, $ticket->subject)); if (false !== @($msgid = $headers['message-id'])) { $mail->headers->set('Message-Id', $msgid); } if (false !== @($in_reply_to = $headers['in-reply-to'])) { $mail->headers->set('References', $in_reply_to); $mail->headers->set('In-Reply-To', $in_reply_to); } $mail->headers->set('X-Mailer', 'Cerberus Helpdesk (Build ' . APP_BUILD . ')'); $mail->headers->set('Precedence', 'List'); $mail->headers->set('Auto-Submitted', 'auto-generated'); $mail->attach(new Swift_Message_Part($message->getContent(), 'text/plain', 'base64', LANG_CHARSET_CODE)); // Send message attachments with watcher if (is_array($mime_attachments)) { foreach ($mime_attachments as $mime_attachment) { $mail->attach($mime_attachment); } } $mailer->send($mail, $rcpt_to, $mail_from); } } } catch (Exception $e) { $fields = array(DAO_MessageNote::MESSAGE_ID => $message_id, DAO_MessageNote::CREATED => time(), DAO_MessageNote::WORKER_ID => 0, DAO_MessageNote::CONTENT => 'Exception thrown while sending watcher email: ' . $e->getMessage(), DAO_MessageNote::TYPE => Model_MessageNote::TYPE_ERROR); DAO_MessageNote::create($fields); } }
function showTab() { $response = DevblocksPlatform::getHttpResponse(); $path = $response->path; array_shift($path); // iphone array_shift($path); // tickets array_shift($path); // current ('display') $id = array_shift($path); // ticket id @($active_worker = CerberusApplication::getActiveWorker()); $tpl = DevblocksPlatform::getTemplateService(); $tpl->assign('path', $this->_TPL_PATH); $tpl->assign('expand_all', $expand_all); $ticket = DAO_Ticket::getTicket($id); $tpl->assign('requesters', $ticket->getRequesters()); // Drafts $drafts = DAO_MailQueue::getWhere(sprintf("%s = %d AND %s = %s", DAO_MailQueue::TICKET_ID, $id, DAO_MailQueue::TYPE, C4_ORMHelper::qstr(Model_MailQueue::TYPE_TICKET_REPLY))); if (!empty($drafts)) { $tpl->assign('drafts', $drafts); } // Only unqueued drafts $pending_drafts = array(); if (!empty($drafts) && is_array($drafts)) { foreach ($drafts as $draft_id => $draft) { if (!$draft->is_queued) { $pending_drafts[$draft_id] = $draft; } } } if (!empty($pending_drafts)) { $tpl->assign('pending_drafts', $pending_drafts); } // Messages $messages = $ticket->getMessages(); arsort($messages); $tpl->assign('latest_message_id', key($messages)); $tpl->assign('messages', $messages); // Thread comments and messages on the same level $convo_timeline = array(); // Track senders and their orgs $message_senders = array(); $message_sender_orgs = array(); // Loop messages foreach ($messages as $message_id => $message) { /* @var $message Model_Message */ $key = $message->created_date . '_m' . $message_id; // build a chrono index of messages $convo_timeline[$key] = array('m', $message_id); // If we haven't cached this sender address yet if (!isset($message_senders[$message->address_id])) { if (null != ($sender_addy = DAO_Address::get($message->address_id))) { $message_senders[$sender_addy->id] = $sender_addy; // If we haven't cached this sender org yet if (!isset($message_sender_orgs[$sender_addy->contact_org_id])) { if (null != ($sender_org = DAO_ContactOrg::get($sender_addy->contact_org_id))) { $message_sender_orgs[$sender_org->id] = $sender_org; } } } } } $tpl->assign('message_senders', $message_senders); $tpl->assign('message_sender_orgs', $message_sender_orgs); @($mail_inline_comments = DAO_WorkerPref::get($active_worker->id, 'mail_inline_comments', 1)); if ($mail_inline_comments) { // if inline comments are enabled $comments = DAO_TicketComment::getByTicketId($id); arsort($comments); $tpl->assign('comments', $comments); // build a chrono index of comments foreach ($comments as $comment_id => $comment) { /* @var $comment Model_TicketComment */ $key = $comment->created . '_c' . $comment_id; $convo_timeline[$key] = array('c', $comment_id); } } // Thread drafts into conversation if (!empty($drafts)) { foreach ($drafts as $draft_id => $draft) { /* @var $draft Model_MailQueue */ $key = $draft->updated . '_d' . $draft_id; $convo_timeline[$key] = array('d', $draft_id); } } // sort the timeline if (!$expand_all) { krsort($convo_timeline); } else { ksort($convo_timeline); } $tpl->assign('convo_timeline', $convo_timeline); // Message Notes $notes = DAO_MessageNote::getByTicketId($id); $message_notes = array(); // Index notes by message id if (is_array($notes)) { foreach ($notes as $note) { if (!isset($message_notes[$note->message_id])) { $message_notes[$note->message_id] = array(); } $message_notes[$note->message_id][$note->id] = $note; } } $tpl->assign('message_notes', $message_notes); // Message toolbar items $messageToolbarItems = DevblocksPlatform::getExtensions('cerberusweb.message.toolbaritem', true); if (!empty($messageToolbarItems)) { $tpl->assign('message_toolbaritems', $messageToolbarItems); } // Workers $workers = DAO_Worker::getAll(); $tpl->assign('workers', $workers); $tpl->display('file:' . $this->_TPL_PATH . 'tickets/display/conversation.tpl'); }