mandrill.module 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. <?php
  2. /**
  3. * @file
  4. * Enables Drupal to send email directly through Mandrill.
  5. *
  6. * Overriding mail handling in Drupal to make Mandrill the default
  7. * transport layer, requires to change the mail_system variable's
  8. * default value array('default-system' => 'DefaultMailSystem').
  9. * This module uses array('default-system' => 'MailChimpMandrillMailSystem').
  10. */
  11. define('MANDRILL_QUEUE', 'mandrill_queue');
  12. define('MANDRILL_EMAIL_REGEX', '/^\s*(.+?)\s*<\s*([^>]+)\s*>$/');
  13. /**
  14. * Implements hook_help().
  15. */
  16. function mandrill_help($path, $arg) {
  17. switch ($path) {
  18. case 'admin/help#mandrill':
  19. return t('Allow for site emails to be sent through Mandrill.');
  20. }
  21. }
  22. /**
  23. * Implements hook_menu().
  24. */
  25. function mandrill_menu() {
  26. $items = array();
  27. $items['admin/config/services/mandrill'] = array(
  28. 'title' => 'Mandrill',
  29. 'page callback' => 'drupal_get_form',
  30. 'page arguments' => array('mandrill_admin_settings'),
  31. 'access arguments' => array('administer mandrill'),
  32. 'description' => 'Send emails through the Mandrill transactional email service.',
  33. 'file' => 'mandrill.admin.inc',
  34. 'type' => MENU_NORMAL_ITEM,
  35. );
  36. $items['admin/config/services/mandrill/settings'] = array(
  37. 'title' => 'Settings',
  38. 'type' => MENU_DEFAULT_LOCAL_TASK,
  39. 'weight' => 0,
  40. );
  41. $items['admin/config/services/mandrill/test'] = array(
  42. 'title' => 'Send test email',
  43. 'page callback' => 'drupal_get_form',
  44. 'page arguments' => array('mandrill_test_form'),
  45. 'access callback' => 'mandrill_test_access',
  46. 'description' => 'Send a test email using the Mandrill API.',
  47. 'file' => 'mandrill.admin.inc',
  48. 'type' => MENU_LOCAL_TASK,
  49. 'weight' => 1,
  50. );
  51. return $items;
  52. }
  53. /**
  54. * Access callback for sending test email.
  55. *
  56. * @return bool
  57. * True if current user has access to send test messages
  58. */
  59. function mandrill_test_access() {
  60. $a = user_access('administer mandrill');
  61. $b = variable_get('mandrill_api_key');
  62. return $a & !empty($b);
  63. }
  64. /**
  65. * Implements hook_permission().
  66. */
  67. function mandrill_permission() {
  68. return array(
  69. 'administer mandrill' => array(
  70. 'title' => t('Administer Mandrill'),
  71. 'description' => t('Perform administration tasks for the Mandrill email service.'),
  72. "restrict access" => TRUE,
  73. ),
  74. );
  75. }
  76. /**
  77. * Implements hook_cron_queue_info().
  78. */
  79. function mandrill_cron_queue_info() {
  80. $queues = array();
  81. $queues[MANDRILL_QUEUE] = array(
  82. 'worker callback' => 'mandrill_queue_worker_mailsend',
  83. 'time' => variable_get('mandrill_queue_worker_timeout', 15),
  84. );
  85. return $queues;
  86. }
  87. /**
  88. * Sends a queued email.
  89. * @see mandrill_cron_queue_info()
  90. */
  91. function mandrill_queue_worker_mailsend($data) {
  92. // Send the message stored in the queue item.
  93. mandrill_mailsend(
  94. $data['message'],
  95. $data['function'],
  96. $data['args']
  97. );
  98. }
  99. /**
  100. * Implements hook_mail().
  101. */
  102. function mandrill_mail($key, &$message, $params) {
  103. if ($key == 'test') {
  104. $message['subject'] = $params['subject'];
  105. $message['body'] = $params['body'];
  106. if ($params['include_attachment']) {
  107. $message['attachments'][] = drupal_realpath('misc/druplicon.png');
  108. $message['body'] .= ' ' . t('The Drupal icon is included as an attachment to test the attachment functionality.');
  109. }
  110. }
  111. }
  112. /**
  113. * Abstracts sending of messages, allowing queueing option.
  114. *
  115. * @param array $message
  116. * A message array formatted for Mandrill's sending API, plus 2 additional
  117. * indexes for the send_function and an array of $args, if needed by the send
  118. * function.
  119. *
  120. * @return bool
  121. * TRUE if no exception thrown
  122. */
  123. function mandrill_mailsend($message, $function, $args = array()) {
  124. try {
  125. if (!function_exists($function)) {
  126. watchdog('mandrill', 'Error sending email from %from to %to. Function %function not found.',
  127. array(
  128. '%from' => $message['from_email'],
  129. '%to' => $message['to'],
  130. '%function' => $function,
  131. ),
  132. WATCHDOG_ERROR
  133. );
  134. return FALSE;
  135. }
  136. $params = array($message) + $args;
  137. $results = call_user_func_array($function, $params);
  138. foreach ($results as $result) {
  139. // Allow other modules to react based on a send result.
  140. module_invoke_all('mandrill_mailsend_result', $result);
  141. switch ($result['status']) {
  142. case "error":
  143. case "invalid":
  144. case "rejected":
  145. $to = isset($result['email']) ? $result['email'] : 'recipient';
  146. $status = isset($result['status']) ? $result['status'] : 'message';
  147. $error_message = isset($result['message']) ? $result['message'] : 'no message';
  148. watchdog('mandrill', 'Failed sending email from %from to %to. @status: @message',
  149. array(
  150. '%from' => $message['from_email'],
  151. '%to' => $to,
  152. '@status' => $status,
  153. '@message' => $error_message,
  154. ),
  155. WATCHDOG_ERROR
  156. );
  157. return FALSE;
  158. case "queued":
  159. watchdog('mandrill', 'Email from %from to %to queued by Mandrill App.',
  160. array(
  161. '%from' => $message['from_email'],
  162. '%to' => $result['email'],
  163. ),
  164. WATCHDOG_INFO
  165. );
  166. break;
  167. }
  168. }
  169. return TRUE;
  170. }
  171. catch (MandrillException $e) {
  172. watchdog('mandrill', 'Error sending email from %from to %to. @code: @message',
  173. array(
  174. '%from' => $message['from_email'],
  175. '%to' => $message['to'],
  176. '@code' => $e->getCode(),
  177. '@message' => $e->getMessage(),
  178. ),
  179. WATCHDOG_ERROR
  180. );
  181. return FALSE;
  182. }
  183. }
  184. /**
  185. * The actual function that calls the API send message.
  186. *
  187. * This is the default function used by mandrill_mailsend().
  188. *
  189. * @array $message
  190. * Associative array containing message data.
  191. *
  192. * @return array
  193. * Results of sending the message.
  194. *
  195. * @throws MandrillException
  196. */
  197. function mandrill_sender_plain($message) {
  198. if ($mailer = mandrill_get_api_object()) {
  199. return $mailer->messages_send($message);
  200. }
  201. else {
  202. throw new MandrillException('Missing API key.');
  203. }
  204. }
  205. /**
  206. * Return Mandrill API object for communication with the mailchimp server.
  207. *
  208. * @param bool $reset
  209. * Pass in TRUE to reset the statically cached object.
  210. * @param string $classname
  211. * The Mandrill class to use for sending emails. Passing a parameter allows
  212. * the class used to be overridden, E.g., for tests.
  213. *
  214. * @throws MandrillException
  215. *
  216. * @return Mandrill|bool
  217. * Mandrill Object upon success
  218. * FALSE if variable_get('mandrill_api_key') is unset
  219. */
  220. function mandrill_get_api_object($reset = FALSE, $classname = 'DrupalMandrill') {
  221. $api =& drupal_static(__FUNCTION__, NULL);
  222. if ($api === NULL || $reset === TRUE) {
  223. $api_key = variable_get('mandrill_api_key', '');
  224. $api_timeout = variable_get('mandrill_api_timeout', 60);
  225. if (empty($api_key)) {
  226. return FALSE;
  227. }
  228. $api = new $classname($api_key, $api_timeout);
  229. }
  230. return $api;
  231. }
  232. /**
  233. * Display the names of the modules that are using Mailsystem.
  234. *
  235. * This is consistent with with Mailsystem's display. In the future, if
  236. * Mailsystem were to provide an API for their labeling, that should be used.
  237. *
  238. * @return array
  239. * Array of all module names indexing to their "display" names,
  240. * and some special items for non-module values like null, default-system,
  241. * and some clarification talked onto the end of the Mandrill module's name.
  242. */
  243. function mandrill_get_module_key_names() {
  244. $name_array = array(
  245. '' => '--none--',
  246. 'default-system' => "Site-wide default",
  247. );
  248. $descriptions = array();
  249. foreach (system_rebuild_module_data() as $item) {
  250. if ($item->status) {
  251. $descriptions[$item->name] = (empty($item->info['package']) ? '' : $item->info['package']) . ' » ' . t('!module module', array('!module' => $item->info['name']));
  252. }
  253. }
  254. asort($descriptions);
  255. $mailsystem_settings = mailsystem_get();
  256. unset($mailsystem_settings['default-system']);
  257. foreach ($mailsystem_settings as $id => $class) {
  258. // Separate $id into $module and $key.
  259. $module = $id;
  260. while ($module && empty($descriptions[$module])) {
  261. // Remove a key from the end.
  262. $module = implode('_', explode('_', $module, -1));
  263. }
  264. // If an array key of the $mail_system variable is neither "default-system"
  265. // nor begins with a module name, then it should be unset.
  266. if (empty($module)) {
  267. // This shouldn't happen.
  268. }
  269. // Set $title to the human-readable module name.
  270. $title = preg_replace('/^.* » /', '', $descriptions[$module]);
  271. if ($key = substr($id, strlen($module) + 1)) {
  272. $title .= " ($key key)";
  273. }
  274. $name_array[$id] = $title;
  275. }
  276. return $name_array;
  277. }
  278. /**
  279. * Get a list of mandrill template objects.
  280. *
  281. * @return array
  282. * An of available templates with complete data or NULL if none are available.
  283. */
  284. function mandrill_get_templates() {
  285. // Only show the template settings if the mandrill api can be called.
  286. $templates = NULL;
  287. try {
  288. $mailer = mandrill_get_api_object();
  289. $templates = $mailer->templates_list();
  290. }
  291. catch (MandrillException $e) {
  292. drupal_set_message(t('Mandrill: %message', array('%message' => check_plain($e->getMessage()))), 'error');
  293. watchdog_exception('mandrill', $e);
  294. }
  295. return $templates;
  296. }
  297. /**
  298. * Get a list of subaccounts.
  299. */
  300. function mandrill_get_subaccounts() {
  301. $subaccounts = array();
  302. try {
  303. $mandrill = mandrill_get_api_object();
  304. $subaccounts = $mandrill->subaccounts();
  305. }
  306. catch (MandrillException $e) {
  307. drupal_set_message(t('Mandrill: %message', array('%message' => check_plain($e->getMessage()))), 'error');
  308. watchdog_exception('mandrill', $e);
  309. }
  310. return $subaccounts;
  311. }
  312. /**
  313. * Helper to return a comma delimited list of mail keys to not log content for.
  314. *
  315. * @return string
  316. * a comma delimited list of mail keys
  317. */
  318. function mandrill_mail_key_blacklist() {
  319. return variable_get('mandrill_mail_key_blacklist', 'user_password_reset');
  320. }
  321. /**
  322. * Helper to generate an array of recipients.
  323. *
  324. * @param mixed $to
  325. * a comma delimited list of email addresses in 1 of 2 forms:
  326. * user@domain.com
  327. * any number of names <user@domain.com>
  328. *
  329. * @return array
  330. * array of email addresses
  331. */
  332. function mandrill_get_to($to) {
  333. $recipients = array();
  334. $to_array = explode(',', $to);
  335. foreach ($to_array as $email) {
  336. if (preg_match(MANDRILL_EMAIL_REGEX, $email, $matches)) {
  337. $recipients[] = array(
  338. 'email' => $matches[2],
  339. 'name' => $matches[1],
  340. );
  341. }
  342. else {
  343. $recipients[] = array('email' => $email);
  344. }
  345. }
  346. return $recipients;
  347. }
  348. /**
  349. * Determine if mail should be processed asynchronously.
  350. *
  351. * @return bool
  352. * True if asyncronous processing is enabled
  353. */
  354. function mandrill_process_async() {
  355. return variable_get('mandrill_process_async', FALSE);
  356. }
  357. /**
  358. * Returns an array containing the from information for a Mandrill message.
  359. *
  360. * @return array
  361. * array(
  362. * 'email' => 'admin@example.com',
  363. * 'name' => 'My Site',
  364. * )
  365. */
  366. function mandrill_from() {
  367. $default_from = variable_get('site_mail', ini_get('sendmail_from'));
  368. $email = variable_get('mandrill_from', $default_from);
  369. $name = variable_get('mandrill_from_name', variable_get('site_name'));
  370. return array(
  371. 'email' => $email,
  372. 'name' => $name,
  373. );
  374. }