diff --git a/README.md b/README.md index fbab33b..4216dd2 100644 --- a/README.md +++ b/README.md @@ -34,22 +34,22 @@ $this->loadComponent('Recaptcha.Recaptcha', [ 'secret' => 'your_secret', 'type' => 'image', // image/audio 'theme' => 'light', // light/dark - 'lang' => 'vi', // default en + 'lang' => 'en', // default 'en' 'size' => 'normal' // normal/compact + 'callback' => null, // `callback` data attribute for the recaptcha div, default `null` + 'scriptBlock' => true // Value for `block` option for HtmlHelper::script() call ]); ``` -Override default configure from app config file: +Override default config from app config file: ```php // file: config/app.php /** * Recaptcha configuration. - * */ 'Recaptcha' => [ - 'enable' => true, 'sitekey' => 'your_site_key', 'secret' => 'your_secret', 'type' => 'image', @@ -62,7 +62,7 @@ Override default configure from app config file: Override default configure from recaptcha config file: ```php -// ffile: config/recaptcha.php +// file: config/recaptcha.php return [ /** @@ -96,12 +96,12 @@ Config preference: ## Usage -Display recaptcha in your view: +Display recaptcha in your template: ```php Form->create() ?> Form->control('email') ?> - // Display recaptcha box in your view, if configure has enable = false, nothing will be displayed + // Display recaptcha box in your template, if configure has enable = false, nothing will be displayed Recaptcha->display() ?> Form->button() ?> Form->end() ?> diff --git a/src/Controller/Component/RecaptchaComponent.php b/src/Controller/Component/RecaptchaComponent.php index 7a8c675..cc4402b 100644 --- a/src/Controller/Component/RecaptchaComponent.php +++ b/src/Controller/Component/RecaptchaComponent.php @@ -5,13 +5,19 @@ use Cake\Controller\Component; use Cake\Core\Configure; +use Cake\Event\EventInterface; use Cake\Http\Client; +use Cake\I18n\I18n; +use Exception; +use Locale; /** * Recaptcha component */ class RecaptchaComponent extends Component { + public const VERIFY_ENDPOINT = 'https://www.google.com/recaptcha/api/siteverify'; + /** * Default config * @@ -25,10 +31,12 @@ class RecaptchaComponent extends Component 'secret' => '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe', 'theme' => 'light', 'type' => 'image', + 'callback' => null, 'enable' => true, - 'lang' => 'en', + 'lang' => null, 'size' => 'normal', 'httpClientOptions' => [], + 'scriptBlock' => true, ]; /** @@ -39,11 +47,26 @@ class RecaptchaComponent extends Component */ public function initialize(array $config = []): void { - if (empty($config)) { - $this->setConfig(Configure::read('Recaptcha', [])); + $config += Configure::read('Recaptcha', []); + $this->setConfig($config); + + if (!$this->getConfig('lang')) { + $this->setConfig('lang', Locale::getPrimaryLanguage(I18n::getLocale())); } + } + + /** + * beforeRender + * + * @param \Cake\Event\EventInterface $event Controller.beforeRender event + * @return void + */ + public function beforeRender(EventInterface $event): void + { + $config = $this->getConfig(); + unset($config['secret'], $config['httpClientOptions']); - $this->getController()->viewBuilder()->addHelpers(['Recaptcha.Recaptcha' => $this->_config]); + $this->getController()->viewBuilder()->addHelpers(['Recaptcha.Recaptcha' => $config]); } /** @@ -53,13 +76,17 @@ public function initialize(array $config = []): void */ public function verify(): bool { - if (!$this->_config['enable']) { + if (!(bool)$this->_config['enable']) { return true; } $controller = $this->_registry->getController(); if ($controller->getRequest()->getData('g-recaptcha-response')) { - $response = json_decode($this->apiCall()); + try { + $response = json_decode($this->apiCall(), flags: JSON_THROW_ON_ERROR); + } catch (Exception $e) { + return false; + } if (isset($response->success)) { return (bool)$response->success; @@ -85,6 +112,6 @@ protected function apiCall(): string 'remoteip' => $controller->getRequest()->clientIp(), ]; - return (string)$client->post('https://www.google.com/recaptcha/api/siteverify', $data)->getBody(); + return (string)$client->post(static::VERIFY_ENDPOINT, $data)->getBody(); } } diff --git a/src/RecaptchaPlugin.php b/src/RecaptchaPlugin.php index 66c5862..51e79f8 100644 --- a/src/RecaptchaPlugin.php +++ b/src/RecaptchaPlugin.php @@ -14,16 +14,43 @@ class RecaptchaPlugin extends BasePlugin { /** * Do bootstrapping or not + * + * @var bool */ protected bool $bootstrapEnabled = false; + /** + * Console middleware + * + * @var bool + */ + protected bool $consoleEnabled = false; + + /** + * Enable middleware + * + * @var bool + */ + protected bool $middlewareEnabled = false; + + /** + * Register container services + * + * @var bool + */ + protected bool $servicesEnabled = false; + /** * Load routes or not + * + * @var bool */ protected bool $routesEnabled = false; /** - * Console middleware + * Load events or not + * + * @var bool */ - protected bool $consoleEnabled = false; + protected bool $eventsEnabled = false; } diff --git a/src/View/Helper/RecaptchaHelper.php b/src/View/Helper/RecaptchaHelper.php index f4e78e5..0059945 100644 --- a/src/View/Helper/RecaptchaHelper.php +++ b/src/View/Helper/RecaptchaHelper.php @@ -3,22 +3,68 @@ namespace Recaptcha\View\Helper; +use Cake\Core\Configure; +use Cake\I18n\I18n; use Cake\View\Helper; +use Locale; /** * Recaptcha helper */ class RecaptchaHelper extends Helper { + /** + * Default config + * + * These are merged with user-provided config when the component is used. + * + * @var array + */ + protected array $_defaultConfig = [ + // This is test only key + 'sitekey' => '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI', + 'theme' => 'light', + 'type' => 'image', + 'callback' => null, + 'enable' => true, + 'lang' => null, + 'size' => 'normal', + 'scriptBlock' => true, + ]; + + /** + * Helpers + * + * @var array + */ + protected array $helpers = ['Form']; + + /** + * initialize + * + * @param array $config config + * @return void + */ + public function initialize(array $config = []): void + { + $config += Configure::read('Recaptcha', []); + $this->setConfig($config); + + if (!$this->getConfig('lang')) { + $this->setConfig('lang', Locale::getPrimaryLanguage(I18n::getLocale())); + } + } + /** * Display recaptcha function * + * @param array $config Config * @return string */ - public function display() + public function display(array $config = []): string { - $recaptcha = $this->getConfig(); - if (!$recaptcha['enable']) { + $recaptcha = $config + $this->getConfig(); + if (!(bool)$recaptcha['enable']) { return ''; } diff --git a/templates/element/recaptcha.php b/templates/element/recaptcha.php index 393a3f9..cb8641f 100644 --- a/templates/element/recaptcha.php +++ b/templates/element/recaptcha.php @@ -1,19 +1,33 @@ -Html->script('https://www.google.com/recaptcha/api.js?hl=' . $recaptcha['lang'] . '&onload=CaptchaCallback&render=explicit') ?> - +Form->unlockField('g-recaptcha-response'); +} catch (CakeException) { + // If FormProtectorComponent is not loaded, an exception in thrown in older CakePHP versions. +} +?> +Html->script( + 'https://www.google.com/recaptcha/api.js?hl=' . $recaptcha['lang'], + [ + 'block' => $recaptcha['scriptBlock'], + 'async' => true, + 'defer' => true, + ] +) ?>
+ + data-callback="" + +>