0
votes

I have 3 controllers named Site, Ads and Message. All controllers have captcha action class definition and they are allowed in accessrules of each controller.

The captcha works well in the site controller but in other controllers its validation wont work. It shows captcha image and recaptcha works too but just validation doesn't work, any idea to debug?

UPDATE :

I've probed into Yii and found something. CCaptcha widget uses controller name as a part of session key to store captcha verification code.

CCaptchaAction.php

protected function getSessionKey(){
    return self::SESSION_VAR_PREFIX . Yii::app()->getId() . '.' .   $this->getController()->getUniqueId() . '.' . $this->getId();
}

As you can see $this->getController()->getUniqueId() is a part of session key.

CCaptchaAction uses this function to generate and validate verification code.

The problem is that generation and verification occures in different controllers.

In a clear example assume we have Controllers A and B .

when captcha generation is ocuured in controller A , print_r($_SESSION) is something like :

Array ( [Yii.CCaptchaAction.12bd9136.A.captcha] => eyntri [Yii.CCaptchaAction.12bd9136.A.captchacount] => 1

and when captcha validation occures in controller B the validation method checks if

New Generated and showed code in controller B === $_SESSION[Yii.CCaptchaAction.12bd9136.A.captcha] is true or not.

And they are not equal always when generator and validator controllers are not same!!

2
Could you post some of the code related to the captchas? Right now answering this question is mostly a guessing game. From personal experience there should be captcha related code in the controller, the view and the related model class. - Nikolas Grottendieck
Is there a rule in model? - rinat.io
I updated the question in detail - Yoones Mehdian
Am I right to say that you have exactly one captcha that is simply embedded in multiple different context but should always apply to the same controller/action combination, namely Site/Login? In that case I can see the $this->beginWidget() method to be an issue. You may want to check the returned HTML on all other pages and compare whether it actually matches up to what is generated for Site/Login. - Nikolas Grottendieck
HTML markup is same in all contexts because I print login form in "site" layout. - Yoones Mehdian

2 Answers

5
votes
  1. In the model validation rule specify captchaAction to point exactly to your controller captcha action:

    array('verifyCode', 'captcha', 'allowEmpty'=>!CCaptcha::checkRequirements(), 'captcaAction' => 'site/captcha'),

  2. In the view when capthca widget is used again point exactly to your controller captcha action via captchaAction:

    <?php $this->widget('CCaptcha', array('captchaAction' => 'site/captcha')); ?>

0
votes

I think my hole problem is that CCaptchaAction uses name of controller to name the session key to store verification key.

Finally As a not bad solution I overloaded CCaptchaAction by following class :

class CCaptchaActionExtension extends CCaptchaAction{
    protected function getSessionKey(){
           return self::SESSION_VAR_PREFIX . Yii::app()->getId() . '.' . $this->getId();
    }
}

And insead of CCaptchaAction I used CCaptchaActionExtention in all controllers :

    public function actions()
    {
        return array(
            'captcha'=>array(
                'class'=>'CCaptchaActionExtension',
                'backColor'=>0xFFFFFF,
            ),   
        );
    }

As you can see the new class overloads getSessionKey() method. Because name of controller had bothered me I removed it.

I don't accept my solution because I want to hear your comments about it or a new better solution ( if exists; :) )