From 8de8eb6b0c9aaeaa0cc87c92818a4f1eef3a05b5 Mon Sep 17 00:00:00 2001 From: kagg-design Date: Sun, 30 Jul 2023 11:12:25 +0300 Subject: [PATCH] Add hCaptcha check to General page. --- .../js/assets-js-files/integrations.test.js | 2 +- .tests/php/unit/HCaptchaTestCase.php | 6 ++ .tests/php/unit/Settings/GeneralTest.php | 35 ++++++ .tests/php/unit/Settings/IntegrationsTest.php | 2 +- assets/css/integrations.css | 29 ----- assets/css/settings-base.css | 29 +++++ assets/js/general.js | 57 ++++++++++ assets/js/integrations.js | 7 +- src/php/Settings/Abstracts/SettingsBase.php | 21 ++++ src/php/Settings/General.php | 100 ++++++++++++++++++ src/php/Settings/Integrations.php | 10 +- 11 files changed, 259 insertions(+), 39 deletions(-) create mode 100644 assets/js/general.js diff --git a/.tests/js/assets-js-files/integrations.test.js b/.tests/js/assets-js-files/integrations.test.js index 7df28ef6..9a38b785 100644 --- a/.tests/js/assets-js-files/integrations.test.js +++ b/.tests/js/assets-js-files/integrations.test.js @@ -21,7 +21,7 @@ function getDom() {
-
+
diff --git a/.tests/php/unit/HCaptchaTestCase.php b/.tests/php/unit/HCaptchaTestCase.php index 5ca136f3..6b45b7ef 100644 --- a/.tests/php/unit/HCaptchaTestCase.php +++ b/.tests/php/unit/HCaptchaTestCase.php @@ -372,6 +372,12 @@ protected function get_test_general_form_fields() { 'type' => 'password', 'section' => General::SECTION_KEYS, ], + 'check_config' => [ + 'label' => 'Check Site Config', + 'type' => 'button', + 'text' => 'Check', + 'section' => General::SECTION_KEYS, + ], 'theme' => [ 'label' => 'Theme', 'type' => 'select', diff --git a/.tests/php/unit/Settings/GeneralTest.php b/.tests/php/unit/Settings/GeneralTest.php index d9bbf025..bc58413a 100644 --- a/.tests/php/unit/Settings/GeneralTest.php +++ b/.tests/php/unit/Settings/GeneralTest.php @@ -203,6 +203,7 @@ public function dp_test_section_callback() { General::SECTION_KEYS, '

General

+

To use hCaptcha, please register here to get your site and secret keys.

Keys

@@ -236,6 +237,8 @@ public function test_admin_enqueue_scripts() { $plugin_url = 'http://test.test/wp-content/plugins/hcaptcha-wordpress-plugin'; $plugin_version = '1.0.0'; $min_prefix = '.min'; + $ajax_url = 'https://test.test/wp-admin/admin-ajax.php'; + $nonce = 'some_nonce'; $subject = Mockery::mock( General::class )->makePartial(); $subject->shouldAllowMockingProtectedMethods(); @@ -257,6 +260,38 @@ static function ( $name ) use ( $plugin_url, $plugin_version ) { } ); + WP_Mock::userFunction( 'wp_enqueue_script' ) + ->with( + General::HANDLE, + $plugin_url . "/assets/js/general$min_prefix.js", + [ 'jquery' ], + $plugin_version, + true + ) + ->once(); + + WP_Mock::userFunction( 'admin_url' ) + ->with( 'admin-ajax.php' ) + ->andReturn( $ajax_url ) + ->once(); + + WP_Mock::userFunction( 'wp_create_nonce' ) + ->with( General::CHECK_CONFIG_ACTION ) + ->andReturn( $nonce ) + ->once(); + + WP_Mock::userFunction( 'wp_localize_script' ) + ->with( + General::HANDLE, + General::OBJECT, + [ + 'ajaxUrl' => $ajax_url, + 'action' => General::CHECK_CONFIG_ACTION, + 'nonce' => $nonce, + ] + ) + ->once(); + WP_Mock::userFunction( 'wp_enqueue_style' ) ->with( General::HANDLE, diff --git a/.tests/php/unit/Settings/IntegrationsTest.php b/.tests/php/unit/Settings/IntegrationsTest.php index c4109f9a..60079548 100644 --- a/.tests/php/unit/Settings/IntegrationsTest.php +++ b/.tests/php/unit/Settings/IntegrationsTest.php @@ -234,7 +234,7 @@ public function dp_test_section_callback() { '', '

Integrations

-
+

Manage integrations with popular plugins such as Contact Form 7, WPForms, Gravity Forms, and more.

diff --git a/assets/css/integrations.css b/assets/css/integrations.css index 32200b4d..bc24fe46 100644 --- a/assets/css/integrations.css +++ b/assets/css/integrations.css @@ -1,32 +1,3 @@ -.hcaptcha-integrations-success, -.hcaptcha-integrations-error { - background: #fff; - box-shadow: 0 1px 1px rgb(0 0 0 / 4%); - margin: 5px 0 15px 0; - padding: 1px 12px; - border: 1px solid #c3c4c7; - border-left-width: 4px; -} - -#hcaptcha-integrations-message { - z-index: 1; -} - -#hcaptcha-integrations-message p { - font-size: 13px; - font-weight: 600; - line-height: 1.5; - margin: 0.7em 0; -} - -.hcaptcha-integrations-success { - border-left-color: #00a32a; -} - -.hcaptcha-integrations-error { - border-left-color: #d63638; -} - .hcaptcha-integrations table tbody { display: grid; grid-template-columns: repeat(4, minmax(100px, 1fr)); diff --git a/assets/css/settings-base.css b/assets/css/settings-base.css index 57c4ca3b..dc6fffbe 100644 --- a/assets/css/settings-base.css +++ b/assets/css/settings-base.css @@ -139,6 +139,35 @@ color: #fff; } +.hcaptcha-success, +.hcaptcha-error { + background: #fff; + box-shadow: 0 1px 1px rgb(0 0 0 / 4%); + margin: 5px 0 15px 0; + padding: 1px 12px; + border: 1px solid #c3c4c7; + border-left-width: 4px; +} + +#hcaptcha-message { + z-index: 1; +} + +#hcaptcha-message p { + font-size: 13px; + font-weight: 600; + line-height: 1.5; + margin: 0.7em 0; +} + +.hcaptcha-success { + border-left-color: #00a32a; +} + +.hcaptcha-error { + border-left-color: #d63638; +} + @media (max-width: 600px) { #hcaptcha-options table tbody { grid-template-columns: 1fr; diff --git a/assets/js/general.js b/assets/js/general.js new file mode 100644 index 00000000..7d0e2bc7 --- /dev/null +++ b/assets/js/general.js @@ -0,0 +1,57 @@ +/* global jQuery, HCaptchaGeneralObject */ + +const general = function( $ ) { + const msgSelector = '#hcaptcha-message'; + const $message = $( msgSelector ); + + function clearMessage() { + $message.removeClass(); + $message.html( '' ); + } + + function showMessage( message, msgClass ) { + $message.removeClass(); + $message.addClass( msgClass ); + $message.html( `

${ message }

` ); + $message.show(); + } + + function showSuccessMessage( response ) { + showMessage( response, 'hcaptcha-success' ); + } + + function showErrorMessage( response ) { + showMessage( response, 'hcaptcha-error' ); + } + + $( '#check_config' ).on( 'click', function( event ) { + event.preventDefault(); + clearMessage(); + + const data = { + action: HCaptchaGeneralObject.action, + nonce: HCaptchaGeneralObject.nonce, + }; + + // noinspection JSVoidFunctionReturnValueUsed + $.post( { + url: HCaptchaGeneralObject.ajaxUrl, + data, + } ) + .done( function( response ) { + if ( ! response.success ) { + showErrorMessage( response.data ); + return; + } + + showSuccessMessage( response.data ); + } ) + .fail( function( response ) { + showErrorMessage( response.statusText ); + } ); + } ); +}; + +window.hCaptchaGeneral = general; + +jQuery( document ).ready( general ); diff --git a/assets/js/integrations.js b/assets/js/integrations.js index e98aa95f..6f2d1bd8 100644 --- a/assets/js/integrations.js +++ b/assets/js/integrations.js @@ -1,7 +1,7 @@ /* global jQuery, HCaptchaIntegrationsObject */ const integrations = function( $ ) { - const msgSelector = '#hcaptcha-integrations-message'; + const msgSelector = '#hcaptcha-message'; const $message = $( msgSelector ); const $wpwrap = $( '#wpwrap' ); const $adminmenuwrap = $( '#adminmenuwrap' ); @@ -37,11 +37,11 @@ const integrations = function( $ ) { } function showSuccessMessage( response ) { - showMessage( response, 'hcaptcha-integrations-success' ); + showMessage( response, 'hcaptcha-success' ); } function showErrorMessage( response ) { - showMessage( response, 'hcaptcha-integrations-error' ); + showMessage( response, 'hcaptcha-error' ); } function insertIntoTable( $table, key, $element ) { @@ -106,6 +106,7 @@ const integrations = function( $ ) { $tr.addClass( activateClass ); + // noinspection JSVoidFunctionReturnValueUsed $.post( { url: HCaptchaIntegrationsObject.ajaxUrl, data, diff --git a/src/php/Settings/Abstracts/SettingsBase.php b/src/php/Settings/Abstracts/SettingsBase.php index ab48a517..f2996ac2 100644 --- a/src/php/Settings/Abstracts/SettingsBase.php +++ b/src/php/Settings/Abstracts/SettingsBase.php @@ -1025,6 +1025,26 @@ private function print_table_field( array $arguments ) { echo ''; } + /** + * Print button field. + * + * @param array $arguments Field arguments. + * + * @noinspection PhpUnusedPrivateMethodInspection + */ + private function print_button_field( array $arguments ) { + $disabled = $arguments['disabled'] ?? ''; + $field_id = $arguments['field_id'] ?? ''; + $text = $arguments['text'] ?? ''; + + printf( + '', + disabled( $disabled, true, false ), + esc_attr( $field_id ), + esc_attr( $text ) + ); + } + /** * Output settings field. * @@ -1045,6 +1065,7 @@ public function field_callback( array $arguments ) { 'select' => 'print_select_field', 'multiple' => 'print_multiple_select_field', 'table' => 'print_table_field', + 'button' => 'print_button_field', ]; $type = $arguments['type'] ?? ''; diff --git a/src/php/Settings/General.php b/src/php/Settings/General.php index 30d6fab1..1398c946 100644 --- a/src/php/Settings/General.php +++ b/src/php/Settings/General.php @@ -21,6 +21,16 @@ class General extends PluginSettingsBase { */ const HANDLE = 'hcaptcha-general'; + /** + * Script localization object. + */ + const OBJECT = 'HCaptchaGeneralObject'; + + /** + * Check config ajax action. + */ + const CHECK_CONFIG_ACTION = 'hcaptcha-general-check-config'; + /** * Keys section id. */ @@ -79,6 +89,15 @@ protected function section_title(): string { return 'general'; } + /** + * Init class hooks. + */ + protected function init_hooks() { + parent::init_hooks(); + + add_action( 'wp_ajax_' . self::CHECK_CONFIG_ACTION, [ $this, 'check_config' ] ); + } + /** * Init form fields. */ @@ -94,6 +113,12 @@ public function init_form_fields() { 'type' => 'password', 'section' => self::SECTION_KEYS, ], + 'check_config' => [ + 'label' => __( 'Check Site Config', 'hcaptcha-for-forms-and-more' ), + 'type' => 'button', + 'text' => __( 'Check', 'hcaptcha-for-forms-and-more' ), + 'section' => self::SECTION_KEYS, + ], 'theme' => [ 'label' => __( 'Theme', 'hcaptcha-for-forms-and-more' ), 'type' => 'select', @@ -381,6 +406,7 @@ public function section_callback( array $arguments ) {

page_title() ); ?>

+

min_prefix.js", + [ 'jquery' ], + constant( 'HCAPTCHA_VERSION' ), + true + ); + + wp_localize_script( + self::HANDLE, + self::OBJECT, + [ + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'action' => self::CHECK_CONFIG_ACTION, + 'nonce' => wp_create_nonce( self::CHECK_CONFIG_ACTION ), + ] + ); + wp_enqueue_style( self::HANDLE, constant( 'HCAPTCHA_URL' ) . "/assets/css/general$this->min_prefix.css", @@ -433,4 +477,60 @@ public function admin_enqueue_scripts() { constant( 'HCAPTCHA_VERSION' ) ); } + + /** + * Ajax action to check config. + * + * @return void + */ + public function check_config() { + // Run a security check. + if ( ! check_ajax_referer( self::CHECK_CONFIG_ACTION, 'nonce', false ) ) { + wp_send_json_error( esc_html__( 'Your session has expired. Please reload the page.', 'hcaptcha-for-forms-and-more' ) ); + } + + // Check for permissions. + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'hcaptcha-for-forms-and-more' ) ); + } + + $settings = hcaptcha()->settings(); + + $params = [ + 'host' => (string) wp_parse_url( site_url(), PHP_URL_HOST ), + 'sitekey' => $settings->get_site_key(), + 'sc' => 1, + 'swa' => 1, + 'spst' => 0, + ]; + + $url = add_query_arg( $params, 'https://hcaptcha.com/checksiteconfig' ); + + $raw_response = wp_remote_post( $url ); + + $raw_body = wp_remote_retrieve_body( $raw_response ); + + if ( empty( $raw_body ) ) { + wp_send_json_error( esc_html__( 'Error communicating with hCaptcha server', 'hcaptcha-for-forms-and-more' ) ); + } + + $body = json_decode( $raw_body, true ); + + if ( ! isset( $body['pass'] ) || ! $body['pass'] ) { + $error = $body['error'] ? (string) $body['error'] : ''; + $error = $error ? ': ' . $error : ''; + + wp_send_json_error( + esc_html__( 'Site configuration error', 'hcaptcha-for-forms-and-more' ) . $error + ); + } + + $hcaptcha_response = $body['c']['req'] ?? ''; + + $result = hcaptcha_request_verify( $hcaptcha_response ); // Always error: token malformed. + + wp_send_json_success( + esc_html__( 'Site key is valid.', 'hcaptcha-for-forms-and-more' ) + ); + } } diff --git a/src/php/Settings/Integrations.php b/src/php/Settings/Integrations.php index b5e4be54..a3a8f0ae 100644 --- a/src/php/Settings/Integrations.php +++ b/src/php/Settings/Integrations.php @@ -22,14 +22,14 @@ class Integrations extends PluginSettingsBase { const HANDLE = 'hcaptcha-integrations'; /** - * Activate plugin ajax action. + * Script localization object. */ - const ACTIVATE_ACTION = 'hcaptcha-integrations-activate'; + const OBJECT = 'HCaptchaIntegrationsObject'; /** - * Script localization object. + * Activate plugin ajax action. */ - const OBJECT = 'HCaptchaIntegrationsObject'; + const ACTIVATE_ACTION = 'hcaptcha-integrations-activate'; /** * Enabled section id. @@ -456,7 +456,7 @@ public function section_callback( array $arguments ) {

page_title() ); ?>

-
+