EReCaptchaValidator is a validator which uses reCAPTCHA to validate a CAPTCHA rendered by the EReCaptcha widget.
You need to have valid public and private keys for this to work. You can get them by registering yourself at http://recaptcha.net/
Current version: 1.3
Resources ¶
Documentation ¶
Requirements ¶
- Yii 1.1 or above
Installation ¶
- Extract the release file under
protected/extensions
Usage ¶
In the view:
<?php echo CHtml::activeLabel($user, 'validacion'); ?>
<?php $this->widget('application.extensions.recaptcha.EReCaptcha',
array('model'=>$user, 'attribute'=>'validacion',
'theme'=>'red', 'language'=>'es_ES',
'publicKey'=>'<your public key>')) ?>
<?php echo CHtml::error($user, 'validacion'); ?>
In the model:
<?php
class UserModel extends CFormModel
{
public $validacion;
public function rules()
{
return array(
array('validacion',
'application.extensions.recaptcha.EReCaptchaValidator',
'privateKey'=>'<your private key>'),
);
}
public function attributeLabels()
{
return array(
'validacion'=>Yii::t('demo', 'Enter both words separated by a space: '),
);
}
}
imehesz has noted that for this validator to work with ActiveRecord, you'll need to do something like this:
Controller:
public function actionRegister()
{
$form = new User();
$form->scenario = 'registerwcaptcha';
...
if($form->validate())
{
// and here is the actual HACKY part
$form->scenario = NULL;
// save user registration
$form->save();
}
}
Model:
public function rules()
{
return array(
...
array(
'validacion',
'application.extensions.recaptcha.EReCaptchaValidator',
'privateKey'=> ENVII_CAPTCHA_PRIVATE_KEY,
'on' => 'registerwcaptcha'
),
...
);
}
The view remains the same.
Change Log ¶
20110926 ¶
- 1.3 - Updated to support SSL. Some clean-up.
20081125 ¶
- 1.1 - Updated to fix BC broken with new CClientScript methods.
Need fix for IE compatibility with themes
There is an extraneous comma after {$this->tabIndex}, <---
Please remove it from code since this will cause the theme to not work in IE.
$script =<<<EOP
var RecaptchaOptions = {
theme : '{$this->theme}',
custom_theme_widget : {$customthemewidget},
lang : '{$this->language}',
tabindex : {$this->tabIndex}
};
EOP;
very good
I had a little hick-up at the beginning, but now it works great. I will use this everywhere ;)
--iM
very good
~So1
in order to do so just rewrote two lines in both php files of this extension and replace $this->public/private with Yii::app()->params['reCaptcha']['publicKey'] and Yii::app()->params['reCaptcha']['privateKey'].
It works like a charm now.
Great!
Thanks for the extension. I'd like to use it in a little bit different way.
I do not want to type keys manually in each recaptcha widget, instead of this I define keys in config:
>>
Excellent
Thank you!
Works great
Nice interface to recaptcha
Using yii-user with recaptcha
If you want to use yii-user with recaptcha, you'll need to do the following (these are all relative to yii-user module root, NOT app root):
in views/user/registration.php, replace:
<?php if (UserModule::doCaptcha('registration')): ?> <div class="row"> <?php echo CHtml::activeLabelEx($form,'verifyCode'); ?> <div> <?php $this->widget('CCaptcha'); ?> <?php echo CHtml::activeTextField($form,'verifyCode'); ?> </div> <p class="hint"><?php echo UserModule::t("Please enter the letters as they are shown in the image above."); ?> <br/><?php echo UserModule::t("Letters are not case-sensitive."); ?></p> </div> <?php endif; ?>
with
<?php if (UserModule::doCaptcha('registration')): ?> <div class="row"> <?php echo CHtml::activeLabel($form, 'verifyCode'); ?> <?php $this->widget('application.extensions.recaptcha.EReCaptcha', array('model' => $form, 'attribute' => 'verifyCode', 'theme' => 'red', 'language' => 'en_US', 'publicKey' => 'PUBLIC KEY HERE')) ?> </div> <?php endif; ?>
in models/RegistrationForm.php, replace the verifyCode rule with this:
array('verifyCode', 'application.extensions.recaptcha.EReCaptchaValidator', 'privateKey' => 'PRIVATE KEY HERE'),
and very important:
In controllers/RegistrationController.php, around line 45, find this line:
if ($model->save()) {
and change it to:
if ($model->save(false)) {
Here's the reason the last bit is important: The call to the reCaptcha server is only valid ONCE. However, this controller ends up calling it twice, once at line 34 ($model->validate()) and then again at line 45 ($model->save()) because the save() actually calls the validate() again.
Passing false as a parameter to save() will bypass the validation, which is okay because we just did it a few lines earlier.
Hope this saves someone a few hours. :)
Using yii-user with recaptcha Part 2
Still learning here. I hardcoded the reCaptcha keys in the code with my previous example, but it's better to follow the tips in these comments too:
http://www.yiiframework.com/extension/recaptcha/#c1350
http://www.yiiframework.com/extension/recaptcha/#c1383
That is, put the keys in the config. Mind the case sensitivity as the two linked comments have different cases.
Add SSL support
I use this extension for a project, a website which runs over SSL.
I know that reCaptcha has support for HTTPS requests so users will not get warnings in browser (like "this page has some resources that are not sent via a secured connection") when loading a page that has reCaptcha.
This is how I got this done:
In EReCaptcha.php, I modified the line ~153 as follows:
echo recaptcha_get_html(Yii::app()->params['recaptcha_public'], null, Yii::app()->params['https']);
_You do not need to change the first argument Yii::app()->params['recaptcha_public'] to use ReCaptcha via HTTPS. I did this just to avoid hardcoding it in my application. See this comment._
In configuration, protected/config/main.php, I added
params => array( [...], 'https'=>true, [...], ),
And, finally, I updated the reCAPTCHA/recaptchalib.php file. The latest one can be downloaded from here.
thanks
thanks, very helpful.
Excellent!
Thank you.
Suggestion
I suggest to update setLanguage() in EReCaptcha.php to this:
public function setLanguage($value) { $this->language = empty($value) ? 'en' : (($p=strpos($value,'_'))!==false) ? strtolower(substr($value,0,$p)) : strtolower($value); if (!in_array($this->language, $this->validLanguages)) $this->language = 'en'; }
Then you can put your custom language or set it dynamically by application.
And update setTheme() method too to put your custom theme.
public function setTheme($value) { $this->theme = $value; if (!in_array($this->theme, $this->validThemes)) $this->theme = 'red'; }
IT'S PERFECT!!!!!!!!!!
THANKS A LOT!!!!!!!
Doesn't work with AJAX Validation
The form was successfully submitting as if the reCaptcha was not there at all. Even if it was left blank. I had to set enableAjaxValidation to FALSE and replaced the ajax validation code in my controller action, with the $model->validate() to get it work.
Very useful extension!
Just want to say thank you for this extension. Good work!
P.S. If someone knows how to make this work with ajax validation it would be good to share with the rest of us.
Notice for users
Extension works like a charm, so many thanks to the creator.
If you decide to replace default verifyCode with this extension, remember to remove some then unecessary elements from your application:
CCaptchaAction
inactions()
function in controller responsible for handling your previous bulid-in captcha,verifyCode
rule fromrules()
from your form model as well as public symbol$verifyCode
form it,Remember, that you can customize look & feel of your captcha -- look into documentation for more info.
Another notice
Take into consideration, that you should not add your recaptcha field to required validator, as this will fail.
Also, keep in mind, that adding following line:
<?php echo CHtml::error($model, 'captcha'); ?>
below your recaptcha field works only for server-side validation. Even, if you have client-side validation enabled, it will not work this way.
AJAX Validation fix/hack
The problem with the AJAX validation is the the recaptcha API only validates a code once - and when AJAX validation is enabled in Yii the model is usually validated once for the AJAX validation and a second time on the actual form submit action. This second validation will fail, even if it's correct.
To get the AJAX validation working I am caching the captcha API validation response from the AJAX request, so we don't need to re-validate the captcha on the 2nd request.
The trick to making this work securely (to prevent a bot from being able to re-submit the form with the same recaptcha values) is to delete the cached captcha from the user session. I included the code for this at the end of my comment.
Here is the new validateAttribute() method for the EReCaptchaValidator class to cache the recaptcha validation. You can make these modifications as well, or make your own validator that extends EReCaptchaValidator.
protected function validateAttribute($object, $attribute) { $valid = false; // only validate if the captcha has been POSTed if (isset($_POST['recaptcha_challenge_field']) && isset($_POST['recaptcha_response_field'])) { // sessions are enabled, so we can cache the value if ($session = Yii::app()->session) { // if this question that was asked before, check the cached answer if (isset($session['cCache']) && isset($session['cCache']['q']) && $session['cCache']['q'] == $_POST['recaptcha_challenge_field'] ) { // if the cached answer equals the provided answer, // we know it was previously validated as correct if (isset($session['cCache']['a']) && $session['cCache']['a'] == $_POST['recaptcha_response_field']) { $valid = true; } } else { // validate the captcha $resp = recaptcha_check_answer( $this->privateKey, $_SERVER['REMOTE_ADDR'], $_POST['recaptcha_challenge_field'], $_POST['recaptcha_response_field'] ); // if it validates, store challenge and response hash in session if ($resp->is_valid) { if (Yii::app()->session) { Yii::app()->session['cCache'] = array( 'q' => $_POST['recaptcha_challenge_field'], 'a' => sha1($_POST['recaptcha_response_field']), ); } $valid = true; } } } } // if the captcha or cached value were not validated, set the error if (!$valid) { $message = $this->message !== null ? $this->message : Yii::t('yii', 'The verification code is incorrect.'); $this->addError($object, $attribute, $message); } }
Then, add this code after the form model has been successfully saved, to prevent a bot from re-submitting the captcha value.
if (Yii::app()->session && isset(Yii::app()->session['cCache'])) { // delete the cached captcha values unset(Yii::app()->session['cCache']); }
Add customTranslation
ReCaptcha has an option to translate it to another languages (for example I need to translate it to Polish); I think it would be great to extend the code of EReCaptcha with following:
/** * @var array custom translations of reCAPTCHA strings. */ public $customTranslations = ''; ... public function init() { $customthemewidget = (($w = $this->customThemeWidget) != '') ? "'{$w}'" : 'null'; $customtranslations = (($t = $this->customTranslations) != '') ? CJSON::encode($t) : 'null'; $cs = Yii::app()->getClientScript(); if (!$cs->isScriptRegistered(get_class($this) . '_options')) { $script = <<<EOP var RecaptchaOptions = { theme : '{$this->theme}', custom_theme_widget : {$customthemewidget}, custom_translations : {$customtranslations}, lang : '{$this->language}', tabindex : {$this->tabIndex} }; EOP; $cs->registerScript(get_class($this) . '_options', $script, CClientScript::POS_HEAD); } }
Work with active record
public function actionRegister() { $form = new User(); $form->scenario = 'registerwcaptcha'; ... if($form->validate()) { $form->save(false); //do not validate again } }
or
public function actionRegister() { $form = new User(); $form->scenario = 'registerwcaptcha'; ... if($form->save()) { ... } }
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.