├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── config ├── bootstrap.php └── routes.php ├── phpunit.xml.dist ├── src ├── Controller │ ├── Component │ │ └── RecaptchaComponent.php │ └── ContactController.php ├── Form │ └── ContactForm.php ├── Recaptcha │ ├── Exception │ │ └── MissingRecaptchaApiKey.php │ ├── HttpClientInterface.php │ ├── Recaptcha.php │ ├── RecaptchaResponse.php │ └── RecaptchaResponseInterface.php ├── Template │ └── Contact │ │ ├── index.ctp │ │ └── multiple_widgets.ctp ├── Validation │ ├── ConfigValidator.php │ ├── GlobalValidator.php │ └── RecaptchaValidator.php └── View │ └── Helper │ └── RecaptchaHelper.php └── tests ├── TestCase ├── Controller │ ├── Component │ │ └── RecaptchaComponentTest.php │ └── ContactControllerTest.php ├── Recaptcha │ ├── Exception │ │ └── MissingRecaptchaApiKeyTest.php │ ├── RecaptchaResponseTest.php │ └── RecaptchaTest.php ├── Validation │ ├── ConfigValidatorTest.php │ ├── GlobalValidatorTest.php │ └── RecaptchaValidatorTest.php └── View │ └── Helper │ └── RecaptchaHelperTest.php └── bootstrap.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.bat] 15 | end_of_line = crlf 16 | [*.yml] 17 | indent_style = space 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Define the line ending behavior of the different file extensions 2 | # Set default behaviour, in case users don't have core.autocrlf set. 3 | * text=auto 4 | * text eol=lf 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/* 2 | /composer.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.5 5 | - 5.6 6 | 7 | sudo: false 8 | 9 | env: 10 | matrix: 11 | - DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' 12 | - DB=pgsql db_dsn='postgres://travis@127.0.0.1/cakephp_test' 13 | - DB=sqlite db_dsn='sqlite:///:memory:' 14 | 15 | global: 16 | - DEFAULT=1 17 | 18 | matrix: 19 | fast_finish: true 20 | 21 | include: 22 | - php: 7.0 23 | env: PHPCS=1 DEFAULT=0 24 | 25 | - php: 7.0 26 | env: CODECOVERAGE=1 DEFAULT=0 27 | 28 | - php: hhvm 29 | env: HHVM=1 DB=sqlite db_dsn='sqlite:///:memory:' 30 | 31 | - php: hhvm 32 | env: HHVM=1 DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' 33 | 34 | install: 35 | - composer self-update 36 | - composer install --prefer-dist --no-interaction 37 | 38 | before_script: 39 | - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" 40 | - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi" 41 | - sh -c "if [ '$PHPCS' = '1' ]; then composer require cakephp/cakephp-codesniffer:dev-master; fi" 42 | 43 | - phpenv rehash 44 | - set +H 45 | 46 | script: 47 | - sh -c "if [ '$DEFAULT' = '1' ]; then ./vendor/bin/phpunit; fi" 48 | - sh -c "if [ '$PHPCS' = '1' ]; then ./vendor/bin/phpcs -n -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests --ignore=vendor; fi" 49 | 50 | notifications: 51 | email: false 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Google reCAPTCHA for CakePHP 3 2 | ============================== 3 | 4 | [![Build Status](https://api.travis-ci.org/cakephp-fr/recaptcha.png?branch=master)](https://travis-ci.org/cakephp-fr/recaptcha) 5 | [![Latest Stable Version](https://poser.pugx.org/cakephp-fr/recaptcha/v/stable.png)](https://packagist.org/packages/cakephp-fr/recaptcha) 6 | [![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.4-8892BF.svg)](https://php.net/) 7 | [![License](https://poser.pugx.org/cakephp-fr/recaptcha/license.png)](https://packagist.org/packages/cakephp-fr/recaptcha) 8 | [![Total Downloads](https://poser.pugx.org/cakephp-fr/recaptcha/d/total.png)](https://packagist.org/packages/cakephp-fr/recaptcha) 9 | 10 | Be careful version 1.0 is for CakePHP 3.6 minimum. 11 | For a previous CakePHP version please use the 0.4.2 version. 12 | 13 | ## Plugin's Objective ## 14 | 15 | This plugin adds functionalities to use the new reCAPTCHA API version 2.0 in 16 | CakePHP projects. 17 | 18 | This plugin is still under development... For now, multiple widgets on a single page is not available. 19 | 20 | ## Requirements ## 21 | 22 | - PHP >= 5.4.16 23 | - [CakePHP 3.x](http://book.cakephp.org/3.0/en/index.html) 24 | - Server under `localhost` name. Be aware that the widgets will not be displayed 25 | if you have a vhost named local.dev/dev/ for instance. 26 | 27 | ## Installation ## 28 | 29 | _[Using [Composer](http://getcomposer.org/)]_ 30 | 31 | Add the plugin to your project's `composer.json` - something like this: 32 | 33 | ```bash 34 | composer require cakephp-fr/recaptcha:~1.0 35 | ``` 36 | 37 | You then need to load the plugin, by running: 38 | 39 | ```bash 40 | bin/cake plugin load -rb Recaptcha 41 | ``` 42 | 43 | You can check that this command has created the line `Plugin::load('Recaptcha', ['routes' => true, 'bootstrap' => true]);` at the bottom of your `config/boostrap.php` file. 44 | 45 | The `'routes' => true` should be deleted in production. It's only useful if you want to see the demo. 46 | 47 | ## Usage of plugin ## 48 | 49 | ### 1. Go to Google reCAPTCHA site 50 | 51 | Go [here](https://www.google.com/recaptcha/intro/index.html) to create a pair 52 | of keys for your website. 53 | 54 | ### 2. Configure the plugin 55 | 56 | The Easiest way is to add the recaptcha config to the `config/app.php`, something like: 57 | 58 | ```php 59 | return [ 60 | 61 | .... (other configs before) 62 | 63 | 'Recaptcha' => [ 64 | // Register API keys at https://www.google.com/recaptcha/admin 65 | 'sitekey' => 'your-sitekey', 66 | 'secret' => 'your-secret', 67 | // reCAPTCHA supported 40+ languages listed 68 | // here: https://developers.google.com/recaptcha/docs/language 69 | 'lang' => 'en', 70 | // either light or dark 71 | 'theme' => 'light', 72 | // either image or audio 73 | 'type' => 'image', 74 | // either normal or compact 75 | 'size' => 'normal' 76 | ] 77 | ] 78 | ``` 79 | 80 | Make sure that `/config/app.php` file is in `.gitignore`. The secret key must stay secret. 81 | 82 | If you don't have a key and a secret, an exception will be raised. 83 | 84 | ### 3. Then add the component in your controller where you need the reCAPTCHA. 85 | 86 | For example: 87 | 88 | ```php 89 | public function initialize() { 90 | parent::initialize(); 91 | if ($this->request->getParam('action') === 'contact') { 92 | $this->loadComponent('Recaptcha.Recaptcha'); 93 | } 94 | } 95 | ``` 96 | 97 | ```php 98 | public function contact() { 99 | if ($this->request->is('post')) { 100 | if ($this->Recaptcha->verify()) { 101 | // Here you can validate your data 102 | if (!empty($this->request->getData())) { 103 | $this->Flash->success(__('We will get back to you soon.')); 104 | return $this->redirect($this->referer()); 105 | } else { 106 | $this->Flash->error(__('There was a problem submitting your form.')); 107 | } 108 | } else { 109 | // You can debug developers errors with 110 | // debug($this->Recaptcha->errors()); 111 | $this->Flash->error(__('Please check your Recaptcha Box.')); 112 | } 113 | } 114 | } 115 | ``` 116 | 117 | ### 4. Finally add `Recaptcha->display() ?>` in your view template inside the form. 118 | 119 | **No need** to add the helper: it will be added with the component. 120 | 121 | For example: 122 | 123 | ```php 124 | Form->create() ?> 125 | 126 | Form->control('name', [ 127 | 'label' => __('Your Name'), 128 | // 'default' => $this->request->query('name'); // in case you add the Prg Component 129 | ]) ?> 130 | Form->control('message', [ 131 | 'type' => 'textarea', 132 | // 'default' => $this->request->query('message'); // in case you add the Prg Component 133 | 'label' => __('Your Message') 134 | ]) ?> 135 | 136 | Recaptcha->display() ?> 137 | 138 | Form->button(__('OK')) ?> 139 | Form->end() ?> 140 | ``` 141 | 142 | See another example of contact with no form in 143 | `src/Controller/ContactController.php`, `src/Template/Contact/index.ctp` and 144 | `src/Form/ContactForm.php`. You can test it by going to 145 | `http://localhost/recaptcha/contact`. 146 | 147 | ## What's inside ? ## 148 | 149 | **COMPONENT** 150 | 151 | - RecaptchaComponent 152 | 153 | **HELPERS** 154 | 155 | - RecaptchaHelper (Automatically added when the RecaptchaComponent is added) 156 | 157 | **EXAMPLE** 158 | 159 | - Controller : ContactController 160 | - Form : ContactForm 161 | - Template : Contact/index.ctp 162 | 163 | ## Tests ## 164 | 165 | To test the plugin, clone it and run `composer install`. Then run: 166 | 167 | ```bash 168 | ./vendor/bin/phpunit 169 | ./vendor/bin/phpcs -n -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests --ignore=vendor 170 | ``` 171 | 172 | ## Support & Contribution ## 173 | 174 | For support and feature request, please contact me through Github issues 175 | 176 | Please feel free to contribute to the plugin with new issues, requests, unit 177 | tests and code fixes or new features. If you want to contribute some code, 178 | create a feature branch, and send us your pull request. 179 | Unit tests for new features and issues detected are mandatory to keep quality 180 | high. 181 | 182 | ## License ## 183 | 184 | Copyright (c) [2014-2018] [cakephp-fr] 185 | 186 | Permission is hereby granted, free of charge, to any person obtaining a copy of 187 | this software and associated documentation files (the "Software"), to deal in 188 | the Software without restriction, including without limitation the rights to 189 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 190 | of the Software, and to permit persons to whom the Software is furnished to do 191 | so, subject to the following conditions: 192 | 193 | The above copyright notice and this permission notice shall be included in all 194 | copies or substantial portions of the Software. 195 | 196 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 197 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 198 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 199 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 200 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 201 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 202 | SOFTWARE. 203 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cakephp-fr/recaptcha", 3 | "type": "cakephp-plugin", 4 | "description": "To easily use Google Recaptcha (free CAPTCHA service that protect websites from spam and abuse) in CakePHP projects", 5 | "keywords": [ 6 | "cakephp", 7 | "bootstrap", 8 | "google", 9 | "recaptcha", 10 | "captcha" 11 | ], 12 | "homepage": "https://github.com/cakephp-fr/recaptcha", 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "cake17", 17 | "email": "cake17@cake-websites.com" 18 | } 19 | ], 20 | "support": { 21 | "issues": "https://github.com/cake17/cakephp-recaptcha/issues", 22 | "source": "https://github.com/cake17/cakephp-recaptcha", 23 | "wiki": "https://github.com/google/ReCAPTCHA" 24 | }, 25 | "require": { 26 | "php": ">=7.2", 27 | "cakephp/cakephp": "^4.0" 28 | }, 29 | "require-dev": { 30 | "phpunit/phpunit": "*", 31 | "cakephp/cakephp-codesniffer": "2.*" 32 | }, 33 | "suggest": { 34 | "friendsofcake/search": "dev-master" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Recaptcha\\": "src" 39 | } 40 | }, 41 | "autoload-dev": { 42 | "psr-4": { 43 | "Recaptcha\\Test\\": "tests", 44 | "Cake\\Test\\": "./vendor/cakephp/cakephp/tests" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /config/bootstrap.php: -------------------------------------------------------------------------------- 1 | errors(Configure::read('Recaptcha')); 21 | 22 | if (!empty($errors)) { 23 | $errMsg = ''; 24 | $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($errors)); 25 | foreach($it as $v) { 26 | $errMsg .= "- " . $v . "
"; 27 | } 28 | throw new \Exception(__d('recaptcha', 'One of your recaptcha config value is incorrect:
' . $errMsg)); 29 | } 30 | -------------------------------------------------------------------------------- /config/routes.php: -------------------------------------------------------------------------------- 1 | true when plugin is 7 | * loaded 8 | * 9 | * @author cake17 10 | * @license http://www.opensource.org/licenses/mit-license.php The MIT License 11 | * @link http://blog.cake-websites.com/ 12 | */ 13 | use Cake\Routing\Router; 14 | 15 | Router::plugin('Recaptcha', function ($routes) { 16 | $routes->connect( 17 | '/contact', 18 | ['controller' => 'Contact', 'action' => 'index'] 19 | ); 20 | $routes->connect( 21 | '/contact/multiple-widgets', 22 | ['controller' => 'Contact', 'action' => 'multiple-widgets'] 23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ./tests/TestCase 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ./vendor/ 36 | ./vendor/ 37 | 38 | ./tests/ 39 | ./tests/ 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/Controller/Component/RecaptchaComponent.php: -------------------------------------------------------------------------------- 1 | viewBuilder()->getHelpers())) { 53 | $controller->viewBuilder()->setHelpers(['Recaptcha.Recaptcha'], true); 54 | } 55 | } 56 | 57 | /** 58 | * startup callback 59 | * 60 | * @param \Cake\Event\Event $event Event. 61 | * 62 | * @return void 63 | */ 64 | public function startup(Event $event) 65 | { 66 | $secret = Configure::consume('Recaptcha.secret'); 67 | // throw an exception if the secret is not defined in config/recaptcha.php file 68 | if (empty($secret)) { 69 | throw new Exception(__d('recaptcha', "You must set the secret Recaptcha key in config/recaptcha.php file")); 70 | } 71 | 72 | // instantiate Recaptcha object that deals with retrieving data from google recaptcha 73 | $this->recaptcha = new Recaptcha(new RecaptchaResponse(), $secret); 74 | $controller = $event->getSubject(); 75 | 76 | $this->setController($controller); 77 | } 78 | 79 | /** 80 | * Verify Response 81 | * 82 | * @return bool 83 | */ 84 | public function verify() 85 | { 86 | $controller = $this->_registry->getController(); 87 | $gRecaptchaResponse = $controller->getRequest()->getData("g-recaptcha-response"); 88 | if (!empty($gRecaptchaResponse)) { 89 | 90 | $resp = $this->recaptcha->verifyResponse( 91 | new Client(), 92 | $gRecaptchaResponse 93 | ); 94 | 95 | // if verification is correct, 96 | if ($resp) { 97 | return true; 98 | } 99 | } 100 | return false; 101 | } 102 | 103 | /** 104 | * Return an array with errors : missing secret, connexion issue, ... 105 | * 106 | * @return array 107 | */ 108 | public function errors() 109 | { 110 | return $this->recaptcha->setErrors(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Controller/ContactController.php: -------------------------------------------------------------------------------- 1 | request->getParam('action'), ['index', 'multipleWidgets'])) { 31 | $this->loadComponent('Recaptcha.Recaptcha'); 32 | } 33 | } 34 | 35 | /** 36 | * Contact Form Page 37 | * 38 | * @return void 39 | */ 40 | public function index() 41 | { 42 | $contact = new ContactForm(); 43 | if ($this->request->is('post')) { 44 | if ($this->Recaptcha->verify()) { 45 | // Here you can validate your data instead 46 | if ($contact->execute($this->request->getData())) { 47 | $this->Flash->success(__('We will get back to you soon.')); 48 | } else { 49 | $this->Flash->error(__('There was a problem submitting your form.')); 50 | } 51 | } else { 52 | // You can debug developers errors with 53 | // debug($this->Recaptcha->errors()); 54 | $this->Flash->error(__('Please check your Recaptcha Box.')); 55 | } 56 | } 57 | $this->set(compact('contact')); 58 | } 59 | 60 | /** 61 | * Contact Form Page With multiple Widgets 62 | * 63 | * @return void 64 | */ 65 | public function multipleWidgets() 66 | { 67 | $contact = new ContactForm(); 68 | if ($this->request->is('post')) { 69 | if ($this->Recaptcha->verify()) { 70 | if ($contact->execute($this->request->getData())) { 71 | $this->Flash->success(__('We will get back to you soon.')); 72 | } else { 73 | $this->Flash->error(__('There was a problem submitting your form.')); 74 | } 75 | } else { 76 | // debug($contact); 77 | // debug($this->Recaptcha); 78 | $this->Flash->error(__('Please check your Recaptcha Box.')); 79 | } 80 | } 81 | $this->set(compact('contact')); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Form/ContactForm.php: -------------------------------------------------------------------------------- 1 | addField('name', 'string') 31 | // ->addField('email', ['type' => 'string']) 32 | ->addField('body', ['type' => 'text']); 33 | } 34 | 35 | /** 36 | * Build Validator 37 | * 38 | * @param Validator $validator Validator. 39 | * @return validator 40 | */ 41 | protected function _buildValidator(Validator $validator) 42 | { 43 | return $validator 44 | ->add('name', 'length', [ 45 | 'rule' => ['minLength', 2], 46 | 'message' => 'A name is required' 47 | ]) 48 | ->add('email', 'format', [ 49 | 'rule' => 'email', 50 | 'message' => 'A valid email address is required', 51 | ]); 52 | } 53 | 54 | /** 55 | * Execute 56 | * 57 | * @param array $data Data. 58 | * @return bool 59 | */ 60 | protected function _execute(array $data) 61 | { 62 | // Send an email. 63 | return true; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Recaptcha/Exception/MissingRecaptchaApiKey.php: -------------------------------------------------------------------------------- 1 | %s'; 21 | } 22 | -------------------------------------------------------------------------------- /src/Recaptcha/HttpClientInterface.php: -------------------------------------------------------------------------------- 1 | recaptchaResponse = $recaptchaResponse; 86 | if ($secret == null || $secret == "") { 87 | throw new MissingRecaptchaApiKey([ 88 | 'link' => self::$signupUrl, 89 | 'name' => 'here' 90 | ]); 91 | } 92 | $this->secret = $secret; 93 | } 94 | 95 | /** 96 | * Calls the reCAPTCHA siteverify API to verify whether the user passes 97 | * CAPTCHA test. 98 | * 99 | * @param HttpClientInterface $httpClient Required. HttpClient. 100 | * @param string $response Required. The user response token provided by the reCAPTCHA to the user and provided to your site on. 101 | * @param string $remoteIp Optional. The user's IP address. 102 | * 103 | * @return bool 104 | */ 105 | public function verifyResponse(Client $httpClient, $response, $remoteIp = null) 106 | { 107 | if (is_null($this->secret)) { 108 | $this->errors['missing-secret'] = __d('recaptcha', 'secret is null'); 109 | return false; 110 | } 111 | // Get Json GRecaptchaResponse Obj from Google server 112 | $postOptions = [ 113 | 'secret' => $this->secret, 114 | 'response' => $response 115 | ]; 116 | if (!is_null($remoteIp)) { 117 | $postOptions['remoteip'] = $remoteIp; 118 | } 119 | $gRecaptchaResponse = $httpClient->post(self::$siteVerifyUrl, $postOptions); 120 | 121 | // problem while accessing remote 122 | if (!$gRecaptchaResponse->isOk()) { 123 | $this->errors['remote-not-accessible'] = __d('recaptcha', 'Remote is not accessible'); 124 | return false; 125 | } 126 | 127 | $this->recaptchaResponse->setJson($gRecaptchaResponse->getJson()); 128 | 129 | if ($this->recaptchaResponse->isSuccess()) { 130 | return true; 131 | } 132 | $this->errors['not-checked'] = __d('recaptcha', 'Recaptcha is not checked'); 133 | return false; 134 | } 135 | 136 | /** 137 | * Return an array with errors : missing secret, connexion issue, ... 138 | * 139 | * @return array 140 | */ 141 | public function errors() 142 | { 143 | return $this->errors; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Recaptcha/RecaptchaResponse.php: -------------------------------------------------------------------------------- 1 | 'The secret parameter is missing.', 64 | 'invalid-input-secret' => 'The secret parameter is invalid or malformed.', 65 | 'missing-input-response' => 'The response parameter is missing.', 66 | 'invalid-input-response' => 'The response parameter is invalid or malformed.' 67 | ]; 68 | 69 | /** 70 | * Return true/false if Success/Fails. 71 | * 72 | * @return bool 73 | */ 74 | public function isSuccess() 75 | { 76 | return $this->success; 77 | } 78 | 79 | /** 80 | * Return the Code Errors if any. 81 | * 82 | * @return array 83 | */ 84 | public function errorCodes() 85 | { 86 | return $this->errorCodes; 87 | } 88 | 89 | /** 90 | * Sets the success. 91 | * 92 | * @param bool $success Success. 93 | * 94 | * @return void 95 | */ 96 | public function setSuccess($success) 97 | { 98 | if ($this->_validateSuccess($success)) { 99 | $this->success = $success; 100 | } 101 | } 102 | 103 | /** 104 | * Validates the success 105 | * Only if success is a boolean. 106 | * 107 | * @param bool $success Success. 108 | * 109 | * @return bool 110 | */ 111 | protected function _validateSuccess($success) 112 | { 113 | if (is_bool($success)) { 114 | return true; 115 | } 116 | return false; 117 | } 118 | 119 | /** 120 | * Sets the Code Errors. 121 | * 122 | * @param array $errorCodes Error Codes. 123 | * 124 | * @return void 125 | */ 126 | public function setErrorCodes(array $errorCodes) 127 | { 128 | if ($this->_validateErrorCodes($errorCodes)) { 129 | $this->errorCodes = $this->_purifyErrorCodes($errorCodes); 130 | } 131 | } 132 | 133 | /** 134 | * Validates the errorCodes 135 | * Only if errorCodes is an array and is in available errorCodes. 136 | * 137 | * @param array $errorCodes Error Codes. 138 | * 139 | * @return bool 140 | */ 141 | protected function _validateErrorCodes(array $errorCodes) 142 | { 143 | if (empty($errorCodes)) { 144 | return false; 145 | } 146 | if (!is_array($errorCodes)) { 147 | return false; 148 | } 149 | 150 | return true; 151 | } 152 | 153 | /** 154 | * Return a array with only the authorized errorCodes. 155 | * 156 | * @param array $errorCodes Error Codes. 157 | * @return array 158 | */ 159 | protected function _purifyErrorCodes(array $errorCodes) 160 | { 161 | $errorCodesPurified = []; 162 | foreach ($errorCodes as $num => $errorCode) { 163 | if (key_exists($errorCode, self::$errorCodesAuthorized)) { 164 | $errorCodesPurified[$num] = $errorCode; 165 | } 166 | } 167 | return $errorCodesPurified; 168 | } 169 | 170 | /** 171 | * Hydrate the Object with $json data. 172 | * 173 | * @param array $json Json response of GRecaptcha server. 174 | * 175 | * @return void 176 | */ 177 | public function setJson(array $json) 178 | { 179 | if (isset($json['error-codes']) && !empty($json['error-codes'])) { 180 | $this->setErrorCodes($json['error-codes']); 181 | } 182 | if (isset($json['success']) && !empty($json['success'])) { 183 | $this->setSuccess($json['success']); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/Recaptcha/RecaptchaResponseInterface.php: -------------------------------------------------------------------------------- 1 | 17 | Form->create($contact) ?> 18 | Form->control('name') ?> 19 | Form->control('email') ?> 20 | Form->control('body') ?> 21 | Recaptcha->display([ 22 | // This options override global configs 23 | 'theme' => 'dark', 24 | 'type' => 'image', 25 | 'size' => 'normal' 26 | ]) ?> 27 | Form->button('Submit') ?> 28 | Form->end() ?> 29 | -------------------------------------------------------------------------------- /src/Template/Contact/multiple_widgets.ctp: -------------------------------------------------------------------------------- 1 | 17 | Form->create($contact) ?> 18 | Form->control('name') ?> 19 | Form->control('email') ?> 20 | Form->control('body') ?> 21 | Form->button('Submit') ?> 22 | Form->end() ?> 23 | 24 | Recaptcha->widget(['id' => 'widget']); 26 | $this->Recaptcha->widget(['id' => 'widget2']); 27 | ?> 28 | Recaptcha->render(); ?> 29 | -------------------------------------------------------------------------------- /src/Validation/ConfigValidator.php: -------------------------------------------------------------------------------- 1 | requirePresence('secret') 29 | ->notEmptyString('secret', __d('recaptcha', 'A secret should not be blank.')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Validation/GlobalValidator.php: -------------------------------------------------------------------------------- 1 | [ 25 | 'ar', 26 | 'af', 27 | 'am', 28 | 'hy', 29 | 'az', 30 | 'eu', 31 | 'bn', 32 | 'bg', 33 | 'ca', 34 | 'zh-HK', 35 | 'zh-CN', 36 | 'zh-TW', 37 | 'hr', 38 | 'cs', 39 | 'da', 40 | 'nl', 41 | 'en-GB', 42 | 'en', 43 | 'et', 44 | 'fil', 45 | 'fi', 46 | 'fr', 47 | 'fr-CA', 48 | 'gl', 49 | 'ka', 50 | 'de', 51 | 'de-AT', 52 | 'de-CH', 53 | 'el', 54 | 'gu', 55 | 'iw', 56 | 'hi', 57 | 'hu', 58 | 'is', 59 | 'id', 60 | 'it', 61 | 'ja', 62 | 'kn', 63 | 'ko', 64 | 'lo', 65 | 'lv', 66 | 'lt', 67 | 'ms', 68 | 'ml', 69 | 'mr', 70 | 'mn', 71 | 'no', 72 | 'fa', 73 | 'pl', 74 | 'pt', 75 | 'pt-BR', 76 | 'pt-PT', 77 | 'ro', 78 | 'ru', 79 | 'sr', 80 | 'si', 81 | 'sk', 82 | 'sl', 83 | 'es', 84 | 'es-419', 85 | 'sw', 86 | 'sv', 87 | 'ta', 88 | 'te', 89 | 'th', 90 | 'tr', 91 | 'uk', 92 | 'ur', 93 | 'vi', 94 | 'zu' 95 | ], 96 | 'theme' => [ 97 | 'light', 98 | 'dark' 99 | ], 100 | 'type' => [ 101 | 'audio', 102 | 'image' 103 | ], 104 | 'size' => [ 105 | 'normal', 106 | 'compact' 107 | ] 108 | ]; 109 | 110 | /** 111 | * Constructor 112 | * 113 | * @return void 114 | */ 115 | public function __construct() 116 | { 117 | parent::__construct(); 118 | $this 119 | ->add('lang', [ 120 | 'inLangList' => [ 121 | 'rule' => ['inList', $this->validList['lang']], 122 | 'message' => __d('recaptcha', 'The lang should be in the following authorized lang ' . implode(',', $this->validList['lang'])), 123 | ] 124 | ]) 125 | ->add('theme', [ 126 | 'inThemeList' => [ 127 | 'rule' => ['inList', $this->validList['theme']], 128 | 'message' => __d('recaptcha', 'The theme should be in the following authorized theme ' . implode(',', $this->validList['theme'])), 129 | ] 130 | ]) 131 | ->add('type', [ 132 | 'inTypeList' => [ 133 | 'rule' => ['inList', $this->validList['type']], 134 | 'message' => __d('recaptcha', 'The type should be in the following authorized type ' . implode(',', $this->validList['type'])), 135 | ] 136 | ]) 137 | ->add('size', [ 138 | 'inSizeList' => [ 139 | 'rule' => ['inList', $this->validList['size']], 140 | 'message' => __d('recaptcha', 'The size should be in the following authorized type ' . implode(',', $this->validList['size'])), 141 | ] 142 | ]); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Validation/RecaptchaValidator.php: -------------------------------------------------------------------------------- 1 | 'en', 41 | // If no theme is found anywhere 42 | 'theme' => 'light', 43 | // If no type is found anywhere 44 | 'type' => 'image', 45 | // If no size is found anywhere 46 | 'size' => 'normal' 47 | ]; 48 | 49 | /** 50 | * Constructor 51 | * 52 | * @param View $view View 53 | * @param array $config Config 54 | * 55 | * @return void 56 | */ 57 | public function __construct(View $view, $config = []) 58 | { 59 | parent::__construct($view, $config); 60 | 61 | // Merge Options given by user in config/recaptcha 62 | $this->setConfig(Configure::read('Recaptcha')); 63 | 64 | $lang = $this->getConfig('lang'); 65 | if (empty($lang)) { 66 | $this->setConfig('lang', I18n::locale()); 67 | } 68 | // Validate the Configure Data 69 | $validator = new RecaptchaValidator(); 70 | $errors = $validator->errors($this->getConfig()); 71 | if (!empty($errors)) { 72 | throw new \Exception(__d('recaptcha', 'One of your recaptcha config value is incorrect')); 73 | // throw an exception with config error that is raised 74 | } 75 | 76 | // Make sure the secret param is 77 | $this->setConfig('secret', ''); 78 | } 79 | 80 | /** 81 | * Render the recaptcha div and js script. 82 | * 83 | * @param array $options Options. 84 | * - sitekey 85 | * - lang 86 | * - theme 87 | * - type 88 | * 89 | * @return string HTML 90 | */ 91 | public function display(array $options = []) 92 | { 93 | // merge options 94 | $options = array_merge($this->getConfig(), $options); 95 | 96 | // Validate the Configure Data 97 | $validator = new RecaptchaValidator(); 98 | $errors = $validator->errors($options); 99 | if (!empty($errors)) { 100 | throw new \Exception(__d('recaptcha', 'One of your recaptcha config value is incorrect')); 101 | // throw an exception with config error that is raised 102 | } 103 | 104 | extract($options); 105 | 106 | return '
107 | '; 110 | } 111 | 112 | /** 113 | * Return html 114 | * 115 | * @return string 116 | */ 117 | public function render() 118 | { 119 | return $this->html() . $this->script(); 120 | } 121 | 122 | /** 123 | * Create a recaptcha widget (for multiple widgets) 124 | * 125 | * @param array $options Options 126 | * - id : Id 127 | * - sitekey : Site Key 128 | * - theme : Theme 129 | * - type : Type 130 | * - lang : Langue 131 | * 132 | * @return void 133 | */ 134 | public function widget(array $options = []) 135 | { 136 | $options = array_merge($this->getConfig(), $options); 137 | 138 | // Validate the Configure Data 139 | $validator = new RecaptchaValidator(); 140 | $errors = $validator->errors($options); 141 | if (!empty($errors)) { 142 | throw new \Exception(__d('recaptcha', 'One of your recaptcha config value is incorrect in a widget')); 143 | // throw an exception with config error that is raised 144 | } 145 | // add infos in widgets for script() 146 | $this->widgets[] = [ 147 | 'id' => $options['id'], 148 | 'sitekey' => $options['sitekey'], 149 | 'theme' => $options['theme'], 150 | 'type' => $options['type'], 151 | 'lang' => $options['lang'], 152 | 'size' => $options['size'] 153 | ]; 154 | } 155 | 156 | /** 157 | * Define the html to render 158 | * 159 | * @return string html code 160 | */ 161 | public function html() 162 | { 163 | $html = ''; 164 | 165 | if (isset($this->widgets) && !empty($this->widgets)) { 166 | foreach ($this->widgets as $widget) { 167 | $actions = [ 168 | 'getResponse' => "javascript:alert(grecaptcha.getResponse(id" . $widget['id'] . "));", 169 | 'reset' => "javascript:grecaptcha.reset(id" . $widget['id'] . ");", 170 | 'render' => '?' 171 | ]; 172 | $html .= '
'; 173 | } 174 | } 175 | return $html; 176 | } 177 | 178 | /** 179 | * Define the script to render 180 | * 181 | * @return string js script 182 | */ 183 | public function script() 184 | { 185 | $js = ""; 207 | $js .= ''; 208 | //debug($js); 209 | return $js; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /tests/TestCase/Controller/Component/RecaptchaComponentTest.php: -------------------------------------------------------------------------------- 1 | controller = $this->getMock( 50 | // 'Cake\Controller\Controller', 51 | // [], 52 | // [$request, $response] 53 | // ); 54 | 55 | $registry = new ComponentRegistry(); 56 | $this->Recaptcha = new RecaptchaComponent($registry); 57 | } 58 | 59 | /** 60 | * tearDown method 61 | * 62 | * @return void 63 | */ 64 | public function tearDown() 65 | { 66 | unset($this->Recaptcha); 67 | 68 | parent::tearDown(); 69 | } 70 | 71 | /** 72 | * Test StartupWithExistingConfigFile 73 | * 74 | * @return void 75 | */ 76 | public function testStartupWithExistingConfigFile() 77 | { 78 | $this->markTestIncomplete("Not yet"); 79 | } 80 | // 81 | // /** 82 | // * testVerify 83 | // * 84 | // * @return void 85 | // */ 86 | // public function testVerify() 87 | // { 88 | // $response = new RecaptchaResponse(); 89 | // // instantiate Recaptcha object that deals with retrieving data from google recaptcha 90 | // $recaptcha = new Recaptcha($response, 'good-secret'); 91 | // 92 | // $this->controller->request->data([ 93 | // "g-recaptcha-response" => "good-response" 94 | // ]); 95 | // $this->assertFalse($this->controller->verify()); 96 | // $this->assertFalse($this->controller->verify()); 97 | // $this->assertEmpty($this->controller->verify()); 98 | // } 99 | } 100 | -------------------------------------------------------------------------------- /tests/TestCase/Controller/ContactControllerTest.php: -------------------------------------------------------------------------------- 1 | get('/recaptcha/contact'); 23 | 24 | // $this->assertResponseOk(); 25 | // $this->assertResponseContains('form'); 26 | // $this->assertResponseContains('recaptcha'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/TestCase/Recaptcha/Exception/MissingRecaptchaApiKeyTest.php: -------------------------------------------------------------------------------- 1 | markTestIncomplete('Not implemented yet.'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/TestCase/Recaptcha/RecaptchaResponseTest.php: -------------------------------------------------------------------------------- 1 | RecaptchaResponse = new RecaptchaResponse(); 28 | } 29 | 30 | /** 31 | * tearDown method 32 | * 33 | * @return void 34 | */ 35 | public function tearDown() 36 | { 37 | unset($this->RecaptchaResponse); 38 | 39 | parent::tearDown(); 40 | } 41 | 42 | /** 43 | * Test Success To True 44 | * 45 | * @return void 46 | */ 47 | public function testSuccessTrue() 48 | { 49 | $this->RecaptchaResponse->setSuccess(true); 50 | $this->assertEquals(true, $this->RecaptchaResponse->isSuccess()); 51 | } 52 | 53 | /** 54 | * Test Success To False 55 | * 56 | * @return void 57 | */ 58 | public function testSuccessFalse() 59 | { 60 | $this->RecaptchaResponse->setSuccess(false); 61 | $this->assertEquals(false, $this->RecaptchaResponse->isSuccess()); 62 | } 63 | 64 | /** 65 | * Test Success With Wrong Inputs 66 | * 67 | * @return void 68 | */ 69 | public function testSuccessWithWrongInputs() 70 | { 71 | $this->RecaptchaResponse->setSuccess('ee'); 72 | $this->assertEquals(null, $this->RecaptchaResponse->isSuccess()); 73 | 74 | $this->RecaptchaResponse->setSuccess(['eee', 'e']); 75 | $this->assertEquals(null, $this->RecaptchaResponse->isSuccess()); 76 | } 77 | 78 | /** 79 | * Test Error Codes 80 | * 81 | * @return void 82 | */ 83 | public function testErrorCodesThatWorks() 84 | { 85 | $this->RecaptchaResponse->setErrorCodes(['invalid-input-response']); 86 | $this->assertEquals(['invalid-input-response'], $this->RecaptchaResponse->errorCodes()); 87 | } 88 | 89 | /** 90 | * Test Error Codes With Wrong Inputs 91 | * 92 | * @return void 93 | */ 94 | public function testErrorCodesWithWrongInputs() 95 | { 96 | $this->RecaptchaResponse->setErrorCodes(['eee', 'e']); 97 | $this->assertEquals([], $this->RecaptchaResponse->errorCodes()); 98 | } 99 | 100 | /** 101 | * Test Set Response 102 | * 103 | * @return void 104 | */ 105 | public function testSetJsonWithWrongInputs() 106 | { 107 | $this->RecaptchaResponse->setJson(['eee', 'e']); 108 | $this->assertEquals(null, $this->RecaptchaResponse->errorCodes()); 109 | $this->assertEquals(null, $this->RecaptchaResponse->isSuccess()); 110 | } 111 | 112 | /** 113 | * Test Set Response 114 | * 115 | * @return void 116 | */ 117 | public function testSetResponseWithGoodInputs() 118 | { 119 | $this->RecaptchaResponse->setJson(['error-codes' => ['input-error'], 'success' => true]); 120 | $this->assertEquals([], $this->RecaptchaResponse->errorCodes()); 121 | $this->assertEquals(true, $this->RecaptchaResponse->isSuccess()); 122 | 123 | $this->RecaptchaResponse->setJson(['error-codes' => ['missing-input-secret'], 'success' => true]); 124 | $this->assertEquals(['missing-input-secret'], $this->RecaptchaResponse->errorCodes()); 125 | $this->assertEquals(true, $this->RecaptchaResponse->isSuccess()); 126 | 127 | $this->RecaptchaResponse->setJson(['error-codes' => ['missing-input-secret', 'invalid-input-secret', 'eee'], 'success' => true]); 128 | $this->assertEquals(['missing-input-secret', 'invalid-input-secret'], $this->RecaptchaResponse->errorCodes()); 129 | $this->assertEquals(true, $this->RecaptchaResponse->isSuccess()); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tests/TestCase/Recaptcha/RecaptchaTest.php: -------------------------------------------------------------------------------- 1 | Recaptcha = new Recaptcha($recaptchaResponse, $secret); 46 | //$this->assertEquals(); 47 | unset($this->Recaptcha); 48 | } 49 | 50 | public function testWithExistingSecret() 51 | { 52 | $secret = 'goodSecret'; 53 | $recaptchaResponse = new RecaptchaResponse(); 54 | $this->Recaptcha = new Recaptcha($recaptchaResponse, $secret); 55 | //$this->assertEquals(); 56 | unset($this->Recaptcha); 57 | } 58 | 59 | public function testVerifyResponse() 60 | { 61 | $secret = 'goodSecret'; 62 | // $httpClient = new Client(); 63 | $recaptchaResponse = new RecaptchaResponse(); 64 | $this->Recaptcha = new Recaptcha($recaptchaResponse, $secret); 65 | // $this->assertEquals(false, $this->Recaptcha->verifyResponse($httpClient, 'good')); 66 | unset($this->Recaptcha); 67 | } 68 | 69 | public function testVerifyResponseWrong() 70 | { 71 | $secret = 'goodSecret'; 72 | // $httpClient = new Client(); 73 | $recaptchaResponse = new RecaptchaResponse(); 74 | $this->Recaptcha = new Recaptcha($recaptchaResponse, $secret); 75 | // $this->assertEquals(false, $this->Recaptcha->verifyResponse($httpClient, 'wrong')); 76 | unset($this->Recaptcha); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/TestCase/Validation/ConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'fr']; 29 | $errors = $validator->errors($data); 30 | $this->assertArrayHasKey('secret', $errors); 31 | 32 | $validator = new ConfigValidator(); 33 | $data = ['theme' => 'light']; 34 | $errors = $validator->errors($data); 35 | $this->assertArrayHasKey('secret', $errors); 36 | 37 | $validator = new ConfigValidator(); 38 | $data = ['type' => 'image']; 39 | $errors = $validator->errors($data); 40 | $this->assertArrayHasKey('secret', $errors); 41 | 42 | $validator = new ConfigValidator(); 43 | $data = ['size' => 'normal']; 44 | $errors = $validator->errors($data); 45 | $this->assertArrayHasKey('secret', $errors); 46 | } 47 | 48 | /** 49 | * Test Config Validation with existing params 50 | * 51 | * @return void 52 | */ 53 | public function testConfigValidationWithExistingParams() 54 | { 55 | $validator = new ConfigValidator(); 56 | $data = ['secret' => 'fff', 'lang' => 'fr']; 57 | $errors = $validator->errors($data); 58 | $this->assertEmpty($errors); 59 | 60 | $validator = new ConfigValidator(); 61 | $data = ['secret' => 'fff', 'theme' => 'light']; 62 | $errors = $validator->errors($data); 63 | $this->assertEmpty($errors); 64 | 65 | $validator = new ConfigValidator(); 66 | $data = ['secret' => 'fff', 'type' => 'image']; 67 | $errors = $validator->errors($data); 68 | $this->assertEmpty($errors); 69 | 70 | $validator = new ConfigValidator(); 71 | $data = ['secret' => 'fff', 'size' => 'normal']; 72 | $errors = $validator->errors($data); 73 | $this->assertEmpty($errors); 74 | } 75 | 76 | /** 77 | * Test Global Validation with non existing params 78 | * 79 | * @return void 80 | */ 81 | public function testConfigValidationWithNonExistingParams() 82 | { 83 | $validator = new ConfigValidator(); 84 | $data = ['secret' => 'fff', 'lang' => 'non-existing-lang']; 85 | $errors = $validator->errors($data); 86 | $this->assertArrayHasKey('lang', $errors); 87 | 88 | $validator = new ConfigValidator(); 89 | $data = ['secret' => 'fff', 'theme' => 'non-existing-theme']; 90 | $errors = $validator->errors($data); 91 | $this->assertArrayHasKey('theme', $errors); 92 | 93 | $validator = new ConfigValidator(); 94 | $data = ['secret' => 'fff', 'type' => 'non-existing-type']; 95 | $errors = $validator->errors($data); 96 | $this->assertArrayHasKey('type', $errors); 97 | 98 | $validator = new ConfigValidator(); 99 | $data = ['secret' => 'fff', 'size' => 'non-existing-size']; 100 | $errors = $validator->errors($data); 101 | $this->assertArrayHasKey('size', $errors); 102 | } 103 | 104 | /** 105 | * Test Global Validation with block of existing params 106 | * 107 | * @return void 108 | */ 109 | public function testConfigValidationWithBlockOfExistingParams() 110 | { 111 | $validator = new ConfigValidator(); 112 | $data = [ 113 | 'secret' => 'fff', 114 | 'lang' => 'fr', 115 | 'theme' => 'dark', 116 | 'type' => 'audio', 117 | 'size' => 'compact' 118 | ]; 119 | $errors = $validator->errors($data); 120 | $this->assertEmpty($errors); 121 | } 122 | 123 | /** 124 | * Test Global Validation with block of non existing params 125 | * 126 | * @return void 127 | */ 128 | public function testConfigValidationWithBlockOfNonExistingParams() 129 | { 130 | $validator = new ConfigValidator(); 131 | $data = [ 132 | 'secret' => 'fff', 133 | 'lang' => 'non-existing-lang', 134 | 'theme' => 'non-existing-theme', 135 | 'type' => 'non-existing-type', 136 | 'size' => 'non-existing-size' 137 | ]; 138 | $errors = $validator->errors($data); 139 | $this->assertArrayHasKey('lang', $errors); 140 | $this->assertArrayHasKey('theme', $errors); 141 | $this->assertArrayHasKey('type', $errors); 142 | $this->assertArrayHasKey('size', $errors); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /tests/TestCase/Validation/GlobalValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'fr']; 29 | $errors = $validator->errors($data); 30 | $this->assertEmpty($errors); 31 | 32 | $validator = new GlobalValidator(); 33 | $data = ['theme' => 'light']; 34 | $errors = $validator->errors($data); 35 | $this->assertEmpty($errors); 36 | 37 | $validator = new GlobalValidator(); 38 | $data = ['type' => 'image']; 39 | $errors = $validator->errors($data); 40 | $this->assertEmpty($errors); 41 | 42 | $validator = new GlobalValidator(); 43 | $data = ['size' => 'normal']; 44 | $errors = $validator->errors($data); 45 | $this->assertEmpty($errors); 46 | } 47 | 48 | /** 49 | * Test Global Validation with non existing params 50 | * 51 | * @return void 52 | */ 53 | public function testGlobalValidationWithNonExistingParams() 54 | { 55 | $validator = new GlobalValidator(); 56 | $data = ['lang' => 'non-existing-lang']; 57 | $errors = $validator->errors($data); 58 | $this->assertArrayHasKey('lang', $errors); 59 | 60 | $validator = new GlobalValidator(); 61 | $data = ['theme' => 'non-existing-theme']; 62 | $errors = $validator->errors($data); 63 | $this->assertArrayHasKey('theme', $errors); 64 | 65 | $validator = new GlobalValidator(); 66 | $data = ['type' => 'non-existing-type']; 67 | $errors = $validator->errors($data); 68 | $this->assertArrayHasKey('type', $errors); 69 | 70 | $validator = new GlobalValidator(); 71 | $data = ['size' => 'non-existing-size']; 72 | $errors = $validator->errors($data); 73 | $this->assertArrayHasKey('size', $errors); 74 | } 75 | 76 | /** 77 | * Test Global Validation with block of existing params 78 | * 79 | * @return void 80 | */ 81 | public function testGlobalValidationWithBlockOfExistingParams() 82 | { 83 | $validator = new GlobalValidator(); 84 | $data = [ 85 | 'lang' => 'fr', 86 | 'theme' => 'dark', 87 | 'type' => 'audio', 88 | 'size' => 'compact' 89 | ]; 90 | $errors = $validator->errors($data); 91 | $this->assertEmpty($errors); 92 | } 93 | 94 | /** 95 | * Test Global Validation with block of non existing params 96 | * 97 | * @return void 98 | */ 99 | public function testGlobalValidationWithBlockOfNonExistingParams() 100 | { 101 | $validator = new GlobalValidator(); 102 | $data = [ 103 | 'lang' => 'non-existing-lang', 104 | 'theme' => 'non-existing-theme', 105 | 'type' => 'non-existing-type', 106 | 'size' => 'non-existing-size' 107 | ]; 108 | $errors = $validator->errors($data); 109 | $this->assertArrayHasKey('lang', $errors); 110 | $this->assertArrayHasKey('theme', $errors); 111 | $this->assertArrayHasKey('type', $errors); 112 | $this->assertArrayHasKey('size', $errors); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/TestCase/Validation/RecaptchaValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'fr']; 29 | $errors = $validator->errors($data); 30 | $this->assertEmpty($errors); 31 | 32 | $validator = new RecaptchaValidator(); 33 | $data = ['theme' => 'light']; 34 | $errors = $validator->errors($data); 35 | $this->assertEmpty($errors); 36 | 37 | $validator = new RecaptchaValidator(); 38 | $data = ['type' => 'image']; 39 | $errors = $validator->errors($data); 40 | $this->assertEmpty($errors); 41 | 42 | $validator = new RecaptchaValidator(); 43 | $data = ['size' => 'normal']; 44 | $errors = $validator->errors($data); 45 | $this->assertEmpty($errors); 46 | } 47 | 48 | /** 49 | * Test Recaptcha Validation with non existing params 50 | * 51 | * @return void 52 | */ 53 | public function testRecaptchaValidationWithNonExistingParams() 54 | { 55 | $validator = new RecaptchaValidator(); 56 | $data = ['lang' => 'non-existing-lang']; 57 | $errors = $validator->errors($data); 58 | $this->assertArrayHasKey('lang', $errors); 59 | 60 | $validator = new RecaptchaValidator(); 61 | $data = ['theme' => 'non-existing-theme']; 62 | $errors = $validator->errors($data); 63 | $this->assertArrayHasKey('theme', $errors); 64 | 65 | $validator = new RecaptchaValidator(); 66 | $data = ['type' => 'non-existing-type']; 67 | $errors = $validator->errors($data); 68 | $this->assertArrayHasKey('type', $errors); 69 | 70 | $validator = new RecaptchaValidator(); 71 | $data = ['size' => 'non-existing-size']; 72 | $errors = $validator->errors($data); 73 | $this->assertArrayHasKey('size', $errors); 74 | } 75 | 76 | /** 77 | * Test Recaptcha Validation with block of existing params 78 | * 79 | * @return void 80 | */ 81 | public function testRecaptchaValidationWithBlockOfExistingParams() 82 | { 83 | $validator = new RecaptchaValidator(); 84 | $data = [ 85 | 'lang' => 'fr', 86 | 'theme' => 'dark', 87 | 'type' => 'audio', 88 | 'size' => 'compact' 89 | ]; 90 | $errors = $validator->errors($data); 91 | $this->assertEmpty($errors); 92 | } 93 | 94 | /** 95 | * Test Recaptcha Validation with block of non existing params 96 | * 97 | * @return void 98 | */ 99 | public function testRecaptchaValidationWithBlockOfNonExistingParams() 100 | { 101 | $validator = new RecaptchaValidator(); 102 | $data = [ 103 | 'lang' => 'non-existing-lang', 104 | 'theme' => 'non-existing-theme', 105 | 'type' => 'non-existing-type', 106 | 'size' => 'non-existing-size' 107 | ]; 108 | $errors = $validator->errors($data); 109 | $this->assertArrayHasKey('lang', $errors); 110 | $this->assertArrayHasKey('theme', $errors); 111 | $this->assertArrayHasKey('type', $errors); 112 | $this->assertArrayHasKey('size', $errors); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/TestCase/View/Helper/RecaptchaHelperTest.php: -------------------------------------------------------------------------------- 1 | Recaptcha = new RecaptchaHelper($view); 32 | } 33 | 34 | /** 35 | * tearDown method 36 | * 37 | * @return void 38 | */ 39 | public function tearDown() 40 | { 41 | unset($this->Recaptcha); 42 | 43 | parent::tearDown(); 44 | } 45 | 46 | /** 47 | * testConstruct method 48 | * 49 | * @return void 50 | */ 51 | public function testConstruct() 52 | { 53 | $expected = [ 54 | // If no language is found anywhere 55 | 'lang' => 'en', 56 | // If no theme is found anywhere 57 | 'theme' => 'light', 58 | // If no type is found anywhere 59 | 'type' => 'image', 60 | 'secureApiUrl' => 'https://www.google.com/recaptcha/api', 61 | // reCAPTCHA supported 40+ languages listed here: https://developers.google.com/recaptcha/docs/language 62 | 'langAccepted' => [ 63 | 'ar', 64 | 'bg', 65 | 'ca', 66 | 'zh-CN', 67 | 'zh-TW', 68 | 'hr', 69 | 'cs', 70 | 'da', 71 | 'nl', 72 | 'en-GB', 73 | 'en', 74 | 'fil', 75 | 'fi', 76 | 'fr', 77 | 'fr-CA', 78 | 'de', 79 | 'de-AT', 80 | 'de-CH', 81 | 'el', 82 | 'iw', 83 | 'hi', 84 | 'hu', 85 | 'id', 86 | 'it', 87 | 'ja', 88 | 'ko', 89 | 'lv', 90 | 'lt', 91 | 'no', 92 | 'fa', 93 | 'pl', 94 | 'pt', 95 | 'pt-BR', 96 | 'pt-PT', 97 | 'ro', 98 | 'ru', 99 | 'sr', 100 | 'sk', 101 | 'sl', 102 | 'es', 103 | 'es-419', 104 | 'sv', 105 | 'th', 106 | 'tr', 107 | 'uk', 108 | 'vi' 109 | ], 110 | 'themeAccepted' => [ 111 | 'dark', 112 | 'light' 113 | ], 114 | 'typeAccepted' => [ 115 | 'audio', 116 | 'image' 117 | ] 118 | ]; 119 | // $this->assertEquals($expected, $this->Recaptcha->config()); 120 | } 121 | 122 | /** 123 | * Test StartupWithEmptyOptions 124 | * 125 | * @return void 126 | */ 127 | // public function testStartupWithEmptyOptions() 128 | // { 129 | // Configure::config([ 130 | // 'Recaptcha' => [ 131 | // 'sitekey' => 'goodkey', 132 | // 'secret' => 'goodsecret', 133 | // 'lang' => '', 134 | // 'theme' => '', 135 | // 'type' => '', 136 | // ] 137 | // ]); 138 | // 139 | // $this->assertEquals('goodkey', Configure::read('Recaptcha.sitekey')); 140 | // $this->assertEquals('goodsecret', Configure::read('Recaptcha.secret')); 141 | // $this->assertEquals('', Configure::read('Recaptcha.lang')); 142 | // $this->assertEquals('', Configure::read('Recaptcha.theme')); 143 | // $this->assertEquals('', Configure::read('Recaptcha.type')); 144 | // } 145 | 146 | public function testDisplay() 147 | { 148 | $options = [ 149 | 'lang' => 'fr', 150 | 'sitekey' => 'goodkey', 151 | 'theme' => 'light', 152 | 'type' => 'image' 153 | ]; 154 | 155 | $expected = '
156 | '; 159 | 160 | // $this->assertEquals($expected, $this->Recaptcha->display($options)); 161 | } 162 | 163 | public function testDisplayWithEmptyValues() 164 | { 165 | $options = []; 166 | 167 | $expected = '
168 | '; 171 | 172 | // $this->assertEquals($expected, $this->Recaptcha->display($options)); 173 | } 174 | 175 | // public function testMultipleWidgets() 176 | // { 177 | // $id = 1; 178 | // $siteKey = ''; 179 | // $options = [ 180 | // 'theme' => '', 181 | // 'type' => '', 182 | // 'lang' => '', 183 | // 'callback' => '', 184 | // 'action' => '' 185 | // ]; 186 | // 187 | // $expected = ' 188 | //
189 | //
190 | //
191 | // 192 | //
193 | //
194 | //
195 | //
196 | //
197 | // 198 | //
199 | //
200 | //
201 | //
202 | //
203 | // 204 | //
205 | // '; 206 | // 207 | // $actual = $this->Recaptcha->display($id, $siteKey, $options); 208 | // 209 | // //$this->assertEquals($expected, $actual); 210 | // } 211 | // 212 | // public function testMultipleWidgetsHeadScript() 213 | // { 214 | // // add widget 215 | // $id = 1; 216 | // $siteKey = ''; 217 | // $options = [ 218 | // 'theme' => '', 219 | // 'type' => '', 220 | // 'lang' => '', 221 | // 'callback' => '', 222 | // 'action' => '' 223 | // ]; 224 | // $this->Recaptcha->display($id, $siteKey, $options); 225 | // 226 | // $expected = ''; 227 | // 228 | // $actual = $this->Recaptcha->script(); 229 | // 230 | // //$this->assertEquals($expected, $actual); 231 | // } 232 | } 233 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'App', 40 | 'paths' => [ 41 | 'plugins' => [ROOT . 'Plugin' . DS], 42 | 'templates' => [ROOT . 'App' . DS . 'Template' . DS] 43 | ] 44 | ]); 45 | 46 | Cake\Cache\Cache::config([ 47 | '_cake_core_' => [ 48 | 'engine' => 'File', 49 | 'prefix' => 'cake_core_', 50 | 'serialize' => true, 51 | 'path' => '/tmp', 52 | ], 53 | '_cake_model_' => [ 54 | 'engine' => 'File', 55 | 'prefix' => 'cake_model_', 56 | 'serialize' => true, 57 | 'path' => '/tmp', 58 | ] 59 | ]); 60 | 61 | if (!getenv('db_dsn')) { 62 | putenv('db_dsn=sqlite:///:memory:'); 63 | } 64 | if (!getenv('DB')) { 65 | putenv('DB=sqlite'); 66 | } 67 | ConnectionManager::config('test', ['url' => getenv('db_dsn')]); 68 | Plugin::load('Recaptcha', [ 69 | 'path' => dirname(dirname(__FILE__)) . DS, 70 | ]); 71 | --------------------------------------------------------------------------------