File: /home/teamadsc/public_html/wp-content/plugins/wp-defender/src/model/setting/class-captcha.php
<?php
/**
* Handle Captcha settings.
*
* @package WP_Defender\Model\Setting
*/
namespace WP_Defender\Model\Setting;
use Calotes\Model\Setting;
/**
* Model for Captcha settings.
*/
class Captcha extends Setting {
/**
* Turnstile provider key.
*/
const TURNSTILE = 'turnstile';
/**
* Option name.
* We'll keep the current table for all Captcha for backward compatibility. There's no need to edit the table name to store settings in the database.
*
* @var string
*/
protected $table = 'wd_recaptcha_settings';
/**
* Feature status.
*
* @var bool
* @defender_property
*/
public $enabled = false;
/**
* Captcha Provider.
*
* @var string
* @defender_property
*/
public $provider = 'recaptcha';
/**
* Active Captcha type.
*
* @var string
* @defender_property
* @rule required
* @rule in[v2_checkbox,v2_invisible,v3_recaptcha,turnstile]
*/
public $active_type = 'v2_checkbox';
/**
* Data for v2 checkbox reCaptcha.
*
* @var array
* @defender_property
*/
public $data_v2_checkbox;
/**
* Data for v2 invisible reCaptcha.
*
* @var array
* @defender_property
*/
public $data_v2_invisible;
/**
* Data for v3 reCaptcha.
*
* @var array
* @defender_property
*/
public $data_v3_recaptcha;
/**
* Data for Cloudflare Turnstile.
*
* @var array
* @defender_property
*/
public $data_turnstile;
/**
* Language for Captcha.
*
* @var string
* @defender_property
* @rule required
*/
public $language = '';
/**
* Message for reCaptcha.
*
* @var string
* @defender_property
* @sanitize sanitize_textarea_field
*/
public $message = '';
/**
* Locations for Captcha.
*
* @var array
* @defender_property
* @rule required
*/
public $locations = array();
/**
* Flag to detect WooCommerce.
*
* @var bool
* @defender_property
*/
public $detect_woo = false;
/**
* Checked locations for WooCommerce.
*
* @var array
* @defender_property
* @rule required
*/
public $woo_checked_locations = array();
/**
* Flag to detect BuddyPress.
*
* @var bool
* @defender_property
*/
public $detect_buddypress = false;
/**
* Checked locations for BuddyPress.
*
* @var array
* @defender_property
* @rule required
*/
public $buddypress_checked_locations = array();
/**
* Flag to disable Captcha for known users.
*
* @var bool
* @defender_property
*/
public $disable_for_known_users = true;
/**
* Rules for validating the Captcha settings.
*
* @var array
*/
protected $rules = array(
array( array( 'enabled', 'detect_woo', 'detect_buddypress' ), 'boolean' ),
array( array( 'active_type' ), 'in', array( 'v2_checkbox', 'v2_invisible', 'v3_recaptcha', self::TURNSTILE ) ),
);
/**
* Retrieves the default values for the Captcha settings.
*
* @return array An associative array containing the default values.
*/
public function get_default_values(): array {
return array(
'message' => esc_html__( 'reCAPTCHA verification failed. Please try again.', 'wpdef' ),
'turnstile_message' => esc_html__( 'Turnstile verification failed. Please try again.', 'wpdef' ),
);
}
/**
* Load default values.
*
* @return void
*/
protected function before_load(): void {
$default_values = $this->get_default_values();
$this->provider = 'recaptcha';
$this->message = $default_values['message'];
$this->language = 'automatic';
$this->data_v2_checkbox = array(
'key' => '',
'secret' => '',
'size' => 'normal',
'style' => 'light',
);
$this->data_v2_invisible = array(
'key' => '',
'secret' => '',
);
$this->data_v3_recaptcha = array(
'key' => '',
'secret' => '',
'threshold' => '0.5',
);
$this->data_turnstile = array(
'key' => '',
'secret' => '',
'size' => 'normal',
'style' => 'auto',
'message' => $default_values['turnstile_message'],
'language' => 'auto',
);
}
/**
* Checks if the given Captcha type is valid and has all the necessary data.
*
* @param string $active_type The Captcha type to check.
*
* @return bool Returns true if the Captcha type is valid and has all the necessary data, false otherwise.
*/
private function check_captcha_type( string $active_type ): bool {
if (
'v2_checkbox' === $active_type
&& '' !== $this->data_v2_checkbox['key']
&& '' !== $this->data_v2_checkbox['secret']
) {
return true;
} elseif (
'v2_invisible' === $active_type
&& '' !== $this->data_v2_invisible['key']
&& '' !== $this->data_v2_invisible['secret']
) {
return true;
} elseif (
'v3_recaptcha' === $active_type
&& '' !== $this->data_v3_recaptcha['key'] && '' !== $this->data_v3_recaptcha['secret']
) {
return true;
} elseif (
self::TURNSTILE === $active_type &&
'' !== $this->data_turnstile['key'] &&
'' !== $this->data_turnstile['secret']
) {
return true;
} else {
return false;
}
}
/**
* Checks if the CAPTCHA is active.
*
* @return bool Returns true if the CAPTCHA is active, false otherwise.
*/
public function is_active(): bool {
$hook_name = self::TURNSTILE === $this->active_type ? 'wd_turnstile_enable' : 'wd_recaptcha_enable';
$enable = apply_filters(
$hook_name,
$this->enabled
&& '' !== $this->active_type
&& '' !== $this->language
// For each Captcha type.
&& $this->check_captcha_type( $this->active_type )
);
return is_bool( $enable ) ? $enable : (bool) $enable;
}
/**
* Is activated any default location?
*
* @return bool
*/
public function enable_default_location(): bool {
return array() !== $this->locations;
}
/**
* Level#2 check by any activated location. Only if the plugin is enabled.
*
* @return bool
*/
public function enable_woo_location(): bool {
return $this->detect_woo && array() !== $this->woo_checked_locations;
}
/**
* Level#2 check by deactivated locations. Only if the plugin is enabled.
*
* @return bool
*/
public function is_unchecked_woo_locations(): bool {
return $this->detect_woo && array() === $this->woo_checked_locations;
}
/**
* Level#1 check. If the plugin is disabled, there is no point further.
*
* @param bool $is_woo_activated Whether WooCommerce is activated or not.
*
* @return bool
*/
public function check_woo_locations( $is_woo_activated ): bool {
if ( ! $is_woo_activated ) {
return false;
}
return $this->enable_woo_location();
}
/**
* Level#2 check by any activated location. Only if the plugin is enabled.
*
* @return bool
*/
public function enable_buddypress_location(): bool {
return $this->detect_buddypress && array() !== $this->buddypress_checked_locations;
}
/**
* Level#2 check by deactivated locations. Only if the plugin is enabled.
*
* @return bool
*/
public function is_unchecked_buddypress_locations(): bool {
return $this->detect_buddypress && array() === $this->buddypress_checked_locations;
}
/**
* Level#1 Checks if the BuddyPress locations are valid and have all the necessary data.
*
* @param bool $is_buddypress_activated Whether BuddyPress is activated or not.
*
* @return bool Returns true if the BuddyPress locations are valid and have all the necessary data, false otherwise.
*/
public function check_buddypress_locations( $is_buddypress_activated ): bool {
if ( ! $is_buddypress_activated ) {
return false;
}
return $this->enable_buddypress_location();
}
/**
* Define settings labels.
*
* @return array
*/
public function labels(): array {
return array(
'enabled' => self::get_module_name(),
'active_type' => esc_html__( 'Configure CAPTCHA', 'wpdef' ),
'v2_checkbox' => esc_html__( 'V2 Checkbox', 'wpdef' ),
'v2_invisible' => esc_html__( 'V2 Invisible', 'wpdef' ),
'v3_recaptcha' => esc_html__( 'reCAPTCHA V3', 'wpdef' ),
'language' => esc_html__( 'Language', 'wpdef' ),
'message' => esc_html__( 'Error Message', 'wpdef' ),
'locations' => esc_html__( 'CAPTCHA Locations', 'wpdef' ),
'detect_woo' => esc_html__( 'WooCommerce', 'wpdef' ),
'detect_buddypress' => esc_html__( 'BuddyPress', 'wpdef' ),
'disable_for_known_users' => esc_html__( 'Disable for logged in users', 'wpdef' ),
);
}
/**
* Validates the form after submission.
*
* @return void
*/
protected function after_validate(): void {
$provider_label = self::TURNSTILE === $this->provider ? __( 'Turnstile', 'wpdef' ) : __( 'reCAPTCHA', 'wpdef' );
// Case with multi errors.
if ( $this->is_unchecked_woo_locations() && $this->is_unchecked_buddypress_locations() ) {
// The text of the notation is only in the first key, but we add the number of keys depending on the disabled locations of the plugins.
/* translators: Provider label. */
$message = esc_html__(
'You have enabled %s for more than one plugin. Please select at least one form location for each plugin and click Save Changes again.',
'wpdef'
);
$this->errors['enable_woo'] = sprintf( $message, $provider_label );
$this->errors['enable_buddypress'] = '';
} else {
// Individual cases with plugins.
if ( $this->is_unchecked_woo_locations() ) {
/* translators: Provider label. */
$message = esc_html__(
'%s for WooCommerce is enabled, but no WooCommerce forms are selected. Please select at least one WooCommerce form location and then click Save Changes again.',
'wpdef'
);
$this->errors['enable_woo'] = sprintf( $message, $provider_label );
}
if ( $this->is_unchecked_buddypress_locations() ) {
/* translators: Provider label. */
$message = esc_html__(
'%s for BuddyPress is enabled, but no BuddyPress forms are selected. Please select at least one BuddyPress form location and then click Save Changes again.',
'wpdef'
);
$this->errors['enable_buddypress'] = sprintf( $message, $provider_label );
}
}
}
/**
* Disable for logged in users or enable.
*
* @return bool
*/
public function display_for_known_users(): bool {
if ( self::TURNSTILE === $this->provider ) {
return true;
}
return ! ( $this->disable_for_known_users && is_user_logged_in() );
}
/**
* Get module name.
*
* @return string
*/
public static function get_module_name(): string {
return esc_html__( 'CAPTCHA', 'wpdef' );
}
/**
* Get active captcha data based on provider and active type.
*
* @param string|null $provider The provider name.
* @param string|null $active_type The active type.
*
* @return array The captcha data for the specified provider.
*/
public function get_active_captcha_data( ?string $provider = null, ?string $active_type = null ): array {
$provider = $provider ?? $this->provider;
$active_type = $active_type ?? $this->active_type;
if ( self::TURNSTILE === $provider ) {
return $this->data_turnstile;
}
return match ( $active_type ) {
'v2_checkbox' => $this->data_v2_checkbox,
'v2_invisible' => $this->data_v2_invisible,
'v3_recaptcha' => $this->data_v3_recaptcha,
default => array(),
};
}
}