[04/03/2023] Ajout du module ReCaptcha et config
This commit is contained in:
55
local/modules/ReCaptcha/Action/ReCaptchaAction.php
Normal file
55
local/modules/ReCaptcha/Action/ReCaptchaAction.php
Normal 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],
|
||||
];
|
||||
}
|
||||
}
|
||||
60
local/modules/ReCaptcha/Config/config.xml
Normal file
60
local/modules/ReCaptcha/Config/config.xml
Normal 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>
|
||||
26
local/modules/ReCaptcha/Config/module.xml
Normal file
26
local/modules/ReCaptcha/Config/module.xml
Normal 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>
|
||||
10
local/modules/ReCaptcha/Config/routing.xml
Normal file
10
local/modules/ReCaptcha/Config/routing.xml
Normal 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>
|
||||
30
local/modules/ReCaptcha/Config/schema.xml
Normal file
30
local/modules/ReCaptcha/Config/schema.xml
Normal 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>
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
80
local/modules/ReCaptcha/Event/ReCaptchaCheckEvent.php
Normal file
80
local/modules/ReCaptcha/Event/ReCaptchaCheckEvent.php
Normal 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;
|
||||
}
|
||||
}
|
||||
8
local/modules/ReCaptcha/Event/ReCaptchaEvents.php
Normal file
8
local/modules/ReCaptcha/Event/ReCaptchaEvents.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace ReCaptcha\Event;
|
||||
|
||||
class ReCaptchaEvents
|
||||
{
|
||||
const CHECK_CAPTCHA_EVENT = "check_captcha_event";
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
74
local/modules/ReCaptcha/Form/ConfigurationForm.php
Normal file
74
local/modules/ReCaptcha/Form/ConfigurationForm.php
Normal 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";
|
||||
}
|
||||
}
|
||||
34
local/modules/ReCaptcha/Form/MyTheliaFormValidator.php
Normal file
34
local/modules/ReCaptcha/Form/MyTheliaFormValidator.php
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
82
local/modules/ReCaptcha/Hook/HookManager.php
Normal file
82
local/modules/ReCaptcha/Hook/HookManager.php
Normal 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,
|
||||
]
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'Submit' => 'Envoyer',
|
||||
'These infos are available here : ' => 'Ces infos sont disponibles ici : ',
|
||||
'reCAPTCHA Configuration' => 'Configuration reCAPTCHA',
|
||||
);
|
||||
4
local/modules/ReCaptcha/I18n/en_US.php
Normal file
4
local/modules/ReCaptcha/I18n/en_US.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return array(
|
||||
// 'an english string' => 'The displayed english string',
|
||||
);
|
||||
11
local/modules/ReCaptcha/I18n/fr_FR.php
Normal file
11
local/modules/ReCaptcha/I18n/fr_FR.php
Normal 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',
|
||||
);
|
||||
21
local/modules/ReCaptcha/LICENSE
Normal file
21
local/modules/ReCaptcha/LICENSE
Normal 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.
|
||||
55
local/modules/ReCaptcha/ReCaptcha.php
Normal file
55
local/modules/ReCaptcha/ReCaptcha.php
Normal 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,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
65
local/modules/ReCaptcha/Readme.md
Normal file
65
local/modules/ReCaptcha/Readme.md
Normal 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)
|
||||
|
||||

|
||||
|
||||
- An invisible captcha
|
||||
|
||||

|
||||
|
||||
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;
|
||||
```
|
||||
11
local/modules/ReCaptcha/composer.json
Normal file
11
local/modules/ReCaptcha/composer.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user