$tpl->assign("issue", $details); if (!Issue::canAccess($issue_id, $usr_id)) { $tpl->setTemplate("permission_denied.tpl.html"); $tpl->displayTemplate(); exit; } if (@$HTTP_POST_VARS["cat"] == "post_note") { // change status if (!@empty($HTTP_POST_VARS['new_status'])) { $res = Issue::setStatus($issue_id, $HTTP_POST_VARS['new_status']); if ($res != -1) { $new_status = Status::getStatusTitle($HTTP_POST_VARS['new_status']); History::add($issue_id, $usr_id, History::getTypeID('status_changed'), "Status changed to '{$new_status}' by " . User::getFullName($usr_id)); } } $res = Note::insert($usr_id, $issue_id); $tpl->assign("post_result", $res); // enter the time tracking entry about this phone support entry if (!empty($HTTP_POST_VARS['time_spent'])) { $HTTP_POST_VARS['issue_id'] = $issue_id; $HTTP_POST_VARS['category'] = $HTTP_POST_VARS['time_category']; $HTTP_POST_VARS['summary'] = 'Time entry inserted when sending an internal note.'; Time_Tracking::insertEntry(); } } elseif (@$HTTP_GET_VARS["cat"] == "reply") { if (!@empty($HTTP_GET_VARS["id"])) { $note = Note::getDetails($HTTP_GET_VARS["id"]); $date = Misc::formatReplyDate($note["timestamp"]); $header = "\n\n\nOn {$date}, " . $note["not_from"] . " wrote:\n>\n"; $note["not_body"] = $header . Misc::formatReply($note["not_note"]); $tpl->bulkAssign(array("note" => $note, "parent_note_id" => $HTTP_GET_VARS["id"]));
} elseif ($HTTP_POST_VARS['target'] == 'reference') { $res = Support::associateEmail(Auth::getUserID(), $HTTP_POST_VARS['issue'], $HTTP_POST_VARS['item']); if ($res == 1) { Workflow::handleManualEmailAssociation(Issue::getProjectID($HTTP_POST_VARS['issue']), $HTTP_POST_VARS['issue']); } $tpl->assign("associate_result", $res); } else { for ($i = 0; $i < count($HTTP_POST_VARS['item']); $i++) { $email = Support::getEmailDetails(Email_Account::getAccountByEmail($HTTP_POST_VARS['item'][$i]), $HTTP_POST_VARS['item'][$i]); // add the message body as a note $HTTP_POST_VARS['blocked_msg'] = $email['seb_full_email']; $HTTP_POST_VARS['title'] = $email['sup_subject']; $HTTP_POST_VARS['note'] = $email['seb_body']; // XXX: probably broken to use the current logged in user as the 'owner' of // XXX: this new note, but that's how it was already $res = Note::insert(Auth::getUserID(), $HTTP_POST_VARS['issue']); // remove the associated email if ($res) { list($HTTP_POST_VARS["from"]) = Support::getSender(array($HTTP_POST_VARS['item'][$i])); Workflow::handleBlockedEmail(Issue::getProjectID($HTTP_POST_VARS['issue']), $HTTP_POST_VARS['issue'], $HTTP_POST_VARS, 'associated'); Support::removeEmail($HTTP_POST_VARS['item'][$i]); } } $tpl->assign("associate_result", $res); } @$tpl->assign('total_emails', count($HTTP_POST_VARS['item'])); } else { @$tpl->assign('emails', $HTTP_GET_VARS['item']); @$tpl->assign('total_emails', count($HTTP_GET_VARS['item'])); $prj_id = Issue::getProjectID($HTTP_GET_VARS['issue']); if (Customer::hasCustomerIntegration($prj_id)) {
/** * Method used to close off an issue. * * @access public * @param integer $usr_id The user ID * @param integer $issue_id The issue ID * @param bool $send_notification Whether to send a notification about this action or not * @param integer $resolution_id The resolution ID * @param integer $status_id The status ID * @param string $reason The reason for closing this issue * @param string $send_notification_to Who this notification should be sent too * @return integer 1 if the update worked, -1 otherwise */ function close($usr_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason, $send_notification_to = 'internal') { global $HTTP_POST_VARS; $usr_id = Misc::escapeInteger($usr_id); $issue_id = Misc::escapeInteger($issue_id); $resolution_id = Misc::escapeInteger($resolution_id); $status_id = Misc::escapeInteger($status_id); $stmt = "UPDATE\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue\n SET\n iss_updated_date='" . Date_API::getCurrentDateGMT() . "',\n iss_last_public_action_date='" . Date_API::getCurrentDateGMT() . "',\n iss_last_public_action_type='closed',\n iss_closed_date='" . Date_API::getCurrentDateGMT() . "',\n"; if (!empty($resolution_id)) { $stmt .= "iss_res_id={$resolution_id},\n"; } $stmt .= "iss_sta_id={$status_id}\n WHERE\n iss_id={$issue_id}"; $res = $GLOBALS["db_api"]->dbh->query($stmt); if (PEAR::isError($res)) { Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); return -1; } else { $prj_id = Issue::getProjectID($issue_id); // record the change History::add($issue_id, $usr_id, History::getTypeID('issue_closed'), "Issue updated to status '" . Status::getStatusTitle($status_id) . "' by " . User::getFullName($usr_id)); if ($send_notification_to == 'all') { $from = User::getFromHeader($usr_id); $message_id = User::getFromHeader($usr_id); $full_email = Support::buildFullHeaders($issue_id, $message_id, $from, '', '', 'Issue closed comments', $reason, ''); $structure = Mime_Helper::decode($full_email, true, false); $email = array('ema_id' => Email_Account::getEmailAccount(), 'issue_id' => $issue_id, 'message_id' => $message_id, 'date' => Date_API::getCurrentDateGMT(), 'subject' => 'Issue closed comments', 'from' => $from, 'has_attachment' => 0, 'body' => $reason, 'full_email' => $full_email, 'headers' => $structure->headers); Support::insertEmail($email, $structure, $sup_id, true); $ids = $sup_id; } else { // add note with the reason to close the issue $HTTP_POST_VARS['title'] = 'Issue closed comments'; $HTTP_POST_VARS["note"] = $reason; Note::insert($usr_id, $issue_id, false, true, true); $ids = false; } if ($send_notification) { if (Customer::hasCustomerIntegration($prj_id)) { // send a special confirmation email when customer issues are closed $stmt = "SELECT\n iss_customer_contact_id\n FROM\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue\n WHERE\n iss_id={$issue_id}"; $customer_contact_id = $GLOBALS["db_api"]->dbh->getOne($stmt); if (!empty($customer_contact_id)) { Customer::notifyIssueClosed($prj_id, $issue_id, $customer_contact_id); } } // send notifications for the issue being closed Notification::notify($issue_id, 'closed', $ids); } Workflow::handleIssueClosed($prj_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason); return 1; } }
/** * Check if this email needs to be blocked and if so, block it. * * */ function blockEmailIfNeeded($email) { global $HTTP_POST_VARS; if (empty($email['issue_id'])) { return false; } $issue_id = $email['issue_id']; $prj_id = Issue::getProjectID($issue_id); $sender_email = strtolower(Mail_API::getEmailAddress($email['headers']['from'])); if (Mail_API::isVacationAutoResponder($email['headers']) || Notification::isBounceMessage($sender_email) || !Support::isAllowedToEmail($issue_id, $sender_email)) { // add the message body as a note $HTTP_POST_VARS = array('blocked_msg' => $email['full_email'], 'title' => @$email['headers']['subject'], 'note' => Mail_API::getCannedBlockedMsgExplanation($issue_id) . $email['body']); // avoid having this type of message re-open the issue if (Mail_API::isVacationAutoResponder($email['headers'])) { $closing = true; } else { $closing = false; } $res = Note::insert(Auth::getUserID(), $issue_id, $email['headers']['from'], false, $closing); // associate the email attachments as internal-only files on this issue if ($res != -1) { Support::extractAttachments($issue_id, $email['full_email'], true, $res); } $HTTP_POST_VARS['issue_id'] = $issue_id; $HTTP_POST_VARS['from'] = $sender_email; // avoid having this type of message re-open the issue if (Mail_API::isVacationAutoResponder($email['headers'])) { $email_type = 'vacation-autoresponder'; } else { $email_type = 'routed'; } Workflow::handleBlockedEmail($prj_id, $issue_id, $HTTP_POST_VARS, $email_type); // try to get usr_id of sender, if not, use system account $usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($email['from'])); if (!$usr_id) { $usr_id = APP_SYSTEM_USER_ID; } // log blocked email History::add($issue_id, $usr_id, History::getTypeID('email_blocked'), "Email from '" . $email['from'] . "' blocked."); return true; } return false; }
/** * Routes a note to the correct issue * * @param string $full_message The full note */ function route_notes($full_message) { global $HTTP_POST_VARS; // save the full message for logging purposes Note::saveRoutedNote($full_message); if (preg_match("/^(boundary=).*/m", $full_message)) { $pattern = "/(Content-Type: multipart\\/)(.+); ?\r?\n(boundary=)(.*)\$/im"; $replacement = '$1$2; $3$4'; $full_message = preg_replace($pattern, $replacement, $full_message); } list($headers, ) = Mime_Helper::splitHeaderBody($full_message); // need some validation here if (empty($full_message)) { return array(66, "Error: The email message was empty.\n"); } // // DON'T EDIT ANYTHING BELOW THIS LINE // // remove the reply-to: header if (preg_match("/^(reply-to:).*/im", $full_message)) { $full_message = preg_replace("/^(reply-to:).*\n/im", '', $full_message, 1); } // check if the email routing interface is even supposed to be enabled $setup = Setup::load(); if (@$setup['note_routing']['status'] != 'enabled') { return array(78, "Error: The internal note routing interface is disabled.\n"); } $prefix = $setup['note_routing']['address_prefix']; // escape plus signs so '*****@*****.**' becomes a valid routing address $prefix = str_replace('+', '\\+', $prefix); $mail_domain = quotemeta($setup['note_routing']['address_host']); if (empty($prefix)) { return array(78, "Error: Please configure the email address prefix.\n"); } if (empty($mail_domain)) { return array(78, "Error: Please configure the email address domain.\n"); } $structure = Mime_Helper::decode($full_message, true, true); // find which issue ID this email refers to @preg_match("/{$prefix}(\\d*)@{$mail_domain}/i", $structure->headers['to'], $matches); @($issue_id = $matches[1]); // validation is always a good idea if (empty($issue_id)) { // we need to try the Cc header as well @preg_match("/{$prefix}(\\d*)@{$mail_domain}/i", $structure->headers['cc'], $matches); if (!empty($matches[1])) { $issue_id = $matches[1]; } else { return array(65, "Error: The routed note had no associated Eventum issue ID or had an invalid recipient address.\n"); } } $prj_id = Issue::getProjectID($issue_id); // check if the sender is allowed in this issue' project and if it is an internal user $users = Project::getUserEmailAssocList($prj_id, 'active', User::getRoleID('Customer')); $sender_email = strtolower(Mail_API::getEmailAddress($structure->headers['from'])); $user_emails = array_map('strtolower', array_values($users)); if (!in_array($sender_email, $user_emails)) { return array(77, "Error: The sender of this email is not allowed in the project associated with issue #{$issue_id}.\n"); } Auth::createFakeCookie(User::getUserIDByEmail($sender_email), $prj_id); // parse the Cc: list, if any, and add these internal users to the issue notification list $users = array_flip($users); $addresses = array(); $to_addresses = Mail_API::getEmailAddresses(@$structure->headers['to']); if (count($to_addresses)) { $addresses = $to_addresses; } $cc_addresses = Mail_API::getEmailAddresses(@$structure->headers['cc']); if (count($cc_addresses)) { $addresses = array_merge($addresses, $cc_addresses); } $cc_users = array(); foreach ($addresses as $email) { if (in_array(strtolower($email), $user_emails)) { $cc_users[] = $users[$email]; } } $body = Mime_Helper::getMessageBody($structure); $reference_msg_id = Mail_API::getReferenceMessageID($headers); if (!empty($reference_msg_id)) { $parent_id = Note::getIDByMessageID($reference_msg_id); } else { $parent_id = false; } // insert the new note and send notification about it $HTTP_POST_VARS = array('title' => @$structure->headers['subject'], 'note' => $body, 'note_cc' => $cc_users, 'add_extra_recipients' => 'yes', 'message_id' => @$structure->headers['message-id'], 'parent_id' => $parent_id); // add the full email to the note if there are any attachments // this is needed because the front end code will display attachment links if (Mime_Helper::hasAttachments($full_message)) { $HTTP_POST_VARS['blocked_msg'] = $full_message; } $res = Note::insert(Auth::getUserID(), $issue_id, false, false); // need to handle attachments coming from notes as well if ($res != -1) { Support::extractAttachments($issue_id, $full_message, true, $res); } History::add($issue_id, Auth::getUserID(), History::getTypeID('note_routed'), "Note routed from " . $structure->headers['from']); return true; }