diff --git a/local/media/images/carousel/sanstitre-1covid-19capbretagne-15.jpg b/local/media/images/carousel/sanstitre-1covid-19capbretagne-15.jpg new file mode 100755 index 00000000..0ac3ebba Binary files /dev/null and b/local/media/images/carousel/sanstitre-1covid-19capbretagne-15.jpg differ diff --git a/local/media/images/product/066f6dd6-0f1f-4603-a648-e863e9e9c41a-1728.jpeg b/local/media/images/product/066f6dd6-0f1f-4603-a648-e863e9e9c41a-1728.jpeg new file mode 100644 index 00000000..3efba92c Binary files /dev/null and b/local/media/images/product/066f6dd6-0f1f-4603-a648-e863e9e9c41a-1728.jpeg differ diff --git a/local/media/images/product/6b1735d4-8c36-4664-ab12-1b8755d8524c-1729.jpeg b/local/media/images/product/6b1735d4-8c36-4664-ab12-1b8755d8524c-1729.jpeg new file mode 100644 index 00000000..7881edcd Binary files /dev/null and b/local/media/images/product/6b1735d4-8c36-4664-ab12-1b8755d8524c-1729.jpeg differ diff --git a/local/media/images/product/71edddcc-bd1d-497e-aa4a-8f751df70279-1730.jpeg b/local/media/images/product/71edddcc-bd1d-497e-aa4a-8f751df70279-1730.jpeg new file mode 100644 index 00000000..87b94aaf Binary files /dev/null and b/local/media/images/product/71edddcc-bd1d-497e-aa4a-8f751df70279-1730.jpeg differ diff --git a/local/media/images/product/cmtrieuxbrique-1711.png b/local/media/images/product/cmtrieuxbrique-1711.png new file mode 100644 index 00000000..4ff3b174 Binary files /dev/null and b/local/media/images/product/cmtrieuxbrique-1711.png differ diff --git a/local/media/images/product/cmtrieuxchoco-1727.jpg b/local/media/images/product/cmtrieuxchoco-1727.jpg new file mode 100644 index 00000000..dee08c59 Binary files /dev/null and b/local/media/images/product/cmtrieuxchoco-1727.jpg differ diff --git a/local/media/images/product/cmtrieuxlauze-1726.jpg b/local/media/images/product/cmtrieuxlauze-1726.jpg new file mode 100644 index 00000000..dee08c59 Binary files /dev/null and b/local/media/images/product/cmtrieuxlauze-1726.jpg differ diff --git a/local/media/images/product/logcoapmarine1wp-1725.jpg b/local/media/images/product/logcoapmarine1wp-1725.jpg new file mode 100644 index 00000000..dee08c59 Binary files /dev/null and b/local/media/images/product/logcoapmarine1wp-1725.jpg differ diff --git a/local/modules/ReCaptcha/Action/ReCaptchaAction.php b/local/modules/ReCaptcha/Action/ReCaptchaAction.php new file mode 100644 index 00000000..5f56a9a4 --- /dev/null +++ b/local/modules/ReCaptcha/Action/ReCaptchaAction.php @@ -0,0 +1,55 @@ +request = $request; + } + + public static function getSubscribedEvents() + { + return [ + ReCaptchaEvents::CHECK_CAPTCHA_EVENT => ['checkCaptcha', 128], + ]; + } + + 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 ($result['success'] == true) { + $event->setHuman(true); + } + } +} diff --git a/local/modules/ReCaptcha/Config/config.xml b/local/modules/ReCaptcha/Config/config.xml new file mode 100644 index 00000000..1d4fb6e5 --- /dev/null +++ b/local/modules/ReCaptcha/Config/config.xml @@ -0,0 +1,54 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + %kernel.environment% + + + + + + + + + + + + + + + + diff --git a/local/modules/ReCaptcha/Config/module.xml b/local/modules/ReCaptcha/Config/module.xml new file mode 100644 index 00000000..29dc4be7 --- /dev/null +++ b/local/modules/ReCaptcha/Config/module.xml @@ -0,0 +1,41 @@ + + + ReCaptcha\ReCaptcha + + ReCaptcha + + + + ReCaptcha + + + + + en_US + fr_FR + + 2.0.2 + + + Vincent Lopes-Vicente + vlopes@openstudio.fr + + + classic + + 2.3.0 + other + diff --git a/local/modules/ReCaptcha/Config/routing.xml b/local/modules/ReCaptcha/Config/routing.xml new file mode 100644 index 00000000..27717162 --- /dev/null +++ b/local/modules/ReCaptcha/Config/routing.xml @@ -0,0 +1,15 @@ + + + + + + ReCaptcha\Controller\ConfigurationController::viewAction + + + + ReCaptcha\Controller\ConfigurationController::saveAction + + + diff --git a/local/modules/ReCaptcha/Config/schema.xml b/local/modules/ReCaptcha/Config/schema.xml new file mode 100644 index 00000000..54c0ad06 --- /dev/null +++ b/local/modules/ReCaptcha/Config/schema.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/local/modules/ReCaptcha/Controller/ConfigurationController.php b/local/modules/ReCaptcha/Controller/ConfigurationController.php new file mode 100644 index 00000000..63f4b4d5 --- /dev/null +++ b/local/modules/ReCaptcha/Controller/ConfigurationController.php @@ -0,0 +1,50 @@ +checkAuth(array(AdminResources::MODULE), 'ReCaptcha', AccessManager::VIEW)) { + return $response; + } + + $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']); + + } catch (\Exception $e) { + $this->setupFormErrorContext( + Translator::getInstance()->trans( + "Error", + [], + ReCaptcha::DOMAIN_NAME + ), + $e->getMessage(), + $form + ); + return $this->viewAction(); + } + + return $this->generateSuccessRedirect($form); + } + + public function viewAction() + { + return $this->render( + "recaptcha/configuration" + ); + } +} diff --git a/local/modules/ReCaptcha/Event/ReCaptchaCheckEvent.php b/local/modules/ReCaptcha/Event/ReCaptchaCheckEvent.php new file mode 100644 index 00000000..42b30ddb --- /dev/null +++ b/local/modules/ReCaptcha/Event/ReCaptchaCheckEvent.php @@ -0,0 +1,80 @@ +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; + } +} diff --git a/local/modules/ReCaptcha/Event/ReCaptchaEvents.php b/local/modules/ReCaptcha/Event/ReCaptchaEvents.php new file mode 100644 index 00000000..2e8b054f --- /dev/null +++ b/local/modules/ReCaptcha/Event/ReCaptchaEvents.php @@ -0,0 +1,8 @@ +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' + ] + ] + ); + } +} diff --git a/local/modules/ReCaptcha/Form/MyTheliaFormValidator.php b/local/modules/ReCaptcha/Form/MyTheliaFormValidator.php new file mode 100644 index 00000000..c5627d8d --- /dev/null +++ b/local/modules/ReCaptcha/Form/MyTheliaFormValidator.php @@ -0,0 +1,37 @@ +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 + } + +} \ No newline at end of file diff --git a/local/modules/ReCaptcha/Hook/FrontHook.php b/local/modules/ReCaptcha/Hook/FrontHook.php new file mode 100644 index 00000000..37d3fd5e --- /dev/null +++ b/local/modules/ReCaptcha/Hook/FrontHook.php @@ -0,0 +1,27 @@ +add("
"); + } +} diff --git a/local/modules/ReCaptcha/I18n/backOffice/default/fr_FR.php b/local/modules/ReCaptcha/I18n/backOffice/default/fr_FR.php new file mode 100644 index 00000000..ca4e7806 --- /dev/null +++ b/local/modules/ReCaptcha/I18n/backOffice/default/fr_FR.php @@ -0,0 +1,8 @@ + 'ReCaptcha configuration', + 'ReCaptcha module configuration' => 'ReCaptcha module configuration', + 'These infos are available here : ' => 'Ces infos sont disponibles ici : ', + 'reCAPTCHA access :' => 'reCAPTCHA accés :', +); diff --git a/local/modules/ReCaptcha/I18n/en_US.php b/local/modules/ReCaptcha/I18n/en_US.php new file mode 100644 index 00000000..0b4fa142 --- /dev/null +++ b/local/modules/ReCaptcha/I18n/en_US.php @@ -0,0 +1,4 @@ + 'The displayed english string', +); diff --git a/local/modules/ReCaptcha/I18n/fr_FR.php b/local/modules/ReCaptcha/I18n/fr_FR.php new file mode 100644 index 00000000..d0fba1e1 --- /dev/null +++ b/local/modules/ReCaptcha/I18n/fr_FR.php @@ -0,0 +1,7 @@ + 'Erreur', + 'Secret key' => 'Clé secrète', + 'Site key' => 'Clé du site', +); diff --git a/local/modules/ReCaptcha/LICENSE b/local/modules/ReCaptcha/LICENSE new file mode 100644 index 00000000..2152256c --- /dev/null +++ b/local/modules/ReCaptcha/LICENSE @@ -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. diff --git a/local/modules/ReCaptcha/ReCaptcha.php b/local/modules/ReCaptcha/ReCaptcha.php new file mode 100644 index 00000000..dafde8f5 --- /dev/null +++ b/local/modules/ReCaptcha/ReCaptcha.php @@ -0,0 +1,55 @@ + 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, + ], + ]; + } +} diff --git a/local/modules/ReCaptcha/Readme.md b/local/modules/ReCaptcha/Readme.md new file mode 100644 index 00000000..cc85cd1e --- /dev/null +++ b/local/modules/ReCaptcha/Readme.md @@ -0,0 +1,62 @@ +# 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.0 +``` + +## 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) + + +Then 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 : +``` + + {hook name="recaptcha.check"} + // End of the 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; +``` diff --git a/local/modules/ReCaptcha/composer.json b/local/modules/ReCaptcha/composer.json new file mode 100644 index 00000000..84b27144 --- /dev/null +++ b/local/modules/ReCaptcha/composer.json @@ -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" + } +} \ No newline at end of file diff --git a/local/modules/ReCaptcha/templates/backOffice/default/recaptcha/configuration.html b/local/modules/ReCaptcha/templates/backOffice/default/recaptcha/configuration.html new file mode 100644 index 00000000..e936031e --- /dev/null +++ b/local/modules/ReCaptcha/templates/backOffice/default/recaptcha/configuration.html @@ -0,0 +1,69 @@ +{extends file="admin-layout.tpl"} + +{block name="after-bootstrap-css"} + +{/block} + +{block name="no-return-functions"} + {$admin_current_location = 'module'} +{/block} + +{block name="page-title"}{intl l='ReCaptcha module configuration' d='recaptcha.bo.default'}{/block} + +{block name="check-resource"}admin.module{/block} +{block name="check-access"}view{/block} +{block name="check-module"}ReCaptcha{/block} + +{block name="main-content"} +
+
+
+

+ {intl l="ReCaptcha configuration" d='recaptcha.bo.default'} +

+
+ {form name="recaptcha_configuration.form"} +
+ {form_hidden_fields form=$form} + + {if $form_error} +
{$form_error_message}
+ {/if} + + {form_field form=$form field='success_url'} + + {/form_field} + +
+
+

{intl l="reCAPTCHA access :" d='recaptcha.bo.default'}

+
+ {render_form_field form=$form field="site_key" value={$data}} +
+
+ {render_form_field form=$form field="secret_key" value={$data}} +
+
+ {render_form_field form=$form field="captcha_style" value={$data}} +
+
+

{intl l="These infos are available here : " d='recaptcha.bo.default'}http://www.google.com/recaptcha/admin

+
+
+ +
+
+
+
+ {/form} +
+
+{/block} + +{block name="javascript-initialization"} + +{/block} \ No newline at end of file diff --git a/local/modules/ReCaptcha/templates/frontOffice/default/recaptcha-js.html b/local/modules/ReCaptcha/templates/frontOffice/default/recaptcha-js.html new file mode 100644 index 00000000..22f0e86a --- /dev/null +++ b/local/modules/ReCaptcha/templates/frontOffice/default/recaptcha-js.html @@ -0,0 +1,22 @@ + + diff --git a/templates/frontOffice/default/contact.html b/templates/frontOffice/default/contact.html index 8d3bd4a3..33991435 100644 --- a/templates/frontOffice/default/contact.html +++ b/templates/frontOffice/default/contact.html @@ -32,6 +32,7 @@ {intl l="Send us a message"}
+ {if $form_error}
{$form_error_message}
{/if}
{form_field field="name"}
@@ -87,6 +88,11 @@
+
+ {hook name="recaptcha.check"} +
+
+ {hook name="contact.form-bottom"}
diff --git a/templates/frontOffice/default/register.html b/templates/frontOffice/default/register.html index 64281ed6..5a68ffce 100644 --- a/templates/frontOffice/default/register.html +++ b/templates/frontOffice/default/register.html @@ -306,7 +306,10 @@
{/form_field} - +
+ {hook name="recaptcha.check"} +
+
{hook name="register.form-bottom"}