Форма обратной связи на Drupal Webform с безопасным хранением прикрепленных файлов
Всем, аллоха! Сегодня пост будет про то, как в Drupal создавать формы обратной связи через Webform с возможностью прикрепления файлов. Казалось бы, «Ха, да очень просто!», но я потратил на решение этой задачи неделю. Хотите знать почему? Тогда прошу к прочтению.
Началась вся эта история, когда меня попросили сделать сайт для одной компании. Решил делать сайт с душой, так как обратились ко мне хорошо знакомые люди. Описывать подробно, что это за сайт и как он разрабатывался, я сейчас не буду. Расскажу лишь про самую интересную задачу в рамках этого проекта, с которой я столкнулся – создание формы обратной связи для привлечения клиентов.
Согласитесь, что при обращении за услугами в любую компанию, вам, как правило, необходимо приложить к письму какие-либо документы. А эти документы в свою очередь могут содержать корпоративную или личную информацию, доступ к которой лучше оставлять ограниченным. Собственно после таких рассуждений я и составил список требований к «правильной» форме обратной связи:
- Возможность загрузки произвольного количества файлов;
- Управление списком доступных расширений файлов для загрузки;
- Разграничение прав и управление доступом к загруженным файлам;
- Отправка письма с прикрепленными файлами (а не ссылкой на них) на указанную почту при успешном заполнении формы.
В моем видении проекта должно быть так: после загрузки файлов доступ к ним имеют лишь пользователи с определенной ролью.
Элемент Webform для загрузки файлов
Да, именно для этого проекта я изначально и написал модуль Webform Multiple File потому, что так и не смог найти никакого другого мало-мальски приличного решения. Разумеется, первым, что мне попалось под руку, был уже готовый модуль Webform Multiple File Upload с Drupal.org. Но даже, если бегло пробежаться глазами по коду, становится понятно, что он не подходит. Модуль работает на JS-библиотеке, которая не позволяет в нужной мере оперировать с файлами: ни валидации, ни возможности использовать AJAX на форме.
Идеальным визуальным решением для мультизагрузки файлов к форме мне тогда показался принцип работы File Field Widget в нодах: после каждого загруженного файла отображается форма для загрузки следующего. Собственно, так и получился модуль Webform Multiple File. Более подробно о модуле сможете прочитать по ссылке.
Private file system для Webform
Надеюсь, для вас не секрет, что Drupal из коробки уже умеет разграничивать доступ к файлам. При работе с Private file system поднимается ядро Drupal и проверяются права (permissions) текущего пользователя перед тем, как отдать ему файл. Чтобы настроить эту систему:
- Перейдите в
и укажите путь к директории для хранения закрытых файлов. Например, у меня это будетadmin/config/media/file-system
. После сохранения настроек будет инициализирована директория "private", закрытая от внешнего мира через .htaccess директивы.sites/default/files/private
- К вашей Webform для обратной связи добавьте элемент ‘Multiple file’ и в настройках Upload destination выберите Private files. Также можете выбрать поддиректорию для хранения файлов, загруженных именно через данный элемент вебформы.
С данными настройками доступом к загруженным файлам будут обладать только те пользователи, роль которых позволяет просматривать результаты Webform. Другими словами, это права “Access all webform results” или “Access own webform results” на странице
.admin/people/permissions
Если у вас появится желание более широко разграничить права для ролей, то хук
вам в помощь. Ну и, возможно, потребуется поставить пару Breakpoint’ов для отладки в функции hook_file_download()
– статья про отладку PHP приложений вам в помощь.file_download()
Отправка писем с прикрепленными файлами
По умолчанию Webform отсылает письмо, в котором предоставляются только ссылки на файлы. Я же решил, что это будет совершенно не практично, ведь письмо вероятнее всего будут пересылать из отдела в отдел компании. И что всей компании предоставлять административный доступ к сайту, чтобы выкачать несколько файлов? Разумеется, что нет. Поэтому загруженные файлы должны прикрепляться к письму.
Для решения этой задачи нам потребуется:
- Установить контриб модуль Mail System;
- Установить контриб модуль Mime Mail;
- Имплементировать
в своем модуле.hook_mail_alter()
Указанные модули необходимо просто установить. Не помню, чтобы изменял настройки, но на всякий случай приложу конфигурацию Mail System.
Теперь необходимо перехватить письмо до того, как его Drupal отправит, и прикрепить к нему загруженные файлы. Код не претендует на изящность – это было быстрое и рабочее решение на момент написания проекта. Итак, в своем модуле имплементируем следующий хук:
- /**
- * Implements hook_mail_alter().
- */
- function MY_MODULE_mail_alter(&$message) {
- if ($message['id'] == 'webform_submission'){
- // Gets file component id from webform node.
- $webform_node = $message['params']['node'];
- $webform_components = $webform_node->webform['components'];
- foreach ($webform_components as $key => $component) {
- if ($component['form_key'] == MY_MODULE_WEBFORM_FILE_COMPONENT_NAME) {
- $webform_file_component = $key;
- break;
- }
- }
-
- // Adds attachment if file component defined.
- if (!empty($webform_file_component)) {
- if (!empty($message['params']['submission']->data[$webform_file_component]['value'])) {
- $submitted_files = file_load_multiple($message['params']['submission']->data[$webform_file_component]['value']);
- foreach ($submitted_files as $file) {
- $file_attachment = (array) $file;
- $file_attachment['filepath'] = $file_attachment['uri'];
- $message['params']['attachments'][] = $file_attachment;
- }
- }
- }
- }
- }
Единственное, что требуется изменить в приведенном коде под конкретный проект – это константу
, храняющую машинное имя компонента Webform, через который загружаются файлы. Взять эти данные можно в настройках компонента – Field Key.MY_MODULE_WEBFORM_FILE_COMPONENT_NAME
Ну вот, собственно, после данных манипуляций с настройками вы получаете удобную и безопасную форму контактов или оформления заявок. Еще могу посоветовать прикрутить к форме Google Analytics трекер, чтобы отслеживать конверсию.
Еще один важный момент: представленное решение является стабильным, проверенным и рабочим на случай, если у вас одна контактная форма (другими словами, одна нода типа Webform), которая содержит только один элемент Multiple File для загрузки файлов. Я просто хочу предупредить что, если у вас немного отличается структура проекта от моего, то существует вероятность появления ошибок. Паниковать и кричать «Ниработае!!!1» не стоит – я уверен, проблема решится изменением нескольких строчек кода.
В случае появления вопросов или неразрешимых проблем при попытке воспроизвести данное решение на вашем проекте – обращайтесь. Ну и обсуждениям в комментариях я тоже буду рад!
Комментарии
Не могу понять, в чём проблема. Гуглю уже 3 дня!! У меня webform не прикрепляет файлы вообще!
Жмёшь "Выбрать файл", выбираешь. Жмёшь "Закачать" и начинается бесконечная загрузка, но файл не прикрепляется.
Может быть вы сталкивались с похожей проблемой?
Здравствуйте. Файл загружается в приват и по умолчанию. Но не прикрепляется к письму (ссылкой идет) на сайте multiple один, но форм несколько
Файл отправляется на почту, а на серваке он остается храниться? Если да, как его убирать. Т.е. нужно просто отправить файл по почте без сохранения на сайте.
Есть два вопроса по твоей форме. Первый - как реализовать загрузку сразу нескольких файлов, а не по одному, как это работает сейчас? И второй - есть ли возможность осуществлять загрузку файлов на сервер не по нажатию кнопки - загрузить, а сразу после закрытия окна, в котором были выбраны файлы для загрузки. Модуль оказался реально самым близким по юзабельности к идеалу, но если устранить эти два шага, вопросы по которым я задал - будет идеал :)
А как сделать через mime mail чтобы один файл прикреплялся и на почту приходил не в виде ссылке, а приложением???Заранее спасибо!
А можно более подробно описать как имплементировать хук??? Ни разу просто этого не делал...
Спасибо!
Здравствуйте!
в своем модуле mail или webform? Пожалуйста можно подробнее где и куда именно вставлять этот код. И еще пожалуйста, объясните где найти Field Key?
Спасибо!
Добавить комментарий