Тема: Замена родной и древней каптчи в модулях на Recaptcha 2.0!
Собственно, делал один модуль с формами, надо было прикрутить стандартную капчушку (которая реализована в библиотеке DX_Auth) к форме. Проблема встала сразу с её проверкой и выводе в попап окне самой каптчи и обновление её.
Лёгких путей искать не пришлось, "затрахавшись" - решено было сделать современную капту и не придумывать велосипед с квадратными колёсами. Выбор пал на API гугла и использование Rechaptcha 2.0.
Может кому пригодится код в своих проектах, было бы не плохо, если разрабочики прикрутили данный допил в свой новый релиз.
И так начнём:
Для того чтобы у нас работала каптча на сайте, нам надо получить код от гугла, секретные ключи:
1. Надо иметь гугл аккаунт и пройти по ссылке: https://www.google.com/recaptcha/admin?hl=ru#list
В название вводим своё имя сайта и доменны сайта на котором будет работать каптча.
2. Создаём библиотеку с названием в папке /application/libraries/Recaptcha.php
3. Помещаем код библиотеки для дальнейшего использования:
<?php
/**
* CodeIgniter NO Captcha ReCAPTCHA a.k.a reCAPTCHA Version 2.0 library
*
* This library is based on official reCAPTCHA library for PHP
*
*/
defined('BASEPATH') OR exit('No direct script access allowed');
class Recaptcha {
private $signup_url = "https://www.google.com/recaptcha/admin";
private $_siteVerifyUrl = "https://www.google.com/recaptcha/api/siteverify?";
private $_secret, $_sitekey, $_lang;
private $_version = "php_1.0";
function __construct() {
$this->ci = & get_instance();
$this->ci->load->config('recaptcha', TRUE);
if ($this->ci->config->item('recaptcha_secretkey', 'recaptcha') == NULL || $this->ci->config->item('recaptcha_secretkey', 'recaptcha') == "") {
die("To use reCAPTCHA you must get an API key from <a href='"
. $this->signup_url . "'>" . $this->signup_url . "</a>");
}
if ($this->ci->config->item('recaptcha_sitekey', 'recaptcha') == NULL || $this->ci->config->item('recaptcha_sitekey', 'recaptcha') == "") {
die("To use reCAPTCHA you must get an API key from <a href='"
. $this->signup_url . "'>" . $this->signup_url . "</a>");
}
$this->_secret = $this->ci->config->item('recaptcha_secretkey', 'recaptcha');
$this->_sitekey = $this->ci->config->item('recaptcha_sitekey', 'recaptcha');
if ($this->ci->config->item('lang', 'recaptcha') == NULL || $this->ci->config->item('lang', 'recaptcha') == "") {
$this->_lang = 'en';
} else {
$this->_lang = $this->ci->config->item('lang', 'recaptcha');
}
}
/**
* Function to convert an array into query string
* @param array $data Array of params
* @return String query string of parameters
*/
private function _encodeQS($data) {
$req = "";
foreach ($data as $key => $value) {
$req .= $key . '=' . urlencode(stripslashes($value)) . '&';
}
return substr($req, 0, strlen($req) - 1);
}
/**
* HTTP GET to communicate with reCAPTCHA server
* @param string $path URL to GET
* @param array $data Array of params
* @return string JSON response from reCAPTCHA server
*/
private function _submitHTTPGet($path, $data) {
$req = $this->_encodeQS($data);
$response = file_get_contents($path . $req);
return $response;
}
/**
* Function for rendering reCAPTCHA widget into views
* Call this function in your view
* @return string embedded HTML
*/
public function render() {
$return = '<div class="g-recaptcha" data-sitekey="' . $this->_sitekey . '"></div><script src="https://www.google.com/recaptcha/api.js?hl=' . $this->_lang . '" async defer></script>';
return $return;
}
/**
* Function for verifying user's input
* @param string $response User's input
* @param string $remoteIp Remote IP you wish to send to reCAPTCHA, if NULL $this->input->ip_address() will be called
* @return array Array of response
*/
public function verifyResponse($response, $remoteIp = NULL) {
if ($response == null || strlen($response) == 0) {
// Empty user's input
$return = array(
'success' => FALSE,
'error_codes' => 'missing-input'
);
}
$getResponse = $this->_submitHttpGet(
$this->_siteVerifyUrl, array(
'secret' => $this->_secret,
'remoteip' => (!is_null($remoteIp)) ? $remoteIp : $this->ci->input->ip_address(),
'v' => $this->_version,
'response' => $response
)
);
$answers = json_decode($getResponse, TRUE);
if (trim($answers ['success']) == true) {
// Right captcha!
$return = array(
'success' => TRUE,
'error_codes' => ''
);
} else {
// Wrong captcha!
$return = array(
'success' => FALSE,
'error_codes' => $answers['error-codes']
);
}
return $return;
}
}
4. Создаём файл настроек, который будет распологаться application/config/recaptcha.php
5. Открываем файл и вносим код, там надо указать свои секретные ключи выданные гуглом при создании
<?php
if (!defined('BASEPATH')) {
exit('No direct script access allowed');
}
$config['recaptcha_sitekey'] = "Сюда ввести ключ выданный гуглом";
$config['recaptcha_secretkey'] = "Сюда ввести ключ выданный гуглом";
// Определяем текущий язык сайта - вернёт ru - если язык является русский
$config['lang'] = MY_Controller::getCurrentLocale();
?>
6. Всё готово для работы.
Как использовать!?
В модуле контроллера добавить строку загрузки библиотеки, обычно помещают в конструктор
$this->load->library('Recaptcha');
Открываем файл шаблона модуля для вывода формы, перед закрывающим тэгом выводим это:
{echo $CI->recaptcha->render()}
Для проверки защитного кода используем проверку:
// Ответ защитного кода
$captcha_answer = $this->input->post('g-recaptcha-response');
// Проверка защитного кода
$response = $this->recaptcha->verifyResponse($captcha_answer);
// Проверка на ошибки
if ($response['success']) {
// Если нет ошибок исполняем код, например отсылаем письмо
} else {
// Если ответ на защитный код не верный
if($response['success'] === FALSE) {
// Выводим сообщение об ошибки
echo $captcha_error = tlang('Ошибка потверждения защиты');
}
else {
echo $captcha_error = '';
}
}
Посмотреть как работает и выглядит каптча
https://www.google.com/recaptcha/api2/demo
ДОПОЛНЕНИЕ: ПОЛНЫЙ КОД ДЛЯ МОДУЛЕЙ FEEDBACK и CALLBACK (version 4.12)
feedback.php
<?php
use cmsemail\email;
if (!defined('BASEPATH')) {
exit('No direct script access allowed');
}
/**
* Image CMS
*
* Feedback module
*/
class Feedback extends MY_Controller
{
public $username_max_len = 30;
public $message_max_len = 600;
public $theme_max_len = 150;
public $admin_mail = 'admin@localhost';
public $message = '';
protected $formErrors = [];
public function __construct() {
parent::__construct();
$this->load->module('core');
// Load library
$this->load->library('Recaptcha');
$this->load_settings();
$this->formErrors = [
'required' => lang('Field is required'),
'min_length' => lang('Length is less than the minimum'),
'valid_email' => lang('Email is not valid'),
'max_length' => lang('Length greater than the maximum'),
];
$lang = new MY_Lang();
$lang->load('feedback');
}
public function autoload() {
}
public function captcha_check($code) {
if (!$this->dx_auth->captcha_check($code)) {
return FALSE;
} else {
return TRUE;
}
}
public function recaptcha_check() {
$result = $this->dx_auth->is_recaptcha_match();
if (!$result) {
$this->form_validation->set_message('recaptcha_check', lang('Improper protection code', 'feedback'));
}
return $result;
}
// Index function
public function index() {
$this->template->registerMeta('ROBOTS', 'NOINDEX, NOFOLLOW');
$this->core->set_meta_tags(lang('Feedback', 'feedback'));
$this->load->library('form_validation');
// Create captcha
//$this->dx_auth->captcha();
//$tpl_data['cap_image'] = $this->dx_auth->get_captcha_image();
$this->template->add_array($tpl_data);
if (count($this->input->post()) > 0) {
$this->form_validation->set_rules('name', lang('Your name', 'feedback'), 'trim|required|min_length[3]|max_length[' . $this->username_max_len . ']|xss_clean');
$this->form_validation->set_rules('email', lang('Email', 'feedback'), 'trim|required|valid_email|xss_clean');
$this->form_validation->set_rules('theme', lang('Subject', 'feedback'), 'trim|max_length[' . $this->theme_max_len . ']|xss_clean');
$this->form_validation->set_rules('message', lang('Message', 'feedback'), 'trim|required|max_length[' . $this->message_max_len . ']|xss_clean');
//if ($this->dx_auth->use_recaptcha) {
// $this->form_validation->set_rules('recaptcha_response_field', lang('Protection code', 'feedback'), 'trim|xss_clean|required|callback_recaptcha_check');
//} else {
// $this->form_validation->set_rules('captcha', lang('Protection code', 'feedback'), 'trim|required|xss_clean|callback_captcha_check');
//}
// Ответ защитного кода
$captcha_answer = $this->input->post('g-recaptcha-response');
// Проверка защитного кода
$response = $this->recaptcha->verifyResponse($captcha_answer);
if ($this->form_validation->run($this) == FALSE OR $response['success']===FALSE) { // there are errors
$recaptcha_error = tlang('Ошибка потверждения защиты');
$this->form_validation->set_error_delimiters('', '');
CMSFactory\assetManager::create()->setData(
'validation', $this->form_validation,
'recaptcha_error', $captcha_error);
form_error();
} else { // form is validate
$feedback_variables = [
'Theme' => $this->input->post('theme'),
'userName' => $this->input->post('name'),
'userEmail' => $this->input->post('email'),
'userMessage' => $this->input->post('message'),
];
email::getInstance()->sendEmail($this->input->post('email'), 'feedback', $feedback_variables);
CMSFactory\assetManager::create()->appendData('message_sent', TRUE);
}
}
CMSFactory\assetManager::create()->render('feedback');
}
private function load_settings() {
$this->db->limit(1);
$this->db->where('name', 'feedback');
$query = $this->db->get('components')->row_array();
$settings = unserialize($query['settings']);
if (is_int($settings['message_max_len'])) {
$this->message_max_len = $settings['message_max_len'];
}
if ($settings['email']) {
$this->admin_mail = $settings['email'];
}
}
}
/* End of file sample_module.php */
callbacks.php
<?php
use callbacks\Exceptions\ValidationException;
use cmsemail\email;
use CMSFactory\assetManager;
use CMSFactory\Events;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Exception\PropelException;
(defined('BASEPATH')) OR exit('No direct script access allowed');
/**
* Image CMS
* Callbacks
*/
class Callbacks extends MY_Controller
{
public function __construct() {
parent::__construct();
$lang = new MY_Lang();
$lang->load('callbacks');
// Load library
$this->load->library('Recaptcha');
}
/**
* Render form and save callback
*
* @return void
* @throws PropelException
*/
public function index() {
$this->core->set_meta_tags(lang('Callback', 'callback'));
$this->template->registerMeta('ROBOTS', 'NOINDEX, NOFOLLOW');
$this->load->library('Form_validation');
if ($this->input->post()) {
try {
$success = $this->createFromPost();
} catch (ValidationException $e) {
$success = false;
}
if (!$this->input->is_ajax_request()) {
$this->session->set_flashdata('success_message', $success);
redirect(site_url('/callbacks'));
}
}
$message = isset($success) ? $success : $this->session->flashdata('success_message');
assetManager::create()
->setData('themes', SCallbackThemesQuery::create()->setComment(__METHOD__)->joinWithI18n(MY_Controller::getCurrentLocale(), Criteria::INNER_JOIN)->find())
->setData(['success' => $message])
->render('callback');
}
/**
* Create new callback from $_POST data
* @return string
* @throws ValidationException
* @throws PropelException
*/
public function createFromPost() {
$this->load->library('Form_validation');
$model = new SCallbacks;
$this->form_validation->set_rules($model->rules());
// Ответ защитного кода
$captcha_answer = $this->input->post('g-recaptcha-response');
// Проверка защитного кода
$response = $this->recaptcha->verifyResponse($captcha_answer);
if (!$this->form_validation->run() OR $response['success']===FALSE) {
throw new ValidationException(
[
'message' => validation_errors(),
'errors' => $this->form_validation->getErrorsArray(),
]
);
}
$theme = SCallbackThemesQuery::create()->setComment(__METHOD__)->orderByPosition()->findOne();
$status = SCallbackStatusesQuery::create()->setComment(__METHOD__)->filterByIsDefault(TRUE)->findOne();
$model->fromArray($this->input->post());
$model->setThemeId($theme ? $theme->getId() : 0);
$model->setStatusId($status ? $status->getId() : 0);
$model->setDate(time());
$model->save();
$this->sendEmail($model);
Events::create()->raiseEvent(['model' => $model], 'Shop:callback');
return $this->getMessage();
}
/**
* @todo move callback configs(success message etc.)
* from answer notifications && shop settings
* to own module configs
*
* @return string
*/
protected function getMessage() {
$notification = $this->db
->where('locale', \MY_Controller::getCurrentLocale())
->where('name', 'callback')->get('answer_notifications');
return $notification->num_rows() > 0 ? $notification->row()->message : '';
}
/**
* @param SCallbacks $callback
* @return bool
* @throws PropelException
*/
protected function sendEmail(SCallbacks $callback) {
$callback_variables = [
'callbackStatus' => $callback->getSCallbackStatuses() ? $callback->getSCallbackStatuses()->getText() : '',
'callbackTheme' => $callback->getSCallbackThemes() ? $callback->getSCallbackThemes()->getText() : '',
'userName' => $callback->getName(),
'userPhone' => $callback->getPhone(),
'dateCreated' => date('d-m-Y H:i:s', $callback->getDate()),
'userComment' => $callback->getComment(),
];
return email::getInstance()->sendEmail($this->dx_auth->get_user_email(), 'callback', $callback_variables);
}
}
/* End of file callbacks.php */
Пользуйтесь на здоровье....
ДОПОЛНЕНИЕ - ДЛЯ РЕГИСТРАЦИИ МАГАЗИНА 4.12
Заходим в конфиг файл /public_html/application/config/auth.php
Правим строчки на эти, это отключит стандартную каптчу в регистрации:
// Registration
$config['DX_captcha_registration'] = FALSE;
Если кому лень делать ручками и настраивать прикручу отдельно или напишу модуль под вас за деньги.