PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.12
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.12
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / fields / FrmFieldCaptcha.php

FrmFieldCaptcha.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.12, at classes/models/fields/FrmFieldCaptcha.php

413 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 /**
7 * @since 3.0
8 */
9 class FrmFieldCaptcha extends FrmFieldType {
10
11 /**
12 * @var string
13 * @since 3.0
14 */
15 protected $type = 'captcha';
16
17 /**
18 * @return string
19 */
20 protected function include_form_builder_file() {
21 return FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/field-captcha.php';
22 }
23
24 /**
25 * Returns the image name for a captcha.
26 *
27 * @return string
28 */
29 public static function get_captcha_image_name() {
30 $frm_settings = FrmAppHelper::get_settings();
31 $active_captcha = $frm_settings->active_captcha;
32 if ( $active_captcha === 'recaptcha' && $frm_settings->re_type === 'v3' ) {
33 $image_name = 'recaptcha_v3';
34 } else {
35 $image_name = $active_captcha;
36 }
37
38 return $image_name;
39 }
40
41 /**
42 * @return array
43 */
44 protected function field_settings_for_type() {
45 $settings = FrmCaptchaFactory::get_settings_object();
46 return array(
47 'required' => false,
48 'invalid' => true,
49 'captcha_size' => $settings->should_show_captcha_size(),
50 'captcha_theme' => $settings->should_show_captcha_theme(),
51 'captcha_theme_auto_option' => $settings->should_show_captcha_theme_auto_option(),
52 'default' => false,
53 );
54 }
55
56 /**
57 * @return array
58 */
59 protected function new_field_settings() {
60 $frm_settings = FrmAppHelper::get_settings();
61
62 return array(
63 'invalid' => $frm_settings->re_msg,
64 );
65 }
66
67 /**
68 * @return array
69 */
70 protected function extra_field_opts() {
71 return array(
72 'label' => 'none',
73 'captcha_size' => 'normal',
74 'captcha_theme' => 'light',
75 );
76 }
77
78 /**
79 * Remove the "for" attribute for captcha
80 *
81 * @param array $args
82 * @param string $html
83 *
84 * @return string
85 */
86 protected function before_replace_html_shortcodes( $args, $html ) {
87 $frm_settings = FrmAppHelper::get_settings();
88 $replace_response = $frm_settings->active_captcha === 'recaptcha' ? 'g-recaptcha-response' : 'h-captcha-response';
89 $replaced_for = str_replace( ' for="field_[key]"', ' for="' . $replace_response . '"', $html );
90
91 return $replaced_for;
92 }
93
94 /**
95 * @param array $args
96 * @param array $shortcode_atts
97 * @return string
98 */
99 public function front_field_input( $args, $shortcode_atts ) {
100 $frm_settings = FrmAppHelper::get_settings();
101 if ( ! self::should_show_captcha() ) {
102 return '';
103 }
104
105 $settings = FrmCaptchaFactory::get_settings_object();
106 $div_attributes = array(
107 'id' => $args['html_id'],
108 'class' => $this->class_prefix( $frm_settings ) . $this->captcha_class( $frm_settings ),
109 'data-sitekey' => $settings->get_pubkey(),
110 );
111 $div_attributes = $settings->add_front_end_element_attributes( $div_attributes, $this->field );
112 $html = '<div ' . FrmAppHelper::array_to_html_params( $div_attributes ) . '></div>';
113
114 return $html;
115 }
116
117 /**
118 * @return void
119 */
120 protected function load_field_scripts( $args ) {
121 $api_js_url = $this->api_url();
122
123 wp_register_script( 'captcha-api', $api_js_url, array( 'formidable' ), '3', true );
124 wp_enqueue_script( 'captcha-api' );
125 }
126
127 /**
128 * Get the URL for the script JS that is loaded on the front end.
129 *
130 * @return string
131 */
132 protected function api_url() {
133 $frm_settings = FrmAppHelper::get_settings();
134 $active_mode = $frm_settings->active_captcha;
135
136 if ( 'recaptcha' === $active_mode ) {
137 return $this->recaptcha_api_url( $frm_settings );
138 }
139
140 if ( 'hcaptcha' === $active_mode ) {
141 return $this->hcaptcha_api_url();
142 }
143
144 return $this->turnstile_api_url();
145 }
146
147 /**
148 * @param FrmSettings $frm_settings
149 * @return string
150 */
151 protected function recaptcha_api_url( $frm_settings ) {
152 $api_js_url = 'https://www.google.com/recaptcha/api.js?';
153
154 $allow_multiple = $frm_settings->re_multi;
155 if ( $allow_multiple ) {
156 $api_js_url .= '&onload=frmRecaptcha&render=explicit';
157 }
158
159 $lang = apply_filters( 'frm_recaptcha_lang', $frm_settings->re_lang, $this->field );
160 if ( $lang ) {
161 $api_js_url .= '&hl=' . $lang;
162 }
163
164 /**
165 * @param string $api_js_url
166 */
167 $api_js_url = apply_filters( 'frm_recaptcha_js_url', $api_js_url );
168
169 return $api_js_url;
170 }
171
172 /**
173 * @since 6.0
174 *
175 * @return string
176 */
177 protected function hcaptcha_api_url() {
178 $api_js_url = 'https://js.hcaptcha.com/1/api.js';
179
180 /**
181 * Allows updating hcaptcha js api url.
182 *
183 * @since 6.0
184 *
185 * @param string $api_js_url
186 */
187 $api_js_url = apply_filters( 'frm_hcaptcha_js_url', $api_js_url );
188
189 return $api_js_url;
190 }
191
192 /**
193 * @since 6.8.4
194 *
195 * @return string
196 */
197 protected function turnstile_api_url() {
198 $api_js_url = 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=frmTurnstile&render=explicit';
199
200 /**
201 * Allows updating hcaptcha js api url.
202 *
203 * @since 6.8.4
204 *
205 * @param string $api_js_url
206 */
207 $api_js_url = apply_filters( 'frm_turnstile_js_url', $api_js_url );
208
209 // Prevent render=explicit from happening twice in case someone patched
210 // the double rendering issue using the frm_turnstile_js_url hook.
211 $api_js_url = str_replace(
212 '&render=explicit&render=explicit',
213 '&render=explicit',
214 $api_js_url
215 );
216
217 return $api_js_url;
218 }
219
220 /**
221 * @param FrmSettings $frm_settings
222 *
223 * @return string
224 *
225 * @psalm-return ''|'frm-'
226 */
227 protected function class_prefix( $frm_settings ) {
228 if ( $this->allow_multiple( $frm_settings ) && $frm_settings->active_captcha === 'recaptcha' ) {
229 $class_prefix = 'frm-';
230 } else {
231 $class_prefix = '';
232 }
233
234 return $class_prefix;
235 }
236
237 /**
238 * @param FrmSettings $frm_settings
239 *
240 * @return string
241 *
242 * @psalm-return 'g-recaptcha'|'h-captcha'
243 */
244 protected function captcha_class( $frm_settings ) {
245 $settings = FrmCaptchaFactory::get_settings_object();
246 return $settings->get_element_class_name();
247 }
248
249 protected function allow_multiple( $frm_settings ) {
250 return $frm_settings->re_multi;
251 }
252
253 /**
254 * @since 4.07
255 * @param array $args
256 * @return array
257 */
258 protected function validate_against_api( $args ) {
259 $errors = array();
260 $frm_settings = FrmAppHelper::get_settings();
261 $resp = $this->send_api_check( $frm_settings );
262 $response = json_decode( wp_remote_retrieve_body( $resp ), true );
263
264 if ( is_wp_error( $resp ) ) {
265 $error_string = $resp->get_error_message();
266 $errors[ 'field' . $args['id'] ] = __( 'There was a problem verifying your captcha', 'formidable' );
267 $errors[ 'field' . $args['id'] ] .= ' ' . $error_string;
268 return $errors;
269 }
270
271 if ( ! is_array( $response ) ) {
272 return $errors;
273 }
274
275 if ( $frm_settings->active_captcha === 'recaptcha' ) {
276 if ( 'v3' === $frm_settings->re_type && array_key_exists( 'score', $response ) ) {
277 $threshold = floatval( $frm_settings->re_threshold );
278 $score = floatval( $response['score'] );
279
280 $this->set_score( $score );
281
282 if ( $score < $threshold ) {
283 $response['success'] = false;
284 }
285 }
286 }
287
288 if ( isset( $response['success'] ) && ! $response['success'] ) {
289 // What happens when the CAPTCHA was entered incorrectly
290 $invalid_message = FrmField::get_option( $this->field, 'invalid' );
291 if ( $invalid_message === __( 'The reCAPTCHA was not entered correctly', 'formidable' ) ) {
292 $invalid_message = '';
293 }
294 $errors[ 'field' . $args['id'] ] = ( $invalid_message === '' ? $frm_settings->re_msg : $invalid_message );
295 }
296
297 return $errors;
298 }
299
300 /**
301 * @param float $score
302 * @return void
303 */
304 private function set_score( $score ) {
305 global $frm_vars;
306 if ( ! isset( $frm_vars['captcha_scores'] ) ) {
307 $frm_vars['captcha_scores'] = array();
308 }
309 $form_id = is_object( $this->field ) ? $this->field->form_id : $this->field['form_id'];
310 if ( ! isset( $frm_vars['captcha_scores'][ $form_id ] ) ) {
311 $frm_vars['captcha_scores'][ $form_id ] = $score;
312 }
313 }
314
315 /**
316 * @param array $args
317 * @return array
318 */
319 public function validate( $args ) {
320 if ( ! $this->should_validate() ) {
321 return array();
322 }
323
324 $missing_token = ! self::post_data_includes_token();
325 if ( $missing_token ) {
326 return array( 'field' . $args['id'] => __( 'The captcha is missing from this form', 'formidable' ) );
327 }
328
329 return $this->validate_against_api( $args );
330 }
331
332 /**
333 * @since 6.8.4
334 *
335 * @return bool
336 */
337 protected static function post_data_includes_token() {
338 $settings = FrmCaptchaFactory::get_settings_object();
339 // phpcs:ignore WordPress.Security.NonceVerification.Missing
340 return ! empty( $_POST[ $settings->token_field ] );
341 }
342
343 /**
344 * Check if the active captcha type's public key is set.
345 *
346 * @since 4.07
347 *
348 * @return bool
349 */
350 public static function should_show_captcha() {
351 $settings = FrmCaptchaFactory::get_settings_object();
352 return $settings->has_pubkey();
353 }
354
355 /**
356 * @return bool
357 */
358 protected function should_validate() {
359 $is_hidden_field = apply_filters( 'frm_is_field_hidden', false, $this->field, wp_unslash( $_POST ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
360 if ( FrmAppHelper::is_admin() || $is_hidden_field ) {
361 return false;
362 }
363
364 // don't require the captcha if it shouldn't be shown
365 return self::should_show_captcha();
366 }
367
368 /**
369 * @param FrmSettings $frm_settings
370 */
371 protected function send_api_check( $frm_settings ) {
372 $captcha_settings = FrmCaptchaFactory::get_settings_object();
373 $arg_array = array(
374 'body' => array(
375 'secret' => $captcha_settings->secret,
376 'response' => FrmAppHelper::get_param( $captcha_settings->token_field, '', 'post', 'sanitize_text_field' ),
377 'remoteip' => FrmAppHelper::get_ip_address(),
378 ),
379 );
380
381 return wp_remote_post( $captcha_settings->endpoint, $arg_array );
382 }
383
384 /**
385 * Updates field name in page builder to the currently activated captcha if it is set to the default.
386 *
387 * @since 6.0
388 *
389 * @param array $values
390 *
391 * @return array $values
392 */
393 public static function update_field_name( $values ) {
394 if ( $values['type'] === 'captcha' ) {
395 $name = $values['name'];
396 if ( in_array( $name, array( __( 'reCAPTCHA', 'formidable' ), __( 'hCaptcha', 'formidable' ) ), true ) ) {
397 $values['name'] = __( 'Captcha', 'formidable' );
398 }
399 }
400
401 return $values;
402 }
403
404 /**
405 * @param FrmSettings $frm_settings
406 * @return string
407 */
408 protected function captcha_size( $frm_settings ) {
409 _deprecated_function( __METHOD__, '6.8.4' );
410 return 'normal';
411 }
412 }
413