30

I'm developing a custom module which contains a form submission. I would like to add a captcha to it. And we want to use the Magento default captcha library so that the captcha is consistent with the one in registration form.

38

You need follow some step for using magento captcha into custom module.

Step 1:

Create Vendor/Module/etc/config.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <customer>
            <captcha>
                <shown_to_logged_in_user>
                    <custom_form>1</custom_form>
                </shown_to_logged_in_user>
                <always_for>
                    <custom_form>1</custom_form>
                </always_for>
            </captcha>
        </customer>
        <captcha translate="label">
            <frontend>
                <areas>
                    <custom_form>
                        <label>Custom Form</label>
                    </custom_form>
                </areas>
            </frontend>
        </captcha>
    </default>
</config>

Step 2:

Goto Admin -> Stores -> Configuration -> Customer -> Customer Configuration -> Captcha and configure. You can able to see new forms value Custom Form

Step 3:

Create Vendor/Module/view/frontend/layout/yourroutid_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <title>Custom Form</title>
    </head>
    <body>
        <referenceContainer name="content">
            <block class="Vendor\Module\Block\CaptchaForm" name="contactForm" template="Vendor_Module::captchaform.phtml">
                <container name="form.additional.info" label="Form Additional Info">
                    <block class="Magento\Captcha\Block\Captcha" name="captcha" after="-" cacheable="false">
                        <action method="setFormId">
                            <argument name="formId" xsi:type="string">custom_form</argument>
                        </action>
                        <action method="setImgWidth">
                            <argument name="width" xsi:type="string">230</argument>
                        </action>
                        <action method="setImgHeight">
                            <argument name="width" xsi:type="string">50</argument>
                        </action>
                    </block>
                </container>
            </block>
        </referenceContainer>
        <referenceBlock name="head.components">
            <block class="Magento\Framework\View\Element\Js\Components" name="captcha_page_head_components" template="Magento_Captcha::js/components.phtml"/>
        </referenceBlock>
    </body>
</page>

Step 4:

Create Vendor/Module/Block/CaptchaForm.php

namespace Vendor\Module\Block;


class CaptchaForm extends \Magento\Framework\View\Element\Template
{
    public function getFormAction()
    {
        return $this->getUrl('yourroute/index/post', ['_secure' => true]);
    }
}

Step 5:

Create Vendor/Moduel/view/frontend/templates/captchaform.phtml

<form class="form contact"
      action="<?php /* @escapeNotVerified */ echo $block->getFormAction(); ?>"
      id="contact-form"
      method="post"
      data-hasrequired="<?php /* @escapeNotVerified */ echo __('* Required Fields') ?>"
      data-mage-init='{"validation":{}}'>
    <fieldset class="fieldset">
        <legend class="legend"><span><?php /* @escapeNotVerified */ echo __('Write Us') ?></span></legend><br />

        <div class="field name required">
            <label class="label" for="name"><span><?php /* @escapeNotVerified */ echo __('Name') ?></span></label>
            <div class="control">
                <input name="name" id="name" title="<?php /* @escapeNotVerified */ echo __('Name') ?>" value="" class="input-text" type="text" data-validate="{required:true}"/>
            </div>
        </div>
        <div class="field email required">
            <label class="label" for="email"><span><?php /* @escapeNotVerified */ echo __('Email') ?></span></label>
            <div class="control">
                <input name="email" id="email" title="<?php /* @escapeNotVerified */ echo __('Email') ?>" value="" class="input-text" type="email" data-validate="{required:true, 'validate-email':true}"/>
            </div>
        </div>
        <?php echo $block->getChildHtml('form.additional.info'); ?>
    </fieldset>
    <div class="actions-toolbar">
        <div class="primary">
            <input type="hidden" name="hideit" id="hideit" value="" />
            <button type="submit" title="<?php /* @escapeNotVerified */ echo __('Submit') ?>" class="action submit primary">
                <span><?php /* @escapeNotVerified */ echo __('Submit') ?></span>
            </button>
        </div>
    </div>
</form>

Now you can able to see captcha into your form. Now need to validation your captcha using observer. So I use post controller predispatch event for validation.

Step 6:

Create Vendor/Module/etc/frontend/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="controller_action_predispatch_yourroute_index_post">
        <observer name="captcha_custom_form" instance="Vendor\Module\Observer\CheckCustomFormObserver" />
    </event>
</config>

Step 7:

Create Vendor/Module/Observer/CheckCustomFormObserver.php

namespace Vendor\Module\Observer;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Captcha\Observer\CaptchaStringResolver;

class CheckCustomFormObserver implements ObserverInterface
{
    /**
     * @var \Magento\Captcha\Helper\Data
     */
    protected $_helper;

    /**
     * @var \Magento\Framework\App\ActionFlag
     */
    protected $_actionFlag;

    /**
     * @var \Magento\Framework\Message\ManagerInterface
     */
    protected $messageManager;

    /**
     * @var \Magento\Framework\App\Response\RedirectInterface
     */
    protected $redirect;

    /**
     * @var CaptchaStringResolver
     */
    protected $captchaStringResolver;

    /**
     * @var DataPersistorInterface
     */
    private $dataPersistor;

    /**
     * @param \Magento\Captcha\Helper\Data $helper
     * @param \Magento\Framework\App\ActionFlag $actionFlag
     * @param \Magento\Framework\Message\ManagerInterface $messageManager
     * @param \Magento\Framework\App\Response\RedirectInterface $redirect
     * @param CaptchaStringResolver $captchaStringResolver
     */
    public function __construct(
        \Magento\Captcha\Helper\Data $helper,
        \Magento\Framework\App\ActionFlag $actionFlag,
        \Magento\Framework\Message\ManagerInterface $messageManager,
        \Magento\Framework\App\Response\RedirectInterface $redirect,
        CaptchaStringResolver $captchaStringResolver
    ) {
        $this->_helper = $helper;
        $this->_actionFlag = $actionFlag;
        $this->messageManager = $messageManager;
        $this->redirect = $redirect;
        $this->captchaStringResolver = $captchaStringResolver;
    }

    /**
     * Check CAPTCHA on Custom Form
     *
     * @param \Magento\Framework\Event\Observer $observer
     * @return void
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $formId = 'custom_form';
        $captcha = $this->_helper->getCaptcha($formId);
        if ($captcha->isRequired()) {
            /** @var \Magento\Framework\App\Action\Action $controller */
            $controller = $observer->getControllerAction();
            if (!$captcha->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) {
                $this->messageManager->addError(__('Incorrect CAPTCHA.'));
                $this->getDataPersistor()->set($formId, $controller->getRequest()->getPostValue());
                $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true);
                $this->redirect->redirect($controller->getResponse(), 'yourroute/index/index');
            }
        }
    }

    /**
     * Get Data Persistor
     *
     * @return DataPersistorInterface
     */
    private function getDataPersistor()
    {
        if ($this->dataPersistor === null) {
            $this->dataPersistor = ObjectManager::getInstance()
                ->get(DataPersistorInterface::class);
        }

        return $this->dataPersistor;
    }
}
| improve this answer | |
  • Very detailed. I will try it out. – Paul Sep 5 '16 at 15:53
  • @Sohel Rana how can be add it into product review form – supriya mishra Jan 12 '17 at 5:57
  • @supriyamishra need to check – Sohel Rana Jan 12 '17 at 6:19
  • 1
    Hi The captcha is displayed but the observer controller_action_predispatch_** is i think not working as this captcha is not being validated – AbdulBasit Feb 20 '18 at 12:06
  • 1
    I resolved above error but i can't see the captcha in my custom form – jafar pinjar Aug 22 '18 at 6:59
4

Implementing Google reCAPTCHA

Magento 2.3 requires the module MSP_ReCaptcha which can be used to implement reCAPTCHA in your custom forms. To do so you can extend the module with the following steps:

1. First off add a configuration option to the admin backend:

app/code/Vendor/Module/etc/adminhtml/system.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="msp_securitysuite_recaptcha">
            <group id="frontend">
                <label>Frontend</label>
                <field id="enabled_custom" translate="label" type="select" sortOrder="300" showInDefault="1"
                       showInWebsite="1" showInStore="0" canRestore="1">
                    <label>Use in Custom Form</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="enabled">1</field>
                    </depends>
                </field>
            </group>
        </section>
    </system>
</config>

Note: It here you need to change the field id.

2. Add a default configuration:

app/code/Vendor/Module/etc/config.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <msp_securitysuite_recaptcha>
            <frontend>
                <enabled_custom>1</enabled_custom>
            </frontend>
        </msp_securitysuite_recaptcha>
    </default>
</config>

Note: Set <enabled_custom> to the id you set in the first step.

3. You need to setup virtual types which will be used by the module:

app/code/Vendor/Module/etc/frontend/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <!-- Module Form ReCaptcha -->
    <virtualType name="Vendor\Module\Model\Provider\Failure\RedirectUrl\CustomForm"
                 type="MSP\ReCaptcha\Model\Provider\Failure\RedirectUrl\SimpleUrlProvider">
        <arguments>
            <argument name="urlPath" xsi:type="string">custom/index/index</argument>
        </arguments>
    </virtualType>
    <virtualType name="Vendor\Module\Model\Provider\Failure\CustomFormObserver"
                 type="MSP\ReCaptcha\Model\Provider\Failure\ObserverRedirectFailure">
        <arguments>
            <argument name="redirectUrlProvider"
                      xsi:type="object">Vendor\Module\Model\Provider\Failure\RedirectUrl\CustomForm</argument>
        </arguments>
    </virtualType>
    <virtualType name="Vendor\Module\Model\Provider\IsCheckRequired\Frontend\CustomForm"
                 type="MSP\ReCaptcha\Model\IsCheckRequired">
        <arguments>
            <argument name="enableConfigFlag"
                      xsi:type="string">msp_securitysuite_recaptcha/frontend/enabled_custom</argument>
            <argument name="area" xsi:type="string">frontend</argument>
        </arguments>
    </virtualType>
    <virtualType name="Vendor\Module\Observer\Frontend\CustomFormObserver"
                 type="MSP\ReCaptcha\Observer\ReCaptchaObserver">
        <arguments>
            <argument name="isCheckRequired"
                      xsi:type="object">Vendor\Module\Model\Provider\IsCheckRequired\Frontend\CustomForm</argument>
            <argument name="failureProvider"
                      xsi:type="object">Vendor\Module\Model\Provider\Failure\CustomFormObserver</argument>
        </arguments>
    </virtualType>
</config>

Note: You have to change <argument name="urlPath" xsi:type="string">custom/index/index</argument> to the path you want your user to be redirected back in case of failure.

4. Define a observer that will handle the validation:

app/code/Vendor/Module/etc/frontend/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="controller_action_predispatch_custom_index_index">
        <observer name="vendor_module_recaptcha"
                  instance="Vendor\Module\Observer\Frontend\CustomFormObserver"/>
    </event>
</config>

Note: Make sure you use the observer defined in di.xml in step 3. You have to set the correct event name too!

5. Add the MSP_ReCaptcha block to your form block:

app/code/Vendor/Module/view/frontend/layout/custom_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="custom_form_block">
            <block class="MSP\ReCaptcha\Block\Frontend\ReCaptcha" name="msp-recaptcha" after="-"
                   template="MSP_ReCaptcha::msp_recaptcha.phtml"
                   cacheable="false"
                   ifconfig="msp_securitysuite_recaptcha/frontend/enabled_custom">

                <arguments>
                    <argument name="jsLayout" xsi:type="array">
                        <item name="components" xsi:type="array">
                            <item name="msp-recaptcha" xsi:type="array">
                                <item name="component" xsi:type="string">MSP_ReCaptcha/js/reCaptcha</item>
                                <item name="zone" xsi:type="string">custom_form</item>
                            </item>
                        </item>
                    </argument>
                </arguments>
            </block>
        </referenceBlock>
    </body>
</page>

6. The last step is to output the MSP_ReCaptcha block within your form:

app/code/Vendor/Module/view/frontend/templates/custom_form.phtml

<form>
    <?= $block->getChildHtml('msp-recaptcha') ?>
</form>

These steps are based on a form in the frontend area. You can apply reCAPTCHA to you adminhtml forms too. To do so you need to change the area for di.xml, events.xml, layout.xml and template.phtml to adminhtml.

| improve this answer | |
2

For those of you that cannot get this to work you may need to do what I did:

The reason you captcha may not be displaying is because the base settings are to use the Default captcha block which in the _toHtml does a check to see if the captcha is required.

If you have you settings for captcha to always show than you probably did not run into this issue however if it is not set to always show captchas and you do not want to always show captchas (ie account create/ login etc) than you need to set the logic for only your custom captcha to "Always be required".

on line 69 of vendor/magento/module-captcha/Block/Captcha/DefaultCaptcha.php you will see:

    /**
 * Renders captcha HTML (if required)
 *
 * @return string
 */
protected function _toHtml()
{

    if ($this->getCaptchaModel()->isRequired()) {
        $this->getCaptchaModel()->generate();
        return parent::_toHtml();
    }
    return '';
}

$this->getCaptchaModel() calls $this->_captchaData->getCaptcha() which is in vendor/magento/module-captcha/Helper/Data.php

    /**
 * Get Captcha
 *
 * @param string $formId
 * @return \Magento\Captcha\Model\CaptchaInterface
 */
public function getCaptcha($formId)
{
    if (!array_key_exists($formId, $this->_captcha)) {
        $captchaType = ucfirst($this->getConfig('type'));
        if (!$captchaType) {
            $captchaType = self::DEFAULT_CAPTCHA_TYPE;
        } elseif ($captchaType == 'Default') {
            $captchaType = $captchaType . 'Model';
        }

        $this->_captcha[$formId] = $this->_factory->create($captchaType, $formId);
    }
    return $this->_captcha[$formId];
}

Here the getCaptcha method checks the config value for the type of captcha to render and loads its factory in with $this->_factory->create()

However stepping into this factory class you will see

 public function create($captchaType, $formId)
{
    $className = 'Magento\Captcha\Model\\' . ucfirst($captchaType);

    $instance = $this->_objectManager->create($className, ['formId' => $formId]);
    if (!$instance instanceof \Magento\Captcha\Model\CaptchaInterface) {
        throw new \InvalidArgumentException(
            $className . ' does not implement \Magento\Captcha\Model\CaptchaInterface'
        );
    }
    return $instance;
}

The issue here is that no matter what the factory will look in the Magento Captcha module for any Factory model.. so

We need to create a plugin to wrap around the helper and check for our form key and if it is our form key being used we need to create a new factory class that loads our model that extends \Magento\Captcha\Model\DefaultModel and overides the isRequired() method. Something that looks like this:

in \Your\Module\etc\di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

<!--Custom Captcha-->
<type name="\Magento\Captcha\Helper\Data">
    <plugin name="custom-captcha" type="Your\Module\Plugin\Helper\CaptchaData" />
</type>

in Your\Module\Plugin\Helper\CaptchaData

<?php

namespace Your\Module\Plugin\Helper;

class CaptchaData
{
protected $_captcha = [];

public function __construct(
    \Your\Module\Model\CaptchaFactory $captchaFactory
) {
    $this->captchaFactory = $captchaFactory;
}

/**
 * @param \Magento\Captcha\Helper\Data $subject
 * @param \Closure $proceed
 * @param $formId
 * @return mixed
 */
public function aroundGetCaptcha(\Magento\Captcha\Helper\Data $subject, \Closure $proceed, $formId)
{
    if ($formId == 'your_form_key') {
        $this->_captcha[$formId] = $this->captchaFactory->create();
        return $this->_captcha[$formId];

    }
    return $proceed($formId);

}

}

in \Your\Module\Model\CaptchaFactory

<?php
/**
* Captcha model factory
*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Your\Module\Model;

class CaptchaFactory
{
/**
 * @var \Magento\Framework\ObjectManagerInterface
 */
protected $_objectManager;

/**
 * @param \Magento\Framework\ObjectManagerInterface $objectManager
 */
public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager)
{
    $this->_objectManager = $objectManager;
}

/**
 * Get captcha instance
 *
 * @param string $captchaType
 * @param string $formId
 * @return \Magento\Captcha\Model\CaptchaInterface
 * @throws \InvalidArgumentException
 */
public function create()
{
    $instance = $this->_objectManager->create('Your\Module\Model\Captcha', ['formId' => 'event_subscriber']);
    if (!$instance instanceof \Magento\Captcha\Model\CaptchaInterface) {
        throw new \InvalidArgumentException(
            'Your\Module\Model\Captcha does not implement \Magento\Captcha\Model\CaptchaInterface'
        );
    }
    return $instance;
}
}

and finally your model to overide the is required param in \Your\Module\Model\Captcha:

<?php

namespace Your\Module\Model;

class Captcha extends \Magento\Captcha\Model\DefaultModel
{
    public function isRequired($login = null)
    {
        return true;
    }
 }
| improve this answer | |
0

I need captcha in newsletter subscriber page thanks sohan i was used newsletter observer and captcha work in newsletter page for me.

1) app/code/Vendorname/Modulename/etc/config.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <customer>
            <captcha>
                <shown_to_logged_in_user>
                    <custom_newsletter>1</custom_newsletter>
                </shown_to_logged_in_user>
                <always_for>
                    <custom_newsletter>1</custom_newsletter>
                </always_for>
            </captcha>
        </customer>
        <captcha translate="label">
            <frontend>
                <areas>
                    <custom_newsletter>
                        <label>Newsletter Form</label>
                    </custom_newsletter>
                </areas>
            </frontend>
        </captcha>
    </default>
</config>

2) Goto 'Admin -> Stores -> Configuration -> Customer -> Customer Configuration -> Captcha' and configure. You can able to see new forms value 'Newsletter Form'.

3) copy layout file in theme (default.xml)

<block class="Magento\Newsletter\Block\Subscribe" name="subscribe form " template="Magento_Newsletter::subscribe.phtml">
                <container name="form.additional.info" label="Form Additional Info">
                    <block class="Magento\Captcha\Block\Captcha" name="captcha" after="-" cacheable="false">
                        <action method="setFormId">
                            <argument name="formId" xsi:type="string">custom_newsletter</argument>
                        </action>
                        <action method="setImgWidth">
                            <argument name="width" xsi:type="string">230</argument>
                        </action>
                        <action method="setImgHeight">
                            <argument name="width" xsi:type="string">50</argument>
                        </action>
                    </block>
                </container>

4) Create observer -> create event.xml file in app/code/Vendorname/Modulename/etc/frontend

 <?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="controller_action_predispatch_newsletter_subscriber_new">
        <observer name="captcha_newletter_form" instance="Vendorname/Modulename/Observer\CheckCustomFormObserver" />
    </event>
</config>

5) Create observer model and check captcha app/code/Vendorname/Modulename/Observer/CheckCustomFormObserver.php

public function execute(\Magento\Framework\Event\Observer $observer)
        {   $formId = 'custom_newsletter';
            $captcha = $this->_helper->getCaptcha($formId);
            if ($captcha->isRequired()) {
                /** @var \Magento\Framework\App\Action\Action $controller */
                $controller = $observer->getControllerAction();
                $params=$controller->getRequest()->getPost();
                $currentpage = $params['currentpage'];


                if (!$captcha->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) {                
                    $this->messageManager->addError(__('Incorrect CAPTCHA.'));
                    $this->getDataPersistor()->set($formId, $controller->getRequest()->getPostValue());
                    $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true);
                    $this->redirect->redirect($controller->getResponse(), $currentpage);
                }
            }
        }
| improve this answer | |
0

Here's the easiest way to add Google Recaptcha in custom form,

Add your configuration in VendorName/ModuleName/etc/adminhtml/system.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="msp_securitysuite_recaptcha">
            <group id="frontend">
                <label>Frontend</label>
                <field id="enabled_customform" translate="label" type="select" sortOrder="300" showInDefault="1"
                       showInWebsite="1" showInStore="0" canRestore="1">
                    <label>Use in Custom Form</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="enabled">1</field>
                    </depends>
                </field>
            </group>
        </section>
    </system>
</config>

Add below block in your refranceBlock in your Frontend Layout File:

  • Here msp_securitysuite_recaptcha/frontend/enabled_customform is your configuration path.

  • <item name="zone" xsi:type="string">index</item> index is controller name change accordingly.

    <block class="MSP\ReCaptcha\Block\Frontend\ReCaptcha" name="msp-recaptcha" after="-"
           template="MSP_ReCaptcha::msp_recaptcha.phtml"
           ifconfig="msp_securitysuite_recaptcha/frontend/enabled_customform">
        <arguments>
            <argument name="jsLayout" xsi:type="array">
                <item name="components" xsi:type="array">
                    <item name="msp-recaptcha" xsi:type="array">
                        <item name="component" xsi:type="string">MSP_ReCaptcha/js/reCaptcha</item>
                        <item name="zone" xsi:type="string">index</item>
                    </item>
                </item>
            </argument>
        </arguments>
    </block>
    

In phtml file add below code to show captcha:

<?= $block->getChildHtml('msp-recaptcha') ?>

I hope this help!

| improve this answer | |
0

Thank you all for your efforts in trying to resolve this issue.

I tried the above solutions pretty much still could not get the Captcha to show earlier, (DOfficial & Ghulam M)'s edited/answer about the restrictions put in for whatever silly reason gave me an idea, ... rather than creating all sorts of objects and files etc, for me it made more sense to just create a new custom Block object (just one) to replace the Captcha with the same code, to be exact:

1) I decided not to use Magento\Captcha\Block\Captcha in my Layout XML, mainly looking at its (_toHtml) code, it's pretty useless, all it has is a little logic that creates a new block and let all logic be processed by another Block (the one it's creating dynamically), this Block is: Magento\Captcha\Block\Captcha\DefaultCaptcha which seems to be preventing the Block from working

2) Looking into the (_toHtml) code of the DefaultCaptcha Block, it has the logic of (Generate the Captcha but "only if" it's Required) otherwise cripple the HTML output no matter what Template is put in place ... I tried to trace the IsRequired function but that was taking too long and seemed like a waste of time, I'm sure somewhere down the line it would have (if "config... enabled" then "yes"), so, to make it work, and being against modifying the original Magento framework files:

My solution was (this worked 100% for me):

3) Rather than having Magento\Captcha\Block\Captcha in your Layout XML, rather create a new Block (with your own VENDOR\MODULE...) and copy it's source code from Magento\Captcha\Block\Captcha\DefaultCaptcha.

4) Change the _toHtml() function by removing the following condition of:

if ($this->getCaptchaModel()->isRequired()) {

5) i.e. Keep only the following lines:

$this->getCaptchaModel()->generate(); 
return parent::_toHtml();

6) Use the new Block in your Layout, also you may add your own Template HTML file if needed, therefore, the final code would look like this:

<?php

namespace YOURVENDOR\YOURMODULE\Block\Page;

class Captcha extends \Magento\Framework\View\Element\Template
{
//    protected $_template = 'Magento_Captcha::default.phtml';  <-- I decided to REMARK this line in either for me to use (Template=".../my_own_template.phtml") on the layout instead. If you like to use default, then you are welcome to un-remark it out. 
    protected $_captcha;
    protected $_captchaData;


    public function __construct
    (
          \Magento\Framework\View\Element\Template\Context $context
        , \Magento\Captcha\Helper\Data $captchaData,
        array $data = []
    )
    {
        parent::__construct($context, $data);
        $this->_captchaData = $captchaData;
    }


    public function getTemplate()
    {
        return $this->getIsAjax() ? '' : $this->_template;
    }


    public function getRefreshUrl()
    {
        $store = $this->_storeManager->getStore();
        return $store->getUrl('captcha/refresh', ['_secure' => $store->isCurrentlySecure()]);
    }


    protected function _toHtml()
    {
        if (true) // $this->getCaptchaModel()->isRequired()) <-- This is the line in question, replaced with (TRUE) to always go through, when I do these changes I always use (TRUE) and comment out the old line, this way I can go back one day to understand what I did after leaving this for a long time... 
        {
            $this->getCaptchaModel()->generate();
            return parent::_toHtml();
        }
        return '';
    }


    public function getCaptchaModel()
    {
        return $this->_captchaData->getCaptcha($this->getFormId());
    }

}

7) Therefore, your Layout XML line would look like this:

<block class="YOURVENDOR\YOURMODULE\Block\Page\Captcha" name="heiders.captcha" as="heiders_captcha" template="Magento_Theme::heiders/home/captcha.phtml" cacheable="false" >

8) You can also now have whatever you like in your "captcha.phtml" file, if you wonder what to put in it, then you can also copy it from:

vendor/magento/module-captcha/view/frontend/templates/default.phtml

9) This should help resolving the issue,

Again, thank you all to all the efforts you put in, I am sharing my way of resolving this issue, hopefully it helps.

Regards

H

| improve this answer | |
0

For who came here to add a reCaptcha on a popup (like login popup), here it's what I did:

YouModule/view/frontend/layout/default.xml Declare the loginPopup name component with the msp_recaptcha declaration. That layout adds the block on the page that calls a Knockout JS -> Calls a popup template.html file.

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <referenceContainer name="content">
        <block class="YourModule\Block\Account\LoginPopup" name="login-popup" as="login-popup" template="YourModule::account/login-popup.phtml">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="loginPopup" xsi:type="array">
                            <item name="component" xsi:type="string">YourModule/js/view/login-popup</item>
                            <item name="children" xsi:type="array">
                                <item name="messages" xsi:type="array">
                                    <item name="component" xsi:type="string">Magento_Ui/js/view/messages</item>
                                    <item name="displayArea" xsi:type="string">messages</item>
                                </item>
                                <item name="msp_recaptcha" xsi:type="array">
                                    <item name="component" xsi:type="string">MSP_ReCaptcha/js/reCaptcha</item>
                                    <item name="displayArea" xsi:type="string">additional-login-form-fields</item>
                                    <item name="configSource" xsi:type="string">checkoutConfig</item>
                                    <item name="reCaptchaId" xsi:type="string">msp-recaptcha-popup-login-custom</item>
                                    <item name="zone" xsi:type="string">login</item>
                                    <item name="badge" xsi:type="string">inline</item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </block>
    </referenceContainer>
</page>

Declare your block php: YourModule/Block/Account/LoginPopup.php

/**
 * Class LoginPopup
 * @package YourModule\Block\Account
 */
class LoginPopup extends \Magento\Customer\Block\Account\AuthenticationPopup
{

    /**
     * @return array
     */
    public function getConfig()
    {
        $parentConfig = parent::getConfig();
        $parentConfig['reference_url'] = $this->getCurrentUrl();

        return $parentConfig;
    }

    /**
     * @return mixed
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getCurrentUrl()
    {
        return $this->_storeManager->getStore()->getCurrentUrl(false);
    }
}

Declare: YouModule/view/frontend/templates/account/login-popup.phtml Repair that the phtml calls for the block getJsLayout method where it came from the extended block class \Magento\Customer\Block\Account\AuthenticationPopup

<?php
// @codingStandardsIgnoreFile

/** @var YouModule\Block\Account\LoginPopup $block */
?>
<div id="loginPopup" data-bind="scope:'loginPopup'" style="display: none;">
    <script>
        window.loginPopup = <?= /* @noEscape */ $block->getSerializedConfig() ?>;
    </script>
    <!-- ko template: getTemplate() --><!-- /ko -->
    <script type="text/x-magento-init">
        {
            "#loginPopup": {
                "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?>
            },
            "*": {
                "Magento_Ui/js/block-loader": "<?= $block->escapeJs($block->escapeUrl($block->getViewFileUrl('images/loader-1.gif'))) ?>"
            }
        }
    </script>
</div>

Create the JS to init the whole modal logic: YourModule/view/frontend/web/js/view/login-popup.js Repair that it includes another model/login-popup.js file and it defines template: 'YourModule/login-popup':

define([
    'jquery',
    'ko',
    'Magento_Ui/js/form/form',
    'Magento_Customer/js/action/login',
    'Magento_Customer/js/customer-data',
    'YouModule/js/model/login-popup',
    'mage/translate',
    'mage/url',
    'Magento_Ui/js/modal/alert',
    'mage/cookies',
    'mage/validation'
], function ($, ko, Component, loginAction, customerData, loginPopup, $t, url, alert) {
    'use strict';

    return Component.extend({
        registerUrl: ko.observable(window.loginPopup.customerRegisterUrl).extend({notify: 'always'}),
        forgotPasswordUrl: window.loginPopup.customerForgotPasswordUrl,
        autocomplete: window.loginPopup.autocomplete,
        modalWindow: null,
        isLoading: ko.observable(false),
        trigger: '.action.login-download',
        defaults: {
            template: 'YourModule/login-popup'
        },
        registerTitle: ko.observable(),
        registerReference: ko.observable(),

        /**
         * Init
         */
        initialize: function () {
            var self = this;

            this._super();
            url.setBaseUrl(window.loginPopup.baseUrl);
            loginAction.registerLoginCallback(function () {
                self.isLoading(false);
            });


            self.initModalTrigger();
        },

        initModalTrigger: function () {
            var self = this;
            $(this.trigger).on('click',  $.proxy(function(element){
                var newRegisterUrl = window.loginPopup.customerRegisterUrl + '?';

                var title = $(element.currentTarget).attr("data-title");
                var reference = $(element.currentTarget).attr("data-reference");
                if(title  != null) {
                    newRegisterUrl+= 'title='+(title);
                    self.registerTitle(title);
                }

                if(reference  != null) {
                    newRegisterUrl+= '&reference='+(reference);
                    self.registerReference(reference);
                }

                self.registerUrl(encodeURI(newRegisterUrl));

                if(typeof window.loginPopup.reference_url !== "undefined") {
                    $.cookie('login_redirect', window.loginPopup.reference_url);
                }

                $(loginPopup.modalWindow).modal('openModal').trigger('contentUpdated');

            }, self));
        },

        /** Init popup login window */
        setModalElement: function (element) {
            if (loginPopup.modalWindow == null) {
                this.modalWindow = loginPopup.createPopUp(element);

            }
        },

        /** Is login form enabled for current customer */
        isActive: function () {
            var customer = customerData.get('customer');

            return customer() == false; //eslint-disable-line eqeqeq
        },

        /** Show login popup window */
        showModal: function () {
            if (this.modalWindow) {
                $(this.modalWindow).modal('openModal');

            } else {
                alert({
                    content: $t('Guest checkout is disabled.')
                });
            }
        },

        /**
         * Provide login action
         *
         * @return {Boolean}
         */
        login: function (formUiElement, event) {
            var loginData = {},
                formElement = $(event.currentTarget),
                formDataArray = formElement.serializeArray();

            event.stopPropagation();
            formDataArray.forEach(function (entry) {
                loginData[entry.name] = entry.value;
            });

            if (formElement.validation() &&
                formElement.validation('isValid')
            ) {
                this.isLoading(true);
                loginAction(loginData);
            }

            return false;
        }
    });
});

Create the JS that open the modals: YouModule/view/frontend/web/js/model/login-popup.js


define([
    'jquery',
    'Magento_Ui/js/modal/modal'
], function ($, modal) {
    'use strict';

    return {
        modalWindow: null,

        /**
         * Create popUp window for provided element
         *
         * @param {HTMLElement} element
         */
        createPopUp: function (element) {
            var options = {
                'type': 'popup',
                'modalClass': 'popup-login popup-authentication',
                'focus': '[name=username]',
                'responsive': true,
                'innerScroll': true,
                // 'trigger': '.action.login-download',
                'buttons': [],
                'title': 'Any Title'
            };

            this.modalWindow = element;
            modal(options, $(this.modalWindow));
        },

        /** Show login popup window */
        showModal: function () {
            $(this.modalWindow).modal('openModal').trigger('contentUpdated');

        }
    };
});

Declare the template html: YourModule/view/frontend/web/template/login-popup.html The most important on that file is the call for additional-login-form-fields area where we configured on layout/default.xml to add the recaptcha inside that displayArea

<div class="block-popup"
     data-bind="afterRender: setModalElement, blockLoader: isLoading"
     style="display: none">
    <div class="block block-new-customer"
         data-bind="attr: {'data-label': $t('or')}">
        <div class="block-title">
            <strong id="block-new-customer-heading"
                    role="heading"
                    aria-level="2"
                    data-bind="i18n: 'Create an account'"></strong>
        </div>
        <div class="block-content" aria-labelledby="block-new-customer-heading">
            <p data-bind="i18n: 'Creating an account has many benefits:'"></p>
            <ul>
                <li data-bind="i18n: 'Download documents'"></li>
                <li data-bind="i18n: 'Track Downloads history'"></li>
            </ul>
            <div class="actions-toolbar">
                <div class="primary">
                    <a class="action action-register primary" data-bind="attr: {href: registerUrl}">
                        <span data-bind="i18n: 'Create an Account'"></span>
                    </a>
                </div>
            </div>
        </div>
    </div>

    <div class="block block-customer-login"
         data-bind="attr: {'data-label': $t('or')}">
        <div class="block-title">
            <strong id="block-customer-login-heading"
                    role="heading"
                    aria-level="2"
                    data-bind="i18n: 'Log In'"></strong>
        </div>
        <!-- ko foreach: getRegion('messages') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->
        <!-- ko foreach: getRegion('before') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!-- /ko -->
        <div class="block-content" aria-labelledby="block-customer-login-heading">
            <form class="form form-login"
                  method="post"
                  data-bind="event: {submit: login }"
                  id="login-form__login">
                <div class="fieldset login" data-bind="attr: {'data-hasrequired': $t('* Required Fields')}">
                    <input type="hidden" name="registration_url" id="reference" data-bind="attr: {value: registerReference}">
                    <input type="hidden" name="downloaded_file" id="title" data-bind="attr: {value: registerTitle}">
                    <div class="field email required">
                        <label class="label" for="customer-email"><span data-bind="i18n: 'Email Address'"></span></label>
                        <div class="control">
                            <input name="username"
                                   id="customer-email__login"
                                   type="email"
                                   class="input-text"
                                   data-mage-init='{"mage/trim-input":{}}'
                                   data-bind="attr: {autocomplete: autocomplete}"
                                   data-validate="{required:true, 'validate-email':true}">
                        </div>
                    </div>
                    <div class="field password required">
                        <label for="pass" class="label"><span data-bind="i18n: 'Password'"></span></label>
                        <div class="control">
                            <input name="password"
                                   type="password"
                                   class="input-text"
                                   id="pass__login"
                                   data-bind="attr: {autocomplete: autocomplete}"
                                   data-validate="{required:true}">
                        </div>
                    </div>
                    <!-- ko foreach: getRegion('additional-login-form-fields') -->
                    <!-- ko template: getTemplate() --><!-- /ko -->
                    <!-- /ko -->
                    <div class="actions-toolbar">
                        <input name="context" type="hidden" value="checkout" />
                        <div class="primary">
                            <button type="submit" class="action action-login secondary" name="send" id="send2__login">
                                <span data-bind="i18n: 'Sign In'"></span>
                            </button>
                        </div>
                        <div class="secondary">
                            <a class="action" data-bind="attr: {href: forgotPasswordUrl}">
                                <span data-bind="i18n: 'Forgot Your Password?'"></span>
                            </a>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

Now you need to add the configuration into the msRecaptcha component plugin. You must add it after calling the getJsLayout method. Here it's a way I added with a plugin but you can rewrite it directly on the block.php file. Repair the name we gave to the component: loginPopup where is the same on default.xml layout. Create: YouModule/Plugin/Block/Account/LoginPopup.php Reference on vendor/msp/recaptcha/Plugin/Block/Account/AuthenticationPopupPlugin.php

use Magento\Customer\Block\Account\AuthenticationPopup;
use MSP\ReCaptcha\Model\Config;
use MSP\ReCaptcha\Model\LayoutSettings;
use Zend\Json\Json;

class LoginPopup
{
    /**
     * @var LayoutSettings
     */
    private $layoutSettings;

    /**
     * @var Config
     */
    private $config;

    /**
     * @param LayoutSettings $layoutSettings
     * @param Config|null $config
     */
    public function __construct(
        LayoutSettings $layoutSettings,
        Config $config
    ) {
        $this->layoutSettings = $layoutSettings;
        $this->config = $config;
    }

    /**
     * @param AuthenticationPopup $subject
     * @param string $result
     * @return string
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function afterGetJsLayout(AuthenticationPopup $subject, $result)
    {
        $layout = Json::decode($result, Json::TYPE_ARRAY);

        if ($this->config->isEnabledFrontend()) {
            $layout['components']['loginPopup']['children']['msp_recaptcha']['settings']
                = $this->layoutSettings->getCaptchaSettings();
        }

        if(
            isset($layout['components']['loginPopup']['children']['msp_recaptcha'])
            && !$this->config->isEnabledFrontend()
        ) {
            unset($layout['components']['loginPopup']['children']['msp_recaptcha']);
        }

        return Json::encode($layout);
    }
}

And don't forget to add the plugin call to you frontend/di.xml: YourModule/etc/frontend/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="YourModule\Block\Account\LoginPopup">
        <plugin sortOrder="1" name="YourModule_mSPReCaptchaAuthenticationPopup"
                type="YourModule\Plugin\Block\Account\LoginPopup"/>
    </type>
</config>

I hope it helps because I had to research the MspreCaptcha Module to understand how to use it.

| improve this answer | |
0
<div class="recaptcha" style="overflow:hidden;position:relative;">
   <input type="checkbox" id="recaptcha-verification-1" name="recaptcha-verification-1" value="" class="hide required-entry" style="" />
   <div id="recaptcha-1"></div>
   <script type="text/javascript">
       var onloadCallback = function() {
           grecaptcha.render('recaptcha-1', {
               'sitekey': "6LdE5KoZAAAAAA3pqP2DwRex",
               'theme': "light",
           });
       };
       document.getElementById("newsletter-validate-detail").addEventListener("submit",function(evt)
  {
  var response = grecaptcha.getResponse();
  if(response.length == 0) 
  { 
    //reCaptcha not verified
    alert("Please Complete Google Recaptcha!"); 
    evt.preventDefault();
    return false;
  }
  //captcha verified
  //do the rest of your validations here
});
   </script>
   <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit&hl=en" async defer></script></div>

Add above code before "form" tag. Replace sitekey tag.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.