From bad61801759767ebe24b9aa7cb66ec3cfb1aaf70 Mon Sep 17 00:00:00 2001 From: magentix Date: Wed, 6 Dec 2023 14:11:38 +0100 Subject: [PATCH 01/11] Allow protecting admin forms --- Block/Turnstile.php | 20 +++ Block/Turnstile/Config.php | 8 +- CHANGELOG.md | 5 + Helper/Config.php | 65 ++++++- Model/Config/Source/Forms.php | 21 +-- Model/Config/Source/Forms/Adminhtml.php | 27 +++ Model/Config/Source/Forms/Frontend.php | 35 ++++ Model/Config/Source/Size.php | 48 +++++ .../{ => Adminhtml}/ConfigProvider.php | 11 +- Model/Turnstile/ConfigProviderInterface.php | 21 +++ Model/Turnstile/Frontend/ConfigProvider.php | 43 +++++ Model/Validator.php | 18 +- Observer/Validate.php | 138 +++----------- Observer/Validate/Admin.php | 58 ++++++ Observer/Validate/Frontend.php | 170 ++++++++++++++++++ README.md | 59 +++++- composer.json | 2 +- etc/adminhtml/di.xml | 12 ++ etc/adminhtml/events.xml | 14 ++ etc/adminhtml/system.xml | 30 +++- etc/config.xml | 9 +- etc/frontend/di.xml | 3 +- etc/frontend/events.xml | 2 +- .../layout/adminhtml_auth_forgotpassword.xml | 24 +++ .../adminhtml/layout/adminhtml_auth_login.xml | 24 +++ view/adminhtml/web/css/turnstile.css | 8 + view/adminhtml/web/js/view/turnstile.js | 20 +++ view/{frontend => base}/requirejs-config.js | 0 view/base/templates/turnstile.phtml | 22 +++ .../templates/turnstile/config.phtml | 0 view/base/web/js/view/Component.js | 79 ++++++++ .../web/template/turnstile.html | 0 view/frontend/layout/checkout_index_index.xml | 35 ++++ view/frontend/templates/turnstile.phtml | 19 -- view/frontend/web/js/view/turnstile.js | 57 +----- 35 files changed, 875 insertions(+), 232 deletions(-) create mode 100644 Model/Config/Source/Forms/Adminhtml.php create mode 100644 Model/Config/Source/Forms/Frontend.php create mode 100644 Model/Config/Source/Size.php rename Model/Turnstile/{ => Adminhtml}/ConfigProvider.php (66%) create mode 100644 Model/Turnstile/ConfigProviderInterface.php create mode 100644 Model/Turnstile/Frontend/ConfigProvider.php create mode 100644 Observer/Validate/Admin.php create mode 100644 Observer/Validate/Frontend.php create mode 100644 etc/adminhtml/di.xml create mode 100644 etc/adminhtml/events.xml create mode 100644 view/adminhtml/layout/adminhtml_auth_forgotpassword.xml create mode 100644 view/adminhtml/layout/adminhtml_auth_login.xml create mode 100644 view/adminhtml/web/css/turnstile.css create mode 100644 view/adminhtml/web/js/view/turnstile.js rename view/{frontend => base}/requirejs-config.js (100%) create mode 100644 view/base/templates/turnstile.phtml rename view/{frontend => base}/templates/turnstile/config.phtml (100%) create mode 100644 view/base/web/js/view/Component.js rename view/{frontend => base}/web/template/turnstile.html (100%) create mode 100644 view/frontend/layout/checkout_index_index.xml delete mode 100644 view/frontend/templates/turnstile.phtml diff --git a/Block/Turnstile.php b/Block/Turnstile.php index eb11df6..60fa167 100644 --- a/Block/Turnstile.php +++ b/Block/Turnstile.php @@ -56,6 +56,26 @@ public function getAction(): string return $this->getData('action') ?: $this->config->getAction(); } + /** + * Retrieve size, will override the config if set for block in layout + * + * @return string|null + */ + public function getSize(): ?string + { + return $this->getData('size'); + } + + /** + * Retrieve theme, will override the config if set for block in layout + * + * @return string|null + */ + public function getTheme(): ?string + { + return $this->getData('theme'); + } + /** * Retrieve id * diff --git a/Block/Turnstile/Config.php b/Block/Turnstile/Config.php index 37417d2..4b6573b 100644 --- a/Block/Turnstile/Config.php +++ b/Block/Turnstile/Config.php @@ -10,7 +10,7 @@ namespace PixelOpen\CloudflareTurnstile\Block\Turnstile; -use PixelOpen\CloudflareTurnstile\Model\Turnstile\ConfigProvider; +use PixelOpen\CloudflareTurnstile\Model\Turnstile\ConfigProviderInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\Template\Context; @@ -19,18 +19,18 @@ class Config extends Template { protected SerializerInterface $serializer; - protected ConfigProvider $configProvider; + protected ConfigProviderInterface $configProvider; /** * @param Context $context * @param SerializerInterface $serializer - * @param ConfigProvider $configProvider + * @param ConfigProviderInterface $configProvider * @param mixed[] $data */ public function __construct( Context $context, SerializerInterface $serializer, - ConfigProvider $configProvider, + ConfigProviderInterface $configProvider, array $data = [] ) { $this->serializer = $serializer; diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f07416..8e519d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 100.2.0 + +- Admin login and reset password form validation +- Turnstile added on guest checkout login form + ## 100.1.2 - Config path updated diff --git a/Helper/Config.php b/Helper/Config.php index b9feed4..043422c 100644 --- a/Helper/Config.php +++ b/Helper/Config.php @@ -15,10 +15,17 @@ class Config extends AbstractHelper { public const TURNSTILE_CONFIG_PATH_ENABLED = 'pixel_open_cloudflare_turnstile/settings/enabled'; + public const TURNSTILE_CONFIG_PATH_SECRET_KEY = 'pixel_open_cloudflare_turnstile/settings/secret_key'; public const TURNSTILE_CONFIG_PATH_SITEKEY = 'pixel_open_cloudflare_turnstile/settings/sitekey'; - public const TURNSTILE_CONFIG_PATH_THEME = 'pixel_open_cloudflare_turnstile/settings/theme'; - public const TURNSTILE_CONFIG_PATH_FORMS = 'pixel_open_cloudflare_turnstile/settings/forms'; + + public const TURNSTILE_CONFIG_PATH_FRONTEND_THEME = 'pixel_open_cloudflare_turnstile/frontend/theme'; + public const TURNSTILE_CONFIG_PATH_FRONTEND_SIZE = 'pixel_open_cloudflare_turnstile/frontend/size'; + public const TURNSTILE_CONFIG_PATH_FRONTEND_FORMS = 'pixel_open_cloudflare_turnstile/frontend/forms'; + + public const TURNSTILE_CONFIG_PATH_ADMINHTML_THEME = 'pixel_open_cloudflare_turnstile/adminhtml/theme'; + public const TURNSTILE_CONFIG_PATH_ADMINHTML_SIZE = 'pixel_open_cloudflare_turnstile/adminhtml/size'; + public const TURNSTILE_CONFIG_PATH_ADMINHTML_FORMS = 'pixel_open_cloudflare_turnstile/adminhtml/forms'; /** * Is Turnstile enabled @@ -51,23 +58,65 @@ public function getSitekey(): string } /** - * Retrieve theme + * Retrieve frontend theme + * + * @return string + */ + public function getFrontendTheme(): string + { + return (string)$this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_FRONTEND_THEME); + } + + /** + * Retrieve admin theme + * + * @return string + */ + public function getAdminTheme(): string + { + return (string)$this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_ADMINHTML_THEME); + } + + /** + * Retrieve frontend size * * @return string */ - public function getTheme(): string + public function getFrontendSize(): string { - return (string)$this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_THEME); + return (string)$this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_FRONTEND_SIZE); + } + + /** + * Retrieve admin size + * + * @return string + */ + public function getAdminSize(): string + { + return (string)$this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_ADMINHTML_SIZE); + } + + /** + * Retrieve enabled frontend forms + * + * @return string[] + */ + public function getFrontendForms(): array + { + $forms = $this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_FRONTEND_FORMS); + + return $forms ? array_filter(explode(',', $forms)) : []; } /** - * Retrieve enabled forms + * Retrieve enabled admin forms * * @return string[] */ - public function getForms(): array + public function getAdminForms(): array { - $forms = $this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_FORMS); + $forms = $this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_ADMINHTML_FORMS); return $forms ? array_filter(explode(',', $forms)) : []; } diff --git a/Model/Config/Source/Forms.php b/Model/Config/Source/Forms.php index c8dfb60..3f12ec9 100644 --- a/Model/Config/Source/Forms.php +++ b/Model/Config/Source/Forms.php @@ -12,15 +12,8 @@ use Magento\Framework\Data\OptionSourceInterface; -class Forms implements OptionSourceInterface +abstract class Forms implements OptionSourceInterface { - public const FORM_CONTACT = 'contact'; - public const FORM_REGISTER = 'register'; - public const FORM_LOGIN = 'login'; - public const FORM_LOGIN_AJAX = 'login-ajax'; - public const FORM_PASSWORD = 'password'; - public const FORM_REVIEW = 'review'; - /** * Get options as array * @@ -45,15 +38,5 @@ public function toOptionArray(): array * * @return string[] */ - public function toArray(): array - { - return [ - self::FORM_CONTACT, - self::FORM_REGISTER, - self::FORM_LOGIN, - self::FORM_LOGIN_AJAX, - self::FORM_PASSWORD, - self::FORM_REVIEW, - ]; - } + abstract public function toArray(): array; } diff --git a/Model/Config/Source/Forms/Adminhtml.php b/Model/Config/Source/Forms/Adminhtml.php new file mode 100644 index 0000000..a10e60b --- /dev/null +++ b/Model/Config/Source/Forms/Adminhtml.php @@ -0,0 +1,27 @@ +toArray() as $value) { + $options[] = [ + 'value' => $value, + 'label' => $value, + ]; + } + + return $options; + } + + /** + * Get options as array + * + * @return string[] + */ + public function toArray(): array + { + return [self::SIZE_NORMAL, self::SIZE_COMPACT]; + } +} diff --git a/Model/Turnstile/ConfigProvider.php b/Model/Turnstile/Adminhtml/ConfigProvider.php similarity index 66% rename from Model/Turnstile/ConfigProvider.php rename to Model/Turnstile/Adminhtml/ConfigProvider.php index b6ee496..8f0cf38 100644 --- a/Model/Turnstile/ConfigProvider.php +++ b/Model/Turnstile/Adminhtml/ConfigProvider.php @@ -8,10 +8,10 @@ declare(strict_types=1); -namespace PixelOpen\CloudflareTurnstile\Model\Turnstile; +namespace PixelOpen\CloudflareTurnstile\Model\Turnstile\Adminhtml; use PixelOpen\CloudflareTurnstile\Helper\Config; -use Magento\Checkout\Model\ConfigProviderInterface; +use PixelOpen\CloudflareTurnstile\Model\Turnstile\ConfigProviderInterface; class ConfigProvider implements ConfigProviderInterface { @@ -32,10 +32,11 @@ public function __construct( public function getConfig(): array { return [ - 'turnstile' => [ + 'config' => [ 'sitekey' => $this->config->getSitekey(), - 'theme' => $this->config->getTheme(), - 'forms' => $this->config->getForms(), + 'theme' => $this->config->getAdminTheme(), + 'size' => $this->config->getAdminSize(), + 'forms' => $this->config->getAdminForms(), ] ]; } diff --git a/Model/Turnstile/ConfigProviderInterface.php b/Model/Turnstile/ConfigProviderInterface.php new file mode 100644 index 0000000..2376d8f --- /dev/null +++ b/Model/Turnstile/ConfigProviderInterface.php @@ -0,0 +1,21 @@ +config = $config; + } + + /** + * @inheritdoc + */ + public function getConfig(): array + { + return [ + 'config' => [ + 'sitekey' => $this->config->getSitekey(), + 'theme' => $this->config->getFrontendTheme(), + 'size' => $this->config->getFrontendSize(), + 'forms' => $this->config->getFrontendForms(), + ] + ]; + } +} diff --git a/Model/Validator.php b/Model/Validator.php index dbdea07..af5c548 100644 --- a/Model/Validator.php +++ b/Model/Validator.php @@ -40,22 +40,31 @@ public function __construct( } /** - * Test if form is enabled + * Test if frontend form is enabled * * @param string $action + * @return bool + */ + public function isFrontendFormEnabled(string $action): bool + { + return in_array($action, $this->config->getFrontendForms()); + } + + /** + * Test if admin form is enabled * + * @param string $action * @return bool */ - public function isFormEnabled(string $action): bool + public function isAdminFormEnabled(string $action): bool { - return in_array($action, $this->config->getForms()); + return in_array($action, $this->config->getAdminForms()); } /** * Test if response is valid * * @param string|null $response - * * @return bool */ public function isValid(?string $response): bool @@ -115,7 +124,6 @@ public function getErrorCodes(): array * Retrieve error message from error code * * @param string $code - * * @return string */ protected static function getErrorMessage(string $code): string diff --git a/Observer/Validate.php b/Observer/Validate.php index b8f10ed..c305ba8 100644 --- a/Observer/Validate.php +++ b/Observer/Validate.php @@ -11,39 +11,25 @@ namespace PixelOpen\CloudflareTurnstile\Observer; use Exception; -use Magento\Contact\Controller\Index\Post as ContactPost; -use Magento\Customer\Controller\Account\CreatePost; -use Magento\Customer\Controller\Account\ForgotPasswordPost; -use Magento\Customer\Controller\Account\LoginPost; +use JetBrains\PhpStorm\NoReturn; use Magento\Customer\Controller\Ajax\Login as AjaxLoginPost; -use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\ActionInterface; -use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\App\Request\Http as Request; use Magento\Framework\App\Response\Http as Response; -use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Message\ManagerInterface; use Magento\Framework\Phrase; use Magento\Framework\Serialize\Serializer\Json; -use Magento\Framework\Session\Generic; -use Magento\Review\Controller\Product\Post as ReviewPost; use PixelOpen\CloudflareTurnstile\Helper\Config; -use PixelOpen\CloudflareTurnstile\Model\Config\Source\Forms; use PixelOpen\CloudflareTurnstile\Model\Validator; -class Validate implements ObserverInterface +abstract class Validate implements ObserverInterface { protected ManagerInterface $messageManager; protected Response $response; - protected DataPersistorInterface $dataPersistor; - - protected CustomerSession $customerSession; - - protected Generic $reviewSession; - protected Validator $validator; protected Json $json; @@ -51,40 +37,30 @@ class Validate implements ObserverInterface protected Config $config; /** - * @param ManagerInterface $messageManager - * @param Response $response - * @param DataPersistorInterface $dataPersistor - * @param CustomerSession $customerSession - * @param Generic $reviewSession - * @param Validator $validator - * @param Json $json - * @param Config $config + * @param ManagerInterface $messageManager + * @param Response $response + * @param Validator $validator + * @param Json $json + * @param Config $config */ public function __construct( ManagerInterface $messageManager, Response $response, - DataPersistorInterface $dataPersistor, - CustomerSession $customerSession, - Generic $reviewSession, Validator $validator, Json $json, Config $config ) { - $this->messageManager = $messageManager; - $this->response = $response; - $this->dataPersistor = $dataPersistor; - $this->customerSession = $customerSession; - $this->reviewSession = $reviewSession; - $this->validator = $validator; - $this->json = $json; - $this->config = $config; + $this->messageManager = $messageManager; + $this->response = $response; + $this->validator = $validator; + $this->json = $json; + $this->config = $config; } /** * Validate Cloudflare Turnstile * * @param Observer $observer - * * @return void */ public function execute(Observer $observer): void @@ -120,97 +96,41 @@ public function execute(Observer $observer): void */ public function getCfResponse(Request $request, ActionInterface $action): ?string { - if ($action instanceof AjaxLoginPost) { - return $this->json->unserialize($request->getContent())['cf-turnstile-response'] ?? null; - } return $request->getParam('cf-turnstile-response'); } /** - * Can validate action + * Send error * - * @param Request $request + * @param Request $request * @param ActionInterface $action - * - * @return bool + * @param Phrase $message + * @return void */ - public function canValidate(Request $request, ActionInterface $action): bool + #[NoReturn] protected function error(Request $request, ActionInterface $action, Phrase $message): void { - if (!$this->config->isEnabled()) { - return false; - } - if (!$request->isPost()) { - return false; - } - if ($this->customerSession->isLoggedIn()) { - return false; - } - if ($this->validator->isFormEnabled(forms::FORM_CONTACT) && $action instanceof ContactPost) { - return true; - } - if ($this->validator->isFormEnabled(forms::FORM_PASSWORD) && $action instanceof ForgotPasswordPost) { - return true; - } - if ($this->validator->isFormEnabled(forms::FORM_REGISTER) && $action instanceof CreatePost) { - return true; - } - if ($this->validator->isFormEnabled(forms::FORM_LOGIN) && $action instanceof LoginPost) { - return true; - } - if ($this->validator->isFormEnabled(forms::FORM_LOGIN_AJAX) && $action instanceof AjaxLoginPost) { - return true; - } - if ($this->validator->isFormEnabled(forms::FORM_REVIEW) && $action instanceof ReviewPost) { - return true; - } + $this->messageManager->addErrorMessage($message); + $this->response->setRedirect($request->getServer('HTTP_REFERER', '')); - return false; + $this->response->sendResponse(); + exit(); } /** - * Persist data + * Can validate action * - * @param Request $request + * @param Request $request * @param ActionInterface $action - * - * @return void + * @return bool */ - public function persist(Request $request, ActionInterface $action): void - { - if ($action instanceof ContactPost) { - $this->dataPersistor->set('contact_us', $request->getParams()); - } - if ($action instanceof CreatePost) { - $this->customerSession->setCustomerFormData($request->getParams()); - } - if ($action instanceof ReviewPost) { - $this->reviewSession->setFormData($request->getParams()); - } - } + abstract public function canValidate(Request $request, ActionInterface $action): bool; /** - * Send error + * Persist data * * @param Request $request * @param ActionInterface $action - * @param Phrase $message - * - * @return void + * @return bool */ - protected function error(Request $request, ActionInterface $action, Phrase $message): void - { - if ($action instanceof AjaxLoginPost) { - $data = [ - 'errors' => true, - 'message' => $message - ]; - $this->response->representJson($this->json->serialize($data)); - } else { - $this->messageManager->addErrorMessage($message); - $this->response->setRedirect($request->getServer('HTTP_REFERER', '')); - } - - $this->response->sendResponse(); - exit(); - } + abstract public function persist(Request $request, ActionInterface $action): bool; } diff --git a/Observer/Validate/Admin.php b/Observer/Validate/Admin.php new file mode 100644 index 0000000..4dc1f27 --- /dev/null +++ b/Observer/Validate/Admin.php @@ -0,0 +1,58 @@ +config->isEnabled()) { + return false; + } + if (!$request->isPost()) { + return false; + } + if ($this->validator->isAdminFormEnabled(AdminForms::FORM_LOGIN) && $action instanceof Login) { + return true; + } + if ($this->validator->isAdminFormEnabled(AdminForms::FORM_PASSWORD) && $action instanceof Forgotpassword) { + return true; + } + + return false; + } + + /** + * Persist data + * + * @param Request $request + * @param ActionInterface $action + * @return bool + */ + public function persist(Request $request, ActionInterface $action): bool + { + return true; + } +} diff --git a/Observer/Validate/Frontend.php b/Observer/Validate/Frontend.php new file mode 100644 index 0000000..7327e0a --- /dev/null +++ b/Observer/Validate/Frontend.php @@ -0,0 +1,170 @@ +dataPersistor = $dataPersistor; + $this->customerSession = $customerSession; + $this->reviewSession = $reviewSession; + + parent::__construct($messageManager, $response, $validator, $json, $config); + } + + /** + * Can validate action + * + * @param Request $request + * @param ActionInterface $action + * @return bool + */ + public function canValidate(Request $request, ActionInterface $action): bool + { + if (!$this->config->isEnabled()) { + return false; + } + if (!$request->isPost()) { + return false; + } + if ($this->customerSession->isLoggedIn()) { + return false; + } + if ($this->validator->isFrontendFormEnabled(FrontendForms::FORM_CONTACT) && $action instanceof ContactPost) { + return true; + } + if ($this->validator->isFrontendFormEnabled(FrontendForms::FORM_PASSWORD) && $action instanceof ForgotPasswordPost) { + return true; + } + if ($this->validator->isFrontendFormEnabled(FrontendForms::FORM_REGISTER) && $action instanceof CreatePost) { + return true; + } + if ($this->validator->isFrontendFormEnabled(FrontendForms::FORM_LOGIN) && $action instanceof LoginPost) { + return true; + } + if ($this->validator->isFrontendFormEnabled(FrontendForms::FORM_LOGIN_AJAX) && $action instanceof AjaxLoginPost) { + return true; + } + if ($this->validator->isFrontendFormEnabled(FrontendForms::FORM_REVIEW) && $action instanceof ReviewPost) { + return true; + } + + return false; + } + + /** + * Persist data + * + * @param Request $request + * @param ActionInterface $action + * @return bool + */ + public function persist(Request $request, ActionInterface $action): bool + { + if ($action instanceof ContactPost) { + $this->dataPersistor->set('contact_us', $request->getParams()); + } + if ($action instanceof CreatePost) { + $this->customerSession->setCustomerFormData($request->getParams()); + } + if ($action instanceof ReviewPost) { + $this->reviewSession->setFormData($request->getParams()); + } + + return true; + } + + /** + * Retrieve Cloudflare Turnstile response + * + * @param Request $request + * @param ActionInterface $action + * @return string|null + */ + public function getCfResponse(Request $request, ActionInterface $action): ?string + { + if ($action instanceof AjaxLoginPost) { + return $this->json->unserialize($request->getContent())['cf-turnstile-response'] ?? null; + } + return parent::getCfResponse($request, $action); + } + + /** + * Send error + * + * @param Request $request + * @param ActionInterface $action + * @param Phrase $message + * + * @return void + */ + #[NoReturn] protected function error(Request $request, ActionInterface $action, Phrase $message): void + { + if ($action instanceof AjaxLoginPost) { + $data = [ + 'errors' => true, + 'message' => $message + ]; + $this->response->representJson($this->json->serialize($data)); + + $this->response->sendResponse(); + exit(); + } + + parent::error($request, $action, $message); + } +} diff --git a/README.md b/README.md index ce93acb..799673b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # Magento Cloudflare Turnstile -[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.4-green)](https://php.net/) +[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.0-green)](https://php.net/) [![Minimum Magento Version](https://img.shields.io/badge/magento-%3E%3D%202.4.4-green)](https://business.adobe.com/products/magento/magento-commerce.html) [![GitHub release](https://img.shields.io/github/v/release/Pixel-Open/magento-cloudflare-turnstile)](https://github.com/Pixel-Open/magento-cloudflare-turnstile/releases) ## Presentation -[Turnstile](https://developers.cloudflare.com/turnstile/) is Cloudflare's smart CAPTCHA alternative. The module allows Turnstile to protect your Magento or Adobe Commerce forms: +[Turnstile](https://developers.cloudflare.com/turnstile/) is Cloudflare's smart CAPTCHA alternative. The module allows Turnstile to protect your Magento OpenSource or Adobe Commerce forms. + +![Cloudflare Turnstile](screenshot.png) + +### Frontend Forms - Contact - Login @@ -14,12 +18,15 @@ - Reset password - Review -![Cloudflare Turnstile](screenshot.png) +### Admin Forms + +- Login +- Reset password ## Requirements - Magento >= 2.4.4 -- PHP >= 7.4.0 +- PHP >= 8.0 ## Installation @@ -43,15 +50,55 @@ composer require pixelopen/magento-cloudflare-turnstile - **Enable for Contact Us**: no - **Enable for Product Review**: no +*Stores > Configuration > Security > Google reCAPTCHA Admin Panel > Admin Panel* + +- **Enable for Login**: no +- **Enable for Forgot Password**: no + ### Enable Cloudflare Turnstile -*Stores > Configuration > Customers > Cloudflare Turnstile > Settings* +*Stores > Configuration > Services > Cloudflare Turnstile* + +**Settings** - **Enable**: enable Cloudflare Turnstile - **Sitekey**: the sitekey given for the site in your Cloudflare dashboard - **Secret key**: the secret key given for the site in your Cloudflare dashboard + +**Storefront** + - **Theme**: the Turnstile theme (auto, light or dark) -- **Forms to validate**: the forms where a Turnstile validation is required +- **Size**: the widget size (compact or normal) +- **Forms to validate**: the frontend forms where a Turnstile validation is required + +**Admin Panel** + +- **Theme**: the Turnstile theme (auto, light or dark) +- **Size**: the widget size (compact or normal) +- **Forms to validate**: the admin forms where a Turnstile validation is required + +### Override default config + +You can change the theme and size values for a specific form in the layout: + +```xml + + + + + + + + compact + + + dark + + + + + +``` ### Testing diff --git a/composer.json b/composer.json index 0bacda4..08f8507 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "magento/framework": "*" }, "type": "magento2-module", - "version": "100.1.2", + "version": "100.2.0", "autoload": { "files": [ "registration.php" diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml new file mode 100644 index 0000000..f6abd72 --- /dev/null +++ b/etc/adminhtml/di.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/etc/adminhtml/events.xml b/etc/adminhtml/events.xml new file mode 100644 index 0000000..711e7c3 --- /dev/null +++ b/etc/adminhtml/events.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index be09399..a17648b 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -11,7 +11,7 @@
- customer + service PixelOpen_CloudflareTurnstile::settings @@ -27,13 +27,35 @@ required-entry - + + + + PixelOpen\CloudflareTurnstile\Model\Config\Source\Theme - + + + PixelOpen\CloudflareTurnstile\Model\Config\Source\Size + + + + PixelOpen\CloudflareTurnstile\Model\Config\Source\Forms\Frontend + + + + + + + PixelOpen\CloudflareTurnstile\Model\Config\Source\Theme + + + + PixelOpen\CloudflareTurnstile\Model\Config\Source\Size + + - PixelOpen\CloudflareTurnstile\Model\Config\Source\Forms + PixelOpen\CloudflareTurnstile\Model\Config\Source\Forms\Adminhtml
diff --git a/etc/config.xml b/etc/config.xml index a6be784..7c7f487 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -12,8 +12,15 @@ 0 - light + + light + normal + + + light + compact + diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index daa23f8..6f778b8 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -8,9 +8,10 @@ */ --> - + Magento\Review\Model\Session + diff --git a/etc/frontend/events.xml b/etc/frontend/events.xml index d15814f..3890753 100644 --- a/etc/frontend/events.xml +++ b/etc/frontend/events.xml @@ -9,6 +9,6 @@ --> - + diff --git a/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml b/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml new file mode 100644 index 0000000..40e879f --- /dev/null +++ b/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + password + + + + + + diff --git a/view/adminhtml/layout/adminhtml_auth_login.xml b/view/adminhtml/layout/adminhtml_auth_login.xml new file mode 100644 index 0000000..d8848e3 --- /dev/null +++ b/view/adminhtml/layout/adminhtml_auth_login.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + login + + + + + + diff --git a/view/adminhtml/web/css/turnstile.css b/view/adminhtml/web/css/turnstile.css new file mode 100644 index 0000000..e0bce46 --- /dev/null +++ b/view/adminhtml/web/css/turnstile.css @@ -0,0 +1,8 @@ +#cf-turnstile { + margin-bottom: 3.5rem; +} + +.cloudflare-turnstile { + font-weight: bold; + color: #c00; +} diff --git a/view/adminhtml/web/js/view/turnstile.js b/view/adminhtml/web/js/view/turnstile.js new file mode 100644 index 0000000..5b186d8 --- /dev/null +++ b/view/adminhtml/web/js/view/turnstile.js @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2023 Pixel Développement + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/*global define*/ +define( + [ + 'PixelOpen_CloudflareTurnstile/js/view/Component' + ], + function ( + Component, + ) { + 'use strict'; + + return Component; + } +); diff --git a/view/frontend/requirejs-config.js b/view/base/requirejs-config.js similarity index 100% rename from view/frontend/requirejs-config.js rename to view/base/requirejs-config.js diff --git a/view/base/templates/turnstile.phtml b/view/base/templates/turnstile.phtml new file mode 100644 index 0000000..a6a8060 --- /dev/null +++ b/view/base/templates/turnstile.phtml @@ -0,0 +1,22 @@ + + +
+ +
+ diff --git a/view/frontend/templates/turnstile/config.phtml b/view/base/templates/turnstile/config.phtml similarity index 100% rename from view/frontend/templates/turnstile/config.phtml rename to view/base/templates/turnstile/config.phtml diff --git a/view/base/web/js/view/Component.js b/view/base/web/js/view/Component.js new file mode 100644 index 0000000..c94dd2e --- /dev/null +++ b/view/base/web/js/view/Component.js @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2023 Pixel Développement + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/*global define*/ +define( + [ + 'ko', + 'jquery', + 'uiComponent', + 'cfTurnstile', + 'mage/translate' + ], + function ( + ko, + $, + Component + ) { + 'use strict'; + + return Component.extend({ + defaults: { + template: 'PixelOpen_CloudflareTurnstile/turnstile', + }, + configSource: 'turnstileConfig', + config: { + 'sitekey': '', + 'forms': [], + 'size': 'normal', + 'theme': 'auto', + }, + action: 'default', + size: '', // Override config value if not empty + theme: '', // Override config value if not empty + + /** + * Initialize + */ + initialize: function () { + this._super(); + + if (typeof window[this.configSource] !== 'undefined' && window[this.configSource].config) { + this.config = window[this.configSource].config; + } + }, + + /** + * Can show widget + * + * @returns {boolean} + */ + canShow: function () { + return this.config.forms.indexOf(this.action) >= 0; + }, + + /** + * Show widget + */ + render: function (element) { + if (!this.config.sitekey) { + element.innerText = $.mage.__('Unable to secure the form. The sitekey is missing.'); + } else { + const result = turnstile.render(element, { + sitekey: this.config.sitekey, + theme: this.theme || this.config.theme, + size: this.size || this.config.size, + action: this.action + }); + if (typeof result === 'undefined') { + element.innerText = $.mage.__('Unable to secure the form'); + } + } + } + }); + } +); diff --git a/view/frontend/web/template/turnstile.html b/view/base/web/template/turnstile.html similarity index 100% rename from view/frontend/web/template/turnstile.html rename to view/base/web/template/turnstile.html diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml new file mode 100644 index 0000000..edd5c21 --- /dev/null +++ b/view/frontend/layout/checkout_index_index.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + PixelOpen_CloudflareTurnstile/js/view/turnstile + additional-login-form-fields + login-ajax + turnstileConfig + + + + + + + + + + + diff --git a/view/frontend/templates/turnstile.phtml b/view/frontend/templates/turnstile.phtml deleted file mode 100644 index 8d11267..0000000 --- a/view/frontend/templates/turnstile.phtml +++ /dev/null @@ -1,19 +0,0 @@ - -
- -
- diff --git a/view/frontend/web/js/view/turnstile.js b/view/frontend/web/js/view/turnstile.js index 7e0dac7..8504837 100644 --- a/view/frontend/web/js/view/turnstile.js +++ b/view/frontend/web/js/view/turnstile.js @@ -8,71 +8,30 @@ /*global define*/ define( [ - 'ko', - 'jquery', - 'uiComponent', - 'Magento_Customer/js/customer-data', - 'cfTurnstile', - 'mage/translate' + 'PixelOpen_CloudflareTurnstile/js/view/Component', + 'Magento_Customer/js/customer-data' ], function ( - ko, - $, Component, customerData ) { 'use strict'; return Component.extend({ - defaults: { - template: 'PixelOpen_CloudflareTurnstile/turnstile', - }, customer: customerData.get('customer'), - configSource: 'checkout', - turnstile: { - 'sitekey': '', - 'theme': 'auto', - 'forms': [] - }, - action: 'default', - - /** - * Initialize - */ - initialize: function () { - this._super(); - - if (typeof window[this.configSource] !== 'undefined' && window[this.configSource].turnstile) { - this.turnstile = window[this.configSource].turnstile; - } - }, /** - * Can show message + * Can show widget * * @returns {boolean} */ canShow: function () { - return !this.customer().firstname && this.turnstile.forms.indexOf(this.action) >= 0; - }, - - /** - * Show message - */ - render: function (element) { - if (!this.turnstile.sitekey) { - element.innerText = $.mage.__('Unable to secure the form. The sitekey is missing.'); - } else { - const result = turnstile.render(element, { - sitekey: this.turnstile.sitekey, - theme: this.turnstile.theme, - action: this.turnstile.action - }); - if (typeof result === 'undefined') { - element.innerText = $.mage.__('Unable to secure the form'); - } + if (this.customer().firstname) { + return false; } - } + + return this._super(); + }, }); } ); From 69baf76bc71411ab8b96e3a5cce8bef91e9f0c96 Mon Sep 17 00:00:00 2001 From: magentix Date: Wed, 6 Dec 2023 22:57:16 +0100 Subject: [PATCH 02/11] Turnstile enabled separately for front and admin --- CHANGELOG.md | 6 +- Helper/Config.php | 20 ++++-- Observer/Validate/Admin.php | 2 +- Observer/Validate/Frontend.php | 2 +- README.md | 63 ++++++++++++++++++- etc/adminhtml/system.xml | 42 ++++++++++--- etc/config.xml | 5 +- i18n/en_US.csv | 24 +++++++ i18n/fr_FR.csv | 24 +++++++ .../layout/adminhtml_auth_forgotpassword.xml | 4 +- .../adminhtml/layout/adminhtml_auth_login.xml | 4 +- view/base/web/js/view/Component.js | 2 +- view/frontend/layout/catalog_product_view.xml | 2 +- view/frontend/layout/contact_index_index.xml | 2 +- .../layout/customer_account_create.xml | 2 +- .../customer_account_forgotpassword.xml | 2 +- .../layout/customer_account_login.xml | 2 +- view/frontend/layout/default.xml | 2 +- 18 files changed, 175 insertions(+), 35 deletions(-) create mode 100644 i18n/en_US.csv create mode 100644 i18n/fr_FR.csv diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e519d0..2f70f48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ## 100.2.0 -- Admin login and reset password form validation -- Turnstile added on guest checkout login form +- Admin "login" and "reset password" forms validation +- Validation added on guest checkout login form +- Widget size configuration +- French translation ## 100.1.2 diff --git a/Helper/Config.php b/Helper/Config.php index 043422c..e79bd54 100644 --- a/Helper/Config.php +++ b/Helper/Config.php @@ -14,27 +14,37 @@ class Config extends AbstractHelper { - public const TURNSTILE_CONFIG_PATH_ENABLED = 'pixel_open_cloudflare_turnstile/settings/enabled'; - public const TURNSTILE_CONFIG_PATH_SECRET_KEY = 'pixel_open_cloudflare_turnstile/settings/secret_key'; public const TURNSTILE_CONFIG_PATH_SITEKEY = 'pixel_open_cloudflare_turnstile/settings/sitekey'; + public const TURNSTILE_CONFIG_PATH_FRONTEND_ENABLED = 'pixel_open_cloudflare_turnstile/frontend/enabled'; public const TURNSTILE_CONFIG_PATH_FRONTEND_THEME = 'pixel_open_cloudflare_turnstile/frontend/theme'; public const TURNSTILE_CONFIG_PATH_FRONTEND_SIZE = 'pixel_open_cloudflare_turnstile/frontend/size'; public const TURNSTILE_CONFIG_PATH_FRONTEND_FORMS = 'pixel_open_cloudflare_turnstile/frontend/forms'; + public const TURNSTILE_CONFIG_PATH_ADMINHTML_ENABLED = 'pixel_open_cloudflare_turnstile/adminhtml/enabled'; public const TURNSTILE_CONFIG_PATH_ADMINHTML_THEME = 'pixel_open_cloudflare_turnstile/adminhtml/theme'; public const TURNSTILE_CONFIG_PATH_ADMINHTML_SIZE = 'pixel_open_cloudflare_turnstile/adminhtml/size'; public const TURNSTILE_CONFIG_PATH_ADMINHTML_FORMS = 'pixel_open_cloudflare_turnstile/adminhtml/forms'; /** - * Is Turnstile enabled + * Is Turnstile enabled on front + * + * @return bool + */ + public function isEnabledOnFront(): bool + { + return $this->scopeConfig->isSetFlag(self::TURNSTILE_CONFIG_PATH_FRONTEND_ENABLED); + } + + /** + * Is Turnstile enabled on admin * * @return bool */ - public function isEnabled(): bool + public function isEnabledOnAdmin(): bool { - return $this->scopeConfig->isSetFlag(self::TURNSTILE_CONFIG_PATH_ENABLED); + return $this->scopeConfig->isSetFlag(self::TURNSTILE_CONFIG_PATH_ADMINHTML_ENABLED); } /** diff --git a/Observer/Validate/Admin.php b/Observer/Validate/Admin.php index 4dc1f27..03a9eb2 100644 --- a/Observer/Validate/Admin.php +++ b/Observer/Validate/Admin.php @@ -28,7 +28,7 @@ class Admin extends Validate */ public function canValidate(Request $request, ActionInterface $action): bool { - if (!$this->config->isEnabled()) { + if (!$this->config->isEnabledOnAdmin()) { return false; } if (!$request->isPost()) { diff --git a/Observer/Validate/Frontend.php b/Observer/Validate/Frontend.php index 7327e0a..f7625cd 100644 --- a/Observer/Validate/Frontend.php +++ b/Observer/Validate/Frontend.php @@ -75,7 +75,7 @@ public function __construct( */ public function canValidate(Request $request, ActionInterface $action): bool { - if (!$this->config->isEnabled()) { + if (!$this->config->isEnabledOnFront()) { return false; } if (!$request->isPost()) { diff --git a/README.md b/README.md index 799673b..5ef465d 100644 --- a/README.md +++ b/README.md @@ -61,25 +61,26 @@ composer require pixelopen/magento-cloudflare-turnstile **Settings** -- **Enable**: enable Cloudflare Turnstile - **Sitekey**: the sitekey given for the site in your Cloudflare dashboard - **Secret key**: the secret key given for the site in your Cloudflare dashboard **Storefront** +- **Enable**: enable Cloudflare Turnstile - **Theme**: the Turnstile theme (auto, light or dark) - **Size**: the widget size (compact or normal) - **Forms to validate**: the frontend forms where a Turnstile validation is required **Admin Panel** +- **Enable**: enable Cloudflare Turnstile - **Theme**: the Turnstile theme (auto, light or dark) - **Size**: the widget size (compact or normal) - **Forms to validate**: the admin forms where a Turnstile validation is required ### Override default config -You can change the theme and size values for a specific form in the layout: +You can specifically change theme and size values for a form in the layout: ```xml @@ -100,6 +101,33 @@ You can change the theme and size values for a specific form in the layout: ``` +### Command line configuration + +#### Settings + +```shell +bin/magento config:set pixel_open_cloudflare_turnstile/settings/secret_key {secret_key} +bin/magento config:set pixel_open_cloudflare_turnstile/settings/sitekey {sitekey} +``` + +#### Frontend + +```shell +bin/magento config:set pixel_open_cloudflare_turnstile/frontend/enabled {1|0} +bin/magento config:set pixel_open_cloudflare_turnstile/frontend/theme {auto|light|dark} +bin/magento config:set pixel_open_cloudflare_turnstile/frontend/size {normal|compact} +bin/magento config:set pixel_open_cloudflare_turnstile/frontend/forms contact,register,login,login-ajax,password +``` + +#### Admin + +```shell +bin/magento config:set pixel_open_cloudflare_turnstile/adminhtml/enabled {1|0} +bin/magento config:set pixel_open_cloudflare_turnstile/adminhtml/theme {auto|light|dark} +bin/magento config:set pixel_open_cloudflare_turnstile/adminhtml/size {normal|compact} +bin/magento config:set pixel_open_cloudflare_turnstile/adminhtml/forms login,password +``` + ### Testing Use the following sitekeys and secret keys for testing purposes: @@ -119,3 +147,34 @@ Use the following sitekeys and secret keys for testing purposes: | 1x0000000000000000000000000000000AA | Always passes | | 2x0000000000000000000000000000000AA | Always fails | | 3x0000000000000000000000000000000AA | Yields a "token already spent" error | + +### Definitely remove re-captcha + +To remove all native re-captcha modules, add all modules in the "replace" node of the `composer.json`. + +```json +{ + "replace": { + "magento/module-re-captcha-contact": "*", + "magento/module-re-captcha-customer": "*", + "magento/module-re-captcha-frontend-ui": "*", + "magento/module-re-captcha-migration": "*", + "magento/module-re-captcha-newsletter": "*", + "magento/module-re-captcha-paypal": "*", + "magento/module-re-captcha-review": "*", + "magento/module-re-captcha-send-friend": "*", + "magento/module-re-captcha-store-pickup": "*", + "magento/module-re-captcha-ui": "*", + "magento/module-re-captcha-user": "*", + "magento/module-re-captcha-validation": "*", + "magento/module-re-captcha-validation-api": "*", + "magento/module-re-captcha-version-2-checkbox": "*", + "magento/module-re-captcha-version-2-invisible": "*", + "magento/module-re-captcha-version-3-invisible": "*", + "magento/module-re-captcha-webapi-api": "*", + "magento/module-re-captcha-webapi-graph-ql": "*", + "magento/module-re-captcha-webapi-rest": "*", + "magento/module-re-captcha-webapi-ui": "*" + } +} +``` diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index a17648b..5f8108a 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -15,10 +15,6 @@ PixelOpen_CloudflareTurnstile::settings - - - Magento\Config\Model\Config\Source\Yesno - required-entry @@ -30,32 +26,58 @@ - + + + Magento\Config\Model\Config\Source\Yesno + + PixelOpen\CloudflareTurnstile\Model\Config\Source\Theme + + 1 + - + PixelOpen\CloudflareTurnstile\Model\Config\Source\Size + + 1 + - + PixelOpen\CloudflareTurnstile\Model\Config\Source\Forms\Frontend + + 1 + - + + + Magento\Config\Model\Config\Source\Yesno + + PixelOpen\CloudflareTurnstile\Model\Config\Source\Theme + + 1 + - + PixelOpen\CloudflareTurnstile\Model\Config\Source\Size + + 1 + - + PixelOpen\CloudflareTurnstile\Model\Config\Source\Forms\Adminhtml + + 1 + diff --git a/etc/config.xml b/etc/config.xml index 7c7f487..d320942 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -10,14 +10,13 @@ - - 0 - + 0 light normal + 0 light compact diff --git a/i18n/en_US.csv b/i18n/en_US.csv new file mode 100644 index 0000000..b7dae20 --- /dev/null +++ b/i18n/en_US.csv @@ -0,0 +1,24 @@ +"Security validation error: %1","Security validation error: %1" +"Unable to secure the form. The sitekey is missing.","Unable to secure the form. The sitekey is missing." +"Unable to secure the form","Unable to secure the form" +"Cloudflare Turnstile","Cloudflare Turnstile" +"Settings","Settings" +"Enabled","Enabled" +"Sitekey","Sitekey" +"Secret key","Secret key" +"Storefront","Storefront" +"Theme","Theme" +"Size","Size" +"Forms to validate","Forms to validate" +"Admin Panel","Admin Panel" +"please validate the security field.","please validate the security field." +"unable to validate the form, the secret key is missing.","unable to validate the form, the secret key is missing." +"unable to contact Cloudflare to validate the form.","unable to contact Cloudflare to validate the form." +"the secret parameter was not passed.","the secret parameter was not passed." +"the secret parameter was invalid or did not exist.","the secret parameter was invalid or did not exist." +"the response parameter was not passed.","the response parameter was not passed." +"the response parameter is invalid or has expired.","the response parameter is invalid or has expired." +"the request was rejected because it was malformed.","the request was rejected because it was malformed." +"the response parameter has already been validated before.","the response parameter has already been validated before." +"an internal error happened while validating the response.","an internal error happened while validating the response." +"unknown error.","unknown error." diff --git a/i18n/fr_FR.csv b/i18n/fr_FR.csv new file mode 100644 index 0000000..9e5ba37 --- /dev/null +++ b/i18n/fr_FR.csv @@ -0,0 +1,24 @@ +"Security validation error: %1","Erreur de sécurité : %1" +"Unable to secure the form. The sitekey is missing.","Impossible de sécuriser le formulaire. Le sitekey n'est pas renseigné." +"Unable to secure the form","Impossible de sécuriser le formulaire" +"Cloudflare Turnstile","Cloudflare Turnstile" +"Settings","Paramètres" +"Enabled","Activé" +"Sitekey","Sitekey" +"Secret key","Secret key" +"Storefront","Vitrine" +"Theme","Thème" +"Size","Taille" +"Forms to validate","Formulaires à valider" +"Admin Panel","Administration" +"please validate the security field.","veuillez valider le champ de sécurité." +"unable to validate the form, the secret key is missing.","impossible de valider le formulaire, la clé secrète est manquante." +"unable to contact Cloudflare to validate the form.","impossible de contacter Cloudflare pour valider le formulaire." +"the secret parameter was not passed.","le paramètre secret n'a pas été transmis." +"the secret parameter was invalid or did not exist.","le paramètre secret était invalide ou n'existait pas." +"the response parameter was not passed.","le paramètre de réponse n'a pas été transmis." +"the response parameter is invalid or has expired.","le paramètre de réponse n'est pas valide ou a expiré." +"the request was rejected because it was malformed.","la requête a été rejetée car elle était invalide." +"the response parameter has already been validated before.","le paramètre de réponse a déjà été validé." +"an internal error happened while validating the response.","une erreur interne s'est produite lors de la validation de la réponse." +"unknown error.","erreur inconnue." diff --git a/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml b/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml index 40e879f..937a3af 100644 --- a/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml +++ b/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml @@ -13,12 +13,12 @@ - + password - + diff --git a/view/adminhtml/layout/adminhtml_auth_login.xml b/view/adminhtml/layout/adminhtml_auth_login.xml index d8848e3..1c55d54 100644 --- a/view/adminhtml/layout/adminhtml_auth_login.xml +++ b/view/adminhtml/layout/adminhtml_auth_login.xml @@ -13,12 +13,12 @@ - + login - + diff --git a/view/base/web/js/view/Component.js b/view/base/web/js/view/Component.js index c94dd2e..71b4577 100644 --- a/view/base/web/js/view/Component.js +++ b/view/base/web/js/view/Component.js @@ -30,7 +30,7 @@ define( 'sitekey': '', 'forms': [], 'size': 'normal', - 'theme': 'auto', + 'theme': 'auto' }, action: 'default', size: '', // Override config value if not empty diff --git a/view/frontend/layout/catalog_product_view.xml b/view/frontend/layout/catalog_product_view.xml index 33bf33b..f6787a9 100644 --- a/view/frontend/layout/catalog_product_view.xml +++ b/view/frontend/layout/catalog_product_view.xml @@ -10,7 +10,7 @@ - + review diff --git a/view/frontend/layout/contact_index_index.xml b/view/frontend/layout/contact_index_index.xml index 976a5d4..d9c39be 100644 --- a/view/frontend/layout/contact_index_index.xml +++ b/view/frontend/layout/contact_index_index.xml @@ -10,7 +10,7 @@ - + contact diff --git a/view/frontend/layout/customer_account_create.xml b/view/frontend/layout/customer_account_create.xml index eaf93d1..0ede79e 100644 --- a/view/frontend/layout/customer_account_create.xml +++ b/view/frontend/layout/customer_account_create.xml @@ -10,7 +10,7 @@ - + register diff --git a/view/frontend/layout/customer_account_forgotpassword.xml b/view/frontend/layout/customer_account_forgotpassword.xml index 4879021..168afc0 100644 --- a/view/frontend/layout/customer_account_forgotpassword.xml +++ b/view/frontend/layout/customer_account_forgotpassword.xml @@ -10,7 +10,7 @@ - + password diff --git a/view/frontend/layout/customer_account_login.xml b/view/frontend/layout/customer_account_login.xml index 5b0a8d0..7bc6210 100644 --- a/view/frontend/layout/customer_account_login.xml +++ b/view/frontend/layout/customer_account_login.xml @@ -10,7 +10,7 @@ - + login diff --git a/view/frontend/layout/default.xml b/view/frontend/layout/default.xml index c0bcd86..693f1af 100644 --- a/view/frontend/layout/default.xml +++ b/view/frontend/layout/default.xml @@ -13,7 +13,7 @@ - + From 4d55b85703c8b6427a786878757d507f927553e8 Mon Sep 17 00:00:00 2001 From: magentix Date: Wed, 6 Dec 2023 22:59:22 +0100 Subject: [PATCH 03/11] Config name updated --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ef465d..b9d9a23 100644 --- a/README.md +++ b/README.md @@ -66,14 +66,14 @@ composer require pixelopen/magento-cloudflare-turnstile **Storefront** -- **Enable**: enable Cloudflare Turnstile +- **Enabled**: enable Cloudflare Turnstile - **Theme**: the Turnstile theme (auto, light or dark) - **Size**: the widget size (compact or normal) - **Forms to validate**: the frontend forms where a Turnstile validation is required **Admin Panel** -- **Enable**: enable Cloudflare Turnstile +- **Enabled**: enable Cloudflare Turnstile - **Theme**: the Turnstile theme (auto, light or dark) - **Size**: the widget size (compact or normal) - **Forms to validate**: the admin forms where a Turnstile validation is required From 024ad97406b76ad156b0400036265e41b84c205e Mon Sep 17 00:00:00 2001 From: magentix Date: Wed, 6 Dec 2023 23:14:06 +0100 Subject: [PATCH 04/11] Code style --- Model/Validator.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Model/Validator.php b/Model/Validator.php index af5c548..ac026ec 100644 --- a/Model/Validator.php +++ b/Model/Validator.php @@ -113,6 +113,8 @@ public function getErrorMessages(): array } /** + * Retrieve all error codes + * * @return string[] */ public function getErrorCodes(): array @@ -126,7 +128,7 @@ public function getErrorCodes(): array * @param string $code * @return string */ - protected static function getErrorMessage(string $code): string + protected function getErrorMessage(string $code): string { $messages = [ 'x-missing-response' => 'please validate the security field.', From 76874434cc0296fc8c40ee42e44b33db9d7f47ac Mon Sep 17 00:00:00 2001 From: magentix Date: Mon, 11 Dec 2023 11:37:06 +0100 Subject: [PATCH 05/11] Fix widget resetting on ajax call --- CHANGELOG.md | 1 + view/adminhtml/web/js/view/turnstile.js | 2 +- .../js/view/{Component.js => component.js} | 27 ++++++++++++++++--- view/frontend/web/js/view/turnstile.js | 4 +-- 4 files changed, 28 insertions(+), 6 deletions(-) rename view/base/web/js/view/{Component.js => component.js} (70%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f70f48..d7b09f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## 100.2.0 +- Fix widget resetting on ajax call - Admin "login" and "reset password" forms validation - Validation added on guest checkout login form - Widget size configuration diff --git a/view/adminhtml/web/js/view/turnstile.js b/view/adminhtml/web/js/view/turnstile.js index 5b186d8..7d92527 100644 --- a/view/adminhtml/web/js/view/turnstile.js +++ b/view/adminhtml/web/js/view/turnstile.js @@ -8,7 +8,7 @@ /*global define*/ define( [ - 'PixelOpen_CloudflareTurnstile/js/view/Component' + 'PixelOpen_CloudflareTurnstile/js/view/component' ], function ( Component, diff --git a/view/base/web/js/view/Component.js b/view/base/web/js/view/component.js similarity index 70% rename from view/base/web/js/view/Component.js rename to view/base/web/js/view/component.js index 71b4577..229c2c4 100644 --- a/view/base/web/js/view/Component.js +++ b/view/base/web/js/view/component.js @@ -34,7 +34,8 @@ define( }, action: 'default', size: '', // Override config value if not empty - theme: '', // Override config value if not empty + theme: '', // Override config value if not empty, + widgetId: null, /** * Initialize @@ -63,15 +64,35 @@ define( if (!this.config.sitekey) { element.innerText = $.mage.__('Unable to secure the form. The sitekey is missing.'); } else { - const result = turnstile.render(element, { + const widgetId = turnstile.render(element, { sitekey: this.config.sitekey, theme: this.theme || this.config.theme, size: this.size || this.config.size, action: this.action }); - if (typeof result === 'undefined') { + if (typeof widgetId === 'undefined') { element.innerText = $.mage.__('Unable to secure the form'); + } else { + this.widgetId = widgetId; } + + $(document).on('ajaxComplete', function (event, xhr) { + const result = xhr.responseJSON; + if (result.hasOwnProperty('errors')) { + if (result.errors) { + this.reset(); + } + } + }.bind(this)); + } + }, + + /** + * Reset turnstile + */ + reset: function () { + if (this.widgetId) { + turnstile.reset(this.widgetId); } } }); diff --git a/view/frontend/web/js/view/turnstile.js b/view/frontend/web/js/view/turnstile.js index 8504837..3a73d7d 100644 --- a/view/frontend/web/js/view/turnstile.js +++ b/view/frontend/web/js/view/turnstile.js @@ -8,7 +8,7 @@ /*global define*/ define( [ - 'PixelOpen_CloudflareTurnstile/js/view/Component', + 'PixelOpen_CloudflareTurnstile/js/view/component', 'Magento_Customer/js/customer-data' ], function ( @@ -31,7 +31,7 @@ define( } return this._super(); - }, + } }); } ); From d1d58c09f2546ca3855e30426eb3fad2d06a7f08 Mon Sep 17 00:00:00 2001 From: magentix Date: Mon, 11 Dec 2023 14:37:28 +0100 Subject: [PATCH 06/11] Useless param removed --- view/base/web/js/view/component.js | 31 +++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/view/base/web/js/view/component.js b/view/base/web/js/view/component.js index 229c2c4..5701821 100644 --- a/view/base/web/js/view/component.js +++ b/view/base/web/js/view/component.js @@ -74,16 +74,8 @@ define( element.innerText = $.mage.__('Unable to secure the form'); } else { this.widgetId = widgetId; + this.afterRender(); } - - $(document).on('ajaxComplete', function (event, xhr) { - const result = xhr.responseJSON; - if (result.hasOwnProperty('errors')) { - if (result.errors) { - this.reset(); - } - } - }.bind(this)); } }, @@ -94,6 +86,27 @@ define( if (this.widgetId) { turnstile.reset(this.widgetId); } + }, + + /** + * After render widget + */ + afterRender: function () { + this.ajaxComplete(); + }, + + /** + * Reset turnstile when Ajax request is complete with error + */ + ajaxComplete: function () { + $(document).on('ajaxComplete', function (event, xhr) { + const result = xhr.responseJSON; + if (result.hasOwnProperty('errors')) { + if (result.errors) { + this.reset(); + } + } + }.bind(this)); } }); } From a1439552c7770f0cb7ee4a09d13993c56967963f Mon Sep 17 00:00:00 2001 From: magentix Date: Tue, 12 Dec 2023 01:25:13 +0100 Subject: [PATCH 07/11] Render widget when authentication modal is opened --- Helper/Config.php | 2 +- Model/Turnstile/Adminhtml/ConfigProvider.php | 2 +- Model/Turnstile/Frontend/ConfigProvider.php | 2 +- etc/adminhtml/system.xml | 2 +- view/base/web/js/view/component.js | 67 +++++++++++--------- view/base/web/template/turnstile.html | 2 +- view/frontend/web/js/view/turnstile.js | 51 +++++++++++++++ 7 files changed, 92 insertions(+), 36 deletions(-) diff --git a/Helper/Config.php b/Helper/Config.php index e79bd54..bc15b41 100644 --- a/Helper/Config.php +++ b/Helper/Config.php @@ -62,7 +62,7 @@ public function getSecretKey(): string * * @return string */ - public function getSitekey(): string + public function getSiteKey(): string { return (string)$this->scopeConfig->getValue(self::TURNSTILE_CONFIG_PATH_SITEKEY); } diff --git a/Model/Turnstile/Adminhtml/ConfigProvider.php b/Model/Turnstile/Adminhtml/ConfigProvider.php index 8f0cf38..0828c35 100644 --- a/Model/Turnstile/Adminhtml/ConfigProvider.php +++ b/Model/Turnstile/Adminhtml/ConfigProvider.php @@ -33,7 +33,7 @@ public function getConfig(): array { return [ 'config' => [ - 'sitekey' => $this->config->getSitekey(), + 'sitekey' => $this->config->getSiteKey(), 'theme' => $this->config->getAdminTheme(), 'size' => $this->config->getAdminSize(), 'forms' => $this->config->getAdminForms(), diff --git a/Model/Turnstile/Frontend/ConfigProvider.php b/Model/Turnstile/Frontend/ConfigProvider.php index b179150..c548a00 100644 --- a/Model/Turnstile/Frontend/ConfigProvider.php +++ b/Model/Turnstile/Frontend/ConfigProvider.php @@ -33,7 +33,7 @@ public function getConfig(): array { return [ 'config' => [ - 'sitekey' => $this->config->getSitekey(), + 'sitekey' => $this->config->getSiteKey(), 'theme' => $this->config->getFrontendTheme(), 'size' => $this->config->getFrontendSize(), 'forms' => $this->config->getFrontendForms(), diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 5f8108a..1e0da47 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -16,7 +16,7 @@ - + required-entry diff --git a/view/base/web/js/view/component.js b/view/base/web/js/view/component.js index 5701821..76e0378 100644 --- a/view/base/web/js/view/component.js +++ b/view/base/web/js/view/component.js @@ -36,6 +36,8 @@ define( size: '', // Override config value if not empty theme: '', // Override config value if not empty, widgetId: null, + autoRender: true, + element: null, /** * Initialize @@ -58,55 +60,58 @@ define( }, /** - * Show widget + * Load widget + * + * @param {object} element */ - render: function (element) { + load: function (element) { + this.element = element; + if (!this.config.sitekey) { - element.innerText = $.mage.__('Unable to secure the form. The sitekey is missing.'); + this.element.innerText = $.mage.__('Unable to secure the form. The site key is missing.'); } else { - const widgetId = turnstile.render(element, { - sitekey: this.config.sitekey, - theme: this.theme || this.config.theme, - size: this.size || this.config.size, - action: this.action - }); - if (typeof widgetId === 'undefined') { - element.innerText = $.mage.__('Unable to secure the form'); - } else { - this.widgetId = widgetId; - this.afterRender(); + this.beforeRender(); + if (this.autoRender) { + this.render(); } } }, /** - * Reset turnstile + * Render widget */ - reset: function () { - if (this.widgetId) { - turnstile.reset(this.widgetId); + render: function () { + const widgetId = turnstile.render(this.element, { + sitekey: this.config.sitekey, + theme: this.theme || this.config.theme, + size: this.size || this.config.size, + action: this.action + }); + if (typeof widgetId === 'undefined') { + this.element.innerText = $.mage.__('Unable to secure the form'); + } else { + this.widgetId = widgetId; } + this.afterRender(); }, + /** + * Before render widget + */ + beforeRender: function () {}, + /** * After render widget */ - afterRender: function () { - this.ajaxComplete(); - }, + afterRender: function () {}, /** - * Reset turnstile when Ajax request is complete with error + * Reset widget */ - ajaxComplete: function () { - $(document).on('ajaxComplete', function (event, xhr) { - const result = xhr.responseJSON; - if (result.hasOwnProperty('errors')) { - if (result.errors) { - this.reset(); - } - } - }.bind(this)); + reset: function () { + if (this.widgetId) { + turnstile.reset(this.widgetId); + } } }); } diff --git a/view/base/web/template/turnstile.html b/view/base/web/template/turnstile.html index 85ccfa5..cb837b5 100644 --- a/view/base/web/template/turnstile.html +++ b/view/base/web/template/turnstile.html @@ -1,3 +1,3 @@ -
+
diff --git a/view/frontend/web/js/view/turnstile.js b/view/frontend/web/js/view/turnstile.js index 3a73d7d..9048c49 100644 --- a/view/frontend/web/js/view/turnstile.js +++ b/view/frontend/web/js/view/turnstile.js @@ -8,10 +8,12 @@ /*global define*/ define( [ + 'jquery', 'PixelOpen_CloudflareTurnstile/js/view/component', 'Magento_Customer/js/customer-data' ], function ( + $, Component, customerData ) { @@ -31,6 +33,55 @@ define( } return this._super(); + }, + + /** + * Before Render + */ + beforeRender: function () { + if (this.action === 'login-ajax') { + this.loginAjax(); + } + + this._super(); + }, + + /** + * After render widget + */ + afterRender: function () { + if (this.action === 'login-ajax') { + this.loginAjaxComplete(); + } + + this._super(); + }, + + /** + * Render the widget when authentication popup is open + */ + loginAjax: function () { + this.autoRender = false; + + $('.block-authentication').on('contentUpdated', function () { + this.render(); + }.bind(this)); + }, + + /** + * Reset turnstile when Ajax request is complete with error + */ + loginAjaxComplete: function () { + if (this.widgetId) { + $(document).on('ajaxComplete', function (event, xhr) { + const result = xhr.responseJSON; + if (result.hasOwnProperty('errors')) { + if (result.errors) { + this.reset(); + } + } + }.bind(this)); + } } }); } From ae54739167f10f7055993a7fba59f19d3854fa6c Mon Sep 17 00:00:00 2001 From: magentix Date: Tue, 12 Dec 2023 09:12:37 +0100 Subject: [PATCH 08/11] Code improvements --- CHANGELOG.md | 3 +- Model/Turnstile/Adminhtml/ConfigProvider.php | 1 + Model/Turnstile/Frontend/ConfigProvider.php | 1 + view/base/web/js/view/component.js | 39 +++++++++++-------- view/frontend/layout/checkout_index_index.xml | 1 + view/frontend/layout/default.xml | 1 + view/frontend/web/js/view/turnstile.js | 16 ++++---- 7 files changed, 36 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7b09f8..fdb077d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ ## 100.2.0 -- Fix widget resetting on ajax call - Admin "login" and "reset password" forms validation - Validation added on guest checkout login form - Widget size configuration - French translation +- Fix API request on each page with authentication popup +- Fix widget resetting on ajax call ## 100.1.2 diff --git a/Model/Turnstile/Adminhtml/ConfigProvider.php b/Model/Turnstile/Adminhtml/ConfigProvider.php index 0828c35..f8e961e 100644 --- a/Model/Turnstile/Adminhtml/ConfigProvider.php +++ b/Model/Turnstile/Adminhtml/ConfigProvider.php @@ -33,6 +33,7 @@ public function getConfig(): array { return [ 'config' => [ + 'enabled' => $this->config->isEnabledOnAdmin(), 'sitekey' => $this->config->getSiteKey(), 'theme' => $this->config->getAdminTheme(), 'size' => $this->config->getAdminSize(), diff --git a/Model/Turnstile/Frontend/ConfigProvider.php b/Model/Turnstile/Frontend/ConfigProvider.php index c548a00..8d0ecdb 100644 --- a/Model/Turnstile/Frontend/ConfigProvider.php +++ b/Model/Turnstile/Frontend/ConfigProvider.php @@ -33,6 +33,7 @@ public function getConfig(): array { return [ 'config' => [ + 'enabled' => $this->config->isEnabledOnFront(), 'sitekey' => $this->config->getSiteKey(), 'theme' => $this->config->getFrontendTheme(), 'size' => $this->config->getFrontendSize(), diff --git a/view/base/web/js/view/component.js b/view/base/web/js/view/component.js index 76e0378..32a1c94 100644 --- a/view/base/web/js/view/component.js +++ b/view/base/web/js/view/component.js @@ -27,6 +27,7 @@ define( }, configSource: 'turnstileConfig', config: { + 'enabled': false, 'sitekey': '', 'forms': [], 'size': 'normal', @@ -36,7 +37,7 @@ define( size: '', // Override config value if not empty theme: '', // Override config value if not empty, widgetId: null, - autoRender: true, + autoRendering: true, element: null, /** @@ -56,7 +57,7 @@ define( * @returns {boolean} */ canShow: function () { - return this.config.forms.indexOf(this.action) >= 0; + return this.config.enabled && this.config.forms.indexOf(this.action) >= 0; }, /** @@ -71,7 +72,7 @@ define( this.element.innerText = $.mage.__('Unable to secure the form. The site key is missing.'); } else { this.beforeRender(); - if (this.autoRender) { + if (this.autoRendering) { this.render(); } } @@ -81,29 +82,35 @@ define( * Render widget */ render: function () { - const widgetId = turnstile.render(this.element, { - sitekey: this.config.sitekey, - theme: this.theme || this.config.theme, - size: this.size || this.config.size, - action: this.action - }); - if (typeof widgetId === 'undefined') { - this.element.innerText = $.mage.__('Unable to secure the form'); - } else { - this.widgetId = widgetId; + if (this.element) { + const widgetId = turnstile.render(this.element, { + sitekey: this.config.sitekey, + theme: this.theme || this.config.theme, + size: this.size || this.config.size, + action: this.action + }); + if (typeof widgetId === 'undefined') { + this.element.innerText = $.mage.__('Unable to secure the form'); + } else { + this.widgetId = widgetId; + } + this.afterRender(); } - this.afterRender(); }, /** * Before render widget */ - beforeRender: function () {}, + beforeRender: function () { + // Do something before rendering the widget + }, /** * After render widget */ - afterRender: function () {}, + afterRender: function () { + // Do something after rendering the widget + }, /** * Reset widget diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index edd5c21..e0ed2e1 100644 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -22,6 +22,7 @@ additional-login-form-fields login-ajax turnstileConfig + false diff --git a/view/frontend/layout/default.xml b/view/frontend/layout/default.xml index 693f1af..65f84e7 100644 --- a/view/frontend/layout/default.xml +++ b/view/frontend/layout/default.xml @@ -26,6 +26,7 @@ additional-login-form-fields login-ajax turnstileConfig + false diff --git a/view/frontend/web/js/view/turnstile.js b/view/frontend/web/js/view/turnstile.js index 9048c49..6c5c4ca 100644 --- a/view/frontend/web/js/view/turnstile.js +++ b/view/frontend/web/js/view/turnstile.js @@ -21,6 +21,7 @@ define( return Component.extend({ customer: customerData.get('customer'), + authentication: '.block-authentication', /** * Can show widget @@ -28,7 +29,8 @@ define( * @returns {boolean} */ canShow: function () { - if (this.customer().firstname) { + if (this.customer().hasOwnProperty('firstname') && this.customer().firstname) { + // Widget is disabled when the customer is logged in return false; } @@ -58,12 +60,10 @@ define( }, /** - * Render the widget when authentication popup is open + * Render widget only when auth popup is open */ loginAjax: function () { - this.autoRender = false; - - $('.block-authentication').on('contentUpdated', function () { + $(this.authentication).on('contentUpdated', function () { this.render(); }.bind(this)); }, @@ -75,10 +75,8 @@ define( if (this.widgetId) { $(document).on('ajaxComplete', function (event, xhr) { const result = xhr.responseJSON; - if (result.hasOwnProperty('errors')) { - if (result.errors) { - this.reset(); - } + if (result.hasOwnProperty('errors') && result.errors) { + this.reset(); } }.bind(this)); } From c7c4b00f608a26bca89e1c8513ab1792fcbfd3e4 Mon Sep 17 00:00:00 2001 From: magentix Date: Tue, 12 Dec 2023 09:54:41 +0100 Subject: [PATCH 09/11] Fix widget rendering on checkout login modal --- view/frontend/web/js/view/turnstile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/view/frontend/web/js/view/turnstile.js b/view/frontend/web/js/view/turnstile.js index 6c5c4ca..ed5d616 100644 --- a/view/frontend/web/js/view/turnstile.js +++ b/view/frontend/web/js/view/turnstile.js @@ -21,7 +21,7 @@ define( return Component.extend({ customer: customerData.get('customer'), - authentication: '.block-authentication', + authentication: '.authentication-dropdown, .popup-authentication', /** * Can show widget @@ -60,10 +60,10 @@ define( }, /** - * Render widget only when auth popup is open + * Render widget only when modal is open */ loginAjax: function () { - $(this.authentication).on('contentUpdated', function () { + $(this.authentication).on('transitionend', function () { this.render(); }.bind(this)); }, From 13946012dca715bed86bec63b2d8bd7105da9a75 Mon Sep 17 00:00:00 2001 From: magentix Date: Tue, 12 Dec 2023 11:20:11 +0100 Subject: [PATCH 10/11] NoReturn attribute removed --- Observer/Validate.php | 2 +- Observer/Validate/Frontend.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Observer/Validate.php b/Observer/Validate.php index c305ba8..c7a734b 100644 --- a/Observer/Validate.php +++ b/Observer/Validate.php @@ -107,7 +107,7 @@ public function getCfResponse(Request $request, ActionInterface $action): ?strin * @param Phrase $message * @return void */ - #[NoReturn] protected function error(Request $request, ActionInterface $action, Phrase $message): void + protected function error(Request $request, ActionInterface $action, Phrase $message): void { $this->messageManager->addErrorMessage($message); $this->response->setRedirect($request->getServer('HTTP_REFERER', '')); diff --git a/Observer/Validate/Frontend.php b/Observer/Validate/Frontend.php index f7625cd..3f81aa7 100644 --- a/Observer/Validate/Frontend.php +++ b/Observer/Validate/Frontend.php @@ -152,7 +152,7 @@ public function getCfResponse(Request $request, ActionInterface $action): ?strin * * @return void */ - #[NoReturn] protected function error(Request $request, ActionInterface $action, Phrase $message): void + protected function error(Request $request, ActionInterface $action, Phrase $message): void { if ($action instanceof AjaxLoginPost) { $data = [ From 74891f21a0ec596670ebba1b5741cb1cc524df9d Mon Sep 17 00:00:00 2001 From: magentix Date: Tue, 12 Dec 2023 11:21:40 +0100 Subject: [PATCH 11/11] PHP 8.0 as minimum required version --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 08f8507..e92b4f5 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,7 @@ "name": "pixelopen/magento-cloudflare-turnstile", "description": "Protect your store from spam messages and spam user accounts with Cloudflare Turnstile", "require": { + "php": "^8", "magento/module-checkout": "*", "magento/module-config": "*", "magento/module-contact": "*",