false, 'acknowledged' => false, 'sitekey' => '', 'secret' => '' ); } public static function config( $form ) { if ( empty( $form['id'] ) ) { return self::defaults(); } $raw = get_post_meta( (int) $form['id'], self::META, true ); if ( '' !== $raw && ! is_array( $raw ) ) { return new WP_Error( 'kconv_spam_config', __( 'Review the saved spam protection settings.', 'killerconversions' ) ); } return self::sanitize( is_array( $raw ) ? $raw : array() ); } public static function sanitize( $raw ) { if ( ! is_array( $raw ) ) { return new WP_Error( 'kconv_spam_config', __( 'Review the spam protection settings.', 'killerconversions' ) ); } $config = self::defaults(); foreach ( array( 'enabled', 'acknowledged' ) as $key ) { if ( isset( $raw[ $key ] ) && ! is_scalar( $raw[ $key ] ) ) { return new WP_Error( 'kconv_spam_config', __( 'Review the spam protection settings.', 'killerconversions' ) ); } $config[ $key ] = ! empty( $raw[ $key ] ); } foreach ( array( 'sitekey', 'secret' ) as $key ) { if ( isset( $raw[ $key ] ) && ! is_string( $raw[ $key ] ) ) { return new WP_Error( 'kconv_spam_config', __( 'Turnstile keys must be text.', 'killerconversions' ) ); } $config[ $key ] = isset( $raw[ $key ] ) ? trim( $raw[ $key ] ) : ''; if ( '' !== $config[ $key ] && ! preg_match( '/^[A-Za-z0-9_-]{10,256}$/D', $config[ $key ] ) ) { return new WP_Error( 'kconv_spam_config', __( 'Use valid Turnstile keys without spaces.', 'killerconversions' ) ); } } if ( $config['enabled'] && ( ! $config['sitekey'] || ! $config['secret'] || ! $config['acknowledged'] ) ) { return new WP_Error( 'kconv_spam_config', __( 'Add both Turnstile keys and acknowledge the external service before enabling protection.', 'killerconversions' ) ); } return $config; } public static function from_post( $form, $post ) { if ( ! isset( $post['kconv_spam'] ) || ! is_array( $post['kconv_spam'] ) ) { return new WP_Error( 'kconv_spam_config', __( 'Spam protection settings are incomplete. Reload the editor.', 'killerconversions' ) ); } $raw = $post['kconv_spam']; $stored = get_post_meta( (int) $form['id'], self::META, true ); if ( ! empty( $raw['clear_secret'] ) ) { $raw['secret'] = ''; } elseif ( isset( $raw['secret'] ) && '' === $raw['secret'] && isset( $stored['secret'] ) ) { $raw['secret'] = $stored['secret']; } return self::sanitize( $raw ); } private static function guarded( $post ) { if ( ! current_user_can( 'manage_options' ) ) { return false; } if ( ! isset( $post['kconv_spam_nonce'] ) || ! is_string( $post['kconv_spam_nonce'] ) ) { return false; } if ( ! wp_verify_nonce( sanitize_text_field( $post['kconv_spam_nonce'] ), 'kconv_spam_save' ) ) { return false; } return isset( $post['kconv_spam_complete'] ) && '1' === $post['kconv_spam_complete']; } public static function validate_save( $errors, $form, $post ) { if ( ! isset( $post['kconv_spam_present'] ) ) { return $errors; } if ( ! self::guarded( $post ) ) { $errors->add( 'kconv_spam_incomplete', __( 'Spam protection settings are incomplete or expired. Reload the editor before saving. Existing settings have not changed.', 'killerconversions' ) ); return $errors; } $config = self::from_post( $form, $post ); if ( is_wp_error( $config ) ) { $errors->add( $config->get_error_code(), $config->get_error_message() ); } return $errors; } public static function save( $id, $form, $post ) { if ( ! isset( $post['kconv_spam_present'] ) || ! self::guarded( $post ) ) { return; } $config = self::from_post( $form, $post ); if ( ! is_wp_error( $config ) ) { update_post_meta( $id, self::META, wp_slash( $config ) ); } } public static function admin_fields( $form ) { if ( ! current_user_can( 'manage_options' ) ) { return; } $config = self::config( $form ); $invalid = is_wp_error( $config ); if ( $invalid ) { $config = self::defaults(); } wp_enqueue_style( 'kconv-spam', KCONV_URL . 'assets/spam.css', array(), KCONV_VERSION ); wp_enqueue_script( 'kconv-spam-admin', KCONV_URL . 'assets/spam-admin.js', array( 'kconv-workspace' , 'wp-i18n' ), KCONV_VERSION, true ); wp_set_script_translations( 'kconv-spam-admin', 'killerconversions', KCONV_DIR . 'languages' ); echo '

' . esc_html__( 'Spam protection', 'killerconversions' ) . '

' . esc_html__( 'Built-in validation, a honeypot and rate limits remain active. Add Cloudflare Turnstile when your form needs another layer.', 'killerconversions' ) . '

'; if ( $invalid ) { echo '

' . esc_html__( 'The saved Turnstile configuration needs attention. Protected submissions are blocked until you correct it or save protection as disabled.', 'killerconversions' ) . '

'; } wp_nonce_field( 'kconv_spam_save', 'kconv_spam_nonce' ); echo ''; echo '

'; echo '
'; echo '

'; echo '

' . esc_html__( 'Create a Managed Turnstile widget for this website hostname:', 'killerconversions' ) . ' ' . esc_html( wp_parse_url( home_url(), PHP_URL_HOST ) ) . '. ' . esc_html__( 'Preview never loads Turnstile. Test the published form after saving.', 'killerconversions' ) . '

'; // phpcs:ignore PluginCheck.CodeAnalysis.Offloading.OffloadedContent -- Optional Cloudflare Turnstile is an explicitly disclosed remote CAPTCHA service, disabled by default and owner-enabled per form. Its required widget/siteverify and privacy links are not remotely hosted plugin UI assets. echo '
' . esc_html__( 'External service and privacy', 'killerconversions' ) . '

' . esc_html__( 'When enabled, published forms load a security widget from challenges.cloudflare.com. Cloudflare processes the visitor’s IP address, browser/device signals, site key and page origin to distinguish people from bots. On submission, this server sends the challenge token and secret key to Cloudflare’s Siteverify service over HTTPS. KillerConversions does not send form answers or files to Turnstile.', 'killerconversions' ) . '

' . esc_html__( 'Disable protection and save to stop future Turnstile loading and verification. Remove the saved secret to delete it from this form’s settings. Update your website privacy notice for this optional service.', 'killerconversions' ) . '

' . esc_html__( 'Cloudflare Turnstile privacy', 'killerconversions' ) . ' · ' . esc_html__( 'Cloudflare terms', 'killerconversions' ) . '

'; echo '
'; } public static function action( $form ) { return 'kconv_form_' . (int) $form['id']; } public static function render_form( $html, $form, $instance ) { if ( ! empty( $form['_kconv_preview'] ) ) { return $html; } $config = self::config( $form ); if ( ! is_wp_error( $config ) && ! $config['enabled'] ) { return $html; } wp_enqueue_style( 'kconv-spam', KCONV_URL . 'assets/spam.css', array(), KCONV_VERSION ); $message = __( 'Security verification is unavailable. Please try again later or contact the website owner.', 'killerconversions' ); $box = '

' . esc_html__( 'Protected by Cloudflare Turnstile.', 'killerconversions' ) . ' ' . esc_html__( 'Privacy', 'killerconversions' ) . '

'; return str_replace( '