412 lines
No EOL
9.6 KiB
PHP
412 lines
No EOL
9.6 KiB
PHP
<?php
|
|
|
|
if ( ! defined( 'ABSPATH' ) || ! class_exists( 'NF_Abstracts_Action' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Class NF_Actions_Recaptcha
|
|
*/
|
|
final class NF_Actions_Recaptcha extends NF_Abstracts_Action {
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $_name = 'recaptcha';
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $_tags = array( 'spam', 'filtering', 'recaptcha' );
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $_timing = 'normal';
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected $_priority = '10';
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $site_key;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $site_secret;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected $form_id;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $forms_with_action;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $_settings_exclude = array( 'conditions' );
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
parent::__construct();
|
|
|
|
$this->_nicename = esc_html__( 'reCAPTCHA v3', 'ninja-forms' );
|
|
$settings = Ninja_Forms::config( 'ActionRecaptchaV3Settings' );
|
|
$this->_settings = array_merge( $this->_settings, $settings );
|
|
|
|
$this->site_key = Ninja_Forms()->get_setting( 'recaptcha_site_key_3' );
|
|
$this->site_secret = Ninja_Forms()->get_setting( 'recaptcha_secret_key_3' );
|
|
|
|
add_filter( 'ninja_forms_action_type_settings', array( $this, 'maybe_remove_action' ) );
|
|
|
|
add_action( 'nf_get_form_id', array( $this, 'set_form_id' ), 15, 1 );
|
|
|
|
add_filter( 'ninja_forms_display_fields', array( $this, 'maybe_inject_field'), 10, 2 );
|
|
add_filter( 'ninja_forms_form_fields', array( $this, 'maybe_remove_v2_field') );
|
|
add_filter( 'ninja_forms_field_show_in_builder', array( $this, 'maybe_remove_v2_field_from_builder'), 10, 2 );
|
|
add_action( 'ninja_forms_output_templates', array( $this, 'maybe_output_field_template') );
|
|
add_filter( 'nf_display_enqueue_scripts', array( $this, 'enqueue_script' ) );
|
|
}
|
|
|
|
/**
|
|
* Setter method for the form_id and callback for the nf_get_form_id action.
|
|
* @since 3.2.2
|
|
*
|
|
* @param string $form_id The ID of the current form.
|
|
* @return void
|
|
*/
|
|
public function set_form_id( $form_id )
|
|
{
|
|
$this->form_id = $form_id;
|
|
}
|
|
|
|
public function get_form_id() {
|
|
if ( $this->form_id ) {
|
|
return $this->form_id;
|
|
}
|
|
|
|
$this->form_id = filter_input( INPUT_GET, 'form_id', FILTER_VALIDATE_INT );
|
|
|
|
return $this->form_id;
|
|
}
|
|
|
|
/**
|
|
* Remove the action registration if Akismet functions not available.
|
|
*
|
|
* @param array $action_type_settings
|
|
*
|
|
* @return array
|
|
*/
|
|
public function maybe_remove_action( $action_type_settings ) {
|
|
if ( ! $this->is_recaptcha_configured() ) {
|
|
unset( $action_type_settings[ $this->_name ] );
|
|
}
|
|
|
|
return $action_type_settings;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
protected function is_action_enabled_for_form() {
|
|
$form_id = $this->get_form_id();
|
|
|
|
if ( isset( $this->forms_with_action[ $form_id ] ) ) {
|
|
return $this->forms_with_action[ $form_id ];
|
|
}
|
|
|
|
$actions = Ninja_Forms()->form( $form_id )->get_actions();
|
|
|
|
$enabled = false;
|
|
foreach ( $actions as $action ) {
|
|
if ( $this->_name == $action->get_settings('type') && 1 == $action->get_setting( 'active' ) ) {
|
|
$enabled = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$this->forms_with_action[ $form_id ] = $enabled;
|
|
|
|
return $enabled;
|
|
}
|
|
|
|
/**
|
|
* Is the reCAPTCHA configured correctly
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function is_recaptcha_configured() {
|
|
if ( empty( $this->site_key ) || empty( $this->site_secret) ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Is the reCAPTCHA action enabled
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function is_action_configured() {
|
|
if ( ! $this->is_recaptcha_configured() ) {
|
|
return false;
|
|
}
|
|
|
|
if ( ! $this->is_action_enabled_for_form() ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function maybe_output_field_template() {
|
|
if ( ! $this->is_action_configured() ) {
|
|
return;
|
|
}
|
|
|
|
$file_path = Ninja_Forms::$dir . 'includes/Templates/';
|
|
|
|
echo file_get_contents( $file_path . "fields-recaptcha-v3.html" );
|
|
}
|
|
|
|
protected function get_field_id_hash( $form_id ) {
|
|
return substr( base_convert( md5( $form_id ), 16, 10 ), - 5 );
|
|
}
|
|
|
|
/**
|
|
* Remove v2 reCAPTCHA fields if still configured, when using the v3 Action
|
|
*
|
|
* @param array $fields
|
|
*
|
|
* @return array
|
|
*/
|
|
public function maybe_remove_v2_field( $fields ) {
|
|
if ( ! $this->is_action_configured() ) {
|
|
return $fields;
|
|
}
|
|
|
|
foreach ( $fields as $key => $field ) {
|
|
if ( 'recaptcha' === $field->get_setting('type') ) {
|
|
// Remove v2 reCAPTCHA fields if still configured
|
|
unset( $fields[ $key ] );
|
|
}
|
|
}
|
|
|
|
return $fields;
|
|
}
|
|
|
|
/**
|
|
* Don't show the v2 reCAPTCHA field in the builder when using the v3 Action
|
|
*
|
|
* @param bool $show
|
|
* @param NF_Abstracts_Field $field
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function maybe_remove_v2_field_from_builder( $show, $field ) {
|
|
if ( ! $this->is_action_configured() ) {
|
|
return $show;
|
|
}
|
|
|
|
if ( 'recaptcha' !== $field->get_type() ) {
|
|
return $show;
|
|
}
|
|
|
|
$saved_fields = Ninja_Forms()->form( $this->get_form_id() )->get_fields( array( 'saved' => 1 ), true );
|
|
|
|
foreach ( $saved_fields as $key => $field ) {
|
|
if ( 'recaptcha' === $field->get_setting( 'type' ) ) {
|
|
// recaptcha v2 field exists on form, don't hide it as it will break the JS
|
|
return $show;
|
|
}
|
|
}
|
|
|
|
// Hide the recaptcha v2 field
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param array $fields
|
|
* @param int $form_id
|
|
*
|
|
* @return array
|
|
*/
|
|
public function maybe_inject_field( $fields, $form_id ) {
|
|
if ( ! $this->is_action_configured() ) {
|
|
return $fields;
|
|
}
|
|
|
|
$field_id = $this->get_field_id_hash( $form_id );
|
|
|
|
$field = array(
|
|
'objectType' => 'Field',
|
|
'objectDomain' => 'fields',
|
|
'editActive' => false,
|
|
'order' => number_format( count( $fields ) + 1, 1 ),
|
|
'type' => 'recaptcha_v3',
|
|
'label' => 'Hidden',
|
|
'key' => 'recaptcha_v3',
|
|
'default' => '',
|
|
'admin_label' => '',
|
|
'drawerDisabled' => false,
|
|
'id' => $field_id,
|
|
'beforeField' => '',
|
|
'afterField' => '',
|
|
'value' => '',
|
|
'label_pos' => 'above',
|
|
'parentType' => 'hidden',
|
|
'element_templates' => array(
|
|
'recaptcha-v3',
|
|
'hidden',
|
|
'input',
|
|
),
|
|
'old_classname' => '',
|
|
'wrap_template' => 'wrap-no-label',
|
|
'site_key' => $this->site_key,
|
|
);
|
|
|
|
$fields[] = $field;
|
|
|
|
return $fields;
|
|
}
|
|
|
|
public function enqueue_script() {
|
|
if ( ! $this->is_action_configured() ) {
|
|
return;
|
|
}
|
|
|
|
$recaptcha_lang = Ninja_Forms()->get_setting( 'recaptcha_lang', 'en' );
|
|
|
|
if ( $this->maybe_enqueue_recaptcha_js() ) {
|
|
wp_enqueue_script( 'nf-google-recaptcha', 'https://www.google.com/recaptcha/api.js?hl=' . $recaptcha_lang . '&render=' . $this->site_key, array( 'jquery' ), '3.0', true );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check to not load the Google reCAPTCHA JS if other plugins are doing it
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function maybe_enqueue_recaptcha_js() {
|
|
if ( false !== apply_filters( 'ninja_forms_pre_enqueue_recaptcha_v3_js', false ) ) {
|
|
// Allow other plugins to tell Ninja Forms not to load the Google JS script, if they are doing that
|
|
return false;
|
|
}
|
|
|
|
$scripts = wp_scripts();
|
|
|
|
foreach( $scripts->registered as $script ) {
|
|
if ( false !== strpos( $script->src, 'google.com/recaptcha/api.js' ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function get_form_data() {
|
|
if ( empty( $_POST['formData'] ) ) {
|
|
return false;
|
|
}
|
|
|
|
$form_data = json_decode( $_POST['formData'], true );
|
|
|
|
// php5.2 fallback
|
|
if ( ! $form_data ) {
|
|
$form_data = json_decode( stripslashes( $_POST['formData'] ), true );
|
|
}
|
|
|
|
return $form_data ? $form_data : false;
|
|
}
|
|
|
|
protected function get_recaptcha_response() {
|
|
$form_data = $this->get_form_data();
|
|
|
|
if ( ! $form_data || ! isset( $form_data['id'] ) ) {
|
|
return false;
|
|
}
|
|
|
|
$field_id = $this->get_field_id_hash( $form_data['id'] );
|
|
|
|
if ( ! isset( $form_data['fields'] ) || ! isset( $form_data['fields'][ $field_id ] ) ) {
|
|
return false;
|
|
}
|
|
|
|
return $form_data['fields'][ $field_id ]['value'];
|
|
}
|
|
|
|
/**
|
|
* Process the action
|
|
*
|
|
* @param array $action_settings
|
|
* @param int $form_id
|
|
* @param array $data
|
|
*
|
|
* @return array
|
|
*/
|
|
public function process( $action_settings, $form_id, $data ) {
|
|
if ( ! $this->is_recaptcha_configured() ) {
|
|
return $data;
|
|
}
|
|
|
|
$recaptcha_response = $this->get_recaptcha_response();
|
|
|
|
if ( ! $recaptcha_response) {
|
|
$data['errors']['form']['recaptcha'] = esc_html__( 'Recaptcha validation failed. Please try again later', 'ninja-forms' );
|
|
|
|
return $data;
|
|
}
|
|
|
|
if ( $this->is_submission_human( $recaptcha_response, $action_settings['score'] ) ) {
|
|
return $data;
|
|
}
|
|
|
|
$data['errors']['form']['recaptcha'] = esc_html__( 'Recaptcha validation failed. Please try again later', 'ninja-forms' );
|
|
|
|
return $data;
|
|
}
|
|
|
|
protected function is_submission_human( $token, $score_threshold ) {
|
|
$endpoint = 'https://www.google.com/recaptcha/api/siteverify';
|
|
|
|
$request = array(
|
|
'body' => array(
|
|
'secret' => $this->site_secret,
|
|
'response' => esc_html( $token ),
|
|
),
|
|
);
|
|
|
|
$response = wp_remote_post( esc_url_raw( $endpoint ), $request );
|
|
|
|
if ( 200 != wp_remote_retrieve_response_code( $response ) ) {
|
|
if ( WP_DEBUG ) {
|
|
error_log( print_r( $response, true ) );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
$response_body = wp_remote_retrieve_body( $response );
|
|
$response_body = json_decode( $response_body, true );
|
|
|
|
$score = isset( $response_body['score'] ) ? $response_body['score'] : 0;
|
|
|
|
$threshold = apply_filters( 'ninja_forms_action_recaptcha_score_threshold', $score_threshold );
|
|
$is_human = $threshold < $score;
|
|
|
|
$is_human = apply_filters( 'ninja_forms_action_recaptcha__verify_response', $is_human, $response_body );
|
|
|
|
return $is_human;
|
|
}
|
|
} |