'Mailgun', 'description' => 'Configure Mailgun settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('mailgun_admin_settings'), 'access arguments' => array('administer mailgun'), 'file' => 'mailgun.admin.inc', ); $items[MAILGUN_ADMIN_PAGE . '/settings'] = array( 'title' => 'Settings', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, ); $items[MAILGUN_ADMIN_PAGE . '/test'] = array( 'title' => 'Send test email', 'page callback' => 'drupal_get_form', 'page arguments' => array('mailgun_test_form'), 'access arguments' => array('administer mailgun'), 'description' => 'Send a test e-mail using the Mailgun API.', 'file' => 'mailgun.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 1, ); return $items; } /** * Implements hook_permission(). */ function mailgun_permission() { return array( 'administer mailgun' => array( 'title' => t('Administer Mailgun'), 'description' => t('Perform administration tasks for the Mailgun e-mail sending service.'), 'restrict access' => TRUE, ), ); } /** * Implements hook_theme(). */ function mailgun_theme($existing, $type, $theme, $path) { return array( 'mailgun_message' => array( 'variables' => array( 'subject' => NULL, 'body' => NULL, 'message' => array(), ), 'template' => 'mailgun-message', 'path' => drupal_get_path('module', 'mailgun') . '/templates', 'mail theme' => TRUE, ), ); } /** * Implements hook_help(). */ function mailgun_help($path, $arg) { switch ($path) { case MAILGUN_ADMIN_PAGE: return '

' . t('See !link for instructions on installing and configuring Mailgun.', array( '!link' => l(t('documentation'), MAILGUN_DOCUMENTATION_LINK), )) . '

'; case MAILGUN_ADMIN_PAGE . '/test': return '

' . t('Use this form to send a test e-mail to ensure you have correctly configured Mailgun.') . '

'; } } /** * Implements hook_cron_queue_info(). */ function mailgun_cron_queue_info() { $queues = array(); $queues['mailgun_queue'] = array( 'worker callback' => 'mailgun_send', 'time' => 60, ); return $queues; } /** * Implements hook_mail(). */ function mailgun_mail($key, &$message, $params) { switch ($key) { case 'test': $message['subject'] = t('Mailgun test email'); $message['body'] = $params['message']; if ($params['attachment']) { $message['params']['attachments'] = array(drupal_realpath('misc/druplicon.png')); } break; } } /** * Implements hook_libraries_info(). */ function mailgun_libraries_info() { $libraries['mailgun'] = array( 'name' => 'Mailgun PHP library', 'vendor url' => 'https://documentation.mailgun.com/en/latest/libraries.html#php', 'download url' => 'https://github.com/mailgun/mailgun-php', 'version arguments' => array( 'file' => 'vendor/mailgun/mailgun-php/CHANGELOG.md', 'pattern' => '/##\W+((\d+)\.(\d+))/', ), // Path to the 'autoload.php' created by Composer. 'path' => 'vendor', 'files' => array( 'php' => array('autoload.php'), ), ); return $libraries; } /** * Implements hook_form_FORM_ID_alter(). */ function mailgun_form_libraries_admin_library_status_form_alter(&$form, &$form_state, $form_id) { $library = drupal_array_get_nested_value($form_state, array( 'build_info', 'args', 0, )); if (empty($library['machine name']) || $library['machine name'] !== 'mailgun') { return; } // Libraries module provides own instruction "How to install the library". // We override it because this instruction is not correct and may confuse. $form['instructions'] = array( '#markup' => t('The Mailgun PHP library is not installed. Please see Installation section in the !link.', array( '!link' => l(t('documentation'), MAILGUN_DOCUMENTATION_LINK), )), ); } /** * Get the Mailgun client to access Mailgun's endpoints. * * @param string $key * The Mailgun API key. Leave empty to use the API key saved in database. * * @return \Mailgun\Mailgun * Mailgun object. */ function mailgun_get_client($key = '') { // Check if the Mailgun PHP library is installed. if (!mailgun_check_library()) { watchdog('mailgun', 'Mailgun client initialization failed: Unable to load the Mailgun PHP library.', array(), WATCHDOG_ERROR); return FALSE; } $key = (empty($key)) ? variable_get('mailgun_api_key', '') : $key; if (empty($key)) { watchdog('mailgun', 'Mailgun client initialization failed: Missing API key.', array(), WATCHDOG_ERROR); return FALSE; } return Mailgun::create($key); } /** * Detect if Mailgun library is installed. * * @return bool * TRUE if library is installed, FALSE otherwise. */ function mailgun_check_library() { if (module_exists('libraries')) { libraries_load('mailgun'); } if (method_exists('\Mailgun\Mailgun', 'create')) { return TRUE; } return FALSE; } /** * Prepares variables for mailgun-message.tpl.php. * * Adds id/module/key-specific hook suggestions. * * @see templates/mailgun-message.tpl.php */ function template_preprocess_mailgun_message(&$variables) { $variables['theme_hook_suggestions'][] = 'mailgun_message__' . $variables['message']['id']; $variables['theme_hook_suggestions'][] = 'mailgun_message__' . $variables['message']['module']; $variables['theme_hook_suggestions'][] = 'mailgun_message__' . $variables['message']['key']; } /** * Send an e-mail using the Mailgun API. * * @param array $mailgun_message * A Mailgun message array. Contains the following keys: * - from: The e-mail addressthe message will be sent from. * - to: The e-mail addressthe message will be sent to. * - subject: The subject of the message. * - text: The plain-text version of the message. Processed using * drupal_html_to_text(). * - html: The original message content. May contain HTML tags. * - cc: One or more carbon copy recipients. If multiple, separate with * commas. * - bcc: One or more blind carbon copy recipients. If multiple, separate * with commas. * - o:tag: An array containing the tags to add to the message. * See: https://documentation.mailgun.com/user_manual.html#tagging. * - o:campaign: The campaign ID this message belongs to. * https://documentation.mailgun.com/user_manual.html#um-campaign-analytics * - o:deliverytime: Desired time of delivery. Messages can be scheduled for * a maximum of 3 days in the future. * See: https://documentation.mailgun.com/api-intro.html#date-format. * - o:dkim: Boolean indicating whether or not to enable DKIM signatures on * per-message basis. * - o:testmode: Boolean indicating whether or not to enable test mode. * See: https://documentation.mailgun.com/user_manual.html#manual-testmode. * - o:tracking: Boolean indicating whether or not to toggle tracking on a * per-message basis. * See: https://documentation.mailgun.com/user_manual.html#tracking-messages. * - o:tracking-clicks: Boolean or string "htmlonly" indicating whether or * not to toggle clicks tracking on a per-message basis. Has higher * priority than domain-level setting. * - o:tracking-opens: Boolean indicating whether or not to toggle clicks * tracking on a per-message basis. Has higher priority than domain-level * setting. * - h:X-My-Header: h: prefix followed by an arbitrary value allows to append * a custom MIME header to the message (X-My-Header in this case). * For example, h:Reply-To to specify Reply-To address. * - v:my-var: v: prefix followed by an arbitrary name allows to attach a * custom JSON data to the message. * See: https://documentation.mailgun.com/user_manual.html#manual-customdata. * * @return bool * TRUE if the mail was successfully accepted, FALSE otherwise. */ function mailgun_send(array $mailgun_message) { $client = mailgun_get_client(); if (!$client) { return FALSE; } // Test mode. Mailgun will accept the message but will not send it. if (variable_get('mailgun_test', FALSE)) { $mailgun_message['o:testmode'] = 'yes'; } // Merge the $mailgun_message array with options. $mailgun_message += $mailgun_message['params']; unset($mailgun_message['params']); if (variable_get('mailgun_domain', '_sender') === '_sender') { // Extract the domain from the sender's email address. // Use regular expression to check since it could be either a plain email // address or in the form "Name ". $tokens = (preg_match('/^\s*(.+?)\s*<\s*([^>]+)\s*>$/', $mailgun_message['from'], $matches) === 1) ? explode('@', $matches[2]) : explode('@', $mailgun_message['from']); $mail_domain = array_pop($tokens); // Retrieve a list of available domains first. $domains = array(); try { $result = $client->domains()->index(); if (!empty($result)) { foreach ($result->getDomains() as $domain) { $domains[$domain->getName()] = $domain->getName(); } } else { watchdog('mailgun', 'Could not retrieve domain list.', array(), WATCHDOG_ERROR); } } catch (Exception $e) { watchdog('mailgun', 'An exception occurred while retrieving domains. @code: @message', array( '@code' => $e->getCode(), '@message' => $e->getMessage(), ), WATCHDOG_ERROR); } if (empty($domains)) { // No domain available. // Although this shouldn't happen, doesn't hurt to check. return FALSE; } // Now, we need to get the working domain. This is generally the domain the // From address is on or the root domain of it. $working_domain = ''; if (in_array($mail_domain, $domains, TRUE)) { // Great. Found it. $working_domain = $mail_domain; } else { // Oops. No match. Perhaps it's a subdomain instead. foreach ($domains as $domain) { if (strpos($domain, $mail_domain) !== FALSE) { // Got it. $working_domain = $domain; break; } } } // There is a chance that the user is attempting to send from an email // address that's on a domain not yet added to the Mailgun account. // In that case, abort sending and report error. if (empty($working_domain)) { watchdog('mailgun', 'Unable to locate a working domain for From address %mail. Aborting sending.', array( '%mail' => $mailgun_message['from'], ), WATCHDOG_ERROR); return FALSE; } } else { $working_domain = variable_get('mailgun_domain', ''); } // Send message with attachments. if (!empty($mailgun_message['attachment'])) { foreach ($mailgun_message['attachment'] as &$attachment) { // Ignore array constructions. Not sure what values can be here. if (is_array($attachment)) { continue; } $attachment = array('filePath' => $attachment); } } try { $result = $client->messages()->send($working_domain, $mailgun_message); if (!empty($result)) { if (variable_get('mailgun_log', FALSE)) { watchdog('mailgun', 'Successfully sent message from %from to %to. %message.', array( '%from' => $mailgun_message['from'], '%to' => $mailgun_message['to'], '%message' => $result->getMessage(), )); } return TRUE; } else { watchdog('mailgun', 'Failed to send message from %from to %to. %message.', array( '%from' => $mailgun_message['from'], '%to' => $mailgun_message['to'], '%message' => $result->getMessage(), ), WATCHDOG_ERROR); return FALSE; } } catch (Exception $e) { watchdog('mailgun', 'Exception occurred while trying to send test email from %from to %to. @code: @message.', array( '%from' => $mailgun_message['from'], '%to' => $mailgun_message['to'], '@code' => $e->getCode(), '@message' => $e->getMessage(), )); return FALSE; } }