Прикрепление в MODX файлов к форме с помощью FormIt
Статья, в которой рассмотрим, как в MODX Revolution создать форму обратной связи (FormIt + AjaxForm) с возможностью прикрепления к ней файлов. Кроме этого, для защиты формы от вложения в неё нежелательных файлов, разработаем дополнительный валидатор, с помощью которого сниппет FormIt будет проверять каждый файл на то, соответствует ли он необходимому размеру и указанному расширению.
Если вы не знакомы с дополнением FormIt, то разбираться с ним желательно начать с изучения статьи, в которой рассматривается создание простой AJAX формы обратной связи. Сам по себе компонент FormIt не обеспечивает работу с ним по технологии AJAX. Для того чтобы это обеспечить можно воспользоваться ещё одним дополнением MODX – AjaxForm.
Разрабатывать контактную форму с вложениями начнём с выполнения подготовительных действий:
1. Создание копии чанка tpl.AjaxForm.example
(например, с именем tpl.AjaxForm.Attach
).
<form action="" method="post" class="ajax_form af_example">
<div class="form-group">
<label class="control-label" for="af_name">[[%af_label_name]]</label>
<div class="controls">
<input type="text" id="af_name" name="name" value="[[+fi.name]]" placeholder="" class="form-control"/>
<span class="error_name">[[+fi.error.name]]</span>
</div>
</div>
<div class="form-group">
<label class="control-label" for="af_email">[[%af_label_email]]</label>
<div class="controls">
<input type="email" id="af_email" name="email" value="[[+fi.email]]" placeholder="" class="form-control"/>
<span class="error_email">[[+fi.error.email]]</span>
</div>
</div>
<div class="form-group">
<label class="control-label" for="af_message">[[%af_label_message]]</label>
<div class="controls">
<textarea id="af_message" name="message" class="form-control" rows="5">[[+fi.message]]</textarea>
<span class="error_message">[[+fi.error.message]]</span>
</div>
</div>
<div class="form-group">
<div class="controls">
<button type="reset" class="btn btn-default">[[%af_reset]]</button>
<button type="submit" class="btn btn-primary">[[%af_submit]]</button>
</div>
</div>
[[+fi.success:is=`1`:then=`
<div class="alert alert-success">[[+fi.successMessage]]</div>
`]]
[[+fi.validation_error:is=`1`:then=`
<div class="alert alert-danger">[[+fi.validation_error_message]]</div>
`]]
</form>
2. Создание ресурса, в котором необходимо отобразить контактную форму с вложениями. В ресурсе или связанном с ним шаблоном необходимо поместить вызов сниппета AjaxForm.
[[!AjaxForm?
&snippet=`FormIt`
&form=`tpl.AjaxForm.Attach`
&hooks=`FormItSaveForm,email`
&emailSubject=`Тема письма`
&emailTo=`my@email.ru`
&emailFrom=`no-reply@mysite.ru`
&emailFromName=`Мой сайт`
&emailTpl=`tpl.email.attach`
&validate=`name:required,email:required,message:required`
&validationErrorMessage=`Пожалуйста, исправьте ошибки!`
&successMessage=`Ваше сообщение успешно отправлено`
]]
3. Разработка чанка tpl.email.attach
, который будет определять тело (шаблон) письма.
<p>Сообщение с формы обратной связи:</p>
<hr>
<p>От кого: [[+name]]</p>
<p>E-mail: [[+email]]</p>
<p>Сообщение: [[+message]]</p>
Прикрепление файлов к форме FormIt
Для отправки формы с вложениями необходимо указать для неё метод кодирования данных multipart/form-data
. Это осуществляется в чанке tpl.AjaxForm.Attach
.
<form action="" method="post" class="ajax_form af_example" enctype="multipart/form-data">
Добавить к форме файлы, в зависимости от задачи, можно используя следующие варианты:
1. Один файл (в необходимое место HTML формы):
<div class="form-group">
<label for="upload">Прикрепить файл</label>
<input type="file" name="upload">
<p class="error_upload">[[+fi.error.upload]]</p>
</div>
2. Несколько файлов (с помощью нескольких элементов input
):
<div class="form-group">
<label for="uploads">Прикрепить файлы</label>
<input type="file" name="uploads[]">
<input type="file" name="uploads[]">
<input type="file" name="uploads[]">
<p class="error_uploads">[[+fi.error.uploads]]</p>
</div>
3. Несколько файлов (с помощью одного элемента input
):
<div class="form-group">
<label for="files">Прикрепить файлы</label>
<input type="file" name="files[]" multiple="multiple">
<p class="error_files">[[+fi.error.files]]</p>
</div>
После выполнения 2 этих простых шагов форма обратной связи сможет отправлять письма на указанный адрес электронный почты (email) с вложениями.


input
, если у него установлен атрибут multiple
. Чтобы его поправить необходимо, найти файл fihooks.class
, который расположен в следующем каталоге:\core\components\formit\model\formit\Открыть его, найти в нём строчку:
if(count($v['name']) > 1){Изменить эту строчку на следующую:
if(is_array($v['name']) && (count($v['name']) >= 1)){
Проверка расширений и размеров файлов на сервере
При загрузке файлов на сервер, FormIt не проверяет их тип и размер. Чтобы осуществить данную возможность необходимо написать пользовательский валидатор, который будет проверять загруженные файлы на сервере и выдавать ошибки, если хотя бы один из них не соответствует указанным требованиям. Это позволит защитить форму от нежелательных файлов.
Создадим 2 валидатора (сниппета). Первый (formit2checkfile) предназначен для использования с формой, которая может прикрепить максимум один файл (для 1 варианта). Второй валидатор (formit2checkfiles) будем использовать для проверки массива файлов, т.е. когда на форме используем 2 или 3 вариант.
Код валидатора formit2checkfile
для проверки одного файла (не массива):
<?php
// инициализируем переменную output, отвечающую за результат работы валидатора, со значением true
$output = true;
// разрешённые расширения файлов
$allowedExt = array('jpg','png');
// максимальный размер файла (512 Кбайт)
$maxFileSize = 512 * 1024;
// имя файла
$fileName = basename( $_FILES[$key]['name'] );
// размер
$fileSize = filesize( $_FILES[$key]['tmp_name'] );
// расширение файла
$fileExt = mb_strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
// если имя файла не пустое
if ($fileName != '') {
if(!in_array($fileExt, $allowedExt)) {
// файл имеет недопустимый тип
$errorMsg = 'Файл ' . $fileName . ' имеет не разрешённый тип.';
$validator->addError($key, $errorMsg);
$output = false; // возвращаем false
}
if($fileSize > $maxFileSize) {
// файл имеет размер больше максимального
$errorMsg = 'Размер файла '. $fileName .' превышает 512 Кбайт.';
$validator->addError($key,$errorMsg);
$output = false; // возвращаем false
}
}
return $output;
Код валидатора formit2checkfiles
для проверки нескольких файлов (массива):
<?php
// инициализируем переменную output, отвечающую за результат работы хука, со значением true
$output = true;
// разрешённые расширения файлов
$allowedExt = array('jpg','png');
// максимальный размер файла (512 Кбайт)
$maxFileSize = 512 * 1024;
// если ассоциативный массив $_FILES[$keys] существует, то
if(isset($_FILES[$key]["error"])) {
// переберём все файлы (изображения)
foreach ($_FILES[$key]["error"] as $fkey => $error) {
// если ошибок не возникло, т.е. файл был успешно загружен на сервер, то...
if ($error == UPLOAD_ERR_OK) {
// имя файла
$fileName = basename($_FILES[$key]['name'][$fkey]);
// расширение файла
$fileExt = mb_strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
// размер файла
$fileSize = filesize($_FILES[$key]['tmp_name'][$fkey]);
// проверка расширения файла
if(!in_array($fileExt, $allowedExt)) {
// файл имеет недопустимый тип
$errorMsg = 'Файл ' . $fileName . ' имеет не разрешённый тип.';
$validator->addError($key, $errorMsg);
$output = false; // возвращаем false
break;
}
if($fileSize > $maxFileSize) {
// файл имеет размер больше максимального
$errorMsg = 'Файл '. $fileName .' имеет не разрешённый размер.';
$validator->addError($key,$errorMsg);
$output = false; // возвращаем false
break;
}
} else {
// произошла ошибка при загрузке файла на сервер
$errorMsg = 'Произошла ошибка при загрузке файла ' . $fileName .' на сервер.';
$validator->addError($key,$errorMsg);
$output = false; // возвращаем false
break;
}
}
}
return $output;
// валидные типы файлов для загрузки
$allowedExt = array('jpg','png');
// максимальный размер файла (512 Кбайт)
$maxFileSize = 512 * 1024;
Подключения созданного валидатора к форме осуществляется с помощью параметра customValidators
, а файл, который необходимо проверить в параметре validate
. При этом если пользователь не указал файл, то валиадатор это не считает это за ошибку. Т.к. прикрепление файла или файлов к форме обратной связи в этом примере считается не обязательным.
Проверка формы с одним файлом (input
с name, равным upload):
[[!AjaxForm?
&snippet=`FormIt`
&form=`tpl.AjaxForm.Attach`
&hooks=`FormItSaveForm,email`
&customValidators=`formit2checkfile`
&emailSubject=`Тема письма`
&emailTo=`my@email.ru`
&emailFrom=`no-reply@mysite.ru`
&emailFromName=`Мой сайт`
&emailTpl=`tpl.email.attach`
&validate=`name:required,email:required,message:required,upload: formit2checkfile`
&validationErrorMessage=`Пожалуйста, исправьте ошибки!`
&successMessage=`Ваше сообщение успешно отправлено`
]]


Проверка формы с возможностью загрузки нескольких файлов (элементы input
с name
, равным uploads
):
[[!AjaxForm?
&snippet=`FormIt`
&form=`tpl.AjaxForm.Attach`
&hooks=`FormItSaveForm,email`
&customValidators=`formit2checkfiles`
&emailSubject=`Тема письма`
&emailTo=`my@email.ru`
&emailFrom=`no-reply@mysite.ru`
&emailFromName=`Мой сайт`
&emailTpl=`tpl.email.attach`
&validate=`name:required,email:required,message:required,uploads:formit2checkfiles`
&validationErrorMessage=`Пожалуйста, исправьте ошибки!`
&successMessage=`Ваше сообщение успешно отправлено`
]]

Проверка формы с возможностью загрузки нескольких файлов (элемент input
с name
, равным files[]
):
[[!AjaxForm?
&snippet=`FormIt`
&form=`tpl.AjaxForm.Attach`
&hooks=`FormItSaveForm,email`
&customValidators=`formit2checkfiles`
&emailSubject=`Тема письма`
&emailTo=`my@email.ru`
&emailFrom=`no-reply@mysite.ru`
&emailFromName=`Мой сайт`
&emailTpl=`tpl.email.attach`
&validate=`name:required,email:required,message:required,files:formit2checkfiles`
&validationErrorMessage=`Пожалуйста, исправьте ошибки!`
&successMessage=`Ваше сообщение успешно отправлено`
]]

В моем случае (openserver php 7.2) форма без файлов не отправлялась, т.к ассоциативный массив $_FILES всегда существует.
Удалил эту ветку и все заработало.
Как можно сделать по изящному, проверку в самом начале? Чтобы форма отправлялась без файла?
$output = true;
break;
}
отправляет без файлов
itchief.ru/assets/uploadify/1/0/f/10f16846960502829918780c02862abe.jpg
Все работает спасибо вам за работу. Только кнопке «обзор» хотелось бы придать цвет в общем вот эти стали class=«zakazat» подскажите пожалуйста как это сделать. к комментарию картинку пробовал прикрепить если получилось посмотрите. Прикрепление файла для одного те первый вариант с вашего сайта.
Всё правильно, вы отображаете label, a input скрываете. Как в этом случае что-то может появиться в label? Здесь необходимо использовать JavaScript.
Например:
Сделал проверку «расширений и размеров файлов на сервере»
По вашему описанию, но она не работает.
Подключение AjaxForm:
[[AjaxForm?
&snippet=`FormIt`
&hooks=`spam,email`
&customValidators=`formit1checkfile`
&emailFrom=`мой адрес`
&form=`tpl.AjaxForm`
&emailTpl=`modal-zv-tpl`
&emailTo=`мой адрес`
&emailSubject=`Отклик на вакансию`
&validate=`phone-zv:required, name-zv:required, upload: formit1checkfile`
&validationErrorMessage=`Пожалуйста, заполните поля помеченные *`
&successMessage=`Сообщение успешно отправлено, наш специалист свяжется с вами!`
]]
Чанк:
…
[[+fi.error.upload]]
…
Снипет:
<?php
// инициализируем переменную output, отвечающую за результат работы валидатора, со значением true
$output = true;
// разрешённые расширения файлов
$allowedExt = array('jpg','png');
// максимальный размер файла (512 Кбайт)
$maxFileSize = 512 * 1024;
// имя файла
$fileName = basename( $_FILES[$key]['name'] );
// размер
$fileSize = filesize( $_FILES[$key]['tmp_name'] );
// расширение файла
$fileExt = mb_strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
// если имя файла не пустое
if ($fileName != '') {
if(!in_array($fileExt, $allowedExt)) {
// файл имеет недопустимый тип
$errorMsg = 'Файл '. $fileName. ' имеет не разрешённый тип.';
$validator->addError($key, $errorMsg);
$output = false; // возвращаем false
}
if($fileSize > $maxFileSize) {
// файл имеет размер больше максимального
$errorMsg = 'Размер файла '. $fileName .' превышает 512 Кбайт.';
$validator->addError($key,$errorMsg);
$output = false; // возвращаем false
}
}
return $output;
У меня на сайте byantipov.ru есть блок с . Это, грубо говоря, опросник из нескольких шагов для точного расчета стоимости кухни. На последнем шаге есть окно с возможностью добавления файла, например, картинки. На самом последнем шаге, при нажатии на кнопку «Заказать» скриптом собираются значения всех инпутов, которые отправляются в соответствующие , которые уже дальше отправляются на почту и администратору и клиенту. Только файл не отправляется, вместо файла приходит только путь, по которому находится этот файл.
Подскажите, как можно сделать чтобы отправлялся именно файл.
Спасибо за Ваш труд, узнал у Вас много полезного.
У меня вопрос: при мультизагрузке все файлы загружаются и отправляются, но если поставить валидацию , сообщение об ошибке выводится, но после добавления файла(ов) не пропадает, и файлы не отправляются. В одиночной загрузке валидация работает прекрасно.
Весть код для теста скопировал с Вашего сайта, все по дефолту.
Не подскажете, где рыть? Буду очень благодарен Вам!
Собрал новую сборку с указанными выше версиями; оба варианта отрабатывают отлично. Как с использованием атрибута multiple, так и с использованием нескольких input с type, равным file. Может что-то где-то пропустили. Обратите внимание, что для проверки формы с одним input и несколькими используются разные валидаторы.
Версия PHP: 7.1.12
Спасибо Вам за быстрый ответ!
Может я не корректно описал проблему.
У меня тоже все работает, НО если не ставить В моей форме должны быть обязательно прикрепленные файлы.
Вы проверяли с ?
Извините, если морочаю Вам голову…
Версия PHP: 7.2.х64
Конструкции break заменил на continue, чтобы отображались сразу все ошибки, а не по одной.
Спасибо за статьи, очень помогают.
Вопрос по самой первой теме — при мультизагрузке загружается только последний прикрепленный файл. Думала может дело в моем коде, но скопировала ваш код — тоже не подгружаются все файлы. Хотя Formit переустановила на версию 2.2.11. Версия MODx 2.6.1. Что еще может быть причиной?
Вот так вывожу сниппет:
[[!AjaxForm?
&snippet=`FormIt`
&form=`lid-form`
&hooks=`FormItSaveForm,email`
&emailSubject=`Заявка с сайта`
&emailTo=`vladklevtsov@gmail.com`
&emailFrom=`no-reply@mysite.ru`
&emailFromName=`Сайт Get All`
&emailTpl=`email_tpl`
&validate=`name:required,phone:required`
&validationErrorMessage=`Пожалуйста, исправьте ошибки!`
&successMessage=`Ваше сообщение успешно отправлено`
]]
Подскажите, пожалуйста, в чем может быть причина? Почему не прикрепляется несколько файлов, а только один?
Если прикрепляю к форме файлы размером около 10 Мб, то в одном случае появляется сообщение об ошибке — «Не указан ключ формы (action).» или же форма просто зависает. В чем может быть причина?
Например, в файле .htaccess (до 30Мбайт):
Такую обработку необходимо выполнять в браузере (JavaScript). Т.е. после выбора файла вы проверяете его размер, и если он превышает допустимый, то выводите соответствующее сообщение и отменяете отправку формы на сервер.
Если использовать этот метод, то хук Хук saveFile уже не работает.
Мне необходима возможность отправлять файлы на почту и вдобавок сохранять файлы в папке и вытаскивать их имена:
а потом вытащить имена файлов
2. Создать чанк tpl.email.uploads (для оформления отдельной ссылки на файл в теле письма):
3. Добавить в tpl.email.attach плейсхолдер [[+uploadFiles]], в который будут выводиться ссылки на файлы:
4. Добавить перед email созданный хук:
А если есть способ еще и в приложение в админку saveFormIt туда картинки выводить — вообще красота будет, искал гуглом на эту тему — тоже тишина, неужели никому не надо такое?
&hooks=`FormItSaveForm,savefiles,gramm-biggestForm,email,redirect`
Делаю <img src="[[+pic]]" — не пашет. Делаю <img src=«cid:[[+pic]]» — та ж фигня.
Что еще попробовать, что бы на мэйлре и гмэйле норм отображалось??
Например, для элемента input с именем upload:
1. В HTML форме:
2. Создать сниппет, например, имеющим имя saveFile:
Данный снипет (хук) будет перемещать файл под уникальным именем в каталог /assets/uploads и устанавливать значения ключам upload (URL файла) и uploadName (имя файла).
3. Добавить в вызов сниппета AjaxForm только что созданный хук (saveFile) перед email:
4. В шаблон письма добавить:
Скажите, пользовались ли вы AjaxUpload? Есть рецепт присоединения его к AjaxForm, правда я его до конца еще не испробовал
Мне очень нравится, что он показывает что фото загружено, но не нравится, что он не прячет кнопку «Загрузить» после последнего загруженного файла из числа дозволенных, и еще он какой-то корявенький))) Там видно, что есть возможность загрузки драг-н-дропом, но она спрятана и не работает)))
А еще языки не переключаются...((
Но вы просто ниндзя, я такие хуки писать покамест не умею(
При желании, если не понравиться, можно же использовать и какой-то другой.
Еще один малюсенький вопросик, а как сделать так, что бы при этой нашей манипуляции загружаемый в форму файл из атача письма не пропадал? Потому что если удалить из набора хуков наш saveFile — то картинка в атаче письма есть, а если вернуть — картинка есть в теле письма через ссылку, но отсутствует в атаче((
Помогите пожалуйста!
Для тех кто пойдет моим путем — сообщаю — папку /assets/uploads нужно создавать вручную ;)
Заранее большое человеческое спасибо
Хук saveFile будет иметь следующий код:
В чанке письма изменить имена плейсхолдеров на следующие:
FormIt 2.2.11
Чтобы это отключить нужно к полю message добавить allowSpecialChars:
а после добавления вот так: