Загрузка формы на страницу с помощью AJAX

Создаётся узел структуры - Обратная связь (/zayavka/)
Подключается ТДС "Отображение формы".
Кнопка вызова формы.
<button
    class="btn btn-secondary btn-xl"
    type="button"
    onclick="
    return
     $.openForm('/zayavka/', 18  // здесь меняем ID формы
    )"
>Заполнить заявку</button>

PHP . Код настроек ТДС "Отображение формы"

<?php
// Добавленный код
if (Core_Array::getRequest('openForm') == "1") 
ob_start(); 
$form_id = intval(Core_Array::getRequest('form_id')); 
// ФОРМА
$oForm = Core_Entity::factory('Form', $form_id); 
$Form_Controller_Show = new Form_Controller_Show($oForm); 
if (!is_null(Core_Array::getPost($oForm->button_name))) 
$Form_Controller_Show 
->values($_POST + $_FILES)
// 0 - html, 1- plain text 
->mailType(0) 
->mailXsl( 
Core_Entity::factory('Xsl')->getByName(Core_Array::get(Core_Page::instance()->libParams, 'notificationMailXsl'))
->mailFromFieldName(Core_Array::get(Core_Page::instance()->libParams, 'emailFieldName'))
->process(); 
// echo "<pre>";
    //       print_r($_POST + $_FILES);
    //    echo "</pre>";
$Form_Controller_Show 
->xsl( 
Core_Entity::factory('Xsl')->getByName(Core_Array::get(Core_Page::instance()->libParams, 'formXsl'))
->show(); 
echo json_encode(ob_get_clean()); 
exit(); 
}
// end Добавленный код
// Page doesn't accept subpages, 404 error
$oCore_Page = Core_Page::instance();
if ($oCore_Page->structure->getPath() != Core::$url['path'])
{
$oCore_Page->error404();
}

XSL . Шаблон "ОтобразитьФормуЗаявкаВМодальномОкне" с отправкой изображений

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet SYSTEM "lang://351">
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hostcms="http://www.hostcms.ru/"
exclude-result-prefixes="hostcms">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml" />
<!-- ОтобразитьФорму -->
<xsl:template match="/">
<xsl:apply-templates select="/form" />
</xsl:template>
<xsl:template match="/form">
<xsl:choose>
<xsl:when test="success/node() and success = 1">
<!--div id="fade" style="display: block;"></div-->
<div class="modal fade form-success-result" id="modalSuccess" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<!--div class="modal-title w-100 fs-4 fw-bold">&labelModalTitle1;</div-->
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:value-of disable-output-escaping="yes" select="success_text" />
</div>
</div>
</div>
</div>
<script>
let modalSuccess = new bootstrap.Modal(document.getElementById('modalSuccess'));
modalSuccess.show();
</script>
</xsl:when>
<!-- Выводим ошибку (error), если она была передана через внешний параметр -->
<xsl:when test="error != ''">
<div class="modal fade form-success-result" id="modalError" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<div class="modal-title w-100 fs-3 fw-bold">ОШИБКА!</div>
<button class=" " type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body"><b><xsl:value-of disable-output-escaping="yes" select="error" /></b></div>
</div>
</div>
</div>
<script>
let modalError = new bootstrap.Modal(document.getElementById('modalError'));
modalError.show();
</script>
</xsl:when>
<xsl:when test="errorId/node()">
<div class="modal fade form-success-result" id="modalError" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<div class="modal-title w-100 fs-3 fw-bold">&labelERROR;</div>
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:choose>
<xsl:when test="errorId = 0">
&labelTextErrorId0;
</xsl:when>
<xsl:when test="errorId = 1">
&labelTextErrorId1;
</xsl:when>
<xsl:when test="errorId = 2">
&labelTextErrorId2;
</xsl:when>
</xsl:choose>
</div>
</div>
</div>
</div>
<script>
let modalError = new bootstrap.Modal(document.getElementById('modalError'));
modalError.show();
</script>
</xsl:when>
<xsl:otherwise>
<div class="modal fade" id="applicationModal_{@id}" tabindex="-1" aria-labelledby="applicationModalLabel_{@id}" aria-hidden="true">
<div class="modal-dialog  modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header justify-content-center">
<div class="h2 m-0 modal-title" id="applicationModalLabel_{@id}"><xsl:value-of disable-output-escaping="yes" select="name" /></div>
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:if test="description !=''">
<xsl:value-of disable-output-escaping="yes" select="description" />
</xsl:if>
<div id="add-text" class="text-center"> </div>
<!-- Параметр action формы должен быть "./", если обработчик на этой же странице, либо "./form/", если обработчик на другой странице, например ./form/ -->
<form name="form{@id}" id="form{@id}" class="validate" action="./" method="post" enctype="multipart/form-data" >
<!-- Вывод разделов формы 0-го уровня -->
<xsl:apply-templates select="form_field_dir" />
<!-- Вывод списка полей формы 0-го уровня -->
<xsl:apply-templates select="form_field" />
<!-- Код подтверждения -->
<xsl:if test="captcha_id != 0">
<div class="form-group mb-3">
<div class="row">
<div class="col-6 caption">
<input type="hidden" name="captcha_id" value="{/form/captcha_id}"/>
<input type="text" name="captcha" size="15" class="form-control required" minlength="4" title="Введите число, которое указано на картинке."/>
<div class="padding-5">&labelCheckNumber1;<sup><font color="red">*</font></sup></div>
</div>
<div class="col-6 field">
<img id="formCaptcha_{/form/@id}_{/form/captcha_id}" src="/captcha.php?id={captcha_id}&amp;height=30&amp;width=100" class="captcha" name="captcha" />
<div class="captcha">
<img src="/images/refresh.png" /> <span class="small" onclick="$('#formCaptcha_{/form/@id}_{/form/captcha_id}').updateCaptcha('{/form/captcha_id}', 30); return false" style="cursor:pointer">&labelCheckNumber2;</span>
</div>
</div>
</div>
</div>
</xsl:if>
<!--div class="margin-bottom-10">
<div class="g-recaptcha" data-sitekey="6LfemXMiAAAAAPtn9tHaYj5z-0yKJq4Tmi3Ph1Cw"></div>
</div-->
<xsl:if test="csrf_token/node() and csrf_token != ''">
<input type="hidden" name="{csrf_field}" value="{csrf_token}" />
</xsl:if>
<div class="form-group">
<input type="hidden" name="{button_name}" value="submit"/>
<button id="submit_{@id}" value="submit" name="{button_name}" type="submit" class="btn btn-black mt-4 w-100"><xsl:value-of select="button_value" /></button>
</div>
</form>
<div class="mt-4">
<div class="form-check">
<input id="checkbox{@id}" class="form-check-input" type="checkbox" name="checkbox" checked="checked" onchange="document.getElementById('submit_{@id}').disabled = !this.checked;"/>
<label for="checkbox{@id}" class="form-check-label small" style="text-align:justify">Я соглашаюсь на обработку своих персональных данных в соответствии с <a href="/private" class="link-border"> Политикой конфиденциальности</a></label>
</div>
</div>
</div>
</div>
</div>
</div>
<SCRIPT>
<xsl:comment>
<xsl:text disable-output-escaping="yes">
    <![CDATA[
    $(function() {
    $("#fileUpload").on('change', function () {
 
     //Get count of selected files
     var countFiles = $(this)[0].files.length;
     var imgPath = $(this)[0].value;
     var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
     var image_holder = $("#image-holder");
     image_holder.empty();
 
     if (extn == "png" || extn == "jpg" || extn == "jpeg" || extn == "webp") {
         if (typeof (FileReader) != "undefined") {
 
             //loop for each file selected for uploaded.
             for (var i = 0; i < countFiles; i++) {
 
                 var reader = new FileReader();
                 reader.onload = function (e) {
                     $("<img />", {
                         "src": e.target.result,
                             "class": "thumb-image"
                     }).appendTo(image_holder);
                 }
 
                 image_holder.show();
                 reader.readAsDataURL($(this)[0].files[i]);
             }
 
         } else {
             alert("Ваш брузер не поддерживает FileReader!");
         }
     } else {
         alert("Выберите файл в формате JPG, JPEG, PNG или WEBP!");
     }
    });
    });
]]>
</xsl:text>
</xsl:comment>
</SCRIPT>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="form_field_dir">
<!--fieldset class="maillist_fieldset"-->
<!--legend><xsl:value-of select="name" /></legend-->
<!-- Вывод списка полей формы -->
<xsl:apply-templates select="form_field" />
<!-- Вывод разделов формы -->
<xsl:apply-templates select="form_field_dir" />
<!--/fieldset-->
</xsl:template>
<xsl:template match="form_field">
<!-- Не скрытое поле и не надпись -->
<xsl:if test="type != 7 and type != 8">
<div class="form-group mb-3">
<xsl:if test="type = 2">
<xsl:attribute name="class">form-group mb-2 me-4</xsl:attribute>
</xsl:if>
<xsl:variable name="caption">
<xsl:choose>
<xsl:when test="obligatory = 1">
<xsl:value-of select="caption" />*
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="caption" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<!-- Радиокнопки -->
<xsl:when test="type = 3 or type = 9">
<xsl:apply-templates select="list/list_item" />
</xsl:when>
<!-- Checkbox -->
<xsl:when test="type = 4">
<div class="pretty p-default p-pulse">
<input type="checkbox" name="{name}">
<xsl:if test="checked = 1 or value = 1">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input>
<div class="state p-danger-o">
<label><xsl:value-of select="value" /></label>
</div>
</div>
</xsl:when>
<!-- Textarea -->
<xsl:when test="type = 5">
<textarea class="form-control" name="{name}" cols="{cols}" rows="{rows}" placeholder="{$caption}">
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">form-control required</xsl:attribute>
</xsl:if>
<xsl:value-of select="value" />
</textarea>
</xsl:when>
<!-- Список -->
<xsl:when test="type = 6">
<select name="{name}" class="wide">
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">wide required</xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="caption" /></xsl:attribute>
</xsl:if>
<option value="">...</option>
<xsl:apply-templates select="list/list_item" />
</select>
</xsl:when>
<!-- Текстовые поля -->
<xsl:otherwise>
<xsl:if test="type = 2">
<label for="fileUpload" class="download-file">
<i class="svg_icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="21px" height="21px" ><path style="fill:#797979" d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 336c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V112c0-8.822 7.178-16 16-16h416c8.822 0 16 7.178 16 16v288zM112 232c30.928 0 56-25.072 56-56s-25.072-56-56-56-56 25.072-56 56 25.072 56 56 56zm0-80c13.234 0 24 10.766 24 24s-10.766 24-24 24-24-10.766-24-24 10.766-24 24-24zm207.029 23.029L224 270.059l-31.029-31.029c-9.373-9.373-24.569-9.373-33.941 0l-88 88A23.998 23.998 0 0 0 64 344v28c0 6.627 5.373 12 12 12h360c6.627 0 12-5.373 12-12v-92c0-6.365-2.529-12.47-7.029-16.971l-88-88c-9.373-9.372-24.569-9.372-33.942 0zM416 352H96v-4.686l80-80 48 48 112-112 80 80V352z"/></svg>
</i>
<span class="download-file-text ps-2">Выберите сразу несколько фото</span>
</label>
</xsl:if>
<input class="form-control" type="text" name="{name}" value="{value}" size="{size}" placeholder="{$caption}">
<xsl:choose>
<!-- Поле для ввода пароля -->
<xsl:when test="type = 1">
<xsl:attribute name="type">password</xsl:attribute>
</xsl:when>
<!-- Поле загрузки файла -->
<xsl:when test="type = 2">
<xsl:attribute name="type">file</xsl:attribute>
<xsl:attribute name="multiple">true</xsl:attribute>
<xsl:attribute name="class">photo</xsl:attribute>
<xsl:attribute name="name">files[]</xsl:attribute>
<xsl:attribute name="id">fileUpload</xsl:attribute>
<xsl:attribute name="accept">image/jpg,image/jpeg,image/png,image/webp</xsl:attribute>
<div id="image-holder" class="d-flex flex-wrap"></div>
</xsl:when>
<!-- HTML5: Дата -->
<xsl:when test="type = 10">
<xsl:attribute name="type">date</xsl:attribute>
</xsl:when>
<!-- HTML5: Цвет -->
<xsl:when test="type = 11">
<xsl:attribute name="type">color</xsl:attribute>
</xsl:when>
<!-- HTML5: Месяц -->
<xsl:when test="type = 12">
<xsl:attribute name="type">month</xsl:attribute>
</xsl:when>
<!-- HTML5: Неделя -->
<xsl:when test="type = 13">
<xsl:attribute name="type">week</xsl:attribute>
</xsl:when>
<!-- HTML5: Время -->
<xsl:when test="type = 14">
<xsl:attribute name="type">time</xsl:attribute>
</xsl:when>
<!-- HTML5: Дата-Время -->
<xsl:when test="type = 15">
<xsl:attribute name="type">datetime</xsl:attribute>
</xsl:when>
<!-- HTML5: E-mail -->
<xsl:when test="type = 16">
<xsl:attribute name="type">email</xsl:attribute>
</xsl:when>
<!-- HTML5: Поиск -->
<xsl:when test="type = 17">
<xsl:attribute name="type">search</xsl:attribute>
</xsl:when>
<!-- HTML5: Телефон -->
<xsl:when test="type = 18">
<xsl:attribute name="type">tel</xsl:attribute>
</xsl:when>
<!-- HTML5: URL -->
<xsl:when test="type = 19">
<xsl:attribute name="type">url</xsl:attribute>
</xsl:when>
<!-- Текстовое поле -->
<xsl:otherwise>
<xsl:attribute name="type">text</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">form-control required</xsl:attribute>
</xsl:if>
<!--xsl:if test="@id = 15">
<xsl:attribute name="disabled">disabled</xsl:attribute>
</xsl:if-->
</input>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:if>
<!-- скрытое поле -->
<xsl:if test="type = 7">
<input type="hidden" name="{name}" value="{value}" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
//
//
// Заменить в строке
// <!DOCTYPE xsl:stylesheet SYSTEM "lang://351">
// ID 351  на  ID своего SL шаблона 
//
// Вставить во вкладку ru.dtd
//

<!ENTITY labelModalTitle1 "Благодарим вас за заявку!">
<!ENTITY labelModalTitle2 "Заявка получена.">
<!ENTITY labelSuccessText "Мы свяжемся с вами в ближайшее время">
<!ENTITY labelSuccessText2 "Наш менеджер ответит на все ваши вопросы">

<!ENTITY labelERROR "ОШИБКА!">
<!ENTITY labelTextErrorId0 "Вы неправильно ввели код подтверждения отправки формы!">
<!ENTITY labelTextErrorId1 "Заполните все обязательные поля!">
<!ENTITY labelTextErrorId2 "С момента отправки последней формы прошло слишком мало времени!">

<!ENTITY labelTextError1 "Это поле пустое">
<!ENTITY labelTextError2 "E-mail должен иметь вид name@domain.com">

<!ENTITY labelText1 "Обязательное поле">

<!ENTITY labelCheckNumber1 "Проверочный код">
<!ENTITY labelCheckNumber2 "показать другой код">

JavaScript / jQuery . Размещаем в макете сайта. Из XSL шаблонов валидацию форм убрать

// Функции без создания коллекции
$.extend({
// Загрузка форм заявок на ajax с валидацией
openForm: function(path, form_id, form_title) {
if ($('.form-success-result').length) {
$('.form-success-result').remove();
}
if ($('#applicationModal_'+form_id).length) {
$('#applicationModal_'+form_id).modal('show').appendTo('body');
}
else {
$.clientRequest({
path: path + '?openForm=1&form_id=' + form_id,
'callBack': function(data, status, jqXHR){
$.openFormCallback(data, status, jqXHR, path + '?openForm=1&form_id=' + form_id, form_id, form_title)
},
context: ''
});
}
return false;
},
openFormCallback: function(data, status, jqXHR, path, form_id, form_title) {
$.loadingScreen('hide');
var Url = window.location.pathname;
$('body').append(data);
$('#applicationModal_'+form_id).modal('show').appendTo('body');
$('input[name=current_url]').val('https://site.ru'+Url);
$('input[name=form_title]').val(form_title);
$('form#form'+form_id).validate({
focusInvalid: true,
errorClass: "input_error",
onkeyup: false,
onfocusout: false,
rules: {
name: "required",
phone: "required",
email: {
required: true,
email: true
}
},
messages: {
name: "Это поле не заполнено",
phone: "Это поле не заполнено",
email: {
required: "Это поле не заполнено",
email: "E-mail должен иметь вид name@domain.ru"
}
},
submitHandler: function(form) {
var data = new FormData(form);
jQuery.ajax({
url: path,
type: 'POST',
data: data,
dataType: 'json',
processData : false,
contentType : false,
success: function(data) {
$("body").append(data);
if ($('.form-success-result').length) {
$('#applicationModal_'+form_id).modal('hide');
console.log(this);
$('.form-success-result').modal('show').appendTo('body');
}
}
});
return false;
}
});
},
loadingScreen: function(method) {
// Method calling logic
if (methods[method] ) {
return methods[method].apply(this, Array.prototype.slice.call( arguments, 1 ));
} else {
$.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
}
},
clientRequest: function(settings) {
if (typeof settings.callBack == 'undefined')
{
alert('Callback function is undefined');
}
$.loadingScreen('show');
var path = settings.path,
data = (typeof settings.data != 'undefined') ? settings.data : {};
data['_'] = Math.round(new Date().getTime());
jQuery.ajax({
context: settings.context,
url: path,
type: 'POST',
data: data,
dataType: 'json',
success: settings.callBack
});
return false;
}
});
var methods = {
show : function() {
$('body').css('cursor', 'wait');
var fade_div = $('#ajaxLoader'), jWindow = $(window);
if (fade_div.length === 0)
{
fade_div = $('<div></div>')
.appendTo(document.body)
.hide()
.prop('id', 'ajaxLoader')
.css('z-index', '1500')
.css('position', 'absolute')
.append($('<img>').prop('src', '/hostcmsfiles/images/ajax_loader.gif'));
}
fade_div.show()
.css('top', (jWindow.height() - fade_div.outerHeight(true)) / 2 + jWindow.scrollTop())
.css('left', (jWindow.width() - fade_div.outerWidth(true)) / 2 + jWindow.scrollLeft());
},
hide : function( ) {
$('#ajaxLoader').hide().css('left', -1000);
$('body').css('cursor', 'auto');
}
};

Загрузка формы на страницу с помощью AJAX для редакции HostCMS Старт

Создаётся узел структуры - Обратная связь (/zayavka/)
Тип раздела выбираем "Динамическая страница".
Кнопка вызова формы, которая размещается в макете на любой странице сайта.
<button 
    type="button" 
    class="btn btn-transparent btn-lg" 
   onclick="return $.openForm('/zayavka/', 'Вопрос на тему - Ремонт и выкуп элитных  швейцарских часов в Москве')" 
   tabindex="0">Задать вопрос
</button>
Добавляем на сервер в папку, например, /mform, обработчик отправки формы (в также него добавлена возможность отправки вложенных файлов) send-zakaz.php
<?php
$to  = "info@test.com";
$email = "zayavka@test.com";

if ( isset( $_POST['phone'] ) ) {
  $username= substr( $_POST['username'], 0, 40 );
  $phone = substr( $_POST['phone'], 0, 15 );
  $thema = substr( $_POST['thema'], 0, 250);
  $mess = substr( $_POST['mess'], 0, 250);

  $body = "Имя: ".$username."\r\n";
  $body  .= "Телефон: ".$phone."\r\n";
  $body  .= "Тема: ".$thema."\r\n\r\n";
  $body  .= "Сообщение: ".$mess."\r\n\r\n";

  send_mail($to, $body, $email);
}

// Вспомогательная функция для отправки почтового сообщения с вложением
function send_mail($to, $body, $email)
{
  $subject = 'Пользователь сайта test.com заполнил форму Заявки';
  $boundary = "--".md5(uniqid(time())); // генерируем разделитель
  $headers .= "From: ".$email."\r\n";   
  $headers .= "MIME-Version: 1.0\r\n";
  $headers .="Content-Type: multipart/mixed; boundary=\"".$boundary."\"\r\n";
  $multipart = "--".$boundary."\r\n";
  $multipart .= "Content-type: text/plain; charset=\"utf-8\"\r\n";
  $multipart .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";


  $body = $body."\r\n\r\n";
  $multipart .= $body;


  mail($to, $subject, $multipart, $headers);
}
?>

В Основном макете, в самом низу, добавляем код модального окна, в которое будет загружаться наша форма

    <div class="modal fade" id="applicationModal" tabindex="-1" aria-labelledby="applicationModalLabel" aria-modal="true" role="dialog">
        <div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
            <div class="modal-content">
                <div class="modal-header justify-content-center">
                    <div class="h4 m-0 modal-title" id="applicationModalLabel">Заявка</div>
                    <button class="btn-close" type="button" data-bs-dismiss="modal"></button>
                </div>

                <div id="modalContent" class="modal-body"></div>
            </div>
        </div>           
     </div>

PHP . В поле "Динамическая страница" вставляем следующий код формы

    <div id="formUpload">

        <form method="get" id="form2" action="./">
                    <input type="hidden" name="thema" id="thema_2">
                    <div class="mb-3">
                        <input type="text" name="username" class="form-control" maxlength="40" placeholder="Ваше Имя *">
                    </div>
                    <div class="mb-3">
                        <input name="phone" class="form-control" maxlength="15" placeholder="Телефон *" type="text">
                    </div>
                    <div class="mb-3">
                        <textarea name="mess" class="form-control" rows="2" maxlength="300" placeholder="Ваш запрос *" type="text"></textarea>
                    </div>
                    
                    <p>Можете приложить несколько фото высокого разрешения в разных ракурсах</p>
                    <div id="image-holder_2" class="d-flex flex-wrap"></div>
                    <div class="row">
                        <div class="col-12 col-sm-6">
                            <input class="file-upload d-none" type="file" name="files[]" value="" placeholder="Загрузить фото" multiple="true" accept="image" id="fileUpload_2" data-id="2" accepts=".jpg, .jpeg, .png, .webp">
                            <label for="fileUpload_2" class="download-file btn btn-transparent primary">Добавить фото</label>
                        </div>
                        <div class="col-12 col-sm-6 text-end">
                             <button type="submit" id="submit_2" autocomplete="off" class="btn btn-transparent btn-lg primary" disabled>Отправить заявку</button>
                        </div>
                    </div>
                   
                   <div class="mt-4">
                       <div class="form-check">
                          <input id="checkbox_2" class="form-check-input" type="checkbox" name="checkbox" onchange="document.getElementById('submit_2').disabled = !this.checked;">
                             <label for="checkbox_2" class="form-check-label small">Я соглашаюсь на обработку своих персональных данных в соответствии с <a href="/private/" class="link-border"> Политикой конфиденциальности</a></label>
                        </div>
                    </div>
        </form>
                
  </div>

JavaScript / jQuery . Размещаем в макете сайта во вкладке Javascript обработчик AJAX загрузки формы

/

// Функции без создания коллекции
$.extend({
    
  // Загрузка форм заявок на ajax с валидацией
    openForm: function(path, form_title) {
      $("#modalContent").load(path + " #formUpload", function(response){
        // Если ответ не пустой, открываем модальное окно
        if (response) {
            $('#applicationModal').modal('show').appendTo('body');
            $('#thema_2').text(form_title);
            $('#form2').validate({
  rules: {
    phone: "required",
    mess: "required"
  },
  messages: {
    mess: "Заполните это поле",
    phone: "Заполните это поле"
  },
  focusInvalid: true,
  errorClass: "input_error",
  submitHandler: function(form){
        var form = document.forms.form2,
        formData = new FormData(form),
        xhr = new XMLHttpRequest();
        xhr.open("POST", "/mform/send-zakaz.php");
        xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
          if(xhr.status == 200) {
            $("#form2").html('<p style="text-align:center">Ваша Заявка отправлена!<p>');
                setTimeout(function(){
                    location.reload();
                    }, 1000);
     
          }
        }
      };
      xhr.send(formData);
    }
    });
    

    $("#fileUpload_2").on('change', function () {
     //Get count of selected files
     var dataID = $(this).data('id');
     var countFiles = $(this)[0].files.length;
     var imgPath = $(this)[0].value;
     var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
     var image_holder = $("#image-holder_" + dataID);
     console.log(dataID);
     image_holder.empty();
 
     if (extn == "png" || extn == "jpg" || extn == "jpeg" || extn == "webp") {
         if (typeof (FileReader) != "undefined") {
 
             //loop for each file selected for uploaded.
             for (var i = 0; i < countFiles; i++) {
 
                 var reader = new FileReader();
                 reader.onload = function (e) {
                     $("<img />", {
                         "src": e.target.result,
                             "class": "thumb-image"
                     }).appendTo(image_holder);
                 }
 
                 image_holder.show();
                 reader.readAsDataURL($(this)[0].files[i]);
             }
 
         } else {
             alert("Ваш брузер не поддерживает FileReader!");
         }
     } else {
         alert("Выберите файл в формате JPG, JPEG, PNG или WEBP!");
     }
    });
        }
    });
    },

............. остальной код

}

Использование jQuery Validate для проверки форм

Рабочая валидация с проверкой captcha
<script>
      $(function() {
            $(".validate").validate({
            focusInvalid: true,
            errorClass: "input_error",
            onkeyup: false,
            onfocusout: false,
            rules: {
            captcha: {
            required: true,
            remote: '/forms/'
            },
            author: "required",
             text: "required",
             email: {
                   required: true,
                   email: true
                  }
               },
               messages: {
                       author: "Пожалуйста, укажите своё Имя!",
                       text: "Пожалуйста, напишите свой комментарий!",
                       captcha: "Вы не вписали контрольное число!",
                       email: {
                       required: "Введите свой email",
                       email: "E-mail должен быть в формате name@domain.com"
                         }
            }});
        });
   </script>
Проверьте, подключен ли в макете файл jquery.validate.min.js, в системе управления он расположен в hostcmsfiles/jquery/jquery.validate.min.js
Core_Page::instance()
// jQuery
->js('/hostcmsfiles/jquery/jquery.min.js')
...
// Validate
->js('/hostcmsfiles/jquery/jquery.validate.min.js')
...
->showJs();

Указание валидации формы

Валидация конкретной формы или форм с классом validate указывается следующим образом:
<script>
$(".validate").validate();
</script>
Валидация всех форм:

<script>
$("form").validate();
</script>

Способы валидации форм

Использование имена классов как правила

Тем полям, которые нужно проверять, добавляете атрибут class="required"
<form action="." method="post" class="validate">
    <input type="text" name="name" value="" class="required" />
    <input type="submit" value="Submit" />
</form>

С помощью метод addClassRules вы можете расширить и добавить условие валидации:
<form action="." method="post" class="validate">
    <input type="text" name="name" value="" class="name" />
    <input type="text" name="zip" value="" class="zip" />
    <input type="submit" value="Submit" />
</form>
Проверка скриптом
<script >
$.validator.addClassRules("name", {
    required: true,
    minlength: 2
});
</script >
или сразу для нескольких полей:
<script >
$.validator.addClassRules({
name: {
required: true,
minlength: 2
},
zip: {
required: true,
digits: true,
minlength: 5,
maxlength: 5
}});
</script >

Передача опций при инициализации validate()

Методу validate() передаем объекты rules и messages которые состоят из пар ключ/значение. В rules в качестве ключа указываем атрибут name поля, значением указываем правило проверки. В messages в качестве ключа так же идет атрибут name поля, а в качестве значения указываем предупреждающее сообщение.
<script >
$(".validate").validate({
rules: {
name: "required",
email: {
required: true,
email: true
}
},
messages: {
name: "Please specify your name",
email: {
required: "Введите свой email",
email: "E-mail должен быть в формате name@domain.com"
}
}});
</script >
Список правил:
  • required — поле обязательное для заполнения (true или false);
  • remote — указывается файл для проверки поля (например: "check.php");
  • email — проверяет корректность e-mail адреса (true или false);
  • url — проверяет корректность url адреса (true или false);
  • date — проверка корректности даты (true или false);
  • dateISO — проверка корректности даты ISO (true или false);
  • number — проверка на число (true или false);
  • digits — только цифры (true или false);
  • creditcard — корректность номера кредитной карты (true или false);
  • equalTo — равное чему-то (например другому полю equalTo: "#pswd");
  • accept — проверка на правильное расширение (accept: "xls|csv");
  • maxlength — максимальное кол-во символов;
  • minlength — минимальное кол-во символов;
  • rangelength — кол-во символов от скольких и до скольких (rangelength: [2, 5]);
  • range — число должно быть в диапазоне от и до (range: [2, 12]);
  • max — максимальное значение числа;
  • min — минимальное значение числа.

Пример использования в XSL-шаблоне

Указываем форме класс например validate, и далее в XSL-шаблоне формы добавляем код проверки формы (пример взят из формы быстрого заказа в адаптивном шаблоне):
<script >
$(".validate").validate({
rules: {
surname: "required",
name: "required",
email: {
required: true,
email: true
}
},
messages: {
surname: "Введите фамилию!",
name: "Введите имя!",
email: {
required: "Введите e-mail!",
email: "Адрес должен быть вида name@domain.com"
}
},
focusInvalid: true,
errorClass: "input_error"
});
</script>
Проверка валидации и заблокированного E-mail нежелательного Пользователя
<SCRIPT>
            $(document).ready(function() {
            $('.select2').addClass('required');
            $("#form<xsl:value-of select="@id" />").submit(function (event) {
            event.preventDefault();
            var eml = $('input[name=email]').val();
            if(eml =='svinya.svintus@yandex.ru') {
            alert ('Вы не можете отправлять нам письма');
            $(location).attr('href', '/');
            }else{
            
            $(this).validate({
            focusInvalid: true,
            errorClass: "input_error"
            });
            }
            });
            });
        </SCRIPT>

Модальное окно с формой подписки и сохранением куки на jQuery

Добавляем в макет код показа формы:

<?php
        // add ФОРМА ПОДПИСКА ЗА СКИДКУ
     $oForm = Core_Entity::factory('Form', 49);  // заменить ID формы
      
     $Form_Controller_Show = new Form_Controller_Show($oForm);
      
     if (!is_null(Core_Array::getPost($oForm->button_name)))
     {
     $Form_Controller_Show
     ->values($_POST + $_FILES)
     // 0 - html, 1- plain text
     ->mailType(0)
     ->mailXsl(
     Core_Entity::factory('Xsl')->getByName('ПисьмоКураторуФормыВФорматеHTML')
     )
     ->mailFromFieldName('email')
     ->process();
     }
      
     $Form_Controller_Show
     ->xsl(
     Core_Entity::factory('Xsl')->getByName('ОтобразитьФормуВМодальномОкне')
     )
      ->show();
?>

Подключаем в макете скрипт jquery.cookie.min.js:

<script src="/js/jquery.cookie.min.js"></script>

Проверяем куки:

<script>
$(document).ready(function($) {
if ($.cookie('was') == null) {
// Покажем всплывающее окно
setTimeout(function(){$('#popupModal').modal('show');}, 36000);
}
// Запомним в куках, что посетитель к нам уже заходил
$.cookie('was', 'value', { expires: 2, path: '/' });
});
</script>

JavaScript / jQuery . jquery.cookie.min.js

jQuery.cookie=function(b,j,m){if(typeof j!="undefined"){m=m||{};if(j===null){j="";m.expires=-1}var e="";if(m.expires&&(typeof m.expires=="number"||m.expires.toUTCString)){var f;if(typeof m.expires=="number"){f=new Date();f.setTime(f.getTime()+(m.expires*24*60*60*1000))}else{f=m.expires}e="; expires="+f.toUTCString()}var l=m.path?"; path="+(m.path):"";var g=m.domain?"; domain="+(m.domain):"";var a=m.secure?"; secure":"";document.cookie=[b,"=",encodeURIComponent(j),e,l,g,a].join("")}else{var d=null;if(document.cookie&&document.cookie!=""){var k=document.cookie.split(";");for(var h=0;h<k.length;h++){var c=jQuery.trim(k[h]);if(c.substring(0,b.length+1)==(b+"=")){d=decodeURIComponent(c.substring(b.length+1));break}}}return d}};

ФОРМА "ЗАКАЗАТЬ ЗВОНОК" В МОДАЛЬНОМ ОКНЕ НА BOOTSTRAP

Вызов формы
<button type="button" class="button" data-bs-toggle="modal" data-bs-target="#basicModal" >
         <i class="fa fa-phone"></i> <span>Заказать звонок</span>
</button>

Добавить в Макет

<div class="modal fade" id="basicModal" tabindex="-1" role="dialog">
     <div class="modal-dialog">
       <div class="modal-content">
          <div class="modal-header"><button class="close" type="button" data-dismiss="modal">x</button> </div>
        <div class="modal-body">

      <?php
        // add ФОРМА ЗАКАЗАТЬ ЗВОНОК
        $oForm = Core_Entity::factory('Form', 5);  // Заменить id формы
        $Form_Controller_Show = new Form_Controller_Show($oForm);
       if (!is_null(Core_Array::getPost($oForm->button_name)))
          {
         $Form_Controller_Show
         ->values($_POST + $_FILES)
         // 0 - html, 1- plain text
         ->mailType(1)
         ->mailXsl(
            Core_Entity::factory('Xsl')->getByName('ПисьмоКураторуФормы')
         )
         ->mailFromFieldName('mail@spartika.ru')
         ->process();

        // Отправляем письмо-подтверждение пользователю
        $Form_Controller_Show1 = clone $Form_Controller_Show;
        $sEmail = Core_Array::get($Form_Controller_Show1->values, 'email');
        if (Core_Valid::email($sEmail)){
            ob_start();
            $Form_Controller_Show1
                ->xsl(
                    Core_Entity::factory('Xsl')->getByName($Form_Controller_Show1->mailType == 0 ? 'ПисьмоПодтверждениеПользователюФормыВФорматеHTML' : 'ПисьмоПодтверждениеПользователюФормы')
                )
                ->show();
            $sMailText = ob_get_clean();
            if (mb_strpos($sMailText, 'ERROR TRUE') === FALSE){
                $subject = 'Вами была заполнена форма обратного звока на сайте  jvern.club';// Тема письма отредактировать
                // При текстовой отправке нужно преобразовать HTML-сущности в символы
                $Form_Controller_Show->mailType == 1 && $sMailText = html_entity_decode(strip_tags($sMailText), ENT_COMPAT, 'UTF-8');
                $oCore_Mail = Core_Mail::instance()
                    ->to($sEmail)
                    ->from(EMAIL_TO)
                    ->subject($subject)
                    ->message(trim($sMailText))
                    ->contentType($Form_Controller_Show->mailType == 0 ? 'text/html' : 'text/plain')
                    ->header('X-HostCMS-Reason', 'Form');
                // Массив содержащий пути прикрепленных файлов и их имена
                $aForm_Fields = $oForm->Form_Fields->findAll();
                foreach ($aForm_Fields as $oForm_Field){
                    if ($oForm_Field->type == 2){// File
                        $value = Core_Array::get($Form_Controller_Show->values, $oForm_Field->name);
                        if (!is_null($value)){
                            if (is_array($value) && $value['size'] > 0){
                                $oForm_Fill_Fields = Core_Entity::factory('Form_Fill_Field');
                                $oForm_Fill_Fields->queryBuilder()
                                    ->where('form_field_id', '=', $oForm_Field->id)
                                    ->where('value', '=', $value['name'])
                                    ->limit(1);
                                $aForm_Fill_Fields = $oForm_Fill_Fields->findAll();
                                if (isset($aForm_Fill_Fields[0])){
                                    $oCore_Mail->attach(array(
                                        'filepath' => $aForm_Fill_Fields[0]->getPath(),
                                        'filename' => $value['name']
                                    ));
                                }
                            }
                        }
                    }
                }

                $oCore_Mail->send();
            }
        }
        // END add
              }
                $Form_Controller_Show
                  ->xsl(
                  Core_Entity::factory('Xsl')->getByName('ОтобразитьФорму')
                 )
                  ->show();
              ?>
        </div>
    </div>
  </div>
</div>

XSL

// XSL-шаблон "ПисьмоПодтверждениеПользователюФормы"
<xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hostcms="http://www.hostcms.ru/"
exclude-result-prefixes="hostcms">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="form/success/node() and form/success = 1"><xsl:apply-templates select="/form"/></xsl:when>
<xsl:otherwise>ERROR TRUE</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match='/form'>
<p><b>Здравствуйте, <span><xsl:value-of disable-output-escaping="yes" select="form_field[@id=1]/value" /></span>!</b></p>
<p>Вы заполнили Форму на сайте egeriya.ru</p>
<!-- Вывод разделов формы 0-го уровня -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
<!-- Вывод списка полей формы 0-го уровня -->
<xsl:apply-templates select="form_field[value != '']" />
<p>В ближайшее время с Вами свяжется наш менеджер.<br />
Благодарим за доверие!</p>
<p>Команда интернет-магазина "Эгерия"</p>
<p><a href="http://egeriya.ru">egeriya.ru</a></p>
<xsl:value-of select="nameg"/>
</xsl:template>
<xsl:template match="form_field_dir">
- <xsl:value-of select="name" />
<!-- Вывод списка полей формы -->
<xsl:apply-templates select="form_field[value != '']" />
<!-- Вывод разделов формы -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="form_field">
<xsl:value-of select="caption"/>:<xsl:text> </xsl:text><xsl:choose><xsl:when test="values/node()"><xsl:apply-templates select="values/value"/></xsl:when><xsl:otherwise><xsl:value-of select="value" /></xsl:otherwise></xsl:choose><xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="values/value"><xsl:variable name="currentValue" select="." /><xsl:value-of select="../../list/list_item[@id=$currentValue]/value"/><xsl:if test="position() != last()"><xsl:text>, </xsl:text></xsl:if></xsl:template>
</xsl:stylesheet>
//XSL-шаблон "ПисьмоПодтверждениеПользователюФормыВФорматеHTML"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hostcms="http://www.hostcms.ru/"
exclude-result-prefixes="hostcms">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="form/success/node() and form/success = 1"><xsl:apply-templates select="/form"/></xsl:when>
<xsl:otherwise>ERROR TRUE</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/form">
<p><b><xsl:value-of disable-output-escaping="yes" select="name"/></b></p>
<p>Вы заполнили Форму на сайте egeriya.ru</p>
<!-- Вывод разделов формы 0-го уровня -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
<!-- Вывод списка полей формы 0-го уровня -->
<xsl:apply-templates select="form_field[value != '']" />
</xsl:template>
<xsl:template match="form_field_dir">
<p><b><xsl:value-of select="name" /></b></p>
<!-- Вывод списка полей формы -->
<xsl:apply-templates select="form_field[value != '']" />
<!-- Вывод разделов формы -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
</xsl:template>
<xsl:template match="form_field">
<p>
<b>
<xsl:value-of select="caption" disable-output-escaping="yes"/>
</b>:
<xsl:choose>
<xsl:when test="values/node()">
<xsl:apply-templates select="values/value"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="value" />
</xsl:otherwise>
</xsl:choose>
</p>
</xsl:template>
<xsl:template match="values/value"><xsl:variable name="currentValue" select="." /><xsl:value-of select="../../list/list_item[@id=$currentValue]/value"/><xsl:if test="position() != last()"><xsl:text>, </xsl:text></xsl:if></xsl:template>
</xsl:stylesheet>