Remove all plugins / install base theme
This commit is contained in:
@@ -1,158 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms_RECAPTCHA;
|
||||
|
||||
use GF_Field;
|
||||
use GFCommon;
|
||||
use GFAPI;
|
||||
|
||||
/**
|
||||
* Class GF_Field_RECAPTCHA
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA
|
||||
*/
|
||||
class GF_Field_RECAPTCHA extends GF_Field {
|
||||
/**
|
||||
* Recaptcha field type.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'recaptcha';
|
||||
|
||||
/**
|
||||
* Prevent the field being saved to the entry.
|
||||
*
|
||||
* @since 1.1
|
||||
* @var bool
|
||||
*/
|
||||
public $displayOnly = true;
|
||||
|
||||
/**
|
||||
* Decoded field data.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var object
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* Return empty array to prevent the field from showing up in the form editor.
|
||||
*
|
||||
* @since 1.0
|
||||
* @return array
|
||||
*/
|
||||
public function get_form_editor_button() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The field markup.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param array $form The form array.
|
||||
* @param string $value The field value.
|
||||
* @param array|null $entry The entry array.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_field_input( $form, $value = '', $entry = null ) {
|
||||
$plugin_settings = gf_recaptcha()->get_plugin_settings_instance();
|
||||
$site_key = $plugin_settings->get_recaptcha_key( 'site_key_v3' );
|
||||
$secret_key = $plugin_settings->get_recaptcha_key( 'secret_key_v3' );
|
||||
|
||||
if ( empty( $site_key ) || empty( $secret_key ) ) {
|
||||
GFCommon::log_error( __METHOD__ . sprintf( '(): reCAPTCHA secret keys not saved in the reCAPTCHA Settings (%s). The reCAPTCHA field will always fail validation during form submission.', admin_url( 'admin.php' ) . '?page=gf_settings&subview=recaptcha' ) );
|
||||
}
|
||||
|
||||
$this->formId = absint( rgar( $form, 'id' ) );
|
||||
$name = $this->get_input_name();
|
||||
$tabindex = GFCommon::$tab_index > 0 ? GFCommon::$tab_index ++ : 0;
|
||||
|
||||
return "<div class='gf_invisible ginput_recaptchav3' data-sitekey='" . esc_attr( $site_key ) . "' data-tabindex='{$tabindex}'>"
|
||||
. '<input id="' . esc_attr( $name ) . '" class="gfield_recaptcha_response" type="hidden" name="' . esc_attr( $name ) . '" value=""/>'
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the validation result if the Recaptcha response has been altered.
|
||||
*
|
||||
* This is a callback to the gform_validation filter to allow us to validate the values in the hidden field.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @see GF_RECAPTCHA::init()
|
||||
*
|
||||
* @param array $validation_data The validation data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validation_check( $validation_data ) {
|
||||
$this->formId = absint( rgars( $validation_data, 'form/id' ) );
|
||||
|
||||
if ( $this->is_valid_field_data() ) {
|
||||
|
||||
// Set is_spam value.
|
||||
$validation_data['is_spam'] = gf_recaptcha()->is_spam_submission( rgar( $validation_data, 'form' ) );
|
||||
|
||||
return $validation_data;
|
||||
}
|
||||
|
||||
// Set is_valid to false and return the validation data.
|
||||
return $this->invalidate( $validation_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the data in the hidden input is a valid Recaptcha entry.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_field_data() {
|
||||
$data = rgpost( $this->get_input_name() );
|
||||
|
||||
if ( empty( $data ) ) {
|
||||
gf_recaptcha()->log_debug( __METHOD__ . "(): Input {$this->get_input_name()} empty." );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return gf_recaptcha()->get_token_verifier()->verify_submission( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set is_valid to false on the validation data.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param array $validation_data The validation data.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function invalidate( $validation_data ) {
|
||||
$validation_data['is_valid'] = false;
|
||||
|
||||
return $validation_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the input name attribute.
|
||||
*
|
||||
* @since 1.1
|
||||
* @since 1.2 Added optional form_id parameter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_input_name( $form_id = null ) {
|
||||
if ( $form_id ) {
|
||||
$this->formId = absint( $form_id );
|
||||
}
|
||||
|
||||
return 'input_' . md5( 'recaptchav3' . gf_recaptcha()->get_version() . $this->formId );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* API wrapper for the Recaptcha service.
|
||||
*
|
||||
* @since 1.0
|
||||
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms_RECAPTCHA;
|
||||
|
||||
/**
|
||||
* Class RECAPTCHA_API
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA
|
||||
*/
|
||||
class RECAPTCHA_API {
|
||||
/**
|
||||
* Google Recaptcha token verification URL.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var string
|
||||
*/
|
||||
private $verification_url = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
|
||||
/**
|
||||
* Get the result of token verification from the Recaptcha API.
|
||||
*
|
||||
* @param string $token The token to verify.
|
||||
* @param string $secret The site's secret key.
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function verify_token( $token, $secret ) {
|
||||
return wp_remote_post(
|
||||
$this->verification_url,
|
||||
array(
|
||||
'body' => array(
|
||||
'secret' => $secret,
|
||||
'response' => $token,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Class responsible for verifying tokens returned by Recaptcha.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms_RECAPTCHA;
|
||||
|
||||
use GFCommon;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Token_Verifier
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA
|
||||
*/
|
||||
class Token_Verifier {
|
||||
/**
|
||||
* Error code returned if a token or secret is missing.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
const ERROR_CODE_MISSING_TOKEN_OR_SECRET = 'gravityformsrecaptcha-missing-token-or-secret';
|
||||
|
||||
/**
|
||||
* Error code returned if the token cannot be verified.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
const ERROR_CODE_CANNOT_VERIFY_TOKEN = 'gravityforms-cannot-verify-token';
|
||||
|
||||
/**
|
||||
* Instance of the add-on class.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var GF_RECAPTCHA
|
||||
*/
|
||||
private $addon;
|
||||
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var RECAPTCHA_API
|
||||
*/
|
||||
private $api;
|
||||
|
||||
/**
|
||||
* Minimum score the Recaptcha API can return before a form submission is marked as spam.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var float
|
||||
*/
|
||||
private $score_threshold;
|
||||
|
||||
/**
|
||||
* Token generated by the Recaptcha service that requires validation.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var string
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* Recaptcha application secret used to verify the token.
|
||||
*
|
||||
* @since 1.0
|
||||
* @var string
|
||||
*/
|
||||
private $secret;
|
||||
|
||||
/**
|
||||
* Result of the recaptcha request.
|
||||
*
|
||||
* @var stdClass
|
||||
*/
|
||||
private $recaptcha_result;
|
||||
|
||||
/**
|
||||
* The reCAPTCHA action.
|
||||
*
|
||||
* @since 1.4 Previously a dynamic property.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $action;
|
||||
|
||||
/**
|
||||
* Token_Verifier constructor.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param GF_RECAPTCHA $addon Instance of the GF_RECAPTCHA add-on.
|
||||
* @param RECAPTCHA_API $api Instance of the Recaptcha API.
|
||||
*/
|
||||
public function __construct( GF_RECAPTCHA $addon, RECAPTCHA_API $api ) {
|
||||
$this->addon = $addon;
|
||||
$this->api = $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this object for use.
|
||||
*
|
||||
* @param string $token The reCAPTCHA token.
|
||||
* @param string $action The reCAPTCHA action.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public function init( $token = '', $action = '' ) {
|
||||
$this->token = $token;
|
||||
$this->action = $action;
|
||||
$this->secret = $this->addon->get_plugin_settings_instance()->get_recaptcha_key( 'secret_key_v3' );
|
||||
$this->score_threshold = $this->addon->get_plugin_setting( 'score_threshold_v3', 0.5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reCAPTCHA result.
|
||||
*
|
||||
* Returns a stdClass if it's already been processed.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return stdClass|null
|
||||
*/
|
||||
public function get_recaptcha_result() {
|
||||
return $this->recaptcha_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the reCAPTCHA response data has the required properties and meets expectations.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param array $response_data The response data to validate.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validate_response_data( $response_data ) {
|
||||
if (
|
||||
! empty( $response_data->{'error-codes'} )
|
||||
|| ( property_exists( $response_data, 'success' ) && $response_data->success !== true )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$validation_properties = array( 'hostname', 'action', 'success', 'score', 'challenge_ts' );
|
||||
$response_properties = array_filter(
|
||||
$validation_properties,
|
||||
function( $property ) use ( $response_data ) {
|
||||
return property_exists( $response_data, $property );
|
||||
}
|
||||
);
|
||||
|
||||
if ( count( $validation_properties ) !== count( $response_properties ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
$response_data->success
|
||||
&& $this->verify_hostname( $response_data->hostname )
|
||||
&& $this->verify_action( $response_data->action )
|
||||
&& $this->verify_score( $response_data->score )
|
||||
&& $this->verify_timestamp( $response_data->challenge_ts )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the submission data.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $token The Recapatcha token.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verify_submission( $token ) {
|
||||
|
||||
$data = \GFCache::get( 'recaptcha_' . $token, $found );
|
||||
if ( $found ) {
|
||||
$this->addon->log_debug( __METHOD__ . '(): Using cached reCAPTCHA result: ' . print_r( $data, true ) );
|
||||
$this->recaptcha_result = $data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->addon->log_debug( __METHOD__ . '(): Verifying reCAPTCHA submission.' );
|
||||
|
||||
if ( empty( $token ) ) {
|
||||
$this->addon->log_debug( __METHOD__ . '(): Could not verify the submission because no token was found.' . PHP_EOL );
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->init( $token, 'submit' );
|
||||
|
||||
$data = $this->get_response_data( $this->api->verify_token( $token, $this->addon->get_plugin_settings_instance()->get_recaptcha_key( 'secret_key_v3' ) ) );
|
||||
|
||||
if ( is_wp_error( $data ) ) {
|
||||
$this->addon->log_debug( __METHOD__ . '(): Validating the reCAPTCHA response has failed due to the following: ' . $data->get_error_message() );
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'error' => $data->get_error_message(),
|
||||
'code' => self::ERROR_CODE_CANNOT_VERIFY_TOKEN,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $this->validate_response_data( $data ) ) {
|
||||
$this->addon->log_debug(
|
||||
__METHOD__ . '(): Could not validate the token request from the reCAPTCHA service. ' . PHP_EOL
|
||||
. "token: {$token}" . PHP_EOL
|
||||
. "response: " . print_r( $data, true ) . PHP_EOL // @codingStandardsIgnoreLine
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreLine
|
||||
$this->addon->log_debug( __METHOD__ . '(): Validated reCAPTCHA: ' . print_r( $data, true ) );
|
||||
$this->recaptcha_result = $data;
|
||||
|
||||
// Caching result for 1 hour.
|
||||
\GFCache::set( 'recaptcha_' . $token, $data, true, 60 * 60 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data from the response.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param WP_Error|string $response The response from the API request.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function get_response_data( $response ) {
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return json_decode( wp_remote_retrieve_body( $response ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the reCAPTCHA hostname.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $hostname Verify that the host name returned matches the site.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_hostname( $hostname ) {
|
||||
if ( ! has_filter( 'gform_recaptcha_valid_hostnames' ) ) {
|
||||
$this->addon->log_debug( __METHOD__ . '(): gform_recaptcha_valid_hostnames filter not implemented. Skipping.' );
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->addon->log_debug( __METHOD__ . '(): gform_recaptcha_valid_hostnames filter detected. Verifying hostname.' );
|
||||
|
||||
/**
|
||||
* Filter for the set of hostnames considered valid by this site.
|
||||
*
|
||||
* Google returns a 'hostname' value in reCAPTCHA verification results. We validate against this value to ensure
|
||||
* that the data is good. By default, we use only the WordPress installation's home URL, but have extended
|
||||
* this via a filter so developers can define an array of hostnames to allow.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param array $valid_hostnames {
|
||||
* An indexed array of valid hostname strings. Example:
|
||||
* array( 'example.com', 'another-example.com' )
|
||||
* }
|
||||
*/
|
||||
$valid_hostnames = apply_filters(
|
||||
'gform_recaptcha_valid_hostnames',
|
||||
array(
|
||||
wp_parse_url( get_home_url(), PHP_URL_HOST ),
|
||||
)
|
||||
);
|
||||
|
||||
return is_array( $valid_hostnames ) ? in_array( $hostname, $valid_hostnames, true ) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the reCAPTCHA action.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $action The reCAPTCHA result action.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_action( $action ) {
|
||||
$this->addon->log_debug( __METHOD__ . '(): Verifying action from reCAPTCHA response.' );
|
||||
|
||||
return $this->action === $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the score is valid.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param float $score The reCAPTCHA v3 score.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_score( $score ) {
|
||||
$this->addon->log_debug( __METHOD__ . '(): Verifying score from reCAPTCHA response.' );
|
||||
|
||||
return is_float( $score ) && $score >= 0.0 && $score <= 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the timestamp of the submission is valid.
|
||||
*
|
||||
* Google allows a reCAPTCHA token to be valid for two minutes. On multi-page forms, we generate a new token with
|
||||
* the advancement of each page, but the timestamp that's returned is always the same. Thus, we'll allow a longer
|
||||
* time frame for form submissions before considering them to be invalid.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $challenge_ts The challenge timestamp from the reCAPTCHA service.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_timestamp( $challenge_ts ) {
|
||||
$this->addon->log_debug( __METHOD__ . '(): Verifying timestamp from reCAPTCHA response.' );
|
||||
|
||||
return ( gmdate( time() ) - strtotime( $challenge_ts ) ) <= 24 * HOUR_IN_SECONDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the score from the Recaptcha result.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function get_score() {
|
||||
if ( empty( $this->recaptcha_result ) || ! property_exists( $this->recaptcha_result, 'score' ) ) {
|
||||
return $this->addon->is_preview() ? 0.9 : 0.0;
|
||||
}
|
||||
|
||||
return (float) $this->recaptcha_result->score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the decoded response data from the API.
|
||||
*
|
||||
* @param string $token The validation token.
|
||||
* @param string $secret The stored secret key from the settings page.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return WP_Error|mixed|string
|
||||
*/
|
||||
public function verify( $token, $secret ) {
|
||||
return $this->get_response_data( $this->api->verify_token( $token, $secret ) );
|
||||
}
|
||||
}
|
||||
@@ -1,630 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Object responsible for organizing and constructing the plugin settings page.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA\Settings
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms_RECAPTCHA\Settings;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms_RECAPTCHA\GF_RECAPTCHA;
|
||||
use Gravity_Forms\Gravity_Forms\Settings\Fields\Text;
|
||||
use Gravity_Forms\Gravity_Forms_RECAPTCHA\Token_Verifier;
|
||||
use GF_Field_CAPTCHA;
|
||||
use GFCommon;
|
||||
|
||||
/**
|
||||
* Class Plugin_Settings
|
||||
*
|
||||
* @since 1.0
|
||||
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA\Settings
|
||||
*/
|
||||
class Plugin_Settings {
|
||||
/**
|
||||
* Add-on instance.
|
||||
*
|
||||
* @var GF_RECAPTCHA
|
||||
*/
|
||||
private $addon;
|
||||
|
||||
/**
|
||||
* Token_Verifier instance.
|
||||
*
|
||||
* @var Token_Verifier
|
||||
*/
|
||||
private $token_verifier;
|
||||
|
||||
/**
|
||||
* Plugin_Settings constructor.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param GF_RECAPTCHA $addon GF_RECAPTCHA instance.
|
||||
* @param Token_Verifier $token_verifier Instance of the Token_Verifier class.
|
||||
*/
|
||||
public function __construct( $addon, $token_verifier ) {
|
||||
$this->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 '<div class="alert gforms_note_info">' . sprintf( esc_html__( 'Value defined using the %s constant.', 'gravityformsrecaptcha' ), $constant ) . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin settings fields for reCAPTCHA v2.
|
||||
*
|
||||
* @since 1.0
|
||||
* @return array
|
||||
*/
|
||||
private function get_v2_fields() {
|
||||
return array(
|
||||
'id' => 'gravityformsrecaptcha_v2',
|
||||
'title' => esc_html__( 'reCAPTCHA v2', 'gravityformsrecaptcha' ),
|
||||
'fields' => array(
|
||||
array(
|
||||
'name' => 'site_key_v2',
|
||||
'label' => esc_html__( 'Site Key', 'gravityformsrecaptcha' ),
|
||||
'tooltip' => gform_tooltip( 'settings_recaptcha_public', null, true ),
|
||||
'type' => 'text',
|
||||
'feedback_callback' => array( $this, 'validate_key_v2' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'secret_key_v2',
|
||||
'label' => esc_html__( 'Secret Key', 'gravityformsrecaptcha' ),
|
||||
'tooltip' => gform_tooltip( 'settings_recaptcha_private', null, true ),
|
||||
'type' => 'text',
|
||||
'feedback_callback' => array( $this, 'validate_key_v2' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'type_v2',
|
||||
'label' => esc_html__( 'Type', 'gravityformsrecaptcha' ),
|
||||
'tooltip' => gform_tooltip( 'settings_recaptcha_type', null, true ),
|
||||
'type' => 'radio',
|
||||
'horizontal' => true,
|
||||
'default_value' => 'checkbox',
|
||||
'choices' => array(
|
||||
array(
|
||||
'label' => esc_html__( 'Checkbox', 'gravityformsrecaptcha' ),
|
||||
'value' => 'checkbox',
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Invisible', 'gravityformsrecaptcha' ),
|
||||
'value' => 'invisible',
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'name' => 'reset_v2',
|
||||
'label' => esc_html__( 'Validate Keys', 'gravityformsrecaptcha' ),
|
||||
'type' => 'recaptcha_reset',
|
||||
'callback' => array( $this, 'handle_recaptcha_v2_reset' ),
|
||||
'hidden' => true,
|
||||
'validation_callback' => function( $field, $value ) {
|
||||
|
||||
// If reCAPTCHA key is empty, exit.
|
||||
if ( rgblank( $value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$values = $this->addon->get_settings_renderer()->get_posted_values();
|
||||
|
||||
// Get public, private keys, API response.
|
||||
$public_key = rgar( $values, 'site_key_v2' );
|
||||
$private_key = rgar( $values, 'secret_key_v2' );
|
||||
$response = rgpost( 'g-recaptcha-response' );
|
||||
|
||||
// If keys and response are provided, verify and save.
|
||||
if ( $public_key && $private_key && $response ) {
|
||||
// Log public, private keys, API response.
|
||||
// @codingStandardsIgnoreStart - print_r okay for logging.
|
||||
GFCommon::log_debug( __METHOD__ . '(): reCAPTCHA Site Key:' . print_r( $public_key, true ) );
|
||||
GFCommon::log_debug( __METHOD__ . '(): reCAPTCHA Secret Key:' . print_r( $private_key, true ) );
|
||||
GFCommon::log_debug( __METHOD__ . '(): reCAPTCHA Response:' . print_r( $response, true ) );
|
||||
|
||||
// Verify response.
|
||||
$recaptcha = new GF_Field_CAPTCHA();
|
||||
$recaptcha_response = $recaptcha->verify_recaptcha_response( $response, $private_key );
|
||||
|
||||
// Log verification response.
|
||||
GFCommon::log_debug( __METHOD__ . '(): reCAPTCHA verification response:' . print_r( $recaptcha_response, true ) );
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
// If response is false, return validation error.
|
||||
if ( $recaptcha_response === false ) {
|
||||
$field->set_error( __( 'reCAPTCHA keys are invalid.', 'gravityformsrecaptcha' ) );
|
||||
}
|
||||
|
||||
// Save status.
|
||||
update_option( 'gform_recaptcha_keys_status', $recaptcha_response );
|
||||
} else {
|
||||
// Delete existing status.
|
||||
delete_option( 'gform_recaptcha_keys_status' );
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an array containing arrays of translated strings into HTML paragraphs.
|
||||
*
|
||||
* @param array $paragraphs An array of arrays containing translated text.
|
||||
*
|
||||
* @since 1.0
|
||||
* @return string
|
||||
*/
|
||||
private function get_description( array $paragraphs ) {
|
||||
$description_text = array();
|
||||
|
||||
foreach ( $paragraphs as $paragraph ) {
|
||||
$description_text[] = '<p>' . implode( ' ', $paragraph ) . '</p>';
|
||||
}
|
||||
|
||||
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' ),
|
||||
'<ul><li>',
|
||||
esc_html__( 'reCAPTCHA v3 - Adds a script to every page of your site and uploads form content for processing by Google.', 'gravityformsrecaptcha' ),
|
||||
esc_html__( 'All submissions are accepted and suspicious submissions are marked as spam.', 'gravityformsrecaptcha' ),
|
||||
esc_html__( 'When reCAPTCHA v3 is configured, it is enabled automatically on all forms by default. It can be disabled for specific forms in the form settings.', 'gravityformsrecaptcha' ),
|
||||
'</li><li>',
|
||||
esc_html__( 'reCAPTCHA v2 (Invisible) - Displays a badge on your form and will present a challenge to the user if the activity is suspicious e.g. select the traffic lights.', 'gravityformsrecaptcha' ),
|
||||
esc_html__( 'Please note, only v2 keys are supported and checkbox keys are not compatible with invisible reCAPTCHA.', 'gravityformsrecaptcha' ),
|
||||
esc_html__( 'To activate reCAPTCHA v2 on your form, simply add the CAPTCHA field in the form editor.', 'gravityformsrecaptcha' ),
|
||||
sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url( 'https://docs.gravityforms.com/captcha/' ),
|
||||
__( 'Read more about reCAPTCHA.', 'gravityformsrecaptcha' )
|
||||
),
|
||||
'</li><li>',
|
||||
esc_html__( 'reCAPTCHA v2 (Checkbox) - Requires a user to click a checkbox to indicate that they are not a robot and displays a challenge if the activity is suspicious', 'gravityformsrecaptcha' ),
|
||||
'</li></ul>',
|
||||
);
|
||||
|
||||
$description[] = array(
|
||||
esc_html__( 'For more information on reCAPTCHA, which version is right for you, and how to add it to your forms,', 'gravityformsrecaptcha' ),
|
||||
sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url( 'https://docs.gravityforms.com/captcha/' ),
|
||||
esc_html__( 'check out our documentation.', 'gravityformsrecaptcha' )
|
||||
),
|
||||
);
|
||||
|
||||
return $this->get_description( $description );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the score threshold.
|
||||
*
|
||||
* @since 1.0
|
||||
* @return string
|
||||
*/
|
||||
private function get_score_threshold_description() {
|
||||
$description = array(
|
||||
array(
|
||||
esc_html__( 'reCAPTCHA v3 returns a score (1.0 is very likely a good interaction, 0.0 is very likely a bot).', 'gravityformsrecaptcha' ),
|
||||
esc_html__( 'If the score is less than or equal to this threshold, the form submission will be sent to spam.', 'gravityformsrecaptcha' ),
|
||||
esc_html__( 'The default threshold is 0.5.', 'gravityformsrecaptcha' ),
|
||||
sprintf(
|
||||
'<a href="%s">Learn about about reCAPTCHA.</a>',
|
||||
esc_url( 'https://docs.gravityforms.com/captcha/' )
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->get_description( $description );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a reCAPTCHA verification field.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param array $props Field properties.
|
||||
* @param bool $echo Output the field markup directly.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function handle_recaptcha_v2_reset( $props = array(), $echo = true ) {
|
||||
// Add setup message.
|
||||
$html = sprintf(
|
||||
'<p id="gforms_checkbox_recaptcha_message" style="margin-bottom:10px;">%s</p>',
|
||||
esc_html__( 'Please complete the reCAPTCHA widget to validate your reCAPTCHA keys:', 'gravityforms' )
|
||||
);
|
||||
|
||||
// Add reCAPTCHA container, reset input.
|
||||
$html .= '<div id="recaptcha"></div>';
|
||||
$html .= sprintf( '<input type="hidden" name="%s_%s" />', 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 : '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user