Pull to refresh

Отправка сообщения с вложением по e-mail из модуля в Drupal

Reading time8 min
Views1.8K
Понадобилось мне недавно сделать казалось бы простейшую вещь, а именно с помощью некоторой формы на сайте отправить письмо с вложением. И сделать это надо на сайте, построенном на Друпале… Как оказалось, этот пост очень правильный — действительно нужные рецепты не найдешь даже погуглив. В рунете вообще плохо с информацией, на диком западе получше, однако по теме ничего путного найти не удалось, кроме одного способа, который требует установки двух довольно навороченных модулей — ну это просто, по-моему, ни в какие ворота… Как и в случае с джумлой, пришлось копать сорсы различных модулей, api.drupal.org ну и, разумеется, додумывать. Результаты размышлений предлагаю Вашему вниманию. На примере простейшего модуля я покажу один из способов отправки сообщения с вложением по e-mail.



Для нашего модельного примера предположим, что пользователям нашего сайта необходимо иметь возможность отправлять администратору скриншоты (или другие картинки) с небольшими сопроводительными комментариями. Администратор должен получать эту бесценную информацию на свой e-ящик в виде e-писем в формате HTML в кодировке UTF-8, файл с картинкой должен быть присоединен к письму. Скриншоты должны быть в формате jpeg и весом не более 100Кб.

Чтобы не отходить далеко от темы топика, я предполагаю, что читатель знаком с общей теорией создания модулей для Друпала. Если нет, то можно для начала использовать, например, эту статью.

Итак, для реализации нашего модуля (назовем его SendScreen) необходимо написать некоторое количество кода:

Copy Source | Copy HTML
  1. <?php
  2.  
  3. /**<br/> * Реализация hook_menu()<br/> */
  4. function sendscreen_menu() {
  5.   $items = array();
  6.  
  7.   $items['sendscreen'] = array(
  8.     'title' => t('Send screenshot'),
  9.     'page callback' => 'sendscreen_page',
  10.     'access arguments' => array('access content'),
  11.     'description' => t('Форма отправки скриншота'),
  12.     'type' => MENU_CALLBACK,
  13.   );
  14.  
  15.   return $items;
  16. }
  17.  
  18. /**<br/> * Обратный вызов меню<br/> */
  19. function sendscreen_page() {
  20.   return drupal_get_form('sendscreen_mainform');
  21. }
  22.  
  23. /**<br/> * Определение формы<br/> */
  24. function sendscreen_mainform() {
  25.   $form['#attributes'] = array('enctype' => "multipart/form-data");
  26.  
  27.   $form['name'] = array(
  28.     '#type' => 'textfield',
  29.     '#title' => t('Your name'),
  30.     '#required' => TRUE,
  31.     '#maxlength' => 100,
  32.   );
  33.  
  34.   $form['note'] = array(
  35.     '#type' => 'textarea',
  36.     '#title' => t('Description'),
  37.     '#required' => FALSE,
  38.     '#rows' => 5,
  39.     '#maxlength' => 500,
  40.   );
  41.  
  42.   $form['screenshot'] = array(
  43.     '#type' => 'file',
  44.     '#title' => t('Screenshot'),
  45.     '#size' => 40,
  46.     '#description' => t('jpeg, 100Kb max.'),
  47.   );
  48.  
  49.   $form['submit'] = array(
  50.     '#type' => 'submit',
  51.     '#value' => t('Submit'),
  52.   );
  53.  
  54.   return $form;
  55. }
  56.  
  57. /**<br/> * Проверка формы<br/> */
  58. function sendscreen_mainform_validate($form, &$form_state) {
  59.   // Проверка присоединенного файла
  60.   $validators = array(
  61.     'file_validate_extensions' => array('jpg'),
  62.     'file_validate_size' => array(100 * 1024, 100 * 1024),
  63.   );
  64.   $file = file_save_upload('screenshot', $validators);
  65. }
  66.  
  67. /**<br/> * Отправка формы<br/> */
  68. function sendscreen_mainform_submit($form, &$form_state) {
  69.   $params = $form_state['values'];
  70.   drupal_mail('sendscreen', 'send', 'admin@domain.org', language_default(), $params);
  71.   drupal_set_message(t('Ваше сообщение отправлено.'));
  72. }
  73.  
  74. /**<br/> * Реализация hook_mail()<br/> */
  75. function sendscreen_mail($key, &$message, $params) {
  76.   $body = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\">\n";
  77.   $body .= '<html>' . "\n" .
  78.            '<head>' . "\n" .
  79.            '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n" .
  80.            '<style type=\"text/css\">' . '</style>' . "\n" .
  81.            '</head>' . "\n" .
  82.            '<body>' . "\n" . "\n";
  83.  
  84.   $body .= '<p>' . check_plain($params['name']) . '</p>' ."\n";
  85.   $body .= '<p>' . check_plain($params['note']) . '</p>' ."\n";
  86.  
  87.   $body .= '</body>' . "\n" .
  88.            '</html>' . "\n";
  89.  
  90.   // Формирование заголовков для письма в формате HTML
  91.   $message['headers']['Content-Type'] = 'text/html; charset=UTF-8';
  92.   $message['headers']['From'] = 'siteuser@domain.org';
  93.   $message['subject'] = t('Скриншот от @name', array('@name' => $params['name']));
  94.   $message['body'][] = $body;
  95.  
  96.   $msg = _sendscreen_process_attachment($message);
  97.   if (!empty($msg)) {
  98.     $message['headers'] = $msg['headers'];
  99.     $message['body'] = $msg['body'];
  100.   }
  101. }
  102.  
  103. /**<br/> * Подготовка файла для вставки в тело письма<br/> *<br/> * @param $file<br/> *   Файл для добавления к письму<br/> * @return<br/> *   Подготовленный файл<br/> */
  104. function _sendscreen_add_attachment($file) {
  105.   $att = "Content-Type: " . $file->filemime ."; name=\"" . basename($file->filename) . "\"\n";
  106.   $att .= "Content-Transfer-Encoding: base64\n";
  107.   $att .= "Content-Disposition: attachment; filename=\"" . basename($file->filename) . "\"\n\n";
  108.   if (file_exists($file->filepath)) {
  109.     $att .= chunk_split(base64_encode(file_get_contents($file->filepath)));
  110.   }
  111.  
  112.   return $att;
  113. }
  114.  
  115. /**<br/> * Вставка файла в сообщение<br/> *<br/> * @param $message<br/> *   Сообщение<br/> * @return<br/> *   Сообщение<br/> */
  116. function _sendscreen_process_attachment($message) {
  117.   $msg = array();
  118.  
  119.   // Если файла для вставки не выбрано, возвращаем пустой массив
  120.   if ($file = file_save_upload('screenshot')) {
  121.  
  122.     $body = '';
  123.     $msg = array();
  124.  
  125.     // id границы части сообщения
  126.     $boundary_id = md5(uniqid(time()));
  127.  
  128.     // Изменяем заголовок сообщения
  129.     $message['headers']['Content-Type'] = 'multipart/mixed; boundary="' . $boundary_id . '"';
  130.  
  131.     // Формирование тела сообщения. Сначала тело письма из $message...
  132.     $body = "\n--" . $boundary_id . "\n";
  133.     $body .= "Content-Type: text/html; charset=UTF-8; format=flowed;\n\n";
  134.  
  135.     // ... отделяем его от части сообщения, соответствующей вложению...
  136.     $body .= implode("\n\n", $message['body']);
  137.     $body .= "\n\n\n";
  138.  
  139.     // ... и вставляем подготовленное вложение
  140.     $body .= "--" . $boundary_id . "\n";
  141.     $body .= _sendscreen_add_attachment($file);
  142.     $body .= "\n\n";
  143.  
  144.     $body .= "--" . $boundary_id . "--\n\n";
  145.  
  146.     // Новый заголовок сообщения
  147.     $msg['headers'] = $message['headers'];
  148.     // Новое тело сообщения
  149.     $msg['body'] = $body;
  150.   }
  151.  
  152.   return $msg;
  153. }




Думаю, что реализацию основных функций, используемых для построения формы, подробно комментировать нет необходимости, остановлюсь только на нескольких моментах.

Как видно из кода, для отправки сообщения используется встроенная друпаловская функция drupal_mail, однако она не приспособлена для отправки сообщений с вложениями и в формате HTML, поэтому это придется делать руками. Для этого в реализации hook_mail нам требуется установить правильные заголовки для письма в формате HTML и установить кодировку UTF-8. Далее вызывается функция _sendscreen_process_attachment, которая включает в тело письма файл и возвращает новое тело письма и новый заголовок. Если файл в форме не был выбран, она возвращает пустой массив и сообщение $message остается без изменений.

Для того, чтобы вставить файл в письмо, его требуется закодировать с тем, чтобы его можно было включить в текстовое письмо, а также добавить необходимые заголовки, что и делает функция _sendscreen_add_attachment. Письма с вложением мультисекционные, для того, чтобы почтовые клиенты их могли корректно прочитать, необходимо указать идентификатор границ секций. Для этого в таких письмах должен быть специальный заголовок, в котором в параметре boundary указан идентификатор границы.

Собственно, это все. Для того, чтобы попробовать, как это работает, можно создать файл sendscreen.module, скопировать в него весь приведенный код, также создать файл sendscreen.info, например, с таким содержимым:

Copy Source | Copy HTML
  1. ; $Id$
  2. name = SendScreen
  3. description = Allow users to send a screenshot with notes by e-mail
  4. package = Samples
  5. version = 1.0
  6. core = 6.x
  7. php = 5.2




установить полученный модуль на сайте и зайти по адресу yourdomain/sendscreen.

Надеюсь, что этот топик будет кому-нибудь полезен. И т.к. я начинающий друпаллер, вполне допускаю, что что-то упустил, или вообще сделал не так, поэтому буду рад за советы и комменты.
Tags:
Hubs:
Total votes 14: ↑13 and ↓1+12
Comments7

Articles