В семерке поменяли кое что в плане отправки писем, и то что работало в 6ке, в 7ке уже «не катит». Разберем два аспекта отправки писем:
- HTML формат письма
- Отправка писем с вложениями
По умолчанию drupal 7 использует реализацию интерфейса MailSystemInterface в классе DefaultMailSystem (modules/system.mail.inc) . В нём реализуется функция форматирования текста письма с использованием drupal_html_to_text(…), которая нам не оставляет шансов на отправку сообщения в HTML формате. Т.е. нам нужно реализовать интерфейс MailSystemInterface в собственном классе и сообщить о нем drupal.
Реализация интерфейса MailSystemInterface
Предлагаю вашему вниманию вот такую версию реализации интерфейса. Этот код нужно разместить в каком то из своих модулей. Функция format больше не портит нам текст письма, мы можем отправлять его в нужном формате. Функция mail упрощена по сравнению с реализацией в классе DefaultMailSystem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
class CustomMailSystem implements MailSystemInterface { /** * Concatenate and wrap the e-mail body for plain-text mails. * @param $message - A message array, as described in hook_mail_alter(). * @return - The formatted $message. */ public function format(array $message) { $message['body'] = implode("\n\n", $message['body']); return $message; } /** * Send an e-mail message, using Drupal variables and default settings. * * @param $message * A message array, as described in hook_mail_alter(). * @return * TRUE if the mail was successfully accepted, otherwise FALSE. */ public function mail(array $message) { $mimeheaders = array(); foreach ($message['headers'] as $name => $value) { $mimeheaders[] = $name . ': ' . mime_header_encode($value); } $line_endings = variable_get('mail_line_endings', MAIL_LINE_ENDINGS); return mail( $message['to'], mime_header_encode($message['subject']), preg_replace('@\r?\n@', $line_endings, $message['body']), join("\n", $mimeheaders) ); } } |
Подключение кастомного класса к обработке писем
Вторая задача — подключить наш кастомный обработчик почты. Можно просто вставить его в код вашего модуля. Но настройка хранится в таблице variable в переменной mail_system, т.е. настроить нужно всего лишь раз. Последовательным было бы следующее решение — в хуке инсталляции модуля добавляем кастомный обработчик, при деинсталляции — удаляем его. Лично я размещаю следующий код в рамках hook_menu, чтобы он вызывался только при сбросе кеша. А деинсталляция меня не интересует, ведь речь идет о модуле, без которого не будет работать сайт (я собираю в нем весь код кастомизации). А вы сами решайте как это делать у вас.
1 2 3 4 5 6 7 8 9 10 |
//подключение кастомного обработчика объекта почты variable_set('mail_system', array_merge( // ранее установленный mail_system variable_get('mail_system', array('default-system' => 'DefaultMailSystem')), // наш кастомный обработчик, нужно перечислить для каких модулей он действует, // если их несколько array('mymodule' => 'CustomMailSystem', ... ) ) ); |
Отправка HTML писем с вложением
Дальнейшее стоит рассматривать на более конкретном примере. Задача такова — у нас есть какая то заявка от клиента, в которой он сообщает свои контактные данные и прикладывает файл (имена полей в HTML name — имя клиента, tel — его телефон, email — почтовый адрес клиента, comment — текст сообщения, flUp — прикладываемый файл) . Надо отправить эти данные на почту менеджеру.
Клиент отправляет данные формы, и первое что нужно сделать — проверить данные. Это делает следующая функция, которая также решает — можно ли отправлять письмо. Также формируется сообщение о результате операции ($mess).
Проверка данных формы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/* обработка данных формы */ function _mymodule_data_check() { $mess = ''; //проверим тел. номер (просто как пример) $tel = preg_replace("/[^\d]+/", '', $_POST['tel']); if (strlen($tel) < 6) { $mess = "Номер телефона слишком короткий.\r\nВ случае указания городского телефона, укажите также код города, пожалуйста."; } $_POST['email'] = trim($_POST['email']); //адрес почты можно проверить встроенной в друпал функцией if (!empty($_POST['email']) && !valid_email_address($_POST['email'])) { $mess = 'Формат почтового адреса не соответствует принятому стандарту.'; } //проверим ограничения, связанные с загружаемым файлом if (!empty($_FILES['flUp']['size']) && $_FILES['flUp']['size'] > 2000 * 1024) { $mess = 'Размер вложенного файла слишком велик (более 2 Мб).'; } if (empty($mess)) { $mess = 'Спасибо, Ваше сообщение принято!'; $manag_email = 'example_addr@mail.net'; drupal_mail('mymodule', 'message', $manag_email, language_default(), $_POST); } return $mess; } |
В итоге вызывается функция отправки сообщений — drupal_mail.
Отправка сообщения
Мы вплотную подошли к формированию сообщения. У нас уже все готово — подцеплена наша реализация интерфейса обработки писем, проведена первичная проверка данных формы. Тут я покажу как вложить файл в наше письмо. Продолжим рассматривать все в контексте практического примера.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
function mymodule_mail($key, &$message, $params) { //заголовки для HTML документа с кодировкой UFT-8 $message['headers']['Content-Type'] = 'text/html; charset=UTF-8'; $body = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\">\n"; $body .= '<html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <p>Добрый день!</p>'; //здесь можно обрабатывать несколько разных писем, //разделяя из по ключу ($key) switch ($key) { case 'message': $body .= ' <p>Через форму на сайте пользователь отправил следующие данные:</p> <table border=1>'; //составим табличку из полей нашей формы $frm = array( 'tel' => 'Телефон', 'name' => 'Имя', 'email' => 'Адр.эл.почты', 'comment' => 'Текст сообщения', ); foreach ($frm as $k => $v) { if (!empty($params[$k])) { $body .= "<tr bgcolor=#ffffff><td>{$v}:</td><td>" . htmlspecialchars($params[$k]) . "</td></tr>\n"; } } //если передаётся файл - вставим информацию о нем пока в виде текста if (!empty($_FILES['flUp']['size'])) { $body .= "<tr bgcolor=#ffffff><td>Вложенный файл:</td><td>" . htmlspecialchars($_FILES['flUp']['name']) . " / {$_FILES['flUp']['type']}</td></tr>"; } //заканчиваю письмо обращением к менеджеру, //что не нужно делать REPLY на данную заявку $body .= ' </table> <p>Желаю вам успешной работы и удачного дня!</p> <p>Не отвечайте на данное сообщение ф-цией "Ответить". Письмо вернется почтовому роботу на сайте. Чтобы связаться с клиентом, используйте его контактные данные.</p> </body></html>' . "\n"; $message['subject'] = 'Заявка с' . ' ' . $_SERVER['HTTP_HOST']; $message['body'][] = $body; //а вот обработка для файла-вложения if (!empty($_FILES['flUp']['size'])) { $msg = _mime_send_process_attachment($message, 'flUp'); if (!empty($msg)) { $message['headers'] = $msg['headers']; $message['body'][] = $msg['body']; } } break; } } |
Пример длинноватый, но основная часть его посвящена составлению текста сообщения. Мне важно было показать порядок обработки всех частей письма, и этот пример может послужить скелетом для вашего случая.
В конце вызывается функция обработки вложения. Я её где то спер, немного подточил и перевел комментарии. Предлагаю вам также ей воспользоваться. :)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/** * Прикрепляем файл к письму */ function _mime_send_process_attachment($message, $fileFieldName) { $msg = array(); //проверяем существует ли файл if ($file = $_FILES[$fileFieldName]) { $body = ''; $msg = array(); //специальный идентификатор отделяет наше вложение в письме $boundary_id = md5(uniqid(time())); //Меняем заголовок сообщения $message['headers']['Content-Type'] = 'multipart/mixed; boundary="' . $boundary_id . '"'; //Подготавливаем тело сообщения, предваряя его специальным маркером $body = "\n--" . $boundary_id . "\n"; $body .= "Content-Type: text/html; charset=UTF-8; format=flowed;\n\n"; //Separate it from attachment part of message $body .= implode("\n\n", $message['body']); $body .= "\n\n\n"; //и вставим подготовленное вложение $body .= "--" . $boundary_id . "\n"; $body .= _mime_send_add_attachment($file); $body .= "\n\n"; $body .= "--" . $boundary_id . "--\n\n"; //Вот новые заголовки $msg['headers'] = $message['headers']; //Вот новое тело $msg['body'] = $body; } return $msg; } /** * Подготовка файла для вставки в тело письма */ function _mime_send_add_attachment($file) { $filename = "=?utf-8?B?". base64_encode($file['name']). "?="; $att = "Content-Type: " . $file['type'] ."; name=\"$filename\"; charset=UTF-8\n"; $att .= "Content-Transfer-Encoding: base64\n"; $att .= "Content-Disposition: attachment; filename=\"$filename\"\n\n"; if (file_exists($file['tmp_name'])) { $att .= chunk_split(base64_encode(file_get_contents($file['tmp_name']))); } return $att; } |
Это всё, что требуется для отправки вложения с HTML письмом из Drupal 7.
Интересно. Но у меня немного иначе. Как релизовать: Клиент заполняет заявку и ему автоматом приходит емайл с аттачем?
А в чем трудность?
Получается наоборот. Я не принимаю файл, а прикрепляю к уведомлению о заполненной форме. Сам файл лежит в /files. И потом, получается мы изменяем формат вообще всех писем, что не очень хорошо.
Если обсуждать это в контексте данного примера в статье, то
1) нам нет необходимости проверять наши файлы, т.е. _mymodule_data_check() вырождается;
2) нужно подстроиться под вызов _mime_send_add_attachment($file), что довольно просто мне кажется, особо и комментировать нечего;
3) ну и в основной ф-ции mymodule_mail(), обработчик вложения изменяется на подключение подготовленного заранее файла. Это файл может генерироваться динамически перед его подключением и т.п. Я так понимаю какой то счет или ком предложение нужно высылать?
Да, у меня есть форма с калькулятором. Клиент заполняет и сабмитит. Мне уходит письмо о заявке с данными. Ему подробная инструкция и спец предложение.
Форма была на webform, удалил, т.к. форму реализуем в модуле, далее разбираюсь с вашим примером.