[04/03/2023] Ajout du module ReCaptcha et config

This commit is contained in:
2023-03-04 21:47:58 +01:00
parent 437bd2bf4f
commit 533e3439c6
24 changed files with 886 additions and 15 deletions

View File

@@ -0,0 +1,55 @@
<?php
namespace ReCaptcha\Action;
use ReCaptcha\Event\ReCaptchaCheckEvent;
use ReCaptcha\Event\ReCaptchaEvents;
use ReCaptcha\ReCaptcha;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
class ReCaptchaAction implements EventSubscriberInterface
{
/** @var Request */
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function checkCaptcha(ReCaptchaCheckEvent $event)
{
$requestUrl = "https://www.google.com/recaptcha/api/siteverify";
$secretKey = ReCaptcha::getConfigValue('secret_key');
$requestUrl .= "?secret=$secretKey";
$captchaResponse = $event->getCaptchaResponse();
if (null === $captchaResponse) {
$captchaResponse = $this->request->request->get('g-recaptcha-response');
}
$requestUrl .= "&response=$captchaResponse";
$remoteIp = $event->getRemoteIp();
if (null === $remoteIp) {
$remoteIp = $this->request->server->get('REMOTE_ADDR');
}
$requestUrl .= "&remoteip=$remoteIp";
$result = json_decode(file_get_contents($requestUrl), true);
if ((bool) $result['success'] === true) {
$event->setHuman(true);
}
}
public static function getSubscribedEvents()
{
return [
ReCaptchaEvents::CHECK_CAPTCHA_EVENT => ['checkCaptcha', 128],
];
}
}

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns="http://thelia.net/schema/dic/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
<loops>
<!-- sample definition
<loop name="MySuperLoop" class="ReCaptcha\Loop\MySuperLoop" />
-->
</loops>
<forms>
<form name="recaptcha_configuration.form" class="ReCaptcha\Form\ConfigurationForm" />
</forms>
<commands>
<!--
<command class="ReCaptcha\Command\MySuperCommand" />
-->
</commands>
<services>
<service id="recpatcha.action" class="ReCaptcha\Action\ReCaptchaAction">
<tag name="kernel.event_subscriber" />
<argument type="service" id="request"/>
</service>
<service id="thelia.form_validator" class="ReCaptcha\Form\MyTheliaFormValidator">
<argument type="service" id="thelia.translator" />
<argument>%kernel.environment%</argument>
<argument type="service" id="event_dispatcher" />
</service>
<service id="recpatcha.action.contact" class="ReCaptcha\EventListeners\ContactFormListener">
<tag name="kernel.event_subscriber" />
</service>
</services>
<hooks>
<hook id="recaptcha.hook" class="ReCaptcha\Hook\HookManager">
<tag name="hook.event_listener" event="main.head-top" type="front" method="loadRecaptcha" />
<tag name="hook.event_listener" event="recaptcha.js" type="front" method="loadRecaptcha" />
<tag name="hook.event_listener" event="recaptcha.check" type="front" method="addRecaptchaCheck" />
<tag name="hook.event_listener" event="contact.form-bottom" type="front" method="addRecaptchaCheckContact" />
<tag name="hook.event_listener" event="module.configuration" type="back" method="onModuleConfigure" />
</hook>
</hooks>
<!--
<exports>
</exports>
-->
<!--
<imports>
</imports>
-->
</config>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="http://thelia.net/schema/dic/module"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/module http://thelia.net/schema/dic/module/module-2_2.xsd">
<fullnamespace>ReCaptcha\ReCaptcha</fullnamespace>
<descriptive locale="en_US">
<title>ReCaptcha</title>
</descriptive>
<descriptive locale="fr_FR">
<title>ReCaptcha</title>
</descriptive>
<languages>
<language>en_US</language>
<language>fr_FR</language>
</languages>
<version>2.0.4</version>
<authors>
<author>
<name>Vincent Lopes-Vicente</name>
<email>vlopes@openstudio.fr</email>
</author>
</authors>
<type>classic</type>
<thelia>2.3.0</thelia>
<stability>other</stability>
</module>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="admin.recaptcha.config.save" path="/admin/module/recaptcha/configuration" methods="POST">
<default key="_controller">ReCaptcha\Controller\ConfigurationController::saveAction</default>
</route>
</routes>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<database defaultIdMethod="native" name="thelia"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../vendor/propel/propel/resources/xsd/database.xsd" >
<!--
See propel documentation on http://propelorm.org for all information about schema file
<table name="product_rel" namespace="ReCaptcha\Model">
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
<column defaultValue="0" name="visible" required="true" type="TINYINT" />
<column defaultValue="0" name="position" required="true" type="INTEGER" />
<column name="title" size="255" type="VARCHAR" />
<column name="description" type="CLOB" />
<column name="chapo" type="LONGVARCHAR" />
<column name="postscriptum" type="LONGVARCHAR" />
<foreign-key foreignTable="product" name="fk_product_id" onDelete="CASCADE" onUpdate="RESTRICT">
<reference foreign="id" local="product_id" />
</foreign-key>
<behavior name="timestampable" />
<behavior name="i18n">
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
</behavior>
<behavior name="versionable">
<parameter name="log_created_at" value="true" />
<parameter name="log_created_by" value="true" />
</behavior>
</table>
-->
<external-schema filename="local/config/schema.xml" referenceOnly="true" />
</database>

View File

@@ -0,0 +1,49 @@
<?php
namespace ReCaptcha\Controller;
use ReCaptcha\ReCaptcha;
use Thelia\Controller\Admin\BaseAdminController;
use Thelia\Core\Security\AccessManager;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Core\Translation\Translator;
use Thelia\Tools\URL;
class ConfigurationController extends BaseAdminController
{
public function saveAction()
{
if (null !== $response = $this->checkAuth(array(AdminResources::MODULE), 'ReCaptcha', AccessManager::VIEW)) {
return $response;
}
$saveMode = $this->getRequest()->request->get("save_mode");
$form = $this->createForm("recaptcha_configuration.form");
try {
$data = $this->validateForm($form)->getData();
ReCaptcha::setConfigValue('site_key', $data['site_key']);
ReCaptcha::setConfigValue('secret_key', $data['secret_key']);
ReCaptcha::setConfigValue('captcha_style', $data['captcha_style']);
ReCaptcha::setConfigValue('add_to_contact_form', $data['add_to_contact_form'] ? 1 : 0);
if ($saveMode !== 'stay') {
return $this->generateRedirect(URL::getInstance()->absoluteUrl('/admin/modules'));
}
} catch (\Exception $e) {
$this->setupFormErrorContext(
Translator::getInstance()->trans(
"Error",
[],
ReCaptcha::DOMAIN_NAME
),
$e->getMessage(),
$form
);
}
return $this->generateRedirect(URL::getInstance()->absoluteUrl('/admin/module/ReCaptcha'));
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace ReCaptcha\Event;
use Thelia\Core\Event\ActionEvent;
class ReCaptchaCheckEvent extends ActionEvent
{
protected $captchaResponse = null;
protected $remoteIp = null;
/** @var boolean */
protected $human = false;
public function __construct($captchaResponse = null, $remoteIp = null)
{
if (null !== $captchaResponse) {
$this->captchaResponse = $captchaResponse;
}
if (null !== $remoteIp) {
$this->remoteIp = $remoteIp;
}
}
/**
* @return null
*/
public function getCaptchaResponse()
{
return $this->captchaResponse;
}
/**
* @param null $captchaResponse
* @return ReCaptchaCheckEvent
*/
public function setCaptchaResponse($captchaResponse)
{
$this->captchaResponse = $captchaResponse;
return $this;
}
/**
* @return null
*/
public function getRemoteIp()
{
return $this->remoteIp;
}
/**
* @param null $remoteIp
* @return ReCaptchaCheckEvent
*/
public function setRemoteIp($remoteIp)
{
$this->remoteIp = $remoteIp;
return $this;
}
/**
* @return bool
*/
public function isHuman()
{
return $this->human;
}
/**
* @param bool $human
* @return ReCaptchaCheckEvent
*/
public function setHuman($human)
{
$this->human = $human;
return $this;
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace ReCaptcha\Event;
class ReCaptchaEvents
{
const CHECK_CAPTCHA_EVENT = "check_captcha_event";
}

View File

@@ -0,0 +1,59 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace ReCaptcha\EventListeners;
use ReCaptcha\Event\ReCaptchaCheckEvent;
use ReCaptcha\Event\ReCaptchaEvents;
use ReCaptcha\ReCaptcha;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Contact\ContactEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Exception\FormValidationException;
/**
* This listener perfom captcha validation foir the standard contact form, by listening the TheliaEvents::CONTACT_SUBMIT
* event, and throwing a FormValidationException is the captcha could not be vaidated.
*/
class ContactFormListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
// Ensure comptatibility with pre-2.4 versions
if (! defined('\Thelia\Core\Event\TheliaEvents::CONTACT_SUBMIT')
||
false === (bool) ReCaptcha::getConfigValue('add_to_contact_form')
) {
return [];
}
return [
TheliaEvents::CONTACT_SUBMIT => [ 'validateCaptcha', 128 ]
];
}
public function validateCaptcha(ContactEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$checkCaptchaEvent = new ReCaptchaCheckEvent();
$dispatcher->dispatch(ReCaptchaEvents::CHECK_CAPTCHA_EVENT, $checkCaptchaEvent);
if (! $checkCaptchaEvent->isHuman()) {
throw new FormValidationException(
Translator::getInstance()->trans("Captcha validation failed, please try again.", [], ReCaptcha::DOMAIN_NAME)
);
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace ReCaptcha\Form;
use ReCaptcha\ReCaptcha;
use Thelia\Core\Translation\Translator;
use Thelia\Form\BaseForm;
class ConfigurationForm extends BaseForm
{
protected function buildForm()
{
$this->formBuilder
->add(
"site_key",
"text",
[
"data" => ReCaptcha::getConfigValue("site_key"),
"label"=>Translator::getInstance()->trans("Site key", array(), ReCaptcha::DOMAIN_NAME),
"label_attr" => ["for" => "site_key"],
"required" => true
]
)
->add(
"secret_key",
"text",
[
"data" => ReCaptcha::getConfigValue("secret_key"),
"label"=>Translator::getInstance()->trans("Secret key", array(), ReCaptcha::DOMAIN_NAME),
"label_attr" => ["for" => "secret_key"],
"required" => true
]
)
->add(
"captcha_style",
"choice",
[
"data" => ReCaptcha::getConfigValue("captcha_style"),
"label"=>Translator::getInstance()->trans("ReCaptcha style", array(), ReCaptcha::DOMAIN_NAME),
"label_attr" => ["for" => "captcha_style"],
"required" => true,
'choices' => [
'normal'=>'Normal',
'compact'=>'Compact',
'invisible'=>'Invisible'
]
]
);
if (defined('\Thelia\Core\Event\TheliaEvents::CONTACT_SUBMIT')) {
$this->formBuilder
->add(
"add_to_contact_form",
"checkbox",
[
"required" => false,
"data" => (bool) ReCaptcha::getConfigValue("add_to_contact_form"),
"value" => 1,
"label" => $this->translator->trans("Add captcha to standard contact form", [], ReCaptcha::DOMAIN_NAME),
"label_attr" => [
"for" => "add_to_contact_form",
'help' => $this->translator->trans("Check this box to add a captcha to the standard Thelia 2 contact form", [], ReCaptcha::DOMAIN_NAME)
],
]
);
}
}
public function getName()
{
return "recaptcha_configuration_form";
}
}

View File

@@ -0,0 +1,34 @@
<?php namespace ReCaptcha\Form;
use ReCaptcha\Event\ReCaptchaCheckEvent;
use ReCaptcha\Event\ReCaptchaEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Thelia\Form\BaseForm;
use Thelia\Form\Exception\FormValidationException;
class MyTheliaFormValidator extends \Thelia\Core\Form\TheliaFormValidator
{
/** @var EventDispatcherInterface */
protected $dispatcher;
public function __construct(TranslatorInterface $translator, $environment, EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
parent::__construct($translator, $environment);
}
public function validateForm(BaseForm $aBaseForm, $expectedMethod = null)
{
if ($aBaseForm->getRequest()->get('captcha')) {
$checkCaptchaEvent = new ReCaptchaCheckEvent();
$this->dispatcher->dispatch(ReCaptchaEvents::CHECK_CAPTCHA_EVENT, $checkCaptchaEvent);
if ($checkCaptchaEvent->isHuman() == false) {
throw new FormValidationException('Veuillez confirmer que vous n\'êtes pas un robot.');
}
}
return parent::validateForm($aBaseForm, $expectedMethod); // TODO: Change the autogenerated stub
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace ReCaptcha\Hook;
use ReCaptcha\ReCaptcha;
use Thelia\Core\Event\Hook\HookRenderEvent;
use Thelia\Core\Hook\BaseHook;
class HookManager extends BaseHook
{
public function onModuleConfigure(HookRenderEvent $event)
{
$event->add(
$this->render(
'recaptcha/configuration.html',
[
'with_contact_form' => defined('\Thelia\Core\Event\TheliaEvents::CONTACT_SUBMIT')
]
)
);
}
public function addRecaptchaCheckContact(HookRenderEvent $event)
{
// Ensure comptatibility with pre-2.4 versions
if (defined('\Thelia\Core\Event\TheliaEvents::CONTACT_SUBMIT')
&&
(bool) ReCaptcha::getConfigValue('add_to_contact_form')
) {
$this->addRecaptchaCheck($event);
}
}
public function addRecaptchaCheck(HookRenderEvent $event)
{
$siteKey = ReCaptcha::getConfigValue('site_key');
$captchaStyle = ReCaptcha::getConfigValue('captcha_style');
$captchaId= "recaptcha";
$captchaCallback = "";
$type = "";
if ($captchaStyle === 'invisible') {
$captchaCallback = "data-callback='onCompleted'";
$type = "g-invisible";
$captchaId .= '-invisible';
}
if (null !== $event->getArgument('id')) {
$captchaId = $event->getArgument('id');
}
$event->add("<div id='$captchaId' class='g-recaptcha $type' data-sitekey='$siteKey' $captchaCallback data-size='$captchaStyle'></div><input type='hidden' name='captcha' value='1'>");
}
public function loadRecaptcha(HookRenderEvent $event)
{
$siteKey = ReCaptcha::getConfigValue('site_key');
$captchaStyle = ReCaptcha::getConfigValue('captcha_style');
if ($captchaStyle !== 'invisible') {
$event->add($this->render(
'recaptcha-js.html',
[
"siteKey" => $siteKey,
"captchaStyle" => $captchaStyle,
]
));
return;
}
$event->add($this->render(
'recaptcha-js-invisible.html',
[
"siteKey" => $siteKey,
"captchaStyle" => $captchaStyle,
]
));
}
}

View File

@@ -0,0 +1,7 @@
<?php
return array(
'Submit' => 'Envoyer',
'These infos are available here : ' => 'Ces infos sont disponibles ici : ',
'reCAPTCHA Configuration' => 'Configuration reCAPTCHA',
);

View File

@@ -0,0 +1,4 @@
<?php
return array(
// 'an english string' => 'The displayed english string',
);

View File

@@ -0,0 +1,11 @@
<?php
return array(
'Add captcha to standard contact form' => 'Ajouter un CAPTCHA au formulaire de contact',
'Captcha validation failed, please try again.' => 'Nous n\'avons pas pu vérifier ',
'Check this box to add a captcha to the standard Thelia 2 contact form' => 'Cochez cette case pour ajouter un CAPTCHA au formulaire de contact standard de Thelia',
'Error' => 'Erreur',
'ReCaptcha style' => 'Style du CAPTCHA',
'Secret key' => 'Clé secrète',
'Site key' => 'Clé du site',
);

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 OpenStudio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,55 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace ReCaptcha;
use Thelia\Core\Template\TemplateDefinition;
use Thelia\Module\BaseModule;
class ReCaptcha extends BaseModule
{
/** @var string */
const DOMAIN_NAME = 'recaptcha';
/*
* You may now override BaseModuleInterface methods, such as:
* install, destroy, preActivation, postActivation, preDeactivation, postDeactivation
*
* Have fun !
*/
public function getHooks()
{
return [
[
"type" => TemplateDefinition::FRONT_OFFICE,
"code" => "recaptcha.js",
"title" => [
"en_US" => "reCaptcha js",
"fr_FR" => "Js pour recaptcha",
],
"block" => false,
"active" => true,
],
[
"type" => TemplateDefinition::FRONT_OFFICE,
"code" => "recaptcha.check",
"title" => [
"en_US" => "reCaptcha check hook",
"fr_FR" => "reCaptcha check hook",
],
"block" => false,
"active" => true,
],
];
}
}

View File

@@ -0,0 +1,65 @@
# Re Captcha
This module allow you to add easily a reCAPTCHA to your form
## Installation
### Composer
Add it in your main thelia composer.json file
```
composer require thelia/re-captcha-module:~2.0.3
```
## Usage
Before using this module you have to create google api key here http://www.google.com/recaptcha/admin
next configure your reCAPTCHA access here http://your_site.com`/admin/module/ReCaptcha` with keys you obtained in Google's page
and choose which style of captcha you want :
- A standard captcha (or a compact version of this one)
![Checkbox captcha](https://developers.google.com/recaptcha/images/newCaptchaAnchor.gif)
- An invisible captcha
![Invisible captcha](https://developers.google.com/recaptcha/images/invisible_badge.png)
If you're using Thelia 2.4 or better, you can automatically add a CAPTCHA to the standard Thelia
contact form. To do so, just check the "Add captcha to standard contact form" box in the module
configuration.
For thelia 2.3, you'll need help from a developer to add some hooks in template and dispatch the check events, see details below.
### Hook
First if you don't have `{hook name="main.head-top"}` hook in your template you have to put this hook `{hook name="recaptcha.js"}` in the top of your head
Then add this hook `{hook name="recaptcha.check"}` in every form where you want to check if the user is human,
be careful if you want to use the invisible captcha this hook must be placed directly in the form tag like this :
```
<form id="form-contact" action="{url path="/contact"}" method="post">
{hook name="recaptcha.check"}
// End of the form
</form>
```
### Event
To check in server-side if the captcha is valid you have to dispatch the "CHECK_CAPTCHA_EVENT" like this :
```
$checkCaptchaEvent = new ReCaptchaCheckEvent();
$this->dispatch(ReCaptchaEvents::CHECK_CAPTCHA_EVENT, $checkCaptchaEvent);
```
Then the result of check is available in `$checkCaptchaEvent->isHuman()`as boolean so you can do a test like this :
```
if ($checkCaptchaEvent->isHuman() == false) {
throw new \Exception('Invalid captcha');
}
```
Don't forget to add this use at the top of your class :
```
use ReCaptcha\Event\ReCaptchaCheckEvent;
use ReCaptcha\Event\ReCaptchaEvents;
```

View File

@@ -0,0 +1,11 @@
{
"name": "thelia/re-captcha-module",
"license": "LGPL-3.0+",
"type": "thelia-module",
"require": {
"thelia/installer": "~1.1"
},
"extra": {
"installer-name": "ReCaptcha"
}
}

View File

@@ -0,0 +1,48 @@
<div class="row">
<div class="col-md-12 general-block-decorator">
<div class="row">
<div class="col-md-12 title title-without-tabs">
{intl d='payzen.bo.default' l="reCAPTCHA Configuration" d='recaptcha.bo.default'}
</div>
</div>
<div class="form-container">
<div class="row">
<div class="col-md-12">
{form name="recaptcha_configuration.form"}
<form action="{url path="/admin/module/recaptcha/configuration"}" method="post">
{form_hidden_fields form=$form}
{if $form_error}
<div class="alert alert-danger">{$form_error_message}</div>
{/if}
{include file = "includes/inner-form-toolbar.html"
hide_flags = true
page_url = "{url path='/admin/module/ReCaptcha'}"
close_url = "{url path='/admin/modules'}"
}
<div class="row">
<div class="col-md-6">
{render_form_field field="site_key" }
{render_form_field field="secret_key"}
<p>{intl l="These infos are available here : " d='recaptcha.bo.default'}<a href="http://www.google.com/recaptcha/admin">http://www.google.com/recaptcha/admin</a></p>
</div>
<div class="col-md-6">
{render_form_field field="captcha_style"}
{if $with_contact_form}
<label></label>
{render_form_field field="add_to_contact_form"}
{/if}
</div>
</div>
</form>
{/form}
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,56 @@
<script src="https://www.google.com/recaptcha/api.js?render={$siteKey}"></script>
<script>var siteKey = "{$siteKey}";</script>
{literal}
<script type="module">
function verifyRecaptcha(form, dataElement) {
const { sitekey, size } = dataElement.dataset;
const id = dataElement.id;
if (!sitekey) return;
return new Promise((resolve, reject) => {
grecaptcha.execute(sitekey, {action: 'submit'}).then((token) => {
if (token) {
resolve(token);
}
reject('Invalid Captcha');
})
});
}
const allForm = document.querySelectorAll('form');
allForm.forEach((form) => {
const dataElement = form.querySelector('.g-recaptcha');
if (!dataElement) return;
form.addEventListener("submit", async (event) => {
event.preventDefault();
event.stopPropagation();
grecaptcha.ready(() =>
{
const response = verifyRecaptcha(form, dataElement)
response.then((token) => {
const customEvent = new CustomEvent('validRecaptcha', {
detail: {
token,
form,
}
})
form.dispatchEvent(customEvent);
})
.catch((error) => console.log(error))
}
);
})
})
</script>
{/literal}

View File

@@ -0,0 +1,23 @@
<script src="https://www.google.com/recaptcha/api.js?hl={lang attr='code'}" async defer></script>
<script>
window.onload = function() {
var captchaDiv = document.querySelector(".g-recaptcha.g-invisible");
if (captchaDiv !== null) {
var form = captchaDiv.parentElement;
form.addEventListener("submit", function (event) {
if (!grecaptcha.getResponse()) {
event.preventDefault(); //prevent form submit
grecaptcha.execute();
}
});
onCompleted = function () {
if (form.reportValidity() !== false) {
form.submit();
}
}
}
}
</script>