Замена стандартной капчи на Google reCaptcha

Рассмотрим пример замены капчи для модуля форм.

Получение регистрационных данных

1. Перейдите на страницу reCAPTCHA
2. Заполните форму "Регистрация сайта". Например,
   
3. После нажатия кнопки "Регистрация" произойдет переход на страницу с регистрационными данными. Необходима будет пара Ключ + Секретный ключ:
   

Интеграция на стороне клиента

Вставьте этот фрагмент перед закрывающим тегом </head> в HTML-коде:
<script src="https://www.google.com/recaptcha/api.js" type="text/javascript"></script>

Интеграция на стороне сервера

В XSL-шаблон в тег <form> вносим строку виджета каптчи там, где нужен показ:
<div class="g-recaptcha" data-sitekey="{/form/site_key}"></div>
Когда пользователи отправляют форму со встроенной проверкой reCAPTCHA, вместе с прочими данными вы получаете строку "g-recaptcha-response". Чтобы узнать, прошел ли пользователь проверку, отправьте POST-запрос. Если получили успешный ответ, то форма отправляется.
<?php
if (Core::moduleIsActive('form'))
{
    $oForm = Core_Entity::factory('Form', Core_Array::get(Core_Page::instance()->libParams, 'formId'));
    $Form_Controller_Show = new Form_Controller_Show($oForm);
    $xslName = Core_Array::get(Core_Page::instance()->libParams, 'formXsl');
    $sSecretKey = "xxx"; // Указывается секретный ключ reCAPTCHA
    if (!is_null(Core_Array::getPost($oForm->button_name)))
    {
        if (Core_Array::getPost('g-recaptcha-response'))
        {
           // $sResponse = Core_Array::getPost('g-recaptcha-response');
           // $sUrl = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $sSecretKey . "&response=" . $sResponse;    
       $sUrl = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $sSecretKey . '&response=' . (array_key_exists('g-recaptcha-response', $_POST) ? $_POST["g-recaptcha-response"] : '') . '&remoteip=' . $_SERVER['REMOTE_ADDR'];
            /*$Core_Http = Core_Http::instance('curl')
            ->clear()
            ->method('POST')
            ->url($sUrl)
            ->execute();
            $aAnswer = json_decode($Core_Http->getBody(), TRUE);*/
            $aAnswer = json_decode(file_get_contents($sUrl), TRUE);  
            if ($aAnswer['success'])
            {
                $Form_Controller_Show
                    ->values($_POST + $_FILES)
                    // 0 - html, 1- plain text
                    ->mailType(Core_Array::get(Core_Page::instance()->libParams, 'mailType'))
                    ->mailXsl(
                        Core_Entity::factory('Xsl')->getByName(Core_Array::get(Core_Page::instance()->libParams, 'notificationMailXsl'))
                    )
                    ->mailFromFieldName(Core_Array::get(Core_Page::instance()->libParams, 'emailFieldName'))
                    ->process();
            }
            else
            {
                Core_Log::instance()->clear()
                ->status(Core_Log::$ERROR)
                ->write('Google Recaptcha: ' . $aAnswer['error-codes'][0]);
          $Form_Controller_Show
                  ->addEntity(
                  Core::factory('Core_Xml_Entity')
                  ->name('recaptcha_error')->value('1')
               );
            }    
        }    
     else
            {
                
             $Form_Controller_Show
                  ->addEntity(
                  Core::factory('Core_Xml_Entity')
                  ->name('recaptcha_error')->value('1')
               );
            }
        //-------------------------- end recaptha
        
    }
    $Form_Controller_Show
        ->xsl(
            Core_Entity::factory('Xsl')->getByName($xslName)
        )
        ->addEntity(
            Core::factory('Core_Xml_Entity')
                ->name('site_key')->value('yyy') // Указываете ключ reCAPTCHA
        )
        ->show();
}
else
{
    ?>
    <h1>Формы</h1>
    <p>Функционал недоступен, приобретите более старшую редакцию.</p>
    <p>Модуль &laquo;<a href="http://www.hostcms.ru/hostcms/modules/forms/">Формы</a>&raquo; доступен в редакциях &laquo;<a href="http://www.hostcms.ru/hostcms/editions/corporation/">Корпорация</a>&raquo;, &laquo;<a href="http://www.hostcms.ru/hostcms/editions/business/">Бизнес</a>&raquo; и &laquo;<a href="http://www.hostcms.ru/hostcms/editions/small-business/">Малый бизнес</a>&raquo;.</p>
    <?php
}

Замена стандартной капчи на Google reCaptcha в комментариях

Рассмотрим пример замены капчи для комментариев магазина. Для комментариев информационных систем точно такой же алгоритм.

Получение регистрационных данных

1. Производим стандартнуюю регистрацию сайта.Перейдите на страницу reCAPTCHA
2. Заполните форму "Регистрация сайта". Например,
   
3. После нажатия кнопки "Регистрация" произойдет переход на страницу с регистрационными данными. Необходима будет пара Ключ + Секретный ключ:
   
Интеграция на стороне клиента
1. Если у вас стандартные комментарии системы, то вставьте этот фрагмент перед закрывающим тегом </head> в HTML-коде, указав вместо your_key ключ сайта, который был получен ранее:
<script type="text/javascript">
var CaptchaCallback = function(){
$('.g-recaptcha').each(function(index, el) {
grecaptcha.render(el, {'sitekey' : 'your_key'});
});
};
</script>
 <script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>
2. Если у вас комментарии без возможности ответов на уже добавленные комментарии, т.е. форма комментариев одна на странице, то вставьте этот фрагмент перед закрывающим тегом </head> в HTML-коде:
<script src="https://www.google.com/recaptcha/api.js" type="text/javascript"></script>

Интеграция на стороне сервера

В XSL-шаблоне карточки товара, в темлейт формирующий форму комментариев, в тег <form> вносим строку виджета каптчи там, где нужен показ:
<!-- Showing captcha -->
<xsl:if test="/shop/use_captcha = 1 and /shop/siteuser_id = 0">
<div class="g-recaptcha" data-sitekey="{/shop/site_key}"></div>
</xsl:if>
Когда пользователи отправляют форму со встроенной проверкой reCAPTCHA, вместе с прочими данными вы получаете строку "g-recaptcha-response". Чтобы узнать, прошел ли пользователь проверку, отправьте POST-запрос. Если получили успешный ответ, то форма отправляется.
Переходим в типовую динамическую страницу магазина и настраиваем её на обработку reCAPTCHA:
1. В коде типовой динамической страницы, после $Shop_Controller_Show = Core_Page::instance()->object;
добавляем передачу в XML ключа reCAPTCHA, который был получен при регистрации в Google reCaptcha.
   // Google reCaptcha
   $Shop_Controller_Show->addEntity(
   Core::factory('Core_Xml_Entity')
   ->name('site_key')->value('yyy') // Указываете ключ reCAPTCHA
   );

2. Следующий шаг - замена стандартной обработки капчи.

Для этого блок:

 if (Core_Array::getPost('add_comment') && Core_Array::get(Core_Page::instance()->libParams, 'showComments', 1))
   {
   ...
   }

заменяется на:
  
if (Core_Array::getPost('add_comment') && Core_Array::get(Core_Page::instance()->libParams, 'showComments', 1))
   {
   $oShop = $Shop_Controller_Show->getEntity();
   
   $sSecretKey = "xxx"; // Указывается секретный ключ reCAPTCHA
   
   $Shop_Controller_Show->cache(FALSE);
   
   $oLastComment = Core_Entity::factory('Comment')->getLastCommentByIp(
   Core_Array::get($_SERVER, 'REMOTE_ADDR')
   );
   
   $oXmlCommentTag = Core::factory('Core_Xml_Entity')
   ->name('document');
   
   $siteuser_id = 0;
   if (Core::moduleIsActive('siteuser'))
   {
   $oSiteuser = Core_Entity::factory('Siteuser')->getCurrent();
   
   if ($oSiteuser)
   {
   $siteuser_id = $oSiteuser->id;
   }
   }
   
   $oComment = Core_Entity::factory('Comment');
   
   $allowable_tags = '<b><strong><i><em>
<p><u><strike><ul><ol><li>';
   $oComment->parent_id = intval(Core_Array::getPost('parent_id', 0));
   $oComment->active = $oShop->comment_active;
   $oComment->author = Core_Str::stripTags(Core_Array::getPost('author'));
   $oComment->email = Core_Str::stripTags(Core_Array::getPost('email'));
   $oComment->phone = Core_Str::stripTags(Core_Array::getPost('phone'));
   $oComment->grade = intval(Core_Array::getPost('grade', 0));
   $oComment->subject = Core_Str::stripTags(Core_Array::getPost('subject'));
   $oComment->text = nl2br(Core_Str::stripTags(Core_Array::getPost('text'), $allowable_tags));
   $oComment->siteuser_id = $siteuser_id;
   
   $oShop_Item = Core_Entity::factory('Shop_Item', $Shop_Controller_Show->item);
   
   $oXmlCommentTag
   ->addEntity($oComment)
   ->addEntity($oShop_Item);
   
   if (is_null($oLastComment) || time() > Core_Date::sql2timestamp($oLastComment->datetime) + ADD_COMMENT_DELAY)
   {
   if (Core_Array::getPost('g-recaptcha-response'))
   {
   $sResponse = Core_Array::getPost('g-recaptcha-response');
   
   $sUrl = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $sSecretKey . "&response=" . $sResponse;
   
   $Core_Http = Core_Http::instance('curl')
   ->clear()
   ->method('POST')
   ->url($sUrl)
   ->execute();
   
   $oAnswer = json_decode($Core_Http->getBody());
   
   if ($oAnswer->success)
   {
   $oComment->save();
   
   $oComment
   ->dateFormat($oShop->format_date)
   ->dateTimeFormat($oShop->format_datetime);
   
   $oShop_Item->add($oComment)->clearCache();
   
   $oXmlCommentTag->addEntity($oShop);
   
   // Отправка письма администратору
   $sText = Xsl_Processor::instance()
   ->xml($oXmlCommentTag->getXml())
   ->xsl(Core_Entity::factory('Xsl')->getByName(Core_Array::get(Core_Page::instance()->libParams, 'addCommentAdminMailXsl')))
   ->process();
   
   $aFrom = array_map('trim', explode(',', EMAIL_TO));
   
   Core_Mail::instance()
   ->to(EMAIL_TO)
   ->from($aFrom[0])
   ->header('Reply-To', Core_Valid::email($oComment->email)
   ? $oComment->email
   : $aFrom[0]
   )
   ->subject(Core::_('Shop.comment_mail_subject'))
   ->message(trim($sText))
   ->contentType(Core_Array::get(Core_Page::instance()->libParams, 'commentMailNoticeType', 0) == 0
   ? 'text/plain'
   : 'text/html'
   )
   ->send();
   }
   else
   {
   Core_Log::write($oAnswer->error-codes);
   
   $oXmlCommentTag->addEntity(Core::factory('Core_Xml_Entity')
   ->name('error_captcha')->value(1)
   );
   
   $oComment->text = Core_Str::br2nl($oComment->text);
   $Shop_Controller_Show->addEntity($oComment);
   }
   }
   else
   {
   $oXmlCommentTag->addEntity(Core::factory('Core_Xml_Entity')
   ->name('error_captcha')->value(1)
   );
   
   $oComment->text = Core_Str::br2nl($oComment->text);
   $Shop_Controller_Show->addEntity($oComment);
   }
   }
   else
   {
   $oXmlCommentTag->addEntity(Core::factory('Core_Xml_Entity')
   ->name('error_time')->value(1)
   );
   
   $oComment->text = Core_Str::br2nl($oComment->text);
   $Shop_Controller_Show->addEntity($oComment);
   }
   
   // Результат добавления комментария
   $xsl_result = Xsl_Processor::instance()
   ->xml($oXmlCommentTag->getXml())
   ->xsl(Core_Entity::factory('Xsl')->getByName(
   Core_Array::get(Core_Page::instance()->libParams, 'addCommentNoticeXsl'))
   )
   ->process();
   
   $Shop_Controller_Show->addEntity(
   Core::factory('Core_Xml_Entity')
   ->name('message')->value($xsl_result)
   );
   }

где в переменной $sSecretKey указывается секретный ключ, который был получен вместе с ключом сайт при регистрации в Google reCAPTCHA.

Возможные ошибки

- При получении ошибки Exception: The property '_login' does not exist in the model 'lib' необходимо заменить:
- $Core_Http = Core_Http::instance('curl')
  ->clear()
  ->method('POST')
  ->url($sUrl)
  ->execute();
```
- $oAnswer = json_decode($Core_Http->getBody());

на:
- $oAnswer = json_decode(file_get_contents($sUrl));

---
Добавляем reCaptcha в форму, которая выводится в макете страницы
проверить работоспособность
<?php
       // Выводим шаблон страницы
       Core_Page::instance()->execute();
       echo '<div class="clear no-show"></div>';
       // add ФОРМА БЫСТРОГО ЗАКАЗА
       $oForm = Core_Entity::factory('Form', 4);  // Заменить id формы
       $Form_Controller_Show = new Form_Controller_Show($oForm);
       $sSecretKey = "6LeQXykUAAAAAGKJw3v6rNNXLPZ94QcuJEyqLkKK"; // Указывается секретный ключ reCAPTCHA
       if (!is_null(Core_Array::getPost($oForm->button_name)))
          {
       //------------------ add recaptha
        if (Core_Array::getPost('g-recaptcha-response'))
        {
            $sResponse = Core_Array::getPost('g-recaptcha-response');
            $sUrl = 'https://www.google.com/recaptcha/api/siteverify?secret='; . $sSecretKey . "&response=" . $sResponse;    
            $Core_Http = Core_Http::instance('curl')
            ->clear()
            ->method('POST')
            ->url($sUrl)
            ->execute();
            $aAnswer = json_decode($Core_Http->getBody(), TRUE);            
            
            if ($aAnswer['success'])
            {
         //------------------ end recaptha
         $Form_Controller_Show
         ->values($_POST + $_FILES)
         // 0 - html, 1- plain text
         ->mailType(0)
         ->mailXsl(
            Core_Entity::factory('Xsl')->getByName('ПисьмоКураторуФормыВФорматеHTML')
         )
         ->mailFromFieldName('reklama01@yandex.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 = 'Вами была заполнена форма Быстрого заказа на сайте  krepco.ru';// Тема письма отредактировать
           // При текстовой отправке нужно преобразовать 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();
            }
        }
        //-------------------------- add recaptha
         }
            else
            {
                Core_Log::instance()->clear()
                ->status(Core_Log::$ERROR)
                ->write('Google Recaptcha: ' . $aAnswer['error-codes'][0]);
         // add iNik
             $Form_Controller_Show
                  ->addEntity(
                  Core::factory('Core_Xml_Entity')
                  ->name('recaptcha_error')->value('1')
               );
           //----- end iNik
            }    
        }  
        //-------------------------- end recaptha
           }
       $Form_Controller_Show
          ->xsl(
        Core_Entity::factory('Xsl')->getByName('ОтобразитьФормуБыстрыйЗаказNEW3')
               )
           ->addEntity(
        Core::factory('Core_Xml_Entity')
           ->name('site_key')->value('6LeQXykUAAAAABVxo0YpLcOUaswIFlq519dGfWoP') // Указываете ключ reCAPTCHA
               )
           ->show();
    ?>

Использование 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>