atwix

bigCaptcha1

Magento 1.7 came with a lot of changes and new features – one substantial module that was added by default is captcha. So, we would like to shed the light on this amazing feature. You know how many robots/spam bots browse the internet searching unprotected forms. Argh.. Therefore, if you have opened form for unregistered users you need to protect it with captcha to avoid spam attacks :) In this article we’ll show you how to add captcha to the form you want. Let’s start.
First of all, we should register our captcha module, note that captcha functionality also can be a part of your own modules – but we would like to create the separate one. This is registration file Atwix_Captcha.xml:

<config>
    <modules>
        <Atwix_Captcha>
            <active>true</active>
            <codePool>community</codePool>
        </Atwix_Captcha>
    </modules>
</config>

The next step is creating the config.xml file under app/code/community/Atwix/Captcha/etc. Actually, we need to declare only blocks, layout and controllers. Here you go:

<?xml version="1.0"?>
<config>
    <global>
        <blocks>
            <atwix_captcha>
                <class>Atwix_Captcha_Block</class>
            </atwix_captcha>
        </blocks>
    </global>
    <frontend>
        <routers>
            <atwix_captcha>
                <use>standard</use>
                <args>
                    <module>Atwix_Captcha</module>
                    <frontName>atwix-captcha</frontName>
                </args>
            </atwix_captcha>
        </routers>
        <layout>
            <updates>
                <atwix_captcha>
                    <file>atwix_captcha.xml</file>
                </atwix_captcha>
            </updates>
        </layout>
    </frontend>
</config>

After this, we need to create two blocks, which are called Captcha.php and Captcha/Zend.php. See below how to do it.
Captcha.php:

class Atwix_Captcha_Block_Captcha extends Mage_Captcha_Block_Captcha
{
    /**
     * Renders captcha HTML (if required)
     *
     * @return string
     */
    protected function _toHtml()
    {
        $blockPath = 'atwix_captcha/captcha_zend';
        $block = $this->getLayout()->createBlock($blockPath);
        $block->setData($this->getData());
        return $block->toHtml();
    }
}

Captcha/Zend.php:

class Atwix_Captcha_Block_Captcha_Zend extends Mage_Captcha_Block_Captcha_Zend
{
    /**
     * Renders captcha HTML (if required)
     *
     * @return string
     */
    protected function _toHtml()
    {
        $this->getCaptchaModel()->generate();

        if (!$this->getTemplate()) {
            return '';
        }
        $html = $this->renderView();

        return $html;
    }

    /**
     * Returns URL to controller action which returns new captcha image
     *
     * @return string
     */
    public function getRefreshUrl()
    {
        return Mage::getUrl(
            Mage::app()->getStore()->isAdmin() ? 'adminhtml/refresh/refresh' : 'atwix-captcha/captcha/refresh',
            array('_secure' => Mage::app()->getStore()->isCurrentlySecure())
        );
    }
}

Look through the getRefreshUrl() function, it calls controller that we should create to provide refreshing captcha image:

class Atwix_Captcha_CaptchaController extends Mage_Core_Controller_Front_Action
{
    /**
     * Refreshes captcha and returns JSON encoded URL to image (AJAX action)
     * Example: {'imgSrc': 'http://example.com/media/captcha/67842gh187612ngf8s.png'}
     *
     * @return null
     */
    public function refreshAction()
    {
        $formId = $this->getRequest()->getPost('formId', false);
        if ($formId) {
            $captchaModel = Mage::helper('captcha')->getCaptcha($formId);
            $this->getLayout()->createBlock('atwix_captcha/captcha_zend')->setFormId($formId)->setIsAjax(true)->toHtml();
            $this->getResponse()->setBody(json_encode(array('imgSrc' => $captchaModel->getImgSrc())));
        }
        $this->setFlag('', self::FLAG_NO_POST_DISPATCH, true);
    }
}

That’s it, functionality that allows us to use captcha is ready, the next task will be creating form. Supposing that we have layout XML and template with form, take a look at the following example:

<?xml version="1.0"?>
<layout>
    <atwix_captcha_test_form>
        <reference name="root">
            <action method="setTemplate"><template>page/1column.phtml</template></action>
        </reference>
        <reference name="head">
            <action method="addJs"><file>mage/captcha.js</file></action>
        </reference>
        <reference name="content">
            <block type="core/template" name="atwix.test" template="atwix.phtml">
                <block type="atwix_captcha/captcha" name="captcha">
                    <action method="setFormId">
                        <id>form-validate-captcha</id>
                    </action>
                    <action method="setImgWidth">
                        <width>300</width>
                    </action>
                    <action method="setImgHeight">
                        <height>40</height>
                    </action>
                </block>
            </block>
        </reference>
    </atwix_captcha_test_form>
</layout>

Let’s get across XML below:

<block type="atwix_captcha/captcha" name="captcha">
    <action method="setFormId">
        <id>form-validate-captcha</id>
    </action>
    <action method="setImgWidth">
        <width>300</width>
    </action>
    <action method="setImgHeight">
        <height>40</height>
    </action>
</block>

That is captcha block, moreover – setImgHeight and setImgWidth functions allow us to set captcha size, the setFormId function defines unique identificator for captcha that’s required. Note, that such form id is not actual form id. So, make sure that <form id=”test” and <action method=”setFormId”><id>form-validate-captcha</id> are not the same values.

You can see that we have template atwix.phtml with form HTML, and following code snippet allows us to render captcha:

<div><?php echo $this->getChildHtml('captcha') ?></div>

By the way, for rendering this page we need to declare controller TestController.php under app/code/community/Atwix/Captcha/controllers, example:

class Atwix_Captcha_TestController extends Mage_Core_Controller_Front_Action
{
    public function formAction()
    {
        $this->loadLayout();
        $this->renderLayout();
    }
}

Note: if you run Magento Enterprise edition, captcha won’t work on the CMS page because of the cache. To get captcha working with Magento’s Full page cache just add the following code after form closing tag:

<script type="text/javascript">
    //<![CDATA[
    $('form-validate-captcha').captcha.refresh($('catpcha-reload'));
    //]]>
</script>

And finally, you should add captcha validation in the controller that processes your form:

$_data = $this->getRequest()->getPost();
$_captcha = Mage::getModel('customer/session')->getData('form-validate-captcha_word');
if ($_captcha['data'] == $_data['captcha']['form-validate-captcha']) {
    echo 'Well Done!';
} else {
    echo 'You\'re disgusting robot!';
}

That’s all.

Thanks for reading and best regards from the Atwix team :)



Enjoyed this post? us on Facebook and on Twitter to stay tuned.

  • http://www.facebook.com/erinregan Erin Moss Regan

    Would you please specify the exact paths *where* all of these files should be created?

    • NickKravchuk

      Hi, Erin!
      here is list of the all files, see below:
      config files:
      app/etc/modules/Atwix_Captcha.xml
      app/code/community/Atwix/Captcha/etc/config.xml

      Blocks:
      app/code/community/Atwix/Captcha/Block/Captcha.php
      app/code/community/Atwix/Captcha/Block/Captcha/Zend.php

      controllers:
      app/code/community/Atwix/Captcha/controllers/CaptchaController.php
      app/code/community/Atwix/Captcha/controllers/TestController.php

      layout file you can place under:
      app/design/frontend/base/default/layout/yourlayout.xml

      template file located under
      app/design/frontend/base/default/template/atwix.phtml

      Good luck :)

  • Manimaran Kalimuthu

    Hi Nick,

    Nice tutorial and good explanation with standard code style!!!!!!!!!!!!!!!!!

    Your Developer guys Rock!!!

    Thanks.

  • http://www.yandroidapps.com/android-application-development/ Android Apps Development

    Useful tutorials with easy and understandable language.. This surely help many peoples… Keep sharing

  • Sigmund

    Great post! But I think there’s a typo in Captcha/Zend.php:

    This line:
    Mage::app()->getStore()->isAdmin() ? ‘adminhtml/refresh/refresh’ : ‘atwix-captcha/captcha/refresh’,

    Should be:
    Mage::app()->getStore()->isAdmin() ? ‘adminhtml/refresh/refresh’ : ‘atwix_captcha/captcha/refresh’,

    I should be an underscore. :)

    For some reason, the captcha validation code you provided for the controller did not work for me.

    I ended up using this instead.

    $formId = ‘feedback-validate-captcha’;
    $captchaModel = Mage::helper(‘captcha’)->getCaptcha($formId);
    $word = $this->_getCaptchaString($this->getRequest(), $formId); // Get the input from user.

    if (!$captchaModel->isCorrect($word)) { // Check if captcha input is correct.
    $error = true;
    }

    Any thoughts?

    Thanks.

    • NickKravchuk

      Hi, no typo in Captcha/Zend.php, ‘atwix-captcha’ is frontname, I had typo in config.xml, you should use ‘atwix-captcha’ instead of ‘captcha’ frontname in controller declaration scope, thanks for notice, also you provide the great way to check if the captcha is correct.

  • Sigmund

    Great post!

    I notice that there might be a typo in Captcha/Zend.php:
    public function getRefreshUrl()
    {
    return Mage::getUrl(
    Mage::app()->getStore()->isAdmin() ? ‘adminhtml/refresh/refresh’ : ‘atwix-captcha/captcha/refresh’,
    array(‘_secure’ => Mage::app()->getStore()->isCurrentlySecure())
    );
    }

    The line Mage::app()->getStore()->isAdmin() ? ‘adminhtml/refresh/refresh’ : ‘atwix-captcha/captcha/refresh’, should be Mage::app()->getStore()->isAdmin() ? ‘adminhtml/refresh/refresh’ : ‘atwix_captcha/captcha/refresh’,

    Also, the code for the controller did not work for me for some reason. :(

    Thanks.

  • John

    Great!, thakns for sharing.
    Just a little problem, I’m stuck to make working the captcha refrech stuff, any return about that?

    • NickKravchuk

      Hi John, refreshing is provided by AJAX, so you should check what result Ajax returns you after pushing refresh button. Good luck.

  • NickKravchuk

    Hi Bullseye, you should add captcha validation code into controller that process your form you can check the action attribute of your form and find controller file. For example if you have action=”magento.dev/test/index/save” you should find file IndexController.php of module Test and put validation code into the saveAction function, Good luck.