get_encoded_recaptcha_response( $form, $recaptcha_field->get_posted_recaptcha_response() );
if ( $recaptcha_field->verify_decoded_response( $form, $recaptcha_response ) ) {
$result['recaptcha_response'] = $recaptcha_response;
}
return $result;
}
public function get_form_editor_field_title() {
return esc_attr__( 'CAPTCHA', 'gravityforms' );
}
/**
* Returns the field's form editor description.
*
* @since 2.5
*
* @return string
*/
public function get_form_editor_field_description() {
return esc_attr__( 'Adds a captcha field to your form to help protect your website from spam and bot abuse.', 'gravityforms' );
}
/**
* Returns the field's form editor icon.
*
* This could be an icon url or a gform-icon class.
*
* @since 2.5
*
* @return string
*/
public function get_form_editor_field_icon() {
return 'gform-icon--recaptcha';
}
function get_form_editor_field_settings() {
return array(
'captcha_type_setting',
'captcha_badge_setting',
'captcha_size_setting',
'captcha_fg_setting',
'captcha_bg_setting',
'captcha_language_setting',
'captcha_theme_setting',
'conditional_logic_field_setting',
'error_message_setting',
'label_setting',
'label_placement_setting',
'description_setting',
'css_class_setting',
);
}
/**
* Returns the warning message to be displayed in the form editor sidebar.
*
* @since 2.8
*
* @return string|array
*/
public function get_field_sidebar_messages() {
// If the field is a math or simple captcha, we don't need to display a warning.
if ( $this->captchaType === 'math' || $this->captchaType === 'simple_captcha' ) {
return '';
}
// If the reCAPTCHA keys are configured and Conversational Forms is active, we need to display a warning.
if ( ( ! empty( $this->get_site_key() ) && ! empty( $this->get_secret_key() ) ) ) {
if ( is_plugin_active( 'gravityformsconversationalforms/conversationalforms.php' ) ) {
return array(
'type' => 'notice',
'content' => sprintf(
'
%s
',
__( 'The reCAPTCHA v2 field is not supported in Conversational Forms and will be removed, but will continue to work as expected in other contexts.', 'gravityforms' )
),
'icon_helper_text' => __( 'This field is not supported in Conversational Forms', 'gravityforms' ),
);
} else {
return '';
}
}
// If the reCAPTCHA keys are not configured, we need to display a warning.
return array(
'type' => 'notice',
'content' => sprintf(
'%s
%s
',
__( 'Configuration Required', 'gravityforms' ),
// Translators: 1. Opening tag with link to the Forms > Settings > reCAPTCHA page. 2. closing tag.
sprintf(
esc_html__( 'To use the reCAPTCHA field, please configure your %1$sreCAPTCHA settings.%2$s', 'gravityforms' ),
'',
'' . esc_html__('(opens in a new tab)', 'gravityforms') . ''
)
),
'icon_helper_text' => __( 'This field requires additional configuration', 'gravityforms' ),
);
}
/**
* Recaptcha v2 does not work in conversational forms, so we have to remove it.
*
* @since 2.8.13
*
* @param $form
*
* @return void
*/
public static function maybe_remove_recaptcha_v2( $form ) {
if ( ! function_exists( 'is_conversational_form' ) ) {
return $form;
}
if ( ! is_conversational_form( $form ) ) {
return $form;
}
foreach ( $form['fields'] as $key => $field ) {
if ( $field->type === 'captcha' && ( $field->captchaType === '' || $field->captchaType === 'captcha' ) ) {
unset( $form['fields'][ $key ] );
}
}
return $form;
}
/**
* Validate the reCAPTCHA field.
*
* This method always gets called on the last page of a form, as well as on the page where the field is assigned.
*
* @since Unknown
*
* @param array|string $value The field value.
* @param array $form The form data.
*/
public function validate( $value, $form ) {
switch ( $this->captchaType ) {
case 'simple_captcha' :
if ( class_exists( 'ReallySimpleCaptcha' ) ) {
$prefix = rgpost( "input_captcha_prefix_{$this->id}" );
$captcha_obj = $this->get_simple_captcha();
if ( ! $captcha_obj->check( $prefix, str_replace( ' ', '', $value ) ) ) {
$this->set_failed_validation( esc_html__( "The CAPTCHA wasn't entered correctly. Go back and try it again.", 'gravityforms' ) );
}
//removes old files in captcha folder (older than 1 hour);
$captcha_obj->cleanup();
}
break;
case 'math' :
$prefixes = explode( ',', rgpost( "input_captcha_prefix_{$this->id}" ) );
$captcha_obj = $this->get_simple_captcha();
//finding first number
for ( $first = 0; $first < 10; $first ++ ) {
if ( $captcha_obj->check( $prefixes[0], $first ) ) {
break;
}
}
//finding second number
for ( $second = 0; $second < 10; $second ++ ) {
if ( $captcha_obj->check( $prefixes[2], $second ) ) {
break;
}
}
//if it is a +, perform the sum
if ( $captcha_obj->check( $prefixes[1], '+' ) ) {
$result = $first + $second;
} else {
$result = $first - $second;
}
if ( intval( $result ) != intval( $value ) ) {
$this->set_failed_validation( esc_html__( "The CAPTCHA wasn't entered correctly. Go back and try it again.", 'gravityforms' ) );
}
//removes old files in captcha folder (older than 1 hour);
$captcha_obj->cleanup();
break;
default:
$this->validate_recaptcha( $form );
}
}
/**
* Validates the reCAPTCHA response.
*
* In our application flow, we create a decoded string out of the reCAPTCHA service response if the reCAPTCHA field
* is added to the form on a page other than the last page. We therefore first attempt to validate the decoded response,
* falling back to validating the reCAPTCHA with a request to Google.
*
* @see GF_Field_CAPTCHA::verify_decoded_response()
*
* @since unknown
*
* @param array $form The form data.
*
* @return bool
*/
public function validate_recaptcha( $form ) {
if ( rgpost( 'gform_conversational_form' ) ) {
$hash = md5( $form['title'] . $form['id'] );
if ( $hash === rgpost( 'gform_conversational_form' ) && is_plugin_active( 'gravityformsconversationalforms/conversationalforms.php' ) ) {
// This is a conversational form, and recaptcha v2 isn't supported
return true;
}
}
$response = $this->get_posted_recaptcha_response();
if ( ! ( $this->verify_decoded_response( $form, $response ) || $this->verify_recaptcha_response( $response ) ) ) {
$this->set_failed_validation( __( 'The reCAPTCHA was invalid. Go back and try it again.', 'gravityforms' ) );
return false;
}
return true;
}
/**
* Verifies that the decoded response meets the requirements for submitting the form.
*
* Returns false if the decoded response doesn't exist or the reCAPTCHA field is on the last page, as we'll want
* regular validation at that point instead.
*
* @since 2.4.24
*
* @param array $form The form data.
* @param string $response The encoded response to verify.
*
* @return bool
*/
public function verify_decoded_response( $form, $response ) {
$decoded_response = $this->get_decoded_recaptcha_response( $response );
// No decoded object.
if ( ! is_object( $decoded_response ) ) {
return false;
}
return (
$decoded_response->success === true
&& ! empty( $decoded_response->token )
&& gmdate( time() ) <= strtotime( '+1 day', strtotime( $decoded_response->challenge_ts ) )
);
}
/**
* Set validation failed on reCAPTCHA field.
*
* @since 2.4.24
*
* @param string $message The message to set if one does not already exist.
*/
private function set_failed_validation( $message ) {
$this->failed_validation = true;
$this->validation_message = empty( $this->errorMessage ) ? $message : $this->errorMessage;
}
/**
* Get the saved site key.
*
* @since 2.4.24
*
* @return string
*/
public function get_site_key() {
if ( ! $this->site_key ) {
$this->site_key = get_option( 'rg_gforms_captcha_public_key', '' );
}
return $this->site_key;
}
/**
* Get the saved secret key.
*
* @since 2.4.25
*
* @return string
*/
public function get_secret_key() {
if ( ! $this->secret_key ) {
$this->secret_key = get_option( 'rg_gforms_captcha_private_key', '' );
}
return $this->secret_key;
}
/**
* Get the value of the reCAPTCHA response input.
*
* When user clicks on the "I'm not a robot" box, the response token is populated into a hidden field by Google.
* If the current form is a multi-page form and the reCAPTCHA field is on a page other than the last page, this
* value will return an openssl encoded string with the Google reCAPTCHA validation data and some supplemental
* validation data instead.
*
* @see GF_Field_CAPTCHA::get_encoded_recaptcha_response()
*
* @since 2.4.24
*
* @return string
*/
public function get_posted_recaptcha_response() {
return sanitize_text_field( rgpost( 'g-recaptcha-response' ) );
}
/**
* Validate the reCAPTCHA token provided by Google.
*
* @since unknown
*
* @param string $response The token to verify.
* @param null $secret_key The secret key for reCAPTCHA verification.
*
* @return bool
*/
public function verify_recaptcha_response( $response, $secret_key = null ) {
$verify_url = 'https://www.google.com/recaptcha/api/siteverify';
if ( $secret_key == null ) {
$secret_key = $this->get_secret_key();
}
// pass secret key and token for verification of whether the response was valid
$response = wp_remote_post( $verify_url, array(
'method' => 'POST',
'body' => array(
'secret' => $secret_key,
'response' => $response
),
) );
if ( ! is_wp_error( $response ) ) {
$this->response = json_decode( wp_remote_retrieve_body( $response ) );
return $this->response->success == true;
} else {
GFCommon::log_debug( __METHOD__ . '(): Validating the reCAPTCHA response has failed due to the following: ' . $response->get_error_message() );
}
return false;
}
public function get_field_input( $form, $value = '', $entry = null ) {
$form_id = $form['id'];
$is_entry_detail = $this->is_entry_detail();
$is_form_editor = $this->is_form_editor();
$id = (int) $this->id;
$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
switch ( $this->captchaType ) {
case 'simple_captcha' :
$size = empty($this->simpleCaptchaSize) ? 'medium' : esc_attr( $this->simpleCaptchaSize );
$captcha = $this->get_captcha();
$tabindex = $this->get_tabindex();
$dimensions = $is_entry_detail || $is_form_editor ? '' : "width='" . esc_attr( rgar( $captcha, 'width' ) ) . "' height='" . esc_attr( rgar( $captcha, 'height' ) ) . "'";
return "