File: /home/dwauav0tm6jp/www/newvsi/wp-content/plugins/wpforms-lite/src/Forms/AntiSpam.php
<?php
namespace WPForms\Forms;
/**
* Class Anti Spam V3.
*
* This class is used for modern Anti-Spam approach.
*
* @since 1.9.0
*/
class AntiSpam {
/**
* Field ID to insert the honeypot field before.
*
* @since 1.9.0
*
* @var int
*/
private $insert_before_field_id = 1;
/**
* Honeypot field id.
*
* @since 1.9.0
*
* @var int
*/
private $honeypot_field_id = 0;
/**
* Current form ID.
*
* @since 1.9.0
*
* @var int
*/
private $current_form_id = 0;
/**
* Initialise the actions for the Anti-spam.
*
* @since 1.9.0
*/
public function init() {
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.9.0
*/
private function hooks() {
// Frontend hooks.
add_filter( 'wpforms_frontend_strings', [ $this, 'add_frontend_strings' ] );
add_filter( 'wpforms_frontend_fields_base_level', [ $this, 'get_random_field' ], 20 );
add_action( 'wpforms_display_field_before', [ $this, 'maybe_insert_honeypot_field' ], 1, 2 );
add_action( 'wpforms_display_field_after', [ $this, 'maybe_insert_honeypot_init_js' ], 1, 2 );
// The Form Builder hooks.
add_filter( 'wpforms_builder_panel_settings_init_form_data', [ $this, 'init_builder_settings_form_data' ] );
add_filter( 'wpforms_admin_builder_templates_apply_to_new_form_modify_data', [ $this, 'update_template_form_data' ] );
add_filter( 'wpforms_admin_builder_templates_apply_to_existing_form_modify_data', [ $this, 'update_template_form_data' ] );
add_filter( 'wpforms_templates_class_base_template_modify_data', [ $this, 'update_template_form_data' ] );
add_filter( 'wpforms_templates_class_base_template_replace_modify_data', [ $this, 'update_template_form_data' ] );
add_filter( 'wpforms_form_handler_convert_form_data', [ $this, 'update_template_form_data' ] );
}
/**
* Store a random field id to insert a honeypot field later.
*
* @since 1.9.0
*
* @param array|mixed $fields_data Form fields data.
*
* @return array|mixed Form fields data.
*/
public function get_random_field( $fields_data ) {
if ( ! is_array( $fields_data ) ) {
return $fields_data;
}
$random_field_id = array_rand( $fields_data );
if ( ! empty( $random_field_id ) ) {
$this->insert_before_field_id = $random_field_id;
}
return $fields_data;
}
/**
* Insert honeypot field before a random field.
*
* @since 1.9.0
*
* @param array $field Field.
* @param array $form_data Form data.
*/
public function maybe_insert_honeypot_field( array $field, array $form_data ) {
if (
$this->insert_before_field_id !== (int) $field['id'] ||
! $this->is_honeypot_enabled( $form_data )
) {
return;
}
$this->honeypot_field_id = $this->get_honeypot_field_id( $form_data );
$this->current_form_id = (int) $form_data['id'];
$label = $this->get_honeypot_label( $form_data );
$id_attr = $this->get_honeypot_id_attr();
$is_amp = wpforms_is_amp();
if ( $is_amp ) {
echo '<amp-layout layout="nodisplay">';
}
?>
<div id="<?php echo esc_attr( $id_attr ); ?>-container"
class="wpforms-field wpforms-field-text"
data-field-type="text"
data-field-id="<?php echo esc_attr( $this->honeypot_field_id ); ?>"
>
<label class="wpforms-field-label" for="<?php echo esc_attr( $id_attr ); ?>" ><?php echo esc_html( $label ); ?></label>
<input type="text" id="<?php echo esc_attr( $id_attr ); ?>" class="wpforms-field-medium" name="wpforms[fields][<?php echo esc_attr( $this->honeypot_field_id ); ?>]" >
</div>
<?php
if ( $is_amp ) {
echo '</amp-layout>';
}
}
/**
* Insert honeypot field before a random field.
*
* @since 1.9.0
*
* @param array $field Field.
* @param array $form_data Form data.
*/
public function maybe_insert_honeypot_init_js( array $field, array $form_data ) {
if (
$this->insert_before_field_id !== (int) $field['id'] ||
! $this->is_honeypot_enabled( $form_data ) ||
wpforms_is_amp()
) {
return;
}
$this->output_honeypot_init_js();
}
/**
* Output honeypot init javascript.
*
* @since 1.9.0
*/
private function output_honeypot_init_js() {
$id_attr = $this->get_honeypot_id_attr();
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$encoded_id = base64_encode( $id_attr . '-container' );
printf(
"<script>
( function() {
const form = document.currentScript?.closest( '.wpforms-form' );
const field = form?.querySelector( '#' + atob( '%s' ) );
if ( ! field ) {
document.currentScript.remove();
return;
}
field.setAttribute( 'style', 'position: absolute !important; overflow: hidden !important; display: inline !important; height: 1px !important; width: 1px !important; z-index: -1000 !important; padding: 0 !important;' );
field.getElementsByTagName( 'label' )?.[0]?.setAttribute( 'aria-hidden', 'true' );
const input = field.getElementsByTagName( 'input' )?.[0];
if ( input ) {
input.setAttribute( 'style', 'visibility: hidden;' );
input.setAttribute( 'tabindex', '-1' );
input.setAttribute( 'aria-hidden', 'true' );
}
document.currentScript?.remove();
} )();
</script>",
esc_html( $encoded_id )
);
}
/**
* Get honeypot field ID attribute.
*
* @since 1.9.0
*
* @return string
*/
private function get_honeypot_id_attr(): string {
return sprintf(
'wpforms-%1$s-field_%2$s',
$this->current_form_id,
$this->honeypot_field_id
);
}
/**
* Get honeypot field label.
*
* @since 1.9.0
*
* @param array $form_data Form data.
*/
private function get_honeypot_label( array $form_data ): string {
$labels = [];
foreach ( $form_data['fields'] ?? [] as $field ) {
if ( ! empty( $field['label'] ) ) {
$labels[] = $field['label'];
}
}
$words = explode( ' ', implode( ' ', $labels ) );
$count_words = count( $words );
$label_keys = (array) array_rand( $words, min( $count_words, 3 ) );
shuffle( $label_keys );
$label_words = array_map(
static function ( $key ) use ( $words ) {
return $words[ $key ];
},
$label_keys
);
return implode( ' ', $label_words );
}
/**
* Add strings to the frontend.
*
* @since 1.9.0
*
* @param array|mixed $strings Frontend strings.
*
* @return array Frontend strings.
*/
public function add_frontend_strings( $strings ): array {
$strings = (array) $strings;
// Store the honeypot field ID for validation and adding inline styles.
$strings['hn_data'][ $this->current_form_id ] = $this->honeypot_field_id;
return $strings;
}
/**
* Validate Anti-spam if enabled.
*
* @since 1.9.0
*
* @param array $form_data Form data.
* @param array $fields Fields.
* @param array $entry Form entry.
*
* @return bool True if the entry is valid, false otherwise.
* @noinspection PhpUnusedParameterInspection
*/
public function validate( array $form_data, array $fields, array &$entry ): bool { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
// Bail out if we don't have the antispam setting.
if ( ! $this->is_honeypot_enabled( $form_data ) ) {
return true;
}
$honeypot_fields = array_diff_key( $entry['fields'], $form_data['fields'] );
$is_valid = true;
foreach ( $honeypot_fields as $key => $honeypot_field ) {
// Remove the honeypot field from the entry.
unset( $entry['fields'][ $key ] );
// If the honeypot field is not empty, the entry is invalid.
if ( ! empty( $honeypot_field ) ) {
$is_valid = false;
}
}
return $is_valid;
}
/**
* Check if the honeypot is enabled.
*
* @since 1.9.0
*
* @param array $form_data Form data.
*
* @return bool True if the honeypot is enabled, false otherwise.
*/
private function is_honeypot_enabled( array $form_data ): bool {
static $is_enabled;
if ( isset( $is_enabled ) ) {
return $is_enabled;
}
/**
* Filters whether the Anti Spam v3 honeypot is enabled.
*
* @since 1.9.0
*
* @param bool $is_enabled True if the honeypot is enabled, false otherwise.
*/
$is_enabled = (bool) apply_filters( 'wpforms_forms_anti_spam_v3_is_honeypot_enabled', ! empty( $form_data['settings']['antispam_v3'] ) );
return $is_enabled;
}
/**
* Get the honeypot field ID.
*
* @since 1.9.0
*
* @param array $form_data Form data.
*
* @return int Honeypot field ID.
*/
private function get_honeypot_field_id( array $form_data ): int {
$max_key = max( array_keys( $form_data['fields'] ) );
// Find the first available field ID.
for ( $i = 1; $i <= $max_key; $i++ ) {
if ( ! isset( $form_data['fields'][ $i ] ) ) {
return $i;
}
}
// If no available field ID found, use the max ID + 1.
return $max_key + 1;
}
/**
* Update the form data on the builder settings panel.
*
* @since 1.9.0
*
* @param array|bool $form_data Form data.
*
* @return array|bool
*/
public function init_builder_settings_form_data( $form_data ) {
if ( ! $form_data ) {
return $form_data;
}
// Update default time limit duration for the existing form.
if ( empty( $form_data['settings']['anti_spam']['time_limit']['enable'] ) ) {
$form_data['settings']['anti_spam']['time_limit']['duration'] = '2';
}
return $form_data;
}
/**
* Update the template form data. Set Anti spam modern settings.
*
* @since 1.9.0
*
* @param array|mixed $form_data Form data.
*
* @return array
*/
public function update_template_form_data( $form_data ): array {
$form_data = (array) $form_data;
// Unset the old antispam setting.
unset( $form_data['settings']['antispam'] );
// Enable the antispam modern setting.
$form_data['settings']['antispam_v3'] = $form_data['settings']['antispam_v3'] ?? '1';
$form_data['settings']['anti_spam'] = $form_data['settings']['anti_spam'] ?? [];
// Enable the time limit setting.
$form_data['settings']['anti_spam']['time_limit'] = [
'enable' => '1',
'duration' => '2',
];
return $form_data;
}
}