CAPTCHA custom Validation

Hello,

Currently I’m doing a log-in form with behaviour similar to GMail’s. That means - the CAPTCHA appears only on unsuccessful log-in.

First of all, I have tested the CAPTCHA - it works. Then I added a new variable to the CFormModel:




public $verifyCodeRequired = false;



I have changed the action to look like this:




	public function actionLogin()

	{

		$model=new LoginForm;


		// collect user input data

		if(isset($_POST['LoginForm']))

		{

			$model->attributes=$_POST['LoginForm'];

			// validate user input and redirect to the previous page if valid

			if($model->validate() && $model->login()) 

			{

				$this->redirect(Yii::app()->user->returnUrl);

			}

			[b]$model->verifyCodeRequired = true;[/b]

		}

		// display the login form

		$this->render('login', array('model'=>$model));

	}



There is a validation rule:




array('verifyCode', 'captcha', 'allowEmpty'=>!$this->verifyCodeRequired),



So, the verifyCodeRequired gets a value TRUE, but the validator still passes the field when it’s empty. I have also tried making a custom validator:




array('verifyCode', 'validateCaptcha')






        public function validateCaptcha()

	{

		var_dump($this->verifyCodeRequired);

		if($this->verifyCodeRequired)

		{

			$validator = new CCaptchaValidator();

			$validator->attributes = array("verifyCode");

			return $validator->validate($this);

		}

		else {

			return true;

		}

	}



I always get FALSE. How should I achieve desired behaviour?

EDIT: Nevermind, I understood how to achieve that :)

Kind regards,

Darwell

Share with others? ;)

And validation rules are created before you can assign model attributes. Using scenarios is the solution.

I just used a CHttpSession component to store the same value. Could you please tell me more about this being solved with scenarios?

Thanks.

Using sessions is a right solution, because you need to store a value somewhere which indicates that a user was trying to login before. But putting this logic into a model is a wrong way. A model should be independent from sessions.

Scenario is a model’s property: http://www.yiiframework.com/doc/api/1.1/CModel#scenario-detail

You can have different validation rules for different scenarios:




public function rules()

{

    return array(

        array('verifyCode', 'captcha', 'on'=>'captchaRequired'),

    );

}



Now verifyCode won’t be validated while the current scenario is not “captchaRequired”.

Setting a scenario is easy:




$model = new LoginForm;

// if a user already tried to login, then set a scenario:

$model->scenario = 'captchaRequired';



Rules without "on" property will be applied to all scenarios.

Hope this helps ;)

That’s great! Thank you :)

Hello once again,

I still have some issues about this solution:

First of all, I don’t think that the scenarios WOULD BE enough for second-attempt-captcha-validation, because if I re-open the same URL - I see the form without CAPTCHA and it is not necessary to enter one. Refreshing the page works as expected - I still see CAPTCHA, but re-opening the same URL just makes an easy way to avoid the CAPTCHA. Spambots would benefit from this :)

Second - I can’t get it to work. Basically because I am setting the captchaRequired scenario AFTER the form is validated. So it means that the next time I create a model instance, the scenario is not named until the validation passes. I can’t think a way how to get the scenario name before errors occur (not CAPTCHA errors, because I can’t catch them this way).

So, my final solution is this:

combine scenarios with sessions. Store a scenario name of the model in session and re-name the scenario BEFORE the validation like this:




	public function actionLogin()

	{

		$model = new LoginForm;

		if(Yii::app()->session->contains("loginCaptchaRequired"))

		{

			$model->setScenario("captchaRequired");

		}

		if(isset($_POST['LoginForm']))

		{

			$model->attributes = $_POST['LoginForm'];

			if($model->validate() && $model->login()) 

			{

				echo "Successful login";

				Yii::app()->session->remove("loginCaptchaRequired");

			}

			Yii::app()->session->add("loginCaptchaRequired", "true");

			$model->setScenario("captchaRequired");

		}

		$this->render('login', array('model' => $model));

	}



Please tell me if this method is good enough.

tanks .i have same problem .when save model by this soulution.it works. :rolleyes:




public function rules()

{

    return array(

        array('verifyCode', 'captcha', 'on'=>'captchaRequired'),

    );

}



Now verifyCode won’t be validated while the current scenario is not “captchaRequired”.

Setting a scenario is easy:




$model = new LoginForm;

// if a user already tried to login, then set a scenario:

$model->scenario = 'captchaRequired';



[size=“2”][color=#1C2837]it’s working but on view doesn’t show validation message.[/color][/size] [color="#1C2837"][size=“2”]how to show validation message on view.[/size][/color]