Форма обратной связи на Drupal Webform с безопасным хранением прикрепленных файлов

Форма обратной связи на 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 и укажите путь к директории для хранения закрытых файлов. Например, у меня это будет sites/default/files/private. После сохранения настроек будет инициализирована директория "private", закрытая от внешнего мира через .htaccess директивы.
  • К вашей Webform для обратной связи добавьте элемент ‘Multiple file’ и в настройках Upload destination выберите Private files. Также можете выбрать поддиректорию для хранения файлов, загруженных именно через данный элемент вебформы.

С данными настройками доступом к загруженным файлам будут обладать только те пользователи, роль которых позволяет просматривать результаты Webform. Другими словами, это права “Access all webform results” или “Access own webform results” на странице admin/people/permissions.

Если у вас появится желание более широко разграничить права для ролей, то хук hook_file_download() вам в помощь. Ну и, возможно, потребуется поставить пару Breakpoint’ов для отладки в функции file_download() – статья про отладку PHP приложений вам в помощь.

Отправка писем с прикрепленными файлами

По умолчанию Webform отсылает письмо, в котором предоставляются только ссылки на файлы. Я же решил, что это будет совершенно не практично, ведь письмо вероятнее всего будут пересылать из отдела в отдел компании. И что всей компании предоставлять административный доступ к сайту, чтобы выкачать несколько файлов? Разумеется, что нет. Поэтому загруженные файлы должны прикрепляться к письму.

Для решения этой задачи нам потребуется:

  • Установить контриб модуль Mail System;
  • Установить контриб модуль Mime Mail;
  • Имплементировать hook_mail_alter() в своем модуле.

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

Настройки модуля Mail System

Теперь необходимо перехватить письмо до того, как его Drupal отправит, и прикрепить к нему загруженные файлы. Код не претендует на изящность – это было быстрое и рабочее решение на момент написания проекта. Итак, в своем модуле имплементируем следующий хук:

  1. /**
  2.  * Implements hook_mail_alter().
  3.  */
  4. function MY_MODULE_mail_alter(&$message) {
  5. if ($message['id'] == 'webform_submission'){
  6. // Gets file component id from webform node.
  7. $webform_node = $message['params']['node'];
  8. $webform_components = $webform_node->webform['components'];
  9. foreach ($webform_components as $key => $component) {
  10. if ($component['form_key'] == MY_MODULE_WEBFORM_FILE_COMPONENT_NAME) {
  11. $webform_file_component = $key;
  12. break;
  13. }
  14. }
  15.  
  16. // Adds attachment if file component defined.
  17. if (!empty($webform_file_component)) {
  18. if (!empty($message['params']['submission']->data[$webform_file_component]['value'])) {
  19. $submitted_files = file_load_multiple($message['params']['submission']->data[$webform_file_component]['value']);
  20. foreach ($submitted_files as $file) {
  21. $file_attachment = (array) $file;
  22. $file_attachment['filepath'] = $file_attachment['uri'];
  23. $message['params']['attachments'][] = $file_attachment;
  24. }
  25. }
  26. }
  27. }
  28. }

Единственное, что требуется изменить в приведенном коде под конкретный проект – это константу MY_MODULE_WEBFORM_FILE_COMPONENT_NAME, храняющую машинное имя компонента Webform, через который загружаются файлы. Взять эти данные можно в настройках компонента – Field Key.

Ну вот, собственно, после данных манипуляций с настройками вы получаете удобную и безопасную форму контактов или оформления заявок. Еще могу посоветовать прикрутить к форме Google Analytics трекер, чтобы отслеживать конверсию.

Еще один важный момент: представленное решение является стабильным, проверенным и рабочим на случай, если у вас одна контактная форма (другими словами, одна нода типа Webform), которая содержит только один элемент Multiple File для загрузки файлов. Я просто хочу предупредить что, если у вас немного отличается структура проекта от моего, то существует вероятность появления ошибок. Паниковать и кричать «Ниработае!!!1» не стоит – я уверен, проблема решится изменением нескольких строчек кода.

В случае появления вопросов или неразрешимых проблем при попытке воспроизвести данное решение на вашем проекте – обращайтесь. Ну и обсуждениям в комментариях я тоже буду рад!

Комментарии

Аватар пользователя Валентин
Валентин

Не могу понять, в чём проблема. Гуглю уже 3 дня!! У меня webform не прикрепляет файлы вообще!

Жмёшь "Выбрать файл", выбираешь. Жмёшь "Закачать" и начинается бесконечная загрузка, но файл не прикрепляется.

Может быть вы сталкивались с похожей проблемой?

Аватар пользователя angarsky
angarsky
Посмотрите в консоли Firefox нет ли JS ошибок, из-за которых может не происходить обновление HTML после AJAX-запроса. В логах watchdog тоже ничего нет?
Аватар пользователя Sla_ok
Sla_ok

Здравствуйте. Файл загружается в приват и по умолчанию. Но не прикрепляется к письму (ссылкой идет) на сайте multiple один, но форм несколько

Аватар пользователя Дмитрий
Дмитрий

Файл отправляется на почту, а на серваке он остается храниться? Если да, как его убирать. Т.е. нужно просто отправить файл по почте без сохранения на сайте.

Аватар пользователя Руслан
Руслан

Есть два вопроса по твоей форме. Первый - как реализовать загрузку сразу нескольких файлов, а не по одному, как это работает сейчас? И второй - есть ли возможность осуществлять загрузку файлов на сервер не по нажатию кнопки - загрузить, а сразу после закрытия окна, в котором были выбраны файлы для загрузки. Модуль оказался реально самым близким по юзабельности к идеалу, но если устранить эти два шага, вопросы по которым я задал - будет идеал :)

Аватар пользователя Андрей
Андрей

А как сделать через mime mail чтобы один файл прикреплялся и на почту приходил не в виде ссылке, а приложением???Заранее спасибо!

Аватар пользователя angarsky
angarsky
Дружище, ну я ж сниппет предоставил. Адаптируй его под свой проект и будет тебе счастье.
Аватар пользователя Андрей
Андрей

А можно более подробно описать как имплементировать хук??? Ни разу просто этого не делал...
Спасибо!

Аватар пользователя Rammeo
Rammeo

Здравствуйте!

Итак, в своем модуле имплементируем следующий хук:

в своем модуле mail или webform? Пожалуйста можно подробнее где и куда именно вставлять этот код. И еще пожалуйста, объясните где найти Field Key?
Спасибо!

Аватар пользователя angarsky
angarsky
В своем модуле - это значит в любом кастомном модуле, в котором удобно. Field Key - это машинное имя компонента Webform, можно увидеть при редактировании компонента в админке.

Добавить комментарий

        d8888   .d8888b.   888                
d88888 d88P Y88b 888
d88P888 888 888
d88P 888 888d888b. 888 .d88888
d88P 888 888P "Y88b 888 d88" 888
d88P 888 888 888 888 888 888
d8888888888 Y88b d88P 888 Y88b 888
d88P 888 "Y8888P" 88888888 "Y88888
888
888
888
Зарегистрируйтесь для добавления материалов без проверки.