Анти-спам или как на странице сайта скрыть код формы от ботов?


Не ошибусь, если скажу, что практически все владельцы сайтов сталкиваются с такой проблемой, как СПАМ. Это просто кошмар, когда ваш почтовый ящик ежедневно наполняется сотнями, а то и тысячами СПАМ-писем. В таком объёме мусорной корреспонденции очень легко потерять и даже удалить нужную и важную информацию или заказ. При этом, практически ничего не помогает избавиться от ботов назойливых спамеров. Использование обычной каптчи и reCaptcha от Google не помогает, не помогают и различные "фишки", которые рассчитаны на использование javascript. Хотя боты и научились выполнять код Javascript, в подавляющем большинстве случаев они ищут на странице тег <form>. Вот эту их особенность мы и будем использовать.
Я не буду в этой остатье освещать тему, что нужно создавать разные почтовые ящики для обычных заявок и заказов магазина, не показывая эту почту на сайте и более нигде в интернете, ну и т.д. Я хочу сейчас предложить идею, как скрыть форму от ботов для владельцев сайтов на HostCMS. На других CMS такое тоже возможно, а может быть уже где-то и реализовано. Идея мной уже опробована на сайтах клиентов и работает на 100%. С помощью этого решения форму можно подгружать на любую страницу и это может быть "Заказ обратного звонка", "Форма заявки" и любая другая форма коммуникации с Пользователем. Данное решение возможно использовать только на платных версиях, начиная с HostCMS Малый Бизнес, где есть нужный нам модуль "Формы". Для бесплатных версий не делалось.
Поскольку сама интеграция решения является платной услугой, то я опишу только теоретическую часть. Программистам не сложно будет самим реализовать эту идею. Запросить подробное описание с кодом или сделать заявку на реализацию этого решения на вашем сайте можно через форму обратной связи. Предвосхищая вопросы скажу, что для себя это решение пока не использовали в виду отсутствия спама от ботов. От спамеров, работающих "ручками" оно, естественно, не поможет.
Итак перейдём к делу. Как же сделать так, чтобы кода формы не было в дереве DOM после загрузки страницы и она бы подгружалась только тогда, когда её "вызовет" Пользователь, например, кликом на соответствующую кнопку. Пошагово:
1. Создадим узел структуры и назовём его, например, "Обратная связь" (путь условно - /form/). Подключим к структуре стандартный ТДС "Отображение формы" и XSL-шаблоны. В самое начало кода настроек ТДС добавляется обработчик, который на ajax загружает форму в момент обращения к ней. Фрагмент кода:
if (Core_Array::getRequest('openForm') == "1")
{
ob_start();
.... echo json_encode(ob_get_clean());
exit();
}
2. Поскольку форма у нас будет подгружаться в модальном окне, немного доработаем стандартный XSL-шаблон "Отобразить форму". Для показа модального окна воспользуемся библиотекой Bootstrap (если не используете эту библиотеку, то можете добавить стили и скрипт для создания своих модальных окон) и добавим соответствующие теги. Для показа ответа сервера, в случае ошибки или удачной отправки формы, я не стал использовать Bootstrap, а просто добавил немного стилей и несколько строк jQuery. Вы можете усовершенствовать моё решение и использовать только Bootstrap, например.
Свой вариант 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: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-message form-success-result"> <div class="modal-content"> <div class="modal-header text-center"><button class="close" type="button" data-dismiss="modal"></button> <h5 class="modal-title">Спасибо! <br /> Ваша заявка принята.</h5> </div> <div class="box-content text-center"> <div class="margin-bottom-10">Мы свяжемся с вами в ближайшее время</div> </div> </div> </div> </xsl:when> <!-- Выводим ошибку (error), если она была передана через внешний параметр --> <xsl:when test="error != ''"> <div id="fade" style="display: block;"></div> <div class="modal-message"> <div class="modal-content"> <div class="modal-header text-center"><button class="close" type="button" data-dismiss="modal"></button> <h4 class="modal-title">ОШИБКА!</h4> </div> <div class="box-content text-center"><b><xsl:value-of disable-output-escaping="yes" select="error" /></b></div> </div> </div> </xsl:when> <xsl:when test="errorId/node()"> <div id="fade" style="display: block;"></div> <div class="modal-message"> <div class="modal-content"> <div class="modal-header text-center"><button class="close" type="button" data-dismiss="modal"></button> <h4 class="modal-title">ОШИБКА!</h4> </div> <div class="box-content text-center"> <xsl:choose> <xsl:when test="errorId = 0"> Вы неверно ввели число подтверждения отправки формы! </xsl:when> <xsl:when test="errorId = 1"> Заполните все обязательные поля! </xsl:when> <xsl:when test="errorId = 2"> Прошло слишком мало времени с момента последней отправки Вами формы! </xsl:when> </xsl:choose> </div> </div> </div> </xsl:when> <xsl:otherwise> <!-- Проверка формы --> <SCRIPT> $(function() { var myForm<xsl:value-of select="/form/@id" /> = $("#form<xsl:value-of select="/form/@id" />"); $(myForm<xsl:value-of select="/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", email: "E-mail должен быть в формате name@domain.com" } }}); }); </SCRIPT> <div class="modal fade" id="zayavkaModal_{@id}" tabindex="-1" role="dialog"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button class="close" type="button" data-dismiss="modal"></button> <h4 class="modal-title"><xsl:value-of disable-output-escaping="yes" select="name" /></h4> <p> Наш менеджер ответит вам в самое ближайшее время </p> <xsl:value-of disable-output-escaping="yes" select="description" /> <div class="note margin-top-10"><sup>*</sup> поля, обязательные для заполнения</div> </div> <div class="modal-body"> <!-- Параметр action формы должен быть "./", если обработчик на этой же странице, либо "./form/", если обработчик на другой странице, например ./form/ --> <form name="form{@id}" id="form{@id}" class="validate fast_form" action="./" method="post" enctype="multipart/form-data" > <div class="row"> <!-- Вывод разделов формы 0-го уровня --> <xsl:apply-templates select="form_field_dir" /> <!-- Вывод списка полей формы 0-го уровня --> <xsl:apply-templates select="form_field" /> </div> <div class="row form-group"> <div class="small margin-bottom-20"> <input id="checkbox{@id}" type="checkbox" name="checkbox" checked="checked" onchange="document.getElementById('submit_{@id}').disabled = !this.checked;"/> <label for="checkbox{@id}"><span class="grey">Отправляя Заявку, я даю согласие на обработку своих персональных данных, согласно <a href="/private"> Политики конфиденциальности</a></span></label> </div> <input type="hidden" name="{button_name}" value="submit"/> <button id="submit_{@id}" value="submit" name="{button_name}_submit" type="submit" class="btn btn-primary full-width">Отправить</button> </div> </form> </div> </div> </div> </div> </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"> <xsl:if test="type != 5"> <div class="form-group"> <!-- Текстовые поля --> <xsl:if test="type = 0 or type = 1 or type = 2"> <input type="text" name="{name}" value="{value}" size="{size}" placeholder="{caption}" class="form-control"> <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: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:attribute name="minlength">1</xsl:attribute> <xsl:attribute name="title">Заполните поле <xsl:value-of disable-output-escaping="yes" select="caption" /></xsl:attribute> </xsl:if> </input> </xsl:if> <!-- Радиокнопки --> <xsl:if test="type = 3 or type = 9"> <xsl:apply-templates select="list/list_item" /> <label class="input_error" for="{name}" style="display: none">Выберите, пожалуйста, значение.</label> </xsl:if> <!-- Checkbox --> <xsl:if test="type = 4"> <input type="checkbox" name="{name}"> <xsl:if test="checked = 1 or value = 1"> <xsl:attribute name="checked">checked</xsl:attribute> </xsl:if> </input> </xsl:if> <!-- Список --> <xsl:if test="type = 6"> <select name="{name}"> <xsl:if test="obligatory = 1"> <xsl:attribute name="class">required</xsl:attribute> <xsl:attribute name="title">Заполните поле <xsl:value-of disable-output-escaping="yes" select="caption" /></xsl:attribute> </xsl:if> <option value="">...</option> <xsl:apply-templates select="list/list_item" /> </select> </xsl:if> </div> </xsl:if> <!-- Textarea --> <xsl:if test="type = 5"> <div class="form-group"> <textarea name="{name}" cols="{cols}" rows="{rows}" wrap="off" class="form-control" placeholder="{caption}"> <xsl:if test="obligatory = 1"> <xsl:attribute name="class">form-control required</xsl:attribute> <xsl:attribute name="minlength">1</xsl:attribute> <xsl:attribute name="title">Заполните поле <xsl:value-of disable-output-escaping="yes" select="caption" /></xsl:attribute> </xsl:if> <xsl:value-of select="value" /> </textarea> </div> </xsl:if> </xsl:if> <!-- скрытое поле --> <xsl:if test="type = 7"> <input type="hidden" name="{name}" value="{value}" /> </xsl:if> <!-- Надпись --> <xsl:if test="type = 8"> <div class="form-group"> <div id="{name}" class="field white"> <strong><xsl:value-of disable-output-escaping="yes" select="caption" />:</strong> <span></span> </div> </div> </xsl:if> </xsl:template> <!-- Формируем радиогруппу или выпадающий список --> <xsl:template match="list/list_item"> <xsl:choose> <xsl:when test="../../type = 3"> <input id="{../../name}_{@id}" type="radio" name="{../../name}" value="{value}"> <xsl:if test="value = ../../value"> <xsl:attribute name="checked">checked</xsl:attribute> </xsl:if> <xsl:if test="../../obligatory = 1"> <xsl:attribute name="class">required</xsl:attribute> <xsl:attribute name="minlength">1</xsl:attribute> <xsl:attribute name="title">Заполните поле <xsl:value-of disable-output-escaping="yes" select="caption" /></xsl:attribute> </xsl:if> </input><xsl:text> </xsl:text> <label for="{../../name}_{@id}"><xsl:value-of disable-output-escaping="yes" select="value" /></label> <br/> </xsl:when> <xsl:when test="../../type = 6"> <option value="{value}"> <xsl:if test="value = ../../value"> <xsl:attribute name="selected">selected</xsl:attribute> </xsl:if> <xsl:value-of disable-output-escaping="yes" select="value" /> </option> </xsl:when> <xsl:when test="../../type = 9"> <xsl:variable name="currentValue" select="@id" /> <input id="{../../name}_{@id}" type="checkbox" name="{../../name}_{@id}" value="{value}"> <xsl:if test="../../values[value=$currentValue]/node()"> <xsl:attribute name="checked">checked</xsl:attribute> </xsl:if> <xsl:if test="../../obligatory = 1"> <xsl:attribute name="class">required</xsl:attribute> <xsl:attribute name="minlength">1</xsl:attribute> <xsl:attribute name="title">Заполните поле <xsl:value-of disable-output-escaping="yes" select="caption" /></xsl:attribute> </xsl:if> </input><xsl:text> </xsl:text> <label for="{../../name}_{@id}"><xsl:value-of disable-output-escaping="yes" select="value" /></label> <br/> </xsl:when> </xsl:choose> </xsl:template> </xsl:stylesheet>
CSS стили для показа модального окна при ошибках отправки или подтверждения:
.modal-message {
height: 325px;
width: 360px;
position: fixed;
opacity: 1;
overflow: visible;
left: 50%;
z-index: 1101;
vertical-align: middle;
top: 100px;
margin-left: -180px;
display: block;
color: #fff;
}
.modal-message .modal-content {
border-radius: 0;
box-shadow: 0 0 12px rgba(0,0,0,0.2);
position: relative !important;
width: 100%;
max-width: 500px !important;
background-color:#fff;
border: 0;
padding: 45px;
margin: 3% auto !important;
color: #666;
}
.modal-header {
padding: 20px 30px 0;
margin-bottom: 10px;
border:0;
} #fade {
background: #000000;
background: transparent;
background: rgba(0,0,0,0.4);
display: none;
height: 100%;
left: 0;
position: fixed;
top: 0;
width: 100%;
z-index: 1100;
}
И немного jQuery, чтобы закрыть это модальное окно:
$('body').click('#fade', function() {
$('.modal-message, #fade').hide();
$('.modal-message, #fade').remove();
});
$('body').click('.modal-message .close', function() {
$('#fade').hide();
$('#fade').remove();
var message = $(this).closest('.modal-message');
message.hide();
message.remove();
});
3. В Основной макет сайта, во вкладку JS, добавляем функцию openForm
, которая у нас будет отслеживать клик по кнопке и отправлять запрос к созданному в п.1 узлу структуры с формой. В ответе мы как раз и получим код формы.
// Функции без создания коллекции4. И сама кнопка, которая вызовет функцию
$.extend({
// Загрузка форм заявок на ajax
openForm: function(path, form_id, item_name, mess) {
... return false; }); } });
openForm
:
<button class="btn btn-secondary" type="button" onclick="return $.openForm('/form/', 1,)">Заказать звонок</button>
В параметрах функции указыаем путь к узлу структуры с обработчиком и ID формы, которую нам нужно загрузить на страницу и которая созданна в модуле "Формы". На одной странице может быть несколько кнопок для вызова одной и той же формы или разных форм. Меняется только ID формы.
На этом всё. Если вам нужно реализовать это решение на своём сайте, пишите в чат на сайте или через форму обратной связи.