function saveTabInboxAddAction() { $translate = DevblocksPlatform::getTranslationService(); @($id = DevblocksPlatform::importGPC($_REQUEST['id'], 'integer', 0)); @($group_id = DevblocksPlatform::importGPC($_REQUEST['group_id'], 'integer')); @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string', '')); @($active_worker = CerberusApplication::getActiveWorker()); if (!$active_worker->isTeamManager($group_id) && !$active_worker->is_superuser) { return; } /*****************************/ @($name = DevblocksPlatform::importGPC($_POST['name'], 'string', '')); @($is_sticky = DevblocksPlatform::importGPC($_POST['is_sticky'], 'integer', 0)); @($is_stackable = DevblocksPlatform::importGPC($_POST['is_stackable'], 'integer', 0)); @($rules = DevblocksPlatform::importGPC($_POST['rules'], 'array', array())); @($do = DevblocksPlatform::importGPC($_POST['do'], 'array', array())); if (empty($name)) { $name = $translate->_('mail.inbox_filter'); } $criterion = array(); $actions = array(); // Custom fields $custom_fields = DAO_CustomField::getAll(); // Criteria if (is_array($rules)) { foreach ($rules as $rule) { $rule = DevblocksPlatform::strAlphaNumDash($rule); @($value = DevblocksPlatform::importGPC($_POST['value_' . $rule], 'string', '')); // [JAS]: Allow empty $value (null/blank checking) $criteria = array('value' => $value); // Any special rule handling switch ($rule) { case 'dayofweek': // days $days = DevblocksPlatform::importGPC($_REQUEST['value_dayofweek'], 'array', array()); if (in_array(0, $days)) { $criteria['sun'] = 'Sunday'; } if (in_array(1, $days)) { $criteria['mon'] = 'Monday'; } if (in_array(2, $days)) { $criteria['tue'] = 'Tuesday'; } if (in_array(3, $days)) { $criteria['wed'] = 'Wednesday'; } if (in_array(4, $days)) { $criteria['thu'] = 'Thursday'; } if (in_array(5, $days)) { $criteria['fri'] = 'Friday'; } if (in_array(6, $days)) { $criteria['sat'] = 'Saturday'; } unset($criteria['value']); break; case 'timeofday': $from = DevblocksPlatform::importGPC($_REQUEST['timeofday_from'], 'string', ''); $to = DevblocksPlatform::importGPC($_REQUEST['timeofday_to'], 'string', ''); $criteria['from'] = $from; $criteria['to'] = $to; unset($criteria['value']); break; case 'subject': break; case 'from': break; case 'tocc': break; case 'header1': case 'header2': case 'header3': case 'header4': case 'header5': if (null != @($header = DevblocksPlatform::importGPC($_POST[$rule], 'string', null))) { $criteria['header'] = strtolower($header); } break; case 'body': break; case 'attachment': break; default: // ignore invalids // Custom fields if ("cf_" == substr($rule, 0, 3)) { $field_id = intval(substr($rule, 3)); if (!isset($custom_fields[$field_id])) { continue; } // [TODO] Operators switch ($custom_fields[$field_id]->type) { case 'S': // string // string case 'T': // clob // clob case 'U': // URL $oper = DevblocksPlatform::importGPC($_REQUEST['value_cf_' . $field_id . '_oper'], 'string', 'regexp'); $criteria['oper'] = $oper; break; case 'D': // dropdown // dropdown case 'M': // multi-dropdown // multi-dropdown case 'X': // multi-checkbox // multi-checkbox case 'W': // worker $in_array = DevblocksPlatform::importGPC($_REQUEST['value_cf_' . $field_id], 'array', array()); $out_array = array(); // Hash key on the option for quick lookup later if (is_array($in_array)) { foreach ($in_array as $k => $v) { $out_array[$v] = $v; } } $criteria['value'] = $out_array; break; case 'E': // date $from = DevblocksPlatform::importGPC($_REQUEST['value_cf_' . $field_id . '_from'], 'string', '0'); $to = DevblocksPlatform::importGPC($_REQUEST['value_cf_' . $field_id . '_to'], 'string', 'now'); $criteria['from'] = $from; $criteria['to'] = $to; unset($criteria['value']); break; case 'N': // number $oper = DevblocksPlatform::importGPC($_REQUEST['value_cf_' . $field_id . '_oper'], 'string', '='); $criteria['oper'] = $oper; $criteria['value'] = intval($value); break; case 'C': // checkbox $criteria['value'] = intval($value); break; } } else { continue; } break; } $criterion[$rule] = $criteria; } } // Actions if (is_array($do)) { foreach ($do as $act) { $action = array(); switch ($act) { // Move group/bucket case 'move': @($move_code = DevblocksPlatform::importGPC($_REQUEST['do_move'], 'string', null)); if (0 != strlen($move_code)) { list($g_id, $b_id) = CerberusApplication::translateTeamCategoryCode($move_code); $action = array('group_id' => intval($g_id), 'bucket_id' => intval($b_id)); } break; // Assign to worker // Assign to worker case 'assign': @($worker_id = DevblocksPlatform::importGPC($_REQUEST['do_assign'], 'string', null)); if (0 != strlen($worker_id)) { $action = array('worker_id' => intval($worker_id)); } break; // Spam training // Spam training case 'spam': @($is_spam = DevblocksPlatform::importGPC($_REQUEST['do_spam'], 'string', null)); if (0 != strlen($is_spam)) { $action = array('is_spam' => !$is_spam ? 0 : 1); } break; // Set status // Set status case 'status': @($status = DevblocksPlatform::importGPC($_REQUEST['do_status'], 'string', null)); if (0 != strlen($status)) { $action = array('is_waiting' => 3 == $status ? 1 : 0, 'is_closed' => 0 == $status || 3 == $status ? 0 : 1, 'is_deleted' => 2 == $status ? 1 : 0); } break; default: // ignore invalids // Custom fields if ("cf_" == substr($act, 0, 3)) { $field_id = intval(substr($act, 3)); if (!isset($custom_fields[$field_id])) { continue; } $action = array(); // [TODO] Operators switch ($custom_fields[$field_id]->type) { case 'S': // string // string case 'T': // clob // clob case 'D': // dropdown // dropdown case 'U': // URL // URL case 'W': // worker $value = DevblocksPlatform::importGPC($_REQUEST['do_cf_' . $field_id], 'string', ''); $action['value'] = $value; break; case 'M': // multi-dropdown // multi-dropdown case 'X': // multi-checkbox $in_array = DevblocksPlatform::importGPC($_REQUEST['do_cf_' . $field_id], 'array', array()); $out_array = array(); // Hash key on the option for quick lookup later if (is_array($in_array)) { foreach ($in_array as $k => $v) { $out_array[$v] = $v; } } $action['value'] = $out_array; break; case 'E': // date $value = DevblocksPlatform::importGPC($_REQUEST['do_cf_' . $field_id], 'string', ''); $action['value'] = $value; break; case 'N': // number // number case 'C': // checkbox $value = DevblocksPlatform::importGPC($_REQUEST['do_cf_' . $field_id], 'string', ''); $action['value'] = intval($value); break; } } else { continue; } break; } $actions[$act] = $action; } } $fields = array(DAO_GroupInboxFilter::NAME => $name, DAO_GroupInboxFilter::IS_STICKY => $is_sticky, DAO_GroupInboxFilter::CRITERIA_SER => serialize($criterion), DAO_GroupInboxFilter::ACTIONS_SER => serialize($actions)); // Only sticky filters can manual order and be stackable if (!$is_sticky) { $fields[DAO_GroupInboxFilter::STICKY_ORDER] = 0; $fields[DAO_GroupInboxFilter::IS_STACKABLE] = 0; } else { // is sticky $fields[DAO_GroupInboxFilter::IS_STACKABLE] = $is_stackable; } // Create if (empty($id)) { $fields[DAO_GroupInboxFilter::GROUP_ID] = $group_id; $fields[DAO_GroupInboxFilter::POS] = 0; $id = DAO_GroupInboxFilter::create($fields); // Update } else { DAO_GroupInboxFilter::update($id, $fields); } $defaults = new C4_AbstractViewModel(); $defaults->class_name = 'C4_TicketView'; $defaults->id = $view_id; $view = C4_AbstractViewLoader::getView($view_id, $defaults); if (!empty($view_id) && null != $view) { /* @var $view C4_TicketView */ // Loop through all the tickets in this inbox list($inbox_tickets, $null) = DAO_Ticket::search(null, array(new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_TEAM_ID, '=', $group_id), new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_CATEGORY_ID, '=', '0')), -1, 0, null, null, false); if (is_array($inbox_tickets)) { foreach ($inbox_tickets as $inbox_ticket) { /* @var $inbox_ticket CerberusTicket */ // Run only this new rule against all tickets in the group inbox CerberusApplication::runGroupRouting($group_id, intval($inbox_ticket[SearchFields_Ticket::TICKET_ID]), $id); } } $view->render(); return; } DevblocksPlatform::redirect(new DevblocksHttpResponse(array('groups', $group_id, 'inbox'))); }
function saveAddInboxRulePanelAction() { $translate = DevblocksPlatform::getTranslationService(); @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string', '')); @($group_id = DevblocksPlatform::importGPC($_REQUEST['group_id'], 'integer')); $view = C4_AbstractViewLoader::getView('C4_TicketView', $view_id); /* @var $view C4_TicketView */ if (empty($group_id)) { $view->render(); exit; } @($name = DevblocksPlatform::importGPC($_POST['name'], 'string', '')); @($rules = DevblocksPlatform::importGPC($_POST['rules'], 'array', array())); @($do = DevblocksPlatform::importGPC($_POST['do'], 'array', array())); if (empty($name)) { $name = $translate->_('mail.inbox_filter'); } $criterion = array(); $actions = array(); // Criteria if (is_array($rules)) { foreach ($rules as $rule) { $rule = DevblocksPlatform::strAlphaNumDash($rule); @($value = DevblocksPlatform::importGPC($_POST['value_' . $rule], 'string', '')); // [JAS]: Allow empty $value (null/blank checking) $criteria = array('value' => $value); // Any special rule handling switch ($rule) { case 'subject': break; case 'from': break; case 'tocc': break; case 'header1': case 'header2': case 'header3': case 'header4': case 'header5': if (null != @($header = DevblocksPlatform::importGPC($_POST[$rule], 'string', null))) { $criteria['header'] = strtolower($header); } break; case 'body': break; case 'attachment': break; default: // ignore invalids continue; break; } $criterion[$rule] = $criteria; } } // Actions if (is_array($do)) { foreach ($do as $act) { $action = array(); switch ($act) { // Move group/bucket case 'move': @($move_code = DevblocksPlatform::importGPC($_REQUEST['do_move'], 'string', null)); if (0 != strlen($move_code)) { list($g_id, $b_id) = CerberusApplication::translateTeamCategoryCode($move_code); $action = array('group_id' => intval($g_id), 'bucket_id' => intval($b_id)); } break; // Assign to worker // Assign to worker case 'assign': @($worker_id = DevblocksPlatform::importGPC($_REQUEST['do_assign'], 'string', null)); if (0 != strlen($worker_id)) { $action = array('worker_id' => intval($worker_id)); } break; // Spam training // Spam training case 'spam': @($is_spam = DevblocksPlatform::importGPC($_REQUEST['do_spam'], 'string', null)); if (0 != strlen($is_spam)) { $action = array('is_spam' => !$is_spam ? 0 : 1); } break; // Set status // Set status case 'status': @($status = DevblocksPlatform::importGPC($_REQUEST['do_status'], 'string', null)); if (0 != strlen($status)) { $action = array('is_closed' => 0 == $status ? 0 : 1, 'is_deleted' => 2 == $status ? 1 : 0); } break; default: // ignore invalids continue; break; } $actions[$act] = $action; } } $fields = array(DAO_GroupInboxFilter::NAME => $name, DAO_GroupInboxFilter::GROUP_ID => $group_id, DAO_GroupInboxFilter::CRITERIA_SER => serialize($criterion), DAO_GroupInboxFilter::ACTIONS_SER => serialize($actions), DAO_GroupInboxFilter::POS => 0); $routing_id = DAO_GroupInboxFilter::create($fields); // Loop through all the tickets in this inbox list($inbox_tickets, $null) = DAO_Ticket::search(null, array(new DevblocksSearchCriteria(SearchFields_Ticket::TEAM_ID, '=', $group_id), new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_CATEGORY_ID, '=', '0')), -1, 0, null, null, false); if (is_array($inbox_tickets)) { foreach ($inbox_tickets as $inbox_ticket) { /* @var $inbox_ticket CerberusTicket */ // Run only this new rule against all tickets in the group inbox CerberusApplication::runGroupRouting($group_id, intval($inbox_ticket[SearchFields_Ticket::TICKET_ID]), $routing_id); } } $view->render(); exit; }
private function _handleTicketMoved($event) { @($ticket_ids = $event->params['ticket_ids']); @($changed_fields = $event->params['changed_fields']); if (!isset($changed_fields[DAO_Ticket::TEAM_ID])) { return; } @($team_id = $changed_fields[DAO_Ticket::TEAM_ID]); @($bucket_id = $changed_fields[DAO_Ticket::CATEGORY_ID]); $final_moves = array(); // If we're landing in an inbox we need to check its filters if (!empty($ticket_ids) && !empty($team_id) && empty($bucket_id)) { // moving to an inbox if (is_array($ticket_ids)) { foreach ($ticket_ids as $ticket_id) { // Run the new inbox filters $matches = CerberusApplication::runGroupRouting($team_id, $ticket_id); // If we matched no rules, we're stuck in the destination inbox. if (empty($matches)) { $final_moves[] = $ticket_id; } else { // If more inbox rules want to move this ticket don't consider this finished if (is_array($matches)) { foreach ($matches as $match) { if (isset($match->actions['move'])) { // any moves break; } $final_moves[] = $ticket_id; } } } } } // Anything dropping into a non-inbox bucket is done chain-moving } elseif (!empty($ticket_ids) && !empty($team_id) && !empty($bucket_id)) { $final_moves = $ticket_ids; } // Did we have any tickets finish chain-moving? if (!empty($final_moves)) { // Trigger an inbound event for all moves if (is_array($final_moves)) { foreach ($final_moves as $ticket_id) { // Inbound Reply Event $eventMgr = DevblocksPlatform::getEventService(); $eventMgr->trigger(new Model_DevblocksEvent('ticket.reply.inbound', array('ticket_id' => $ticket_id))); } } } }
/** * Enter description here... * * @param CerberusParserMessage $message * @return integer */ public static function parseMessage(CerberusParserMessage $message, $options = array()) { // print_r($rfcMessage); /* * options: * 'no_autoreply' */ $logger = DevblocksPlatform::getConsoleLog(); $settings = CerberusSettings::getInstance(); $helpdesk_senders = CerberusApplication::getHelpdeskSenders(); $headers =& $message->headers; // To/From/Cc/Bcc $sReturnPath = @$headers['return-path']; $sReplyTo = @$headers['reply-to']; $sFrom = @$headers['from']; $sTo = @$headers['to']; $bIsNew = true; // Overloadable $sMask = ''; $iClosed = 0; $enumSpamTraining = ''; $iDate = time(); $from = array(); $to = array(); if (!empty($sReplyTo)) { $from = CerberusParser::parseRfcAddress($sReplyTo); } elseif (!empty($sFrom)) { $from = CerberusParser::parseRfcAddress($sFrom); } elseif (!empty($sReturnPath)) { $from = CerberusParser::parseRfcAddress($sReturnPath); } if (!empty($sTo)) { // [TODO] Do we still need this RFC address parser? $to = CerberusParser::parseRfcAddress($sTo); } // Subject // Fix quote printable subject (quoted blocks can appear anywhere in subject) $sSubject = ""; if (isset($headers['subject']) && !empty($headers['subject'])) { $sSubject = self::fixQuotePrintableString($headers['subject']); } // The subject can still end up empty after QP decode if (empty($sSubject)) { $sSubject = "(no subject)"; } // Date $iDate = @strtotime($headers['date']); // If blank, or in the future, set to the current date if (empty($iDate) || $iDate > time()) { $iDate = time(); } if (empty($from) || !is_array($from)) { $logger->warn("[Parser] Invalid 'From' address: " . $from); return NULL; } @($fromAddress = $from[0]->mailbox . '@' . $from[0]->host); @($fromPersonal = $from[0]->personal); if (null == ($fromAddressInst = CerberusApplication::hashLookupAddress($fromAddress, true))) { $logger->err("[Parser] 'From' address could not be created: " . $fromAddress); return NULL; } else { $fromAddressId = $fromAddressInst->id; } // Is banned? if (1 == $fromAddressInst->is_banned) { $logger->info("[Parser] Ignoring ticket from banned address: " . $fromAddressInst->email); return NULL; } // Message Id / References / In-Reply-To @($sMessageId = $headers['message-id']); $body_append_text = array(); $body_append_html = array(); // [mdf]Check attached files before creating the ticket because we may need to overwrite the message-id // also store any contents of rfc822 files so we can include them after the body foreach ($message->files as $filename => $file) { /* @var $file ParserFile */ switch ($file->mime_type) { case 'message/rfc822': $full_filename = $file->tmpname; $mail = mailparse_msg_parse_file($full_filename); $struct = mailparse_msg_get_structure($mail); $msginfo = mailparse_msg_get_part_data($mail); $inline_headers = $msginfo['headers']; if (isset($headers['from']) && (strtolower(substr($headers['from'], 0, 11)) == 'postmaster@' || strtolower(substr($headers['from'], 0, 14)) == 'mailer-daemon@')) { $headers['in-reply-to'] = $inline_headers['message-id']; } break; } } // [JAS] [TODO] References header may contain multiple message-ids to find if (null != ($ids = self::findParentMessage($headers))) { $bIsNew = false; $id = $ids['ticket_id']; $msgid = $ids['message_id']; // Is it a worker reply from an external client? If so, proxy if (null != ($worker_address = DAO_AddressToWorker::getByAddress($fromAddressInst->email))) { $logger->info("[Parser] Handling an external worker response from " . $fromAddressInst->email); if (!DAO_Ticket::isTicketRequester($worker_address->address, $id)) { // Watcher Commands [TODO] Document on wiki/etc if (0 != ($matches = preg_match_all("/\\[(.*?)\\]/i", $message->headers['subject'], $commands))) { @($command = strtolower(array_pop($commands[1]))); $logger->info("[Parser] Worker command: " . $command); switch ($command) { case 'close': DAO_Ticket::updateTicket($id, array(DAO_Ticket::IS_CLOSED => CerberusTicketStatus::CLOSED)); break; case 'take': DAO_Ticket::updateTicket($id, array(DAO_Ticket::NEXT_WORKER_ID => $worker_address->worker_id)); break; case 'comment': $comment_id = DAO_TicketComment::create(array(DAO_TicketComment::ADDRESS_ID => $fromAddressId, DAO_TicketComment::CREATED => time(), DAO_TicketComment::TICKET_ID => $id, DAO_TicketComment::COMMENT => $message->body)); return $id; break; default: // Typo? break; } } $attachment_files = array(); $attachment_files['name'] = array(); $attachment_files['type'] = array(); $attachment_files['tmp_name'] = array(); $attachment_files['size'] = array(); $i = 0; foreach ($message->files as $filename => $file) { $attachment_files['name'][$i] = $filename; $attachment_files['type'][$i] = $file->mime_type; $attachment_files['tmp_name'][$i] = $file->tmpname; $attachment_files['size'][$i] = $file->file_size; $i++; } CerberusMail::sendTicketMessage(array('message_id' => $msgid, 'content' => $message->body, 'files' => $attachment_files, 'agent_id' => $worker_address->worker_id)); return $id; } else { // ... worker is a requester, treat as normal $logger->info("[Parser] The external worker was a ticket requester, so we're not treating them as a watcher."); } } else { // Reply: Not sent by a worker /* * [TODO] check that this sender is a requester on the matched ticket * Otherwise blank out the $id */ } } @(list($team_id, $matchingToAddress) = CerberusParser::findDestination($headers)); // Pre-parse mail rules if (null != ($pre_filter = self::_checkPreParseRules(empty($id) ? 1 : 0, $fromAddress, $team_id, $message))) { // Do something with matching filter's actions foreach ($pre_filter->actions as $action_key => $action) { switch ($action_key) { case 'blackhole': return NULL; break; case 'redirect': @($to = $action['to']); CerberusMail::reflect($message, $to); return NULL; break; case 'bounce': @($msg = $action['message']); // [TODO] Follow the RFC spec on a true bounce CerberusMail::quickSend($fromAddress, "Delivery failed: " . $sSubject, $msg); return NULL; break; } } } if (empty($id)) { // New Ticket // Are we delivering or bouncing? if (empty($team_id)) { // Bounce return null; } if (empty($sMask)) { $sMask = CerberusApplication::generateTicketMask(); } $fields = array(DAO_Ticket::MASK => $sMask, DAO_Ticket::SUBJECT => $sSubject, DAO_Ticket::IS_CLOSED => $iClosed, DAO_Ticket::FIRST_WROTE_ID => intval($fromAddressId), DAO_Ticket::LAST_WROTE_ID => intval($fromAddressId), DAO_Ticket::CREATED_DATE => $iDate, DAO_Ticket::UPDATED_DATE => $iDate, DAO_Ticket::TEAM_ID => intval($team_id), DAO_Ticket::LAST_ACTION_CODE => CerberusTicketActionCode::TICKET_OPENED); $id = DAO_Ticket::createTicket($fields); } // [JAS]: Add requesters to the ticket if (!empty($fromAddressId) && !empty($id)) { // Don't add a requester if the sender is a helpdesk address if (isset($helpdesk_senders[$fromAddressInst->email])) { $logger->info("[Parser] Not adding ourselves as a requester: " . $fromAddressInst->email); } else { DAO_Ticket::createRequester($fromAddressId, $id); } } // Add the other TO/CC addresses to the ticket // [TODO] This should be cleaned up and optimized if ($settings->get(CerberusSettings::PARSER_AUTO_REQ, 0)) { @($autoreq_exclude_list = $settings->get(CerberusSettings::PARSER_AUTO_REQ_EXCLUDE, '')); $destinations = self::getDestinations($headers); if (is_array($destinations) && !empty($destinations)) { // Filter out any excluded requesters if (!empty($autoreq_exclude_list)) { @($autoreq_exclude = DevblocksPlatform::parseCrlfString($autoreq_exclude_list)); if (is_array($autoreq_exclude) && !empty($autoreq_exclude)) { foreach ($autoreq_exclude as $excl_pattern) { $excl_regexp = DevblocksPlatform::parseStringAsRegExp($excl_pattern); // Check all destinations for this pattern foreach ($destinations as $idx => $dest) { if (@preg_match($excl_regexp, $dest)) { unset($destinations[$idx]); } } } } } foreach ($destinations as $dest) { if (null != ($destInst = CerberusApplication::hashLookupAddress($dest, true))) { // Skip if the destination is one of our senders or the matching TO if (isset($helpdesk_senders[$destInst->email]) || 0 == strcasecmp($matchingToAddress, $destInst->email)) { continue; } DAO_Ticket::createRequester($destInst->id, $id); } } } } $attachment_path = APP_STORAGE_PATH . '/attachments/'; // [TODO] This should allow external attachments (S3) $fields = array(DAO_Message::TICKET_ID => $id, DAO_Message::CREATED_DATE => $iDate, DAO_Message::ADDRESS_ID => $fromAddressId); $email_id = DAO_Message::create($fields); // Content DAO_MessageContent::create($email_id, $message->body); // Headers foreach ($headers as $hk => $hv) { DAO_MessageHeader::create($email_id, $id, $hk, $hv); } // [mdf] Loop through files to insert attachment records in the db, and move temporary files if (!empty($email_id)) { foreach ($message->files as $filename => $file) { /* @var $file ParserFile */ //[mdf] skip rfc822 messages since we extracted their content above if ($file->mime_type == 'message/rfc822') { continue; } $fields = array(DAO_Attachment::MESSAGE_ID => $email_id, DAO_Attachment::DISPLAY_NAME => $filename, DAO_Attachment::MIME_TYPE => $file->mime_type, DAO_Attachment::FILE_SIZE => intval($file->file_size)); $file_id = DAO_Attachment::create($fields); if (empty($file_id)) { @unlink($file->tmpname); // remove our temp file continue; } // Make file attachments use buckets so we have a max per directory $attachment_bucket = sprintf("%03d/", mt_rand(1, 100)); $attachment_file = $file_id; if (!file_exists($attachment_path . $attachment_bucket)) { @mkdir($attachment_path . $attachment_bucket, 0770, true); // [TODO] Needs error checking } rename($file->getTempFile(), $attachment_path . $attachment_bucket . $attachment_file); // [TODO] Split off attachments into its own DAO DAO_Attachment::update($file_id, array(DAO_Attachment::FILEPATH => $attachment_bucket . $attachment_file)); } } // First Thread if ($bIsNew && !empty($email_id)) { // First thread DAO_Ticket::updateTicket($id, array(DAO_Ticket::FIRST_MESSAGE_ID => $email_id)); } // New ticket processing if ($bIsNew) { // Don't replace this with the master event listener if (false !== ($rules = CerberusApplication::runGroupRouting($team_id, $id))) { /* @var $rule Model_GroupInboxFilter */ // Check the last match which moved the ticket if (is_array($rules)) { foreach ($rules as $rule) { // If a rule changed our destination, replace the scope variable $team_id if (isset($rule->actions['move']) && isset($rule->actions['move']['group_id'])) { $team_id = intval($rule->actions['move']['group_id']); } } } } // Allow spam training overloading if (!empty($enumSpamTraining)) { if ($enumSpamTraining == CerberusTicketSpamTraining::SPAM) { CerberusBayes::markTicketAsSpam($id); DAO_Ticket::updateTicket($id, array(DAO_Ticket::IS_CLOSED => 1, DAO_Ticket::IS_DELETED => 1)); } elseif ($enumSpamTraining == CerberusTicketSpamTraining::NOT_SPAM) { CerberusBayes::markTicketAsNotSpam($id); } } else { // No overload $out = CerberusBayes::calculateTicketSpamProbability($id); // [TODO] Move this group logic to a post-parse event listener if (!empty($team_id)) { @($spam_threshold = DAO_GroupSettings::get($team_id, DAO_GroupSettings::SETTING_SPAM_THRESHOLD, 80)); @($spam_action = DAO_GroupSettings::get($team_id, DAO_GroupSettings::SETTING_SPAM_ACTION, '')); @($spam_action_param = DAO_GroupSettings::get($team_id, DAO_GroupSettings::SETTING_SPAM_ACTION_PARAM, '')); if ($out['probability'] * 100 >= $spam_threshold) { $enumSpamTraining = CerberusTicketSpamTraining::SPAM; switch ($spam_action) { default: case 0: // do nothing break; case 1: // delete // [TODO] Would have been much nicer to delete before this point DAO_Ticket::updateTicket($id, array(DAO_Ticket::IS_CLOSED => 1, DAO_Ticket::IS_DELETED => 1)); break; case 2: // move $buckets = DAO_Bucket::getAll(); // Verify bucket exists if (!empty($spam_action_param) && isset($buckets[$spam_action_param])) { DAO_Ticket::updateTicket($id, array(DAO_Ticket::TEAM_ID => $team_id, DAO_Ticket::CATEGORY_ID => $spam_action_param)); } break; } } } } // end spam training // Auto reply @($autoreply_enabled = DAO_GroupSettings::get($team_id, DAO_GroupSettings::SETTING_AUTO_REPLY_ENABLED, 0)); @($autoreply = DAO_GroupSettings::get($team_id, DAO_GroupSettings::SETTING_AUTO_REPLY, '')); /* * Send the group's autoreply if one exists, as long as this ticket isn't spam */ if (!isset($options['no_autoreply']) && $autoreply_enabled && !empty($autoreply) && $enumSpamTraining != CerberusTicketSpamTraining::SPAM) { CerberusMail::sendTicketMessage(array('ticket_id' => $id, 'message_id' => $email_id, 'content' => str_replace(array('#ticket_id#', '#mask#', '#subject#', '#timestamp#', '#sender#', '#sender_first#', '#orig_body#'), array($id, $sMask, $sSubject, date('r'), $fromAddress, $fromAddressInst->first_name, ltrim($message->body)), $autoreply), 'is_autoreply' => true, 'dont_keep_copy' => true)); } } // end bIsNew unset($message); // Re-open and update our date on new replies if (!$bIsNew) { DAO_Ticket::updateTicket($id, array(DAO_Ticket::UPDATED_DATE => time(), DAO_Ticket::IS_WAITING => 0, DAO_Ticket::IS_CLOSED => 0, DAO_Ticket::IS_DELETED => 0, DAO_Ticket::LAST_WROTE_ID => $fromAddressId, DAO_Ticket::LAST_ACTION_CODE => CerberusTicketActionCode::TICKET_CUSTOMER_REPLY)); // [TODO] The TICKET_CUSTOMER_REPLY should be sure of this message address not being a worker } // Inbound Reply Event $eventMgr = DevblocksPlatform::getEventService(); $eventMgr->trigger(new Model_DevblocksEvent('ticket.reply.inbound', array('ticket_id' => $id, 'message_id' => $email_id))); @imap_errors(); // Prevent errors from spilling out into STDOUT return $id; }
private function _handleTicketMoved($event) { @($ticket_ids = $event->params['ticket_ids']); @($changed_fields = $event->params['changed_fields']); if (!isset($changed_fields[DAO_Ticket::TEAM_ID]) || !isset($changed_fields[DAO_Ticket::CATEGORY_ID])) { return; } @($team_id = $changed_fields[DAO_Ticket::TEAM_ID]); @($bucket_id = $changed_fields[DAO_Ticket::CATEGORY_ID]); //============ Check Team Inbox Rules ================ if (!empty($ticket_ids) && !empty($team_id) && empty($bucket_id)) { // moving to an inbox // [JAS]: Build hashes for our event ([TODO] clean up) $tickets = DAO_Ticket::getTickets($ticket_ids); $from_ids = array(); foreach ($tickets as $ticket) { /* @var $ticket CerberusTicket */ $from_ids[$ticket->id] = $ticket->first_wrote_address_id; } $from_addresses = DAO_Address::getWhere(sprintf("%s IN (%s)", DAO_Address::ID, implode(',', $from_ids))); unset($from_ids); if (is_array($tickets)) { foreach ($tickets as $ticket_id => $ticket) { $matches = CerberusApplication::runGroupRouting($team_id, $ticket_id); } } unset($from_addresses); } }