addon = $addon; $this->token_verifier = $token_verifier; } /** * Get the plugin settings fields. * * @since 1.0 * @see GF_RECAPTCHA::plugin_settings_fields() * * @return array */ public function get_fields() { return array( $this->get_description_fields(), $this->get_v3_fields(), $this->get_v2_fields(), ); } /** * Gets any custom plugin settings, ensuring they contain the latest values from the constants. * * @since 1.0 * * @param array $settings Add-on's parent plugin settings. * * @return array */ public function get_settings( $settings ) { if ( ! is_array( $settings ) ) { $settings = array(); } $site_key = $this->get_recaptcha_key( 'site_key_v3', true ); if ( $site_key ) { $settings['site_key_v3'] = $site_key; } $secret_key = $this->get_recaptcha_key( 'secret_key_v3', true ); if ( $secret_key ) { $settings['secret_key_v3'] = $secret_key; } return array_merge( $settings, array( 'site_key_v2' => get_option( 'rg_gforms_captcha_public_key' ), 'secret_key_v2' => get_option( 'rg_gforms_captcha_private_key' ), 'type_v2' => get_option( 'rg_gforms_captcha_type' ), ) ); } /** * Handles updating of custom plugin settings. * * @since 1.0 * * @param array $settings Update the v2 settings. */ public function update_settings( $settings ) { update_option( 'rg_gforms_captcha_public_key', rgar( $settings, 'site_key_v2' ) ); update_option( 'rg_gforms_captcha_private_key', rgar( $settings, 'secret_key_v2' ) ); update_option( 'rg_gforms_captcha_type', rgar( $settings, 'type_v2' ) ); } /** * Get the description section for the plugin settings. * * @since 1.0 * @return array */ private function get_description_fields() { return array( 'id' => 'gravityformsrecaptcha_description', 'title' => esc_html__( 'reCAPTCHA Settings', 'gravityformsrecaptcha' ), 'description' => $this->get_settings_intro_description(), 'fields' => array( array( 'type' => 'html', ), ), ); } /** * Get the plugin settings fields for reCAPTCHA v3. * * @since 1.0 * @return array */ private function get_v3_fields() { $site_key = $this->get_recaptcha_key( 'site_key_v3', true ); $secret_key = $this->get_recaptcha_key( 'secret_key_v3', true ); return array( 'id' => 'gravityformsrecaptcha_v3', 'title' => esc_html__( 'reCAPTCHA v3', 'gravityformsrecaptcha' ), 'fields' => array( array( 'name' => 'site_key_v3', 'label' => esc_html__( 'Site Key', 'gravityformsrecaptcha' ), 'type' => 'text', 'feedback_callback' => array( $this, 'v3_keys_status_feedback_callback' ), 'readonly' => empty( $site_key ) ? '' : 'readonly', 'after_input' => $this->get_constant_message( $site_key, 'GF_RECAPTCHA_V3_SITE_KEY' ), ), array( 'name' => 'secret_key_v3', 'label' => esc_html__( 'Secret Key', 'gravityformsrecaptcha' ), 'type' => 'text', 'feedback_callback' => array( $this, 'v3_keys_status_feedback_callback' ), 'readonly' => empty( $secret_key ) ? '' : 'readonly', 'after_input' => $this->get_constant_message( $secret_key, 'GF_RECAPTCHA_V3_SECRET_KEY' ), ), array( 'name' => 'score_threshold_v3', 'label' => esc_html__( 'Score Threshold', 'gravityformsrecaptcha' ), 'description' => $this->get_score_threshold_description(), 'default_value' => 0.5, 'type' => 'text', 'input_type' => 'number', 'step' => '0.01', 'min' => '0.0', 'max' => '1.0', 'validation_callback' => array( $this, 'validate_score_threshold_v3' ), ), array( 'name' => 'disable_badge_v3', 'label' => esc_html__( 'Disable Google reCAPTCHA Badge', 'gravityformsrecaptcha' ), 'description' => esc_html__( 'By default reCAPTCHA v3 displays a badge on every page of your site with links to the Google terms of service and privacy policy. You are allowed to hide the badge as long as you include the reCAPTCHA branding and links visibly in the user flow.', 'gravityformsrecaptcha' ), 'type' => 'checkbox', 'choices' => array( array( 'name' => 'disable_badge_v3', 'label' => esc_html__( 'I have added the reCAPTCHA branding, terms of service and privacy policy to my site. ', 'gravityformsrecaptcha' ), ), ), ), array( 'name' => 'recaptcha_keys_status_v3', 'type' => 'checkbox', 'default_value' => $this->get_recaptcha_key( 'recaptcha_keys_status_v3' ), 'hidden' => true, 'choices' => array( array( 'type' => 'checkbox', 'name' => 'recaptcha_keys_status_v3', ), ), ), ), ); } /** * Returns the setting info message to be displayed when the value is defined using a constant. * * @since 1.3 * * @param string $value The value. * @param string $constant The constant name. * * @return string */ private function get_constant_message( $value, $constant ) { if ( empty( $value ) ) { return ''; } return '
' . implode( ' ', $paragraph ) . '
'; } return implode( '', $description_text ); } /** * Get the contents of the description field. * * @since 1.0 * @return array */ private function get_settings_intro_description() { $description = array(); $description[] = array( esc_html__( 'Google reCAPTCHA is a free anti-spam service that protects your website from fraud and abuse.', 'gravityformsrecaptcha' ), esc_html__( 'By adding reCAPTCHA to your forms, you can deter automated software from submitting form entries, while still ensuring a user-friendly experience for real people.', 'gravityformsrecaptcha' ), ); $description[] = array( esc_html__( 'Gravity Forms integrates with three types of Google reCAPTCHA.', 'gravityformsrecaptcha' ), '%s
', esc_html__( 'Please complete the reCAPTCHA widget to validate your reCAPTCHA keys:', 'gravityforms' ) ); // Add reCAPTCHA container, reset input. $html .= ''; $html .= sprintf( '', esc_attr( $this->addon->get_settings_renderer()->get_input_name_prefix() ), esc_attr( $props['name'] ) ); return $html; } /** * Validate that the score is a number between 0.0 and 1.0 * * @since 1.0 * * @param Base $field Settings field object. * @param string $score The submitted score threshold. * * @return bool */ public function validate_score_threshold_v3( $field, $score ) { if ( ! $field instanceof Text ) { $field->set_error( esc_html__( 'Unexpected field type.', 'gravityformsrecaptcha' ) ); return false; } $field_value = (float) $score; if ( ! is_numeric( $score ) || $field_value < $field->min || $field_value > $field->max ) { $field->set_error( esc_html__( 'Score threshold must be between 0.0 and 1.0', 'gravityformsrecaptcha' ) ); return false; } return true; } /** * Returns true, false, or null, depending on the state of validation. * * The add-on framework will use this value to determine which field icon to display. * * @since 1.0 * * @param null|string $key_status The status of the key (a string of 1 or 0). * @param string $value The posted value of the field to validate. * * @return bool|null */ public function check_validated_status( $key_status, $value ) { if ( ! is_null( $key_status ) ) { return (bool) $key_status; } return rgblank( $value ) ? null : false; } /** * Return strue, false, or null, depending on the state of validation. * * The add-on framework will use this value to determine which field icon to display. * * @since 1.0 * * @param string $value The posted value of the field. * * @return bool|null */ public function validate_key_v2( $value ) { return $this->check_validated_status( get_option( 'gform_recaptcha_keys_status', null ), $value ); } /** * Feedback callback for v3 key validation. * * @param string $value The posted value. * * @return bool|null */ public function v3_keys_status_feedback_callback( $value ) { return $this->check_validated_status( $this->addon->get_setting( 'recaptcha_keys_status_v3' ), $value ); } /** * Ajax callback to verify the secret key on the plugin settings screen. * * @since 1.0 */ public function verify_v3_keys() { $result = $this->token_verifier->verify( sanitize_text_field( rgpost( 'token' ) ), sanitize_text_field( rgpost( 'secret_key_v3' ) ) ); $this->apply_status_changes( $result ); if ( is_wp_error( $result ) ) { $this->addon->log_debug( __METHOD__ . '(): Failed to verify reCAPTCHA token. ' . $result->get_error_message() ); wp_send_json_error(); } $this->addon->log_debug( __METHOD__ . '(): reCAPTCHA token successfully verified.' ); $result->keys_status = $this->addon->get_plugin_setting( 'recaptcha_keys_status_v3' ); wp_send_json_success( $result ); } /** * Applies updates to the verified key status when the site and secret v3 keys are saved. * * @since 1.0 * * @param object $response The response of the secret key verification process. */ private function apply_status_changes( $response ) { $posted_keys = $this->get_posted_keys(); // Set the updated status of the keys. $posted_keys['recaptcha_keys_status_v3'] = ( ! is_wp_error( $response ) && $response->success === true ) ? '1' : '0'; $this->addon->update_plugin_settings( array_merge( $this->addon->get_plugin_settings(), $posted_keys ) ); } /** * Get the posted of the v3 keys from the settings page. * * @since 1.0 * * @return array */ private function get_posted_keys() { $settings = $this->addon->get_plugin_settings(); $posted_site_key = $this->get_posted_key( 'site_key_v3' ); $posted_secret_key = $this->get_posted_key( 'secret_key_v3' ); if ( $posted_site_key === rgar( $settings, 'site_key_v3' ) && $posted_secret_key === rgar( $settings, 'secret_key_v3' ) ) { return array(); } return array( 'site_key_v3' => $posted_site_key, 'secret_key_v3' => $posted_secret_key, ); } /** * Gets the value of the specified input from the $_POST. * * @since 1.3 * * @param string $key_name The name of the key to retrieve. * * @return string */ private function get_posted_key( $key_name ) { if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) { $key_name = "_gform_setting_{$key_name}"; } return sanitize_text_field( rgpost( $key_name ) ); } /** * Get the value of one of the reCAPTCHA keys from the plugin settings. * * Checks first for a value defined as a constant, and secondarily, the add-on options. * * @since 1.0 * @since 1.3 Added the $only_from_constant param. * * @param string $key_name The name of the key to retrieve. * @param bool $only_from_constant Indicates if value should only be retrieved from the constant. * * @return string */ public function get_recaptcha_key( $key_name, $only_from_constant = false ) { if ( ! $only_from_constant && is_admin() ) { $posted_key = $this->get_posted_key( $key_name ); if ( $posted_key ) { return $posted_key; } } $keys = array( 'site_key_v3' => defined( 'GF_RECAPTCHA_V3_SITE_KEY' ) ? GF_RECAPTCHA_V3_SITE_KEY : '', 'secret_key_v3' => defined( 'GF_RECAPTCHA_V3_SECRET_KEY' ) ? GF_RECAPTCHA_V3_SECRET_KEY : '', 'site_key_v2' => '', 'secret_key_v2' => '', ); if ( ! in_array( $key_name, array_keys( $keys ), true ) ) { return ''; } $key = rgar( $keys, $key_name, '' ); if ( ! empty( $key ) || $only_from_constant ) { return $key; } $key = $this->addon->get_plugin_setting( $key_name ); return ! empty( $key ) ? $key : ''; } }