Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to refresh captcha image on page refresh/load?

I want to force my site to refresh the captcha image everytime it loads, so I have a javascript method triggered with the onload() event. Here I have the following line:

document.getElementById('yw0_button').click;

Firebug doesn't detect any errors, and for testing purposes I have added an alert right after the displayed line, and the alert pops up each time the page is loaded. However, the image doesn't refresh!

This is what I believe is relevant about the view file:

<?php if(extension_loaded('gd')): ?>
    <div class="row">
        <?php echo $form->labelEx($model,'verifyCode'); ?>
        <div>
            <?php
            $this->widget('CCaptcha',
                          array('showRefreshButton'=>true,
                                'buttonType'=>'button',
                                'buttonOptions'=>
                                                    array('type'=>'image',
                                                          'src'=>"/path/images/refresh-icon.png",
                                                          'width'=>30,
                                                    ),                                                            
                                'buttonLabel'=>'Refrescar imagen'),
                          false); 
            ?>
            <br>
            <?php echo $form->textField($model,'verifyCode'); ?>
        </div>
    <div class="hint">
        Porfavor ingrese las letras como las ve en la imagen superior.
        <br/>No hay distincion entre minúsculas y mayúsculas.</div>
    </div>
<?php endif; ?>

Any ideas?


@k to the z just saw this! Yes sure, if you could help me find a more proper solution it would be awesome! This is what i believe is relevant about the view file:

<?php if(extension_loaded('gd')): ?>
        <div class="row">
            <?php echo $form->labelEx($model,'verifyCode'); ?>
            <div>
                <?php
                $this->widget('CCaptcha',
                              array('showRefreshButton'=>true,
                                    'buttonType'=>'button',
                                    'buttonOptions'=>
                                                        array('type'=>'image',
                                                              'src'=>"/path/images/refresh-icon.png",
                                                              'width'=>30,
                                                        ),                                                            
                                    'buttonLabel'=>'Refrescar imagen'),
                              false); 
                ?>
                <br>
                <?php echo $form->textField($model,'verifyCode'); ?>
            </div>
        <div class="hint">
            Porfavor ingrese las letras como las ve en la imagen superior.
            <br/>No hay distincion entre minúsculas y mayúsculas.</div>
        </div>
    <?php endif; ?>

In the controller I grant authorized users permission in the accessRules() method to the captcha action, and thats about all. Is there something else I could post?

like image 773
Soph Avatar asked May 29 '11 15:05

Soph


3 Answers

In case anyone goes through a similar problem whilst getting started with Yii.. This is how I resolved the CAPTCHA issue triggering a click: I have an event triggered the the onload event, which has the following code:

$('#yw0_button').click();

There surely must be a better way, im open to suggestions! However as a temporary solution this works just fine, refreshing the captcha image each time the page is loaded. Good luck!

EDIT: What I did was handle the onload event for my html page like this

<body onload="initialize()"> 
.. 
</body> 

Then in my js file:

function initialize() 
{ 
     $('#yw0_button').click(); 
} 
like image 93
Soph Avatar answered Nov 09 '22 16:11

Soph


I hope I can propose a better way to solve the problem mentioned by Soph. I know this is not JavaScript-related solution this question is addicted to but as I can see this one is the most right way to go.

I faced the same requirement - to refresh the caption on page refresh but preserve this one when user's input is invalid on other fields because repeating a new verification code on each validation's fail is annoying. So this task looks like to not refresh the caption on POST request and refresh it otherwise (GET request). A solution proposed by Soph by himself can't achieve this - as one could know POST requests are sent to server and their data cannot be analyzed by client-side JavaScript.

Therefore I decided to look at Yii's captcha implementation. It consists of two classes located in system.web.widgets.captcha package. The former is CCaptcha extended from CWidget and that we use in our view to add a captcha to web form in the manner like this:

<?php 
    $this->widget("CCaptcha", array(
    'buttonLabel' => "Generate another code",
    'showRefreshButton' => false,
    'clickableImage' => true
));
?>

The latter is CCaptchaAction that does the most significant work to provide a captcha by generating a verification code and creating an image. Taking into account that Yii is designed to be object-oriented we can create a couple of our own classes Captcha and CaptchaAction extended from CCaptcha and CCaptchaAction relatively and place them in components subdirectory of our web application's directory.

My implementation of both these classes is below.

run() method of CCaptchaAction is overriden and is quite similar to original one excepting that there is one additional if-branch executing when render_refreshed GET-parameter is set and where new verification code is being generated on the fly and corresponding new image is being rendered and returned as action's result.

A public refresh variable has been introduced in Captcha class and defaults to false meaning widget's behavior is similar to CCaptcha's one. Overriding renderImage method we alter a code responsible for preparing widget's output HTML code. To be more precisive it's where a link to captcha action is prepared used as src attribute of an img tag. In the case of refresh member is set to true an additional render_refreshed parameter is appended to this link.

Here is CaptchaAction.php:

<?php

Yii::import("system.web.widgets.captcha.CCaptchaAction");

class CaptchaAction extends CCaptchaAction
{
    const RENDER_REFRESHED_GET_VAR = "render_refreshed";

    public function run()
    {
        if (isset($_GET[self::REFRESH_GET_VAR]))  // AJAX request for regenerating code
        {
            $code = $this->getVerifyCode(true);
            echo CJSON::encode(array(
                'hash1' => $this->generateValidationHash($code),
                'hash2' => $this->generateValidationHash(strtolower($code)),
                // we add a random 'v' parameter so that FireFox can refresh the image
                // when src attribute of image tag is changed
                'url'=> $this->getController()->createUrl($this->getId(), array(
                    'v' => uniqid()
                )),
            ));
        }
        else if (isset($_GET[self::RENDER_REFRESHED_GET_VAR]))
        {
            $this->renderImage($this->getVerifyCode(true));
        }
        else
            $this->renderImage($this->getVerifyCode());
        Yii::app()->end();
    }
}

And this is Captcha.php:

<?php

Yii::import("web.system.widgets.CCaptcha");

class Captcha extends CCaptcha
{
    public $refresh = false;

    protected function renderImage()
    {
        if (!isset($this->imageOptions['id']))
            $this->imageOptions['id'] = $this->getId();

        if ($this->refresh)
        {
            $url = $this->getController()->createUrl($this->captchaAction, array(
                'v' => uniqid(),
                CaptchaAction::RENDER_REFRESHED_GET_VAR => 1
            ));
        }
        else
        {
            $url = $this->getController()->createUrl($this->captchaAction, array(
                'v' => uniqid()
            ));
        }
        $alt = isset($this->imageOptions['alt']) ? $this->imageOptions['alt'] : '';
        echo CHtml::image($url, $alt, $this->imageOptions);
    }
}

So the usage is quite simple. In action preparing model's data for site's page do the following:

...

// Creating order model's instance
$model = new MyModel();
$refreshCaptcha = true;

if (isset($_POST['MyModel']))
{
    $refreshCaptcha = false;
    ...
}

...

$this->render("myView", array(
    'model' => $model,
    'refreshCaptcha' => $refreshCaptcha
));

After that modify captcha's widget call in myView template of this page's action:

<?php 
    $this->widget("Captcha", array(
    'buttonLabel' => "Generate another code",
    'showRefreshButton' => false,
    'clickableImage' => true,
    'refresh' => $refreshCaptcha
));

and don't forget to modify actions method of page's controller by replacing CCaptchaAction class by CaptchaAction:

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

Seems to me that it works as expected. Hope this will help somebody.

like image 20
ezze Avatar answered Nov 09 '22 18:11

ezze


Here's another (correct?) way to do it (discussion):

Yii::app()->getController()->createAction('captcha')->getVerifyCode(true);
like image 24
Ben Avatar answered Nov 09 '22 17:11

Ben