public function SendGridMailSystem::mail in SendGrid Integration 7
Implement mail method to send mail via Sendgrid.
Parameters
array $message:
Return value
bool
Overrides MailSystemInterface::mail
File
- inc/
sendgrid.mail.inc, line 31 - Implements Drupal MailSystemInterface.
Class
- SendGridMailSystem
- @file Implements Drupal MailSystemInterface.
Code
public function mail(array $message) {
$key_secret = variable_get('sendgrid_integration_apikey', '');
if (empty($key_secret)) {
// Set a warning int he logs of there is no key.
watchdog('SendGrid Integration', t('No API Secret key has been set'), [], WATCHDOG_ERROR);
// Return false to indicate message was not able to send.
return FALSE;
}
$options = [
'turn_off_ssl_verification' => FALSE,
'protocol' => 'https',
'port' => NULL,
'url' => NULL,
'raise_exceptions' => FALSE,
];
// Create a new SendGrid object.
$sendgrid = new SendGrid\Client($key_secret, $options);
$sendgrid_message = new SendGrid\Email();
$sitename = variable_get('site_name');
// Defining default unique args.
$unique_args = [
'id' => $message['id'],
'module' => $message['module'],
];
// If this is a password reset. Bypass spam filters.
if (strpos($message['id'], 'password')) {
$sendgrid_message
->addFilter('bypass_list_management', 'enable', 1);
}
// If this is a Drupal Commerce message. Bypass spam filters.
if (strpos($message['id'], 'commerce')) {
$sendgrid_message
->addFilter('bypass_list_management', 'enable', 1);
}
// Bypass spam filters if requested by othe modules.
if (!empty($message['sendgrid']['bypass_list_management'])) {
$sendgrid_message
->addFilter('bypass_list_management', 'enable', 1);
}
if (isset($message['params']['account']->uid)) {
$unique_args['uid'] = $message['params']['account']->uid;
}
// Allow other modules to modify unique arguments.
$args = module_invoke_all('sendgrid_integration_unique_args_alter', $unique_args, $message);
// Check if we got any variable back.
if (!empty($args)) {
$unique_args = $args;
}
// Checking if 'from' email-address already exist.
if (isset($message['headers']['from']) || isset($message['headers']['From']) && ($message['headers']['from'] = $message['headers']['From'])) {
$fromaddrarray = sendgrid_integration_parse_address($message['headers']['from']);
$data['from'] = $fromaddrarray[0];
$data['fromname'] = $fromaddrarray[1];
}
else {
$data['from'] = variable_get('site_mail');
$data['fromname'] = $sitename;
}
// Build the Sendgrid mail object.
// The message MODULE and ID is used for the Category. Category is the only
// thing in the Sendgrid UI you can use to sort mail.
// This is an array of categories for Sendgrid statistics.
$categories = [
$sitename,
$message['module'],
$message['id'],
];
// Allow other modules to modify categories.
$result = module_invoke_all('sendgrid_integration_categories_alter', $message, $categories);
// Check if we got any variable back.
if (!empty($result)) {
$categories = $result;
}
$sendgrid_message
->setFrom($data['from'])
->setSubject($message['subject'])
->setCategories($categories)
->setUniqueArgs($unique_args);
if (!empty($data['fromname'])) {
$sendgrid_message
->setFromName($data['fromname']);
}
// If there are multiple recipients we use a different method for To:
if (strpos($message['to'], ',')) {
$sendtosarry = explode(',', $message['to']);
// Don't bother putting anything in "to" and "toName" for
// multiple addresses. Only put multiple addresses in the Smtp header.
$sendgrid_message
->setSmtpapiTos($sendtosarry);
}
else {
$toaddrarray = sendgrid_integration_parse_address($message['to']);
$sendgrid_message
->addTo($toaddrarray[0]);
if (!empty($toaddrarray[1])) {
$sendgrid_message
->addToName($toaddrarray[1]);
}
}
//Add cc and bcc in mail if they exist.
$cc_bcc_keys = [
'cc',
'bcc',
];
$address_cc_bcc = [];
// use Sendgrid Templates if they have been set
if (!empty($message['sendgrid']['template_id'])) {
$sendgrid_message
->setTemplateId($message['sendgrid']['template_id']);
}
// use Sendgrid Substitutions if they have been set
if (!empty($message['sendgrid']['substitutions'])) {
$sendgrid_message
->setSubstitutions($message['sendgrid']['substitutions']);
}
// Beginning of consolidated header parsing.
foreach ($message['headers'] as $key => $value) {
switch (drupal_strtolower($key)) {
case 'content-type':
// Parse several values on the Content-type header, storing them in an array like
// key=value -> $vars['key']='value'
$vars = explode(';', $value);
foreach ($vars as $i => $var) {
if ($cut = strpos($var, '=')) {
$new_var = trim(drupal_strtolower(drupal_substr($var, $cut + 1)));
$new_key = trim(drupal_substr($var, 0, $cut));
unset($vars[$i]);
$vars[$new_key] = $new_var;
}
}
// If $vars is empty then set an empty value at index 0 to avoid a PHP warning in the next statement
$vars[0] = isset($vars[0]) ? $vars[0] : '';
// Nested switch to process the various content types. We only care
// about the first entry in the array.
switch ($vars[0]) {
case 'text/plain':
// The message includes only a plain text part.
$sendgrid_message
->setText(drupal_wrap_mail(drupal_html_to_text($message['body'])));
break;
case 'text/html':
// The message includes only an HTML part.
$sendgrid_message
->setHtml($message['body']);
// Also include a text only version of the email.
$sendgrid_message
->setText(drupal_wrap_mail(drupal_html_to_text($message['body'])));
break;
case 'multipart/related':
// @todo determine how to handle this content type.
// Get the boundary ID from the Content-Type header.
$boundary = $this
->_get_substring($message['body'], 'boundary', '"', '"');
break;
case 'multipart/alternative':
// Get the boundary ID from the Content-Type header.
$boundary = $this
->_get_substring($message['body'], 'boundary', '"', '"');
// Parse text and HTML portions
// Split the body based on the boundary ID.
$body_parts = $this
->_boundary_split($message['body'], $boundary);
foreach ($body_parts as $body_part) {
// If plain/text within the body part, add it to $mailer->AltBody.
if (strpos($body_part, 'text/plain')) {
// Clean up the text.
$body_part = trim($this
->_remove_headers(trim($body_part)));
// Include it as part of the mail object.
$sendgrid_message
->setText(drupal_wrap_mail(drupal_html_to_text($body_part)));
}
elseif (strpos($body_part, 'text/html')) {
// Clean up the text.
$body_part = trim($this
->_remove_headers(trim($body_part)));
// Include it as part of the mail object.
$sendgrid_message
->setHtml($body_part);
}
}
break;
case 'multipart/mixed':
// Get the boundary ID from the Content-Type header.
$boundary = $this
->_get_substring($value, 'boundary', '"', '"');
// Split the body based on the boundary ID.
$body_parts = $this
->_boundary_split($message['body'], $boundary);
// Parse text and HTML portions
foreach ($body_parts as $body_part) {
if (strpos($body_part, 'multipart/alternative')) {
// Get the second boundary ID from the Content-Type header.
$boundary2 = $this
->_get_substring($body_part, 'boundary', '"', '"');
// Clean up the text.
$body_part = trim($this
->_remove_headers(trim($body_part)));
// Split the body based on the internal boundary ID.
$body_parts2 = $this
->_boundary_split($body_part, $boundary2);
// Process the internal parts.
foreach ($body_parts2 as $body_part2) {
// If plain/text within the body part, add it to $mailer->AltBody.
if (strpos($body_part2, 'text/plain')) {
// Clean up the text.
$body_part2 = trim($this
->_remove_headers(trim($body_part2)));
$sendgrid_message
->setText(drupal_wrap_mail(drupal_html_to_text($body_part2)));
}
elseif (strpos($body_part2, 'text/html')) {
// Get the encoding.
$body_part2_encoding = trim($this
->_get_substring($body_part2, 'Content-Transfer-Encoding', ':', "\n"));
// Clean up the text.
$body_part2 = trim($this
->_remove_headers(trim($body_part2)));
// Check whether the encoding is base64, and if so, decode it.
if (drupal_strtolower($body_part2_encoding) == 'base64') {
// Save the decoded HTML content.
$sendgrid_message
->setHtml(base64_decode($body_part2));
}
else {
// Save the HTML content.
$sendgrid_message
->setHtml($body_part2);
}
}
}
}
else {
// This parses the message if there is no internal content
// type set after the multipart/mixed.
// If text/plain within the body part, add it to $mailer->Body.
if (strpos($body_part, 'text/plain')) {
// Clean up the text.
$body_part = trim($this
->_remove_headers(trim($body_part)));
// Set the text message.
$sendgrid_message
->setText(drupal_wrap_mail(drupal_html_to_text($body_part)));
}
elseif (strpos($body_part, 'text/html')) {
// Clean up the text.
$body_part = trim($this
->_remove_headers(trim($body_part)));
// Set the HTML message.
$sendgrid_message
->setHtml($body_part);
}
}
}
break;
default:
// Everything else is unknown so we log and send the message as text.
drupal_set_message(t('The %header of your message is not supported by SendGrid and will be sent as text/plain instead.', [
'%header' => "Content-Type: {$value}",
]), 'error');
watchdog('sendgrid_integration', 'The %header of your message is not supported by PHPMailer and will be sent as text/plain instead.', [
'%header' => "Content-Type: {$value}",
], WATCHDOG_ERROR);
// Force the email to be text.
$sendgrid_message
->setText(drupal_wrap_mail(drupal_html_to_text($message['body'])));
}
break;
case 'reply-to':
$sendgrid_message
->setReplyTo($message['headers'][$key]);
break;
}
// Handle latter case issue for cc and bcc key
if (in_array(drupal_strtolower($key), $cc_bcc_keys)) {
$mail_ids = explode(',', $value);
foreach ($mail_ids as $mail_id) {
list($mail_cc_address, $cc_name) = sendgrid_integration_parse_address($mail_id);
$address_cc_bcc[drupal_strtolower($key)][] = [
'mail' => $mail_cc_address,
'name' => $cc_name,
];
}
}
}
if (array_key_exists('cc', $address_cc_bcc)) {
foreach ($address_cc_bcc['cc'] as $item) {
$sendgrid_message
->addCc($item['mail']);
$sendgrid_message
->addCcName($item['name']);
}
}
if (array_key_exists('bcc', $address_cc_bcc)) {
foreach ($address_cc_bcc['bcc'] as $item) {
$sendgrid_message
->addBcc($item['mail']);
$sendgrid_message
->addBccName($item['name']);
}
}
// Prepare attachments.
$attachments = [];
if (isset($message['attachments']) && !empty($message['attachments'])) {
foreach ($message['attachments'] as $attachmentitem) {
if (is_file($attachmentitem)) {
$attachments[$attachmentitem] = $attachmentitem;
}
}
}
// If we have attachments, add them.
if (!empty($attachments)) {
$sendgrid_message
->setAttachments($attachments);
}
// Integration with the Maillog module to use for debugging.
if (module_exists('maillog')) {
if (variable_get('sendgrid_integration_maillog_log', TRUE)) {
$record = new stdClass();
$record->header_message_id = isset($message->MessageID) ? $message->MessageID : NULL;
$record->subject = $sendgrid_message
->getSubject();
$record->header_from = $sendgrid_message
->getFrom();
// This returns an array of emails so we have to make a string.
// @TODO Restore this once the V3 wrapper is availble.
// $record->header_to = implode(',', $sendgrid_message->getTos());
$record->header_reply_to = $sendgrid_message
->getReplyTo();
$record->header_all = serialize($message['headers']);
$record->sent_date = REQUEST_TIME;
// Used to separate different portions of the body string.
$divider = str_repeat('-', 120) . "\n";
// Initialize object
$record->body = '';
// Load the attachments.
$attachments = $sendgrid_message
->getAttachments();
$toaddresses = $sendgrid_message
->getTos();
$toaddressesnames = $sendgrid_message
->getToNames();
$record->body .= 'Addressing Information' . "\n";
$i = 0;
foreach ($toaddresses as $item) {
$record->body .= 'To Address ' . $i . ' : ' . $item . "\n";
$i++;
}
$i = 0;
foreach ($toaddressesnames as $item) {
$record->body .= 'To Address Names ' . $i . ' : ' . $item . "\n";
$i++;
}
$record->body .= $divider;
$record->body .= $divider;
$fromaddresses = $sendgrid_message
->getFrom(TRUE);
if (is_array($fromaddresses)) {
$i = 0;
foreach ($fromaddresses as $key => $value) {
$record->body .= 'From Address ' . $i . ' : ' . $key . "\n";
$record->body .= 'From Names ' . $i . ' : ' . $value . "\n";
$record->body .= $divider;
$record->body .= $divider;
}
}
else {
$record->body .= 'From Address: ' . $fromaddresses . "\n";
$record->body .= $divider;
$record->body .= $divider;
}
if (!empty($filters = $sendgrid_message->smtpapi
->getFilters())) {
$record->body .= 'Filters:' . "\n";
foreach ($filters as $item) {
$record->body .= print_r($item) . "\n";
}
$record->body .= $divider;
$record->body .= $divider;
}
else {
$record->body .= 'No Filters Declared.' . "\n";
$record->body .= $divider;
$record->body .= $divider;
}
$record->body .= 'Raw Message' . "\n";
$record->body .= $divider;
$record->body .= $message['body'];
$record->body .= $divider;
$record->body .= $divider;
// Check Content-Type of message body and get the appropriate content.
if (!empty($sendgrid_message
->getHtml())) {
$record->body .= 'Body HTML' . "\n";
$record->body .= $divider;
$record->body .= $sendgrid_message
->getHtml() . "\n";
$record->body .= $divider;
$record->body .= $divider;
}
if (!empty($sendgrid_message
->getText())) {
// Message body is text/plain.
$record->body .= 'Body Plain Text' . "\n";
$record->body .= $divider;
$record->body .= $sendgrid_message
->getText() . "\n";
$record->body .= $divider;
$record->body .= $divider;
}
// List the attachments.
if (!empty($attachments)) {
$record->body .= t('Attachments') . ":\n";
$record->body .= $divider;
foreach ($attachments as $file) {
$record->body .= t('Filename') . ':' . $file['filename'] . "\n";
$record->body .= t('Directory Name') . ':' . $file['dirname'] . "\n";
$record->body .= t('Extension') . ':' . $file['extension'] . "\n";
$record->body .= t('Basename') . ':' . $file['basename'] . "\n";
$record->body .= "\n";
}
$record->body .= $divider;
$record->body .= $divider;
}
drupal_write_record('maillog', $record);
}
// Display the e-mail using Devel module.
if (variable_get('sendgrid_integration_maillog_devel', TRUE) && function_exists('dpm')) {
$bodydevel = '';
// Check Content-Type of message body and get the appropriate content.
if (!empty($sendgrid_message
->getHtml())) {
$bodydevel .= 'Body HTML' . "\n" . $divider . "\n" . $sendgrid_message
->getHtml() . $divider;
}
elseif (!empty($sendgrid_message
->getText())) {
$bodydevel .= 'Body Plain Text' . "\n" . $divider . "\n" . $sendgrid_message
->getText() . $divider;
}
$devel_msg = [];
$devel_msg[t('Subject')] = $sendgrid_message
->getSubject();
$devel_msg[t('From')] = $sendgrid_message
->getFrom();
$devel_msg[t('To')] = implode(',', $sendgrid_message
->getTos());
$devel_msg[t('Reply-To')] = !empty($sendgrid_message
->getReplyTo()) ? $sendgrid_message
->getReplyTo() : NULL;
$devel_msg[t('Headers')] = $sendgrid_message
->getHeaders();
$devel_msg[t('Body')] = $bodydevel;
$devel_msg[t('Attachments')] = $sendgrid_message
->getAttachments();
$devel_msg[t('Message ID')] = $message['id'];
dpm($devel_msg, 'maillog');
}
}
// Lets try and send the message and catch the error.
try {
$response = $sendgrid
->send($sendgrid_message);
} catch (\SendGrid\Exception $e) {
$error_code = filter_xss($e
->getCode());
watchdog('SendGrid Integration', 'Sending emails to Sengrind service failed with error code @error_code', [
'@error_code' => $error_code,
], WATCHDOG_ERROR, $link = NULL);
foreach ($e
->getErrors() as $er) {
$error_info = filter_xss($er);
watchdog('SendGrid Integration', 'Sendgrid generated error @error_info', [
'@error_info' => $error_info,
], NULL, WATCHDOG_ERROR, $link = NULL);
}
// Add message to queue if reason for failing was timeout or
// another valid reason. This adds more error tolerance.
$codes = [
-110,
404,
408,
500,
502,
503,
504,
];
if (in_array($error_code, $codes)) {
$queue = DrupalQueue::get('SendGridResendQueue')
->createItem($message);
}
return FALSE;
}
// Sanitize and store the response code for easy processing.
$response_code = filter_xss($response
->getCode());
// Creating hook, allowing other modules react on sent email.
module_invoke_all('sendgrid_integration_sent', $message['to'], $response_code, $unique_args, $response);
if ($response_code == 200) {
// If the code is 200 we are good to finish and proceed.
return TRUE;
}
// Default to low. Sending failed.
return FALSE;
}