PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.1
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.1
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / front-end / default-fields / recaptcha / recaptcha.php

recaptcha.php in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 4.0.1, at front-end/default-fields/recaptcha/recaptcha.php

1,164 lines 57.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
4 /**
5 * Encodes the given data into a query string format
6 * @param $data - array of string elements to be encoded
7 * @return string - encoded request
8 */
9 function _wppb_encodeQS($data)
10 {
11 $req = "";
12 foreach ($data as $key => $value) {
13 $req .= $key . '=' . urlencode(stripslashes($value)) . '&';
14 }
15 // Cut the last '&'
16 $req=substr($req, 0, strlen($req)-1);
17 return $req;
18 }
19
20
21
22 /**
23 * Submits an HTTP GET to a reCAPTCHA server
24 * @param string $path
25 * @param array $data
26 */
27 function _wppb_submitHTTPGet($path, $data)
28 {
29 $req = _wppb_encodeQS($data);
30 $response = wp_remote_get($path . $req);
31
32 if ( is_wp_error( $response ) ) {
33 return '';
34 }
35
36 return isset( $response['body'] ) ? $response['body'] : '';
37 }
38
39 /**
40 * Gets the challenge HTML (javascript and non-javascript version).
41 * This is called from the browser, and the resulting reCAPTCHA HTML widget
42 * is embedded within the HTML form it was called from.
43 * @param string $pubkey A public key for reCAPTCHA
44 * @param string $error The error given by reCAPTCHA (optional, default is null)
45 * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
46
47 * @return string - The HTML to be embedded in the user's form.
48 */
49 function wppb_recaptcha_get_html ( $pubkey, $form_name='' ){
50 global $wppb_recaptcha_forms; // is the counter for the number of forms that have recaptcha so we always have unique ids on the element
51 if( is_null( $wppb_recaptcha_forms ) )
52 $wppb_recaptcha_forms = 0;
53 $wppb_recaptcha_forms++;
54
55 $field = wppb_get_recaptcha_field();
56
57 if ( empty($pubkey) )
58 echo '<span class="error">'. esc_html__("To use reCAPTCHA you must get an API key from", "profile-builder"). " <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a></span><br/><br/>";
59
60 // extra class needed for Invisible reCAPTCHA html
61 $invisible_class = '';
62 $v3_field_html = '';
63 if ( isset($field['recaptcha-type']) && ($field['recaptcha-type'] == 'invisible') ) {
64 $invisible_class = 'wppb-invisible-recaptcha';
65 } elseif ( isset($field['recaptcha-type']) && ($field['recaptcha-type'] == 'v3') ) {
66 $invisible_class = 'wppb-v3-recaptcha';
67 $v3_field_html = '<input type="hidden" name="g-recaptcha-response" class="g-recaptcha-response wppb-v3-recaptcha">';
68 }
69
70 $output = '<div id="wppb-recaptcha-element-'.$form_name.$wppb_recaptcha_forms.'" class="wppb-recaptcha-element '.$invisible_class.'">'.$v3_field_html.'</div>';
71
72 if ( isset($field['recaptcha-type']) && ($field['recaptcha-type'] == 'v3') ) {
73 $output .= '<input type="hidden" name="wppb-recaptcha-v3" value="1">';
74
75 if( $form_name == 'pb_login' ) {
76 add_filter( 'wppb_login_submit_button_extra_attributes', 'wppb_recaptcha_login_submit_button_extra_attributes' );
77 }
78
79 }
80
81 // reCAPTCHA html for all forms and we make sure we have a unique id for v2
82 return $output;
83 }
84
85 /**
86 * Add disabled attribute to login form submit button when reCaptcha v3 is used
87 * This is used to prevent form submission before the reCaptcha script is loaded and a token is received
88 *
89 * @param string $attributes
90 * @return string
91 */
92 function wppb_recaptcha_login_submit_button_extra_attributes( $attributes ) {
93 return $attributes . ' disabled="disabled"';
94 }
95
96 /**
97 * Add reCAPTCHA scripts to both front-end PB forms (with support for multiple forms) as well as Default WP forms
98 */
99 function wppb_recaptcha_script_footer(){
100 $field = wppb_get_recaptcha_field();
101 /* if we do not have a recaptcha field do nothing */
102 if( empty( $field ) )
103 return;
104
105 global $wppb_recaptcha_present;
106 global $wppb_shortcode_on_front;
107
108 //do not add script on regular frontend pages unless a PB shortcode or reCAPTCHA HTML is present
109 if( current_filter() == 'wp_footer' && ( !isset( $wppb_shortcode_on_front ) || $wppb_shortcode_on_front === false ) && ( !isset( $wppb_recaptcha_present ) || $wppb_recaptcha_present === false ) )
110 return;
111
112 //do not add script if the html for the field has not been added
113 if( !isset( $wppb_recaptcha_present ) || $wppb_recaptcha_present === false )
114 return;
115
116 //we don't have jquery on the backend
117 if( current_filter() != 'wp_footer' ) {
118 wp_print_scripts('jquery');
119 }else if(!wp_script_is('jquery')){
120 wp_print_scripts('jquery');
121 }
122
123 //get site key
124 $pubkey = '';
125 if( isset( $field['public-key'] ) ) {
126 $pubkey = sanitize_text_field( $field['public-key'] );
127 }
128
129 // Check if we have a reCAPTCHA type
130 if ( !isset($field['recaptcha-type']) )
131 $field['recaptcha-type'] = 'v2' ;
132
133 /*for invisible recaptcha we have extra parameters and the selector is different. v2 is initialized on the id of the div
134 that must be unique and invisible is on the submit button of the forms that have the div */
135 if ( $field['recaptcha-type'] === 'invisible' ) {
136 $callback_conditions = 'jQuery("input[type=\'submit\']", jQuery( ".wppb-recaptcha-element" ).closest("form") )';
137 $invisible_parameters = '"callback" : wppbInvisibleRecaptchaOnSubmit,"size": "invisible"';
138 } elseif ( $field['recaptcha-type'] === 'v3' ) {
139 $callback_conditions = 'jQuery( jQuery( ".wppb-recaptcha-element" ).closest("form") )';
140 $invisible_parameters = '';
141 } else {
142 $callback_conditions = 'jQuery(".wppb-recaptcha-element")';
143 $invisible_parameters = '';
144 }
145
146 /* For Invisible reCAPTCHA the token is only produced once the async grecaptcha script has loaded and bound the
147 submit button. Until then the submit button behaves like a plain button, so an early click would submit the form
148 with an empty g-recaptcha-response. Since validation now fails closed on a missing token, disable the submit
149 button(s) until the widget is ready and re-enable them afterwards (same approach used for reCAPTCHA v3 login). */
150 $invisible_submit_selector = 'jQuery( "input[type=\'submit\'], button[type=\'submit\']", jQuery( ".wppb-recaptcha-element" ).closest( "form" ) )';
151 $invisible_disable_submit_js = '';
152 $invisible_enable_submit_js = '';
153 if ( $field['recaptcha-type'] === 'invisible' ) {
154 $invisible_disable_submit_js = $invisible_submit_selector . '.prop( "disabled", true ).addClass( "wppb-recaptcha-not-ready" );';
155 $invisible_enable_submit_js = $invisible_submit_selector . '.prop( "disabled", false ).removeClass( "wppb-recaptcha-not-ready" );';
156 }
157
158 if( $field['recaptcha-type'] === 'v3' ) {
159
160 //the section below is properly escaped or the variables contain static strings
161 // phpcs:disable
162 echo '
163 <script>
164 window.wppbRecaptchaCallbackExecuted = false;
165 window.wppbRecaptchaV3 = true;
166 var wppbRecaptchaCallback = function() {
167 if( !window.wppbRecaptchaCallbackExecuted ){
168 '.$callback_conditions.'.each(function() {
169 let wppbElement = jQuery(this),
170 form = wppbElement.is("form") ? wppbElement : wppbElement.find("form"),
171 currentForm = form[0];
172
173 // Ensure we have a PB Form
174 if (form.length === 0) {
175 return;
176 }
177
178 // Listen for PB-Form submission
179 jQuery(currentForm).on("submit.wppbRecaptchaV3", wppbInitializeRecaptchaV3);
180 });
181 window.wppbRecaptchaCallbackExecuted = true;//we use this to make sure we only run the callback once
182
183 // Enable login form submit button
184 if( jQuery("#wppb-loginform input[type=submit]").length > 0 ) {
185 jQuery("#wppb-loginform input[type=submit]").attr("disabled", false);
186 }
187 }
188 };
189
190 function wppbInitializeRecaptchaV3( event = null, current_form = null ){
191
192 if( event ){
193 event.preventDefault();
194 event.stopPropagation();
195 }
196
197 let currentForm = this
198
199 if( current_form != null && current_form && current_form[0] ){
200 currentForm = current_form[0]
201 }
202
203 return new Promise((resolve) => {
204
205 grecaptcha.ready(function() {
206 grecaptcha.execute("' . $pubkey . '", {action: "submit"}).then(function(token) {
207
208 let recaptchaResponse = jQuery(currentForm).find(".wppb-v3-recaptcha.g-recaptcha-response");
209 jQuery(recaptchaResponse).val(token); // Set the recaptcha response
210
211 if( token === false ){
212 return wppbRecaptchaInitializationError();
213 }
214
215 var submitForm = true
216
217 /* dont submit form if PMS gateway is Stripe */
218 if( jQuery(".pms_pay_gate[type=radio]").length > 0 ){
219 jQuery(".pms_pay_gate").each( function(){
220 if( jQuery(this).is(":checked") && !jQuery(this).is(":disabled") && ( jQuery(this).val() == "stripe_connect" || jQuery(this).val() == "stripe_intents" || jQuery(this).val() == "stripe" || jQuery(this).val() == "paypal_connect" ) )
221 submitForm = false
222 })
223 } else if( jQuery(".pms_pay_gate[type=hidden]").length > 0 ) {
224
225 if( !jQuery(".pms_pay_gate[type=hidden]").is(":disabled") && ( jQuery(".pms_pay_gate[type=hidden]").val() == "stripe_connect" || jQuery(".pms_pay_gate[type=hidden]").val() == "stripe_intents" || jQuery(".pms_pay_gate[type=hidden]").val() == "stripe" || jQuery(".pms_pay_gate[type=hidden]").val() == "paypal_connect" ) )
226 submitForm = false
227 } else if( currentForm.classList.contains("wppb-ajax-form") ) {
228 submitForm = false;
229 } else if( currentForm.classList.contains("wppb-2fa-form") ) {
230 submitForm = false;
231 }
232
233 if( currentForm.classList.contains("wppb-2fa-authentication-requested" ) ){
234 submitForm = true;
235 }
236
237 if( submitForm ){
238 jQuery(currentForm).off("submit.wppbRecaptchaV3");
239 if( currentForm.id === "commentform" ){
240 HTMLFormElement.prototype.submit.call(currentForm);
241 } else {
242 currentForm.submit();
243 }
244 } else {
245 jQuery(document).trigger( "wppb_v3_recaptcha_success", jQuery( "input[type=\'submit\']", jQuery( currentForm ) ) )
246 }
247
248 resolve( token );
249
250 });
251 });
252
253 });
254 }
255
256 /* the callback function for when the captcha does not load propperly, maybe network problem or wrong keys */
257 function wppbRecaptchaInitializationError(){
258 window.wppbRecaptchaInitError = true;
259 ';
260
261 } else {
262 //the section below is properly escaped or the variables contain static strings
263 // phpcs:disable
264 echo '
265 <script>
266 window.wppbRecaptchaCallbackExecuted = false;
267 window.wppbRecaptcha = true;
268
269 /* keep the form from being submitted with an empty token before the invisible reCAPTCHA is ready */
270 ' . $invisible_disable_submit_js . '
271
272 var wppbRecaptchaCallback = function() {
273 if( !window.wppbRecaptchaCallbackExecuted ){//see if we executed this before
274 ' . $callback_conditions . '.each(function(){
275 var $recaptchaElement = jQuery(this);
276 var existingRecaptchaId = $recaptchaElement.data("wppb-recaptcha-id");
277
278 if ( typeof existingRecaptchaId !== "undefined" ) {
279 grecaptcha.reset( existingRecaptchaId );
280 return;
281 }
282
283 try {
284 var recID = grecaptcha.render(
285 $recaptchaElement.attr("id"),
286 {
287 "sitekey" : "' . $pubkey . '",
288 "error-callback": wppbRecaptchaInitializationError,
289 ' . $invisible_parameters . '
290 }
291 )
292
293 $recaptchaElement.data("wppb-recaptcha-id", recID);
294 } catch( error ) {
295 if( error && error.message && error.message.indexOf("already been rendered") !== -1 ) {
296 return;
297 }
298
299 throw error;
300 }
301 });
302
303 /* the invisible reCAPTCHA is now bound to the submit button, so it is safe to re-enable it */
304 ' . $invisible_enable_submit_js . '
305
306 window.wppbRecaptchaCallbackExecuted = true;//we use this to make sure we only run the callback once
307 }
308 };
309
310 /* the callback function for when the captcha does not load propperly, maybe network problem or wrong keys */
311 function wppbRecaptchaInitializationError(){
312 window.wppbRecaptchaInitError = true;
313
314 /* the widget could not load, so re-enable the submit button and let the (fallback) submit below run */
315 ' . $invisible_enable_submit_js . '
316 ';
317 }
318
319 if ( $field['recaptcha-type'] === 'invisible' ) {
320 echo '
321 /* make sure that if the invisible recaptcha did not load properly ( network error or wrong keys ) we can still submit the form */
322 jQuery("input[type=\'submit\']", jQuery( ".wppb-recaptcha-element" ).closest("form") ).not("#commentform input[type=\'submit\']").on("click", function(e){
323 jQuery(this).closest("form").submit();
324 });
325 ';
326 }
327
328 echo '
329 //add a captcha field so we do not just let the form submit if we do not have a captcha response
330 jQuery( ".wppb-recaptcha-element" ).after(\'' . wp_nonce_field( 'wppb_recaptcha_init_error', 'wppb_recaptcha_load_error', false, false ) . '\');
331 }
332
333 /* compatibility with other plugins that may include recaptcha with an onload callback. if their script loads first then our callback will not execute so call it explicitly */
334 jQuery( window ).on( "load", function() {
335 wppbRecaptchaCallback();
336 });
337 </script>';
338 // phpcs:enable
339 if ( $field['recaptcha-type'] === 'invisible' ) {
340 echo '<script>
341 /* success callback for invisible recaptcha. it submits the form that contains the right token response */
342 function wppbInvisibleRecaptchaOnSubmit(token){
343
344 var elem = jQuery(".g-recaptcha-response").filter(function(){
345 return jQuery(this).val() === token;
346 });
347
348 var form = elem.closest("form");
349
350 var submitForm = true
351
352 /* dont submit form if PMS gateway is Stripe */
353 if( jQuery(".pms_pay_gate[type=radio]").length > 0 ){
354 jQuery(".pms_pay_gate").each( function(){
355 if( jQuery(this).is(":checked") && !jQuery(this).is(":disabled") && ( jQuery(this).val() == "stripe_connect" || jQuery(this).val() == "stripe_intents" || jQuery(this).val() == "stripe" || jQuery(this).val() == "paypal_connect" ) )
356 submitForm = false
357 })
358 } else if( jQuery(".pms_pay_gate[type=hidden]").length > 0 ) {
359
360 if( !jQuery(".pms_pay_gate[type=hidden]").is(":disabled") && ( jQuery(".pms_pay_gate[type=hidden]").val() == "stripe_connect" || jQuery(".pms_pay_gate[type=hidden]").val() == "stripe_intents" || jQuery(".pms_pay_gate[type=hidden]").val() == "stripe" || jQuery(".pms_pay_gate[type=hidden]").val() == "paypal_connect" ) )
361 submitForm = false
362
363 } else if( form.hasClass("wppb-ajax-form") ) {
364 submitForm = false;
365 } else if( form.hasClass("wppb-2fa-form") ) {
366 submitForm = false;
367 }
368
369 if( form.hasClass("wppb-2fa-authentication-requested" ) ){
370 submitForm = true;
371 }
372
373 if( submitForm ){
374 if( form.attr("id") === "commentform" && form[0] ){
375 HTMLFormElement.prototype.submit.call(form[0]);
376 } else {
377 form.submit();
378 }
379 } else {
380 jQuery(document).trigger( "wppb_invisible_recaptcha_success", jQuery( ".form-submit input[type=\'submit\']", elem.closest("form") ) )
381 return true;
382 }
383 }
384 </script>';
385 }
386
387 $lang = '&hl=en';
388 $locale = get_locale();
389 if(!empty($locale)) {
390 $locale_parts = explode('_',$locale);
391 $lang = '&hl='.urlencode($locale_parts[0]);
392 }
393
394 $source = apply_filters( 'wppb_recaptcha_custom_field_source', 'www.google.com' );
395
396 if( $field['recaptcha-type'] === 'v3' ) {
397 echo '<script src="https://'. esc_attr( $source ) .'/recaptcha/api.js?render='.esc_attr( $pubkey ).'" async defer></script>';
398 } else {
399 echo '<script src="https://'. esc_attr( $source ) .'/recaptcha/api.js?onload=wppbRecaptchaCallback&render=explicit'.esc_attr( $lang ).'" async defer></script>';
400 }
401
402 }
403 add_action('wp_footer', 'wppb_recaptcha_script_footer', 9999);
404 add_action('login_footer', 'wppb_recaptcha_script_footer');
405 add_action('register_form', 'wppb_recaptcha_script_footer');
406 add_action('lost_password', 'wppb_recaptcha_script_footer');
407
408
409 /**
410 * Print style
411 *
412 */
413 function wppb_recaptcha_print_style() {
414 echo '<style type="text/css">
415 /* Hide reCAPTCHA V3 badge */
416 .grecaptcha-badge {
417
418 visibility: hidden !important;
419
420 }
421 </style>';
422 }
423
424 add_action( 'wp_footer', 'wppb_recaptcha_print_style' );
425 add_action( 'login_footer', 'wppb_recaptcha_print_style' );
426
427
428 /**
429 * A wppb_ReCaptchaResponse is returned from wppb_recaptcha_check_answer()
430 */
431 class wppb_ReCaptchaResponse {
432 var $is_valid;
433 }
434
435
436 /**
437 * Calls an HTTP POST function to verify if the user's answer was correct
438 * @param string $privkey
439 * @param string $remoteip
440 * @param string $response
441 * @return wppb_ReCaptchaResponse
442 */
443 function wppb_recaptcha_check_answer ( $privkey, $remoteip, $response, $score_threshold = 0.5 ) {
444
445 if ( $remoteip == null || $remoteip == '' )
446 echo '<span class="error">'. esc_html__("For security reasons, you must pass the remote ip to reCAPTCHA!", "profile-builder") .'</span><br/><br/>';
447
448 // Discard empty solution submissions. Fail closed: a missing token is never valid.
449 // The previous wppb_recaptcha_load_error nonce "escape hatch" was removed - that nonce is printed in the
450 // page HTML, so a bot could replay it to skip verification. A genuinely unconfigured reCAPTCHA (empty keys)
451 // is handled upstream in wppb_validate_captcha_response(), so this does not lock users out on misconfig.
452 if ($response == null || strlen($response) == 0) {
453 $recaptchaResponse = new wppb_ReCaptchaResponse();
454 $recaptchaResponse->is_valid = false;
455
456 return $recaptchaResponse;
457 }
458
459 $source = apply_filters( 'wppb_recaptcha_custom_field_source', 'www.google.com' );
460
461 $getResponse = _wppb_submitHTTPGet(
462 "https://".$source."/recaptcha/api/siteverify?",
463 array (
464 'secret' => $privkey,
465 'remoteip' => $remoteip,
466 'response' => $response
467 )
468 );
469
470 $answers = json_decode( $getResponse, true );
471 $recaptchaResponse = new wppb_ReCaptchaResponse();
472
473 // Fail closed when the HTTP call fails or the body is not valid JSON.
474 if ( ! is_array( $answers ) || empty( $answers['success'] ) ) {
475 $recaptchaResponse->is_valid = false;
476 return $recaptchaResponse;
477 }
478
479 if ( array_key_exists( 'score', $answers ) ) {
480 $recaptchaResponse->is_valid = ( $answers['score'] >= $score_threshold );
481 } else {
482 $recaptchaResponse->is_valid = true;
483 }
484
485 return $recaptchaResponse;
486
487 }
488
489 /* the function to display error message on the registration page */
490 function wppb_validate_captcha_response( $publickey, $privatekey, $score_threshold = 0.5 ){
491 /* If the reCAPTCHA keys are not configured the widget cannot work for anyone, so do not enforce -
492 otherwise an incomplete setup would lock every visitor out of the form. These keys are admin-side
493 configuration, not attacker controlled, so this cannot be used to bypass a properly configured reCAPTCHA. */
494 if ( empty( $publickey ) || empty( $privatekey ) ) {
495 return true;
496 }
497
498 if (isset($_POST['g-recaptcha-response'])){
499 $recaptcha_response_field = sanitize_textarea_field( $_POST['g-recaptcha-response'] );
500 } else {
501 $recaptcha_response_field = '';
502 }
503
504 $already_validated = false;
505 $saved = get_option( 'wppb_recaptcha_validations', array() );
506
507 if( isset( $saved[ $recaptcha_response_field ] ) && $saved[ $recaptcha_response_field ] == true ){
508 $already_validated = true;
509
510 if( !wp_doing_ajax() ){
511 unset( $saved[ $recaptcha_response_field ] );
512
513 update_option( 'wppb_recaptcha_validations', $saved, false );
514 }
515 }
516
517 if( !$already_validated ){
518
519 if( isset( $_SERVER["REMOTE_ADDR"] ) ){
520 $resp = wppb_recaptcha_check_answer($privatekey, sanitize_text_field( $_SERVER["REMOTE_ADDR"] ), $recaptcha_response_field, $score_threshold );
521
522 if( isset( $resp ) ){
523 $already_validated = ( ( !$resp->is_valid ) ? false : true );
524 }
525 }
526
527 }
528
529 // Save valid results when they are being triggered from an ajax request
530 if( wp_doing_ajax() && isset( $_POST['action'] ) && $_POST['action'] == 'pms_validate_checkout' ){
531
532 $saved = get_option( 'wppb_recaptcha_validations', array() );
533
534 if( $already_validated === true )
535 $saved[ $recaptcha_response_field ] = true;
536
537 update_option( 'wppb_recaptcha_validations', $saved, false );
538
539 }
540
541 return $already_validated;
542
543 }
544
545 /* the function to add reCAPTCHA to the registration form of PB */
546 function wppb_recaptcha_handler ( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
547 if ( $field['field'] == 'reCAPTCHA' ){
548 $item_title = apply_filters( 'wppb_'.$form_location.'_recaptcha_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
549 $item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
550
551 wppb_recaptcha_set_default_values();
552
553 if ( ($form_location == 'register') && ( isset($field['captcha-pb-forms']) ) && ( strpos($field['captcha-pb-forms'],'pb_register') !== false || ( $field['recaptcha-type'] == 'v3' && wppb_maybe_enable_recaptcha_v3_on_form( $field ) ) ) ) {
554 $error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
555
556 global $wppb_recaptcha_present;
557 $wppb_recaptcha_present = true;
558
559 if ( array_key_exists( $field['id'], $field_check_errors ) )
560 $error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
561
562 $publickey = trim( $field['public-key'] );
563 $privatekey = trim( $field['private-key'] );
564
565 if ( empty( $publickey ) || empty( $privatekey ) )
566 return '<span class="custom_field_recaptcha_error_message" id="'.$field['meta-name'].'_error_message">'.apply_filters( 'wppb_'.$form_location.'_recaptcha_custom_field_'.$field['id'].'_error_message', __("To use reCAPTCHA you must get an API public key from:", "profile-builder"). '<a href="https://www.google.com/recaptcha/admin/create">https://www.google.com/recaptcha/admin/create</a>' ).'</span>';
567
568 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
569 $output = '<label for="recaptcha_response_field">' . $item_title . $error_mark . '</label>' . wppb_recaptcha_get_html($publickey, 'pb_register');
570 if (!empty($item_description))
571 $output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
572 }
573 else {
574 // html for Invisible reCAPTCHA
575 $output = wppb_recaptcha_get_html($publickey, 'pb_register');
576 }
577
578
579 return $output;
580
581 }
582 }
583 }
584 add_filter( 'wppb_output_form_field_recaptcha', 'wppb_recaptcha_handler', 10, 6 );
585
586
587 /* handle reCAPTCHA field validation on PB Register form */
588 function wppb_check_recaptcha_value( $message, $field, $request_data, $form_location ){
589 if( $field['field'] == 'reCAPTCHA' ){
590 if ( ( $form_location == 'register' ) && ( isset($field['captcha-pb-forms']) ) && ( strpos($field['captcha-pb-forms'],'pb_register') !== false || ( $field['recaptcha-type'] == 'v3' && wppb_maybe_enable_recaptcha_v3_on_form( $field ) ) ) ) {
591 /* theme my login plugin executes the register_errors hook on the frontend on all pages so on our register forms we might have already a recaptcha response
592 so do not verify it again or it will fail */
593 global $wppb_recaptcha_response;
594 if (!isset($wppb_recaptcha_response)){
595 $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
596 }
597 /* reCAPTCHA must fail closed: whenever it is configured to display on this form it has to be
598 verified, regardless of the "required" toggle. A missing/empty token makes
599 wppb_validate_captcha_response() return false, so bots that omit g-recaptcha-response are blocked. */
600 if ( $wppb_recaptcha_response == false ){
601 return wppb_required_field_error($field["field-title"]);
602 }
603 }
604 }
605 return $message;
606 }
607 add_filter( 'wppb_check_form_field_recaptcha', 'wppb_check_recaptcha_value', 10, 4 );
608
609 // Get the reCAPTCHA field information
610 function wppb_get_recaptcha_field(){
611 $wppb_manage_fields = get_option( 'wppb_manage_fields', 'not_found' );
612 $field = array();
613 if ( $wppb_manage_fields != 'not_found' ) {
614 foreach ($wppb_manage_fields as $value) {
615 if ($value['field'] == 'reCAPTCHA'){
616 $field = $value;
617 break;
618 }
619 }
620 }
621 return $field;
622 }
623
624 /* Display reCAPTCHA on PB Recover Password form */
625 function wppb_display_recaptcha_recover_password( $output ){
626 $field = wppb_get_recaptcha_field();
627
628 if ( !empty($field) ) {
629 $publickey = trim($field['public-key']);
630 $item_title = apply_filters('wppb_recover_password_recaptcha_custom_field_' . $field['id'] . '_item_title', wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_title_translation', $field['field-title'], true));
631 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
632
633 // check where reCAPTCHA should display and add reCAPTCHA html
634 if ( isset($field['captcha-pb-forms']) && ( strpos( $field['captcha-pb-forms'],'pb_recover_password' ) !== false || ( $field['recaptcha-type'] == 'v3' && wppb_maybe_enable_recaptcha_v3_on_form( $field ) ) ) ) {
635
636 global $wppb_recaptcha_present;
637 $wppb_recaptcha_present = true;
638
639 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
640 $recaptcha_output = '<label for="recaptcha_response_field">' . $item_title . '</label>' . wppb_recaptcha_get_html($publickey, 'pb_recover_password');
641 if (!empty($item_description))
642 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
643
644 $output = str_replace('</ul>', '<li class="wppb-form-field wppb-recaptcha wppb-recaptcha-'. $field['recaptcha-type'] .'">' . $recaptcha_output . '</li>' . '</ul>', $output);
645 }
646 else {
647 // output Invisible reCAPTCHA html
648 $output = str_replace('</ul>', '<li class="wppb-form-field wppb-recaptcha wppb-recaptcha-'. $field['recaptcha-type'] .'">' . wppb_recaptcha_get_html($publickey, 'pb_recover_password') . '</li>' . '</ul>', $output);
649 }
650 }
651 }
652 return $output;
653 }
654 add_filter('wppb_recover_password_generate_password_input','wppb_display_recaptcha_recover_password');
655
656 /* Function that changes the messageNo from the Recover Password form */
657 function wppb_recaptcha_change_recover_password_message_no($messageNo) {
658
659 if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'recover_password') {
660 $field = wppb_get_recaptcha_field();
661 if (!empty($field)) {
662
663 global $wppb_recaptcha_response;
664 if (!isset($wppb_recaptcha_response)) $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
665
666 if ( isset($field['captcha-pb-forms']) && (strpos($field['captcha-pb-forms'], 'pb_recover_password') !== false) ) {
667
668 if ( $wppb_recaptcha_response == false )
669 $messageNo = '';
670 }
671 }
672 }
673
674 return $messageNo;
675 }
676 add_filter('wppb_recover_password_message_no', 'wppb_recaptcha_change_recover_password_message_no');
677
678 /* Function that adds the reCAPTCHA error message on the Recover Password form */
679 function wppb_recaptcha_recover_password_displayed_message1( $message ) {
680 $field = wppb_get_recaptcha_field();
681
682 if ( !empty($field) ){
683 global $wppb_recaptcha_response;
684 if (!isset($wppb_recaptcha_response)) $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
685
686 if ( isset($field['captcha-pb-forms']) && ( strpos( $field['captcha-pb-forms'],'pb_recover_password' ) !== false ) && ( $wppb_recaptcha_response == false )) {
687
688 // This message is also altered by the plugin-compatibilities.php file, in regards to Captcha plugin ( function wppb_captcha_recover_password_displayed_message1 )
689 if (($message == '<p class="wppb-warning">wppb_recaptcha_error</p>') || ($message == '<p class="wppb-warning">wppb_captcha_error</p>'))
690 $message = '<p class="wppb-warning">' . wppb_recaptcha_field_error($field["field-title"]) . '</p>';
691 else
692 $message = $message . '<p class="wppb-warning">' . wppb_recaptcha_field_error($field["field-title"]) . '</p>';
693
694 }
695 }
696
697 return $message;
698 }
699 add_filter('wppb_recover_password_displayed_message1', 'wppb_recaptcha_recover_password_displayed_message1');
700
701 /* Function that changes the default success message to wppb_recaptcha_error if the reCAPTCHA doesn't validate
702 so that we can change the message displayed with the wppb_recover_password_displayed_message1 filter */
703 function wppb_recaptcha_recover_password_sent_message_1($message) {
704
705 if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'recover_password') {
706 $field = wppb_get_recaptcha_field();
707
708 if (!empty($field)) {
709 global $wppb_recaptcha_response;
710 if (!isset($wppb_recaptcha_response)) $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
711
712 if ( isset($field['captcha-pb-forms']) && ( strpos($field['captcha-pb-forms'], 'pb_recover_password') !== false ) && ( $wppb_recaptcha_response == false ) ){
713 $message = 'wppb_recaptcha_error';
714 }
715 }
716
717 }
718
719 return $message;
720 }
721 add_filter('wppb_recover_password_sent_message1', 'wppb_recaptcha_recover_password_sent_message_1');
722
723 /* Display reCAPTCHA html on PB Login form */
724 function wppb_display_recaptcha_login_form($form_part, $args) {
725
726 if( !isset( $args['form_id'] ) || $args['form_id'] != 'wppb-loginform' )
727 return $form_part;
728
729 $field = wppb_get_recaptcha_field();
730
731 if ( !empty($field) ) {
732 $item_title = apply_filters('wppb_login_recaptcha_custom_field_' . $field['id'] . '_item_title', wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_title_translation', $field['field-title'], true));
733 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
734
735 if ( isset($field['captcha-pb-forms']) && ( strpos( $field['captcha-pb-forms'],'pb_login' ) !== false || ( $field['recaptcha-type'] == 'v3' && wppb_maybe_enable_recaptcha_v3_on_form( $field ) ) ) ) { // check where reCAPTCHA should display and add reCAPTCHA html
736
737 global $wppb_recaptcha_present;
738 $wppb_recaptcha_present = true;
739
740 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
741 $recaptcha_output = '<label for="recaptcha_response_field">' . $item_title . '</label>' . wppb_recaptcha_get_html(trim($field['public-key']), 'pb_login');
742 if (!empty($item_description))
743 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
744
745 $form_part .= '<div class="wppb-form-field wppb-recaptcha wppb-recaptcha-'. $field['recaptcha-type'] .'">' . $recaptcha_output . '</div>';
746 }
747 else {
748 //output Invisible reCAPTCHA html
749 // $form_part .= wppb_recaptcha_get_html(trim($field['public-key']), 'pb_login');
750 $form_part .= '<div class="wppb-form-field wppb-recaptcha wppb-recaptcha-'. $field['recaptcha-type'] .'">' . wppb_recaptcha_get_html(trim($field['public-key']), 'pb_login') . '</div>';
751 }
752 }
753 }
754
755 return $form_part;
756 }
757 add_filter('login_form_middle', 'wppb_display_recaptcha_login_form', 10, 2);
758
759 /* Display reCAPTCHA html on default WP Login form */
760 function wppb_display_recaptcha_wp_login_form(){
761 $field = wppb_get_recaptcha_field();
762
763 if ( !empty($field) ) {
764 $item_title = apply_filters('wppb_login_recaptcha_custom_field_' . $field['id'] . '_item_title', wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_title_translation', $field['field-title'], true));
765 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
766
767 if ( isset($field['captcha-wp-forms']) && (strpos( $field['captcha-wp-forms'],'default_wp_login' ) !== false) ) { // check where reCAPTCHA should display and add reCAPTCHA html
768
769 global $wppb_recaptcha_present;
770 $wppb_recaptcha_present = true;
771
772 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
773 $recaptcha_output = '<label for="recaptcha_response_field" style="padding-left:15px; padding-bottom:7px;">' . $item_title . '</label>' . wppb_recaptcha_get_html(trim($field['public-key']));
774 if (!empty($item_description))
775 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
776
777 echo '<div class="wppb-form-field wppb-recaptcha" style="margin-left:-14px; margin-bottom: 15px;">' . $recaptcha_output . '</div>'; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
778 }
779 else {
780 // output Invisible reCAPTCHA html
781 echo wppb_recaptcha_get_html( trim($field['public-key'])); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
782 }
783 }
784 }
785 }
786 add_action( 'login_form', 'wppb_display_recaptcha_wp_login_form' );
787
788 //Show reCAPTCHA error on Login form (both default and PB one)
789 function wppb_recaptcha_login_wp_error_message($user){
790 //make sure you're on a Login form (WP or PB)
791 if ( isset( $_POST['log'] ) && !is_wp_error($user) && !isset( $_POST['pms_login'] ) ) {
792
793 $field = wppb_get_recaptcha_field();
794 if ( !empty($field) ){
795 global $wppb_recaptcha_response;
796
797 if (!isset($wppb_recaptcha_response)) $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
798
799 $recaptcha_error_message = __('reCaptcha could not be verified. Please try again.','profile-builder');
800
801 if( isset( $field['recaptcha-type'] ) && $field['recaptcha-type'] === 'v2' ) {
802 $recaptcha_error_message = __('Please enter a (valid) reCAPTCHA value','profile-builder');
803 }
804
805 //reCAPTCHA error for displaying on the PB login form
806 if ( isset($_POST['wppb_login']) && ($_POST['wppb_login'] == true) ) {
807
808 // it's a PB login form, check if we have a reCAPTCHA on it and display error if not valid
809 if ((isset($field['captcha-pb-forms'])) && (strpos($field['captcha-pb-forms'], 'pb_login') !== false || ( $field['recaptcha-type'] == 'v3' && wppb_maybe_enable_recaptcha_v3_on_form( $field ) ) ) && ($wppb_recaptcha_response == false)) {
810 $user = new WP_Error('wppb_recaptcha_error', $recaptcha_error_message);
811 remove_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 );
812 remove_filter( 'authenticate', 'wp_authenticate_email_password', 20, 3 );
813 }
814
815 }
816 else {
817 //reCAPTCHA error for displaying on the default WP login form
818 if (isset($field['captcha-wp-forms']) && (strpos($field['captcha-wp-forms'], 'default_wp_login') !== false) && ($wppb_recaptcha_response == false)) {
819 $user = new WP_Error('wppb_recaptcha_error', $recaptcha_error_message);
820 remove_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 );
821 remove_filter( 'authenticate', 'wp_authenticate_email_password', 20, 3 );
822 }
823
824 }
825 }
826 }
827 return $user;
828 }
829 add_filter('authenticate','wppb_recaptcha_login_wp_error_message', 9);
830
831 /**
832 * Add a reCAPTCHA type–specific CSS class to the Register form field
833 *
834 * @param $classes - existing field classes
835 * @param $field - field data
836 * @return mixed|string
837 */
838 function wppb_register_form_recaptcha_type_class( $classes, $field ){
839
840 if ( isset( $field['field'] ) && $field['field'] == 'reCAPTCHA' && ! empty( $field['recaptcha-type'] ) )
841 $classes .= ' wppb-recaptcha-' . $field['recaptcha-type'];
842
843 return $classes;
844 }
845 add_filter( 'wppb_field_css_class', 'wppb_register_form_recaptcha_type_class', 20, 2);
846
847 // Display reCAPTCHA html on default WP Recover Password form
848 function wppb_display_recaptcha_default_wp_recover_password() {
849 $field = wppb_get_recaptcha_field();
850
851 if (!empty($field)) {
852 $publickey = trim($field['public-key']);
853 $item_title = apply_filters('wppb_recover_password_recaptcha_custom_field_' . $field['id'] . '_item_title', wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_title_translation', $field['field-title'], true));
854 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
855
856 if ( isset($field['captcha-wp-forms']) && (strpos( $field['captcha-wp-forms'], 'default_wp_recover_password') !== false) ) { // check where reCAPTCHA should display and add reCAPTCHA html
857
858 global $wppb_recaptcha_present;
859 $wppb_recaptcha_present = true;
860
861 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ){
862 $recaptcha_output = '<label for="recaptcha_response_field" style="padding-left:15px; padding-bottom:7px;">' . $item_title . '</label>' . wppb_recaptcha_get_html($publickey);
863 if (!empty($item_description))
864 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
865
866 echo '<div class="wppb-form-field wppb-recaptcha" style="margin-left:-14px; margin-bottom: 15px;">' . $recaptcha_output . '</div>'; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
867 }
868 else {
869 // output Invisible reCAPTCHA html
870 echo wppb_recaptcha_get_html($publickey); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
871 }
872 }
873 }
874 }
875 add_action('lostpassword_form','wppb_display_recaptcha_default_wp_recover_password');
876
877 // Verify and show reCAPTCHA errors for default WP Recover Password
878 function wppb_verify_recaptcha_default_wp_recover_password(){
879
880 // If field 'username or email' is empty - return
881 if( isset( $_REQUEST['user_login'] ) && "" === $_REQUEST['user_login'] )
882 return;
883
884 $field = wppb_get_recaptcha_field();
885 if ( !empty($field) ){
886 global $wppb_recaptcha_response;
887 if (!isset($wppb_recaptcha_response)) $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
888
889 $recaptcha_error_message = esc_html__('reCaptcha could not be verified. Please try again.','profile-builder');
890
891 if( isset( $field['recaptcha-type'] ) && $field['recaptcha-type'] === 'v2' ) {
892 $recaptcha_error_message = esc_html__('Please enter a (valid) reCAPTCHA value','profile-builder');
893 }
894
895 // Fail closed, but only where reCAPTCHA is configured for this form. Gate on captcha-wp-forms (as the
896 // login path does) instead of isset() of the token, so a missing token is treated as a failed verification
897 // without blocking default WP password recovery on sites that only use reCAPTCHA on PB forms.
898 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_recover_password' ) !== false ) && ( $wppb_recaptcha_response == false ) ) {
899 wp_die( esc_html( $recaptcha_error_message ) . '<br />' . esc_html__( "Click the BACK button on your browser, and try again.", 'profile-builder' ) ) ;
900 }
901 }
902 }
903 add_action('lostpassword_post','wppb_verify_recaptcha_default_wp_recover_password');
904
905 /* Display reCAPTCHA html on default WP Register form */
906 function wppb_display_recaptcha_default_wp_register(){
907 $field = wppb_get_recaptcha_field();
908
909 if (!empty($field)) {
910
911 $publickey = trim($field['public-key']);
912 $item_title = apply_filters('wppb_register_recaptcha_custom_field_' . $field['id'] . '_item_title', wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_title_translation', $field['field-title'], true));
913 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
914
915 wppb_recaptcha_set_default_values();
916 if (isset($field['captcha-wp-forms']) && (strpos($field['captcha-wp-forms'], 'default_wp_register') !== false)) { // check where reCAPTCHA should display and add reCAPTCHA html
917
918 global $wppb_recaptcha_present;
919 $wppb_recaptcha_present = true;
920
921 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
922 $recaptcha_output = '<label for="recaptcha_response_field" style="padding-left:15px; padding-bottom:7px;">' . $item_title . '</label>' . wppb_recaptcha_get_html($publickey);
923 if (!empty($item_description))
924 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
925
926 echo '<div class="wppb-form-field wppb-recaptcha" style="margin-left:-14px; margin-bottom: 15px;">' . $recaptcha_output . '</div>'; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
927 }
928 else {
929 // output reCAPTCHA html
930 echo wppb_recaptcha_get_html($publickey); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
931 }
932 }
933 }
934 }
935 add_action( 'register_form', 'wppb_display_recaptcha_default_wp_register' );
936
937 // Verify and show reCAPTCHA errors for default WP Register form
938 function wppb_verify_recaptcha_default_wp_register( $errors ){
939
940 $field = wppb_get_recaptcha_field();
941 if ( !empty($field) ){
942 global $wppb_recaptcha_response;
943 if (!isset($wppb_recaptcha_response)) $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
944
945 $recaptcha_error_message = esc_html__('reCaptcha could not be verified. Please try again.','profile-builder');
946
947 if( isset( $field['recaptcha-type'] ) && $field['recaptcha-type'] === 'v2' ) {
948 $recaptcha_error_message = esc_html__('Please enter a (valid) reCAPTCHA value','profile-builder');
949 }
950
951 // Fail closed, but only where reCAPTCHA is configured for this form. Gate on captcha-wp-forms (as the
952 // login path does) instead of isset() of the token, so a missing token is treated as a failed verification
953 // without blocking default WP registration on sites that only use reCAPTCHA on PB forms.
954 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_register' ) !== false ) && ( $wppb_recaptcha_response == false ) ) {
955 $errors->add( 'wppb_recaptcha_error', $recaptcha_error_message );
956 }
957 }
958
959 return $errors;
960 }
961 add_filter('registration_errors','wppb_verify_recaptcha_default_wp_register');
962
963 /* Display reCAPTCHA html on default WP Comments form */
964 function wppb_display_recaptcha_default_wp_comments(){
965 $field = wppb_get_recaptcha_field();
966
967 if ( !empty( $field ) ) {
968 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_comments' ) !== false ) ) {
969 $publickey = trim( $field['public-key'] );
970 $item_title = apply_filters( 'wppb_comments_recaptcha_custom_field_' . $field['id'] . '_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_title_translation', $field['field-title'], true ) );
971 $item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true );
972 $recaptcha_type = empty( $field['recaptcha-type'] ) ? 'v2' : $field['recaptcha-type'];
973
974 global $wppb_recaptcha_present;
975 $wppb_recaptcha_present = true;
976
977 if ( $recaptcha_type == 'v2' ) {
978 $recaptcha_output = '<label for="recaptcha_response_field">' . $item_title . '</label>' . wppb_recaptcha_get_html( $publickey, 'default_wp_comments' );
979 if ( !empty( $item_description ) )
980 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
981
982 echo '<div class="wppb-form-field wppb-recaptcha wppb-recaptcha-' . esc_attr( $recaptcha_type ) . '">' . $recaptcha_output . '</div>'; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
983 }
984 else {
985 echo wppb_recaptcha_get_html( $publickey, 'default_wp_comments' ); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
986 }
987 }
988 }
989 }
990 add_action( 'comment_form_after_fields', 'wppb_display_recaptcha_default_wp_comments' );
991 add_action( 'comment_form_logged_in_after', 'wppb_display_recaptcha_default_wp_comments' );
992
993 function wppb_display_recaptcha_default_wp_comments_error(){
994 if ( !isset( $_GET['wppb_comment_recaptcha_error'] ) )
995 return;
996
997 $field = wppb_get_recaptcha_field();
998
999 if ( empty( $field ) || !isset( $field['captcha-wp-forms'] ) || ( strpos( $field['captcha-wp-forms'], 'default_wp_comments' ) === false ) )
1000 return;
1001
1002 echo '<p class="wppb-error wppb-comment-captcha-error" id="wppb_comment_recaptcha_error">' . esc_html( wppb_recaptcha_field_error( $field['field-title'] ) ) . '</p>';
1003 }
1004 add_action( 'comment_form_top', 'wppb_display_recaptcha_default_wp_comments_error' );
1005
1006 // Verify reCAPTCHA for default WP Comments form
1007 function wppb_verify_recaptcha_default_wp_comments( $approved, $commentdata ){
1008 if ( !isset( $_POST['comment_post_ID'] ) )
1009 return $approved;
1010
1011 $field = wppb_get_recaptcha_field();
1012
1013 if ( !empty( $field ) ) {
1014 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_comments' ) !== false ) ) {
1015 global $wppb_recaptcha_response;
1016 if ( !isset( $wppb_recaptcha_response ) )
1017 $wppb_recaptcha_response = wppb_validate_captcha_response( trim( $field['public-key'] ), trim( $field['private-key'] ), isset( $field['score-threshold'] ) ? trim( $field['score-threshold'] ) : 0.5 );
1018
1019 if ( $wppb_recaptcha_response == false ) {
1020 $redirect_to = wp_get_referer();
1021
1022 if ( empty( $redirect_to ) && isset( $commentdata['comment_post_ID'] ) )
1023 $redirect_to = get_permalink( absint( $commentdata['comment_post_ID'] ) );
1024
1025 if ( !empty( $redirect_to ) && !wp_doing_ajax() ) {
1026 $redirect_to = preg_replace( '/#.*$/', '', remove_query_arg( array( 'wppb_comment_recaptcha_error', 'wppb_comment_turnstile_error' ), $redirect_to ) );
1027 wp_safe_redirect( add_query_arg( 'wppb_comment_recaptcha_error', '1', $redirect_to ) . '#respond' );
1028 exit;
1029 }
1030
1031 return new WP_Error( 'wppb_recaptcha_error', wppb_recaptcha_field_error( $field['field-title'] ), 200 );
1032 }
1033 }
1034 }
1035
1036 return $approved;
1037 }
1038 add_filter( 'pre_comment_approved', 'wppb_verify_recaptcha_default_wp_comments', 10, 2 );
1039
1040 // set default values in case there's already an existing reCAPTCHA field in Manage fields (when upgrading)
1041 function wppb_recaptcha_set_default_values() {
1042 $manage_fields = get_option('wppb_manage_fields', 'not_set');
1043 if ($manage_fields != 'not_set') {
1044 foreach ($manage_fields as $key => $value) {
1045 if ($value['field'] == 'reCAPTCHA') {
1046 if ( !isset($value['captcha-pb-forms']) ) $manage_fields[$key]['captcha-pb-forms'] = 'pb_register';
1047 if ( !isset($value['captcha-wp-forms']) ) $manage_fields[$key]['captcha-wp-forms'] = 'default_wp_register';
1048 if ( !isset($value['recaptcha-type']) ) $manage_fields[$key]['recaptcha-type'] = 'v2';
1049 }
1050 }
1051 update_option('wppb_manage_fields', $manage_fields);
1052 }
1053 }
1054
1055 if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'paid-member-subscriptions/index.php' ) && defined( 'PMS_VERSION' ) && version_compare( PMS_VERSION, '2.12.9', '<' ) ) {
1056
1057 $notifications = WPPB_Plugin_Notifications::get_instance();
1058
1059 // this must be unique
1060 $notification_id = 'wppb_pms_recaptcha_compatibility';
1061
1062 $notification_message = '<p>' . __( 'reCAPTCHA v3 is not compatible with Paid Member Subscriptions versions that are older than <strong>2.12.7</strong>. <br>Please update Paid Member Subscriptions to a newer version to avoid any issues.', 'profile-builder' ) . '</p>';
1063 $notification_message .= '<a href="' . wp_nonce_url( add_query_arg( array( 'wppb_dismiss_admin_notification' => $notification_id ) ), 'wppb_plugin_notice_dismiss' ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . __( 'Dismiss this notice.', 'profile-builder' ) . '</span></a>';
1064
1065 // add the notification (we need to add the "notice is-dismissible" classes for the dismiss button to be correctly positioned)
1066 $notifications->add_notification( $notification_id, $notification_message, 'wppb-notice notice notice-warning is-dismissible', false );
1067
1068 }
1069
1070 // Make sure the reCAPTCHA field score threshold is set correctly
1071 function wppb_check_recaptcha_fields_settings( $values ) {
1072 if( isset( $values['field'] ) && $values['field'] == 'reCAPTCHA' ) {
1073 if ( empty( $values['score-threshold'] ) || $values['score-threshold'] < 0 || $values['score-threshold'] > 1 ) {
1074 $values['score-threshold'] = 0.5;
1075 }
1076 }
1077
1078 return $values;
1079 }
1080 add_action( 'wck_update_meta_filter_values_wppb_manage_fields', 'wppb_check_recaptcha_fields_settings' );
1081
1082 function wppb_maybe_enable_recaptcha_v3_on_form( $recaptcha_field ){
1083
1084 // Static cache to avoid repeated calculations
1085 static $cache = array();
1086
1087 // Early validation checks
1088 if( empty( $recaptcha_field ) || empty( $recaptcha_field['captcha-pb-forms'] ) )
1089 return false;
1090
1091 $post_id = get_the_ID();
1092 $post = get_post( $post_id );
1093
1094 // Check if post is set, if not return false
1095 if( empty( $post ) || empty( $post->post_content ) )
1096 return false;
1097
1098 // Create cache key based on post ID and captcha forms configuration
1099 $cache_key = md5( $post_id . serialize( $recaptcha_field['captcha-pb-forms'] ) );
1100
1101 // Return cached result if available
1102 if( isset( $cache[ $cache_key ] ) )
1103 return $cache[ $cache_key ];
1104
1105 $wppb_recaptcha_v3 = false;
1106
1107 // Define form configurations for loop processing
1108 $form_configs = array(
1109 'pb_register' => array(
1110 'shortcode_pattern' => '[wppb-register',
1111 'block_name' => 'wppb/register',
1112 'other_forms' => array(
1113 array( 'shortcode' => '[wppb-login', 'block' => 'wppb/login', 'form_type' => 'pb_login' ),
1114 array( 'shortcode' => '[wppb-recover-password', 'block' => 'wppb/recover-password', 'form_type' => 'pb_recover_password' )
1115 )
1116 ),
1117 'pb_login' => array(
1118 'shortcode_pattern' => '[wppb-login',
1119 'block_name' => 'wppb/login',
1120 'other_forms' => array(
1121 array( 'shortcode' => '[wppb-register', 'block' => 'wppb/register', 'form_type' => 'pb_register' ),
1122 array( 'shortcode' => '[wppb-recover-password', 'block' => 'wppb/recover-password', 'form_type' => 'pb_recover_password' )
1123 )
1124 ),
1125 'pb_recover_password' => array(
1126 'shortcode_pattern' => '[wppb-recover-password',
1127 'block_name' => 'wppb/recover-password',
1128 'other_forms' => array(
1129 array( 'shortcode' => '[wppb-register', 'block' => 'wppb/register', 'form_type' => 'pb_register' ),
1130 array( 'shortcode' => '[wppb-login', 'block' => 'wppb/login', 'form_type' => 'pb_login' )
1131 )
1132 )
1133 );
1134
1135 // Process each form type using loop
1136 foreach( $form_configs as $form_type => $config ) {
1137 // Skip if this form type is already enabled in captcha-pb-forms
1138 if( strpos( $recaptcha_field['captcha-pb-forms'], $form_type ) !== false )
1139 continue;
1140
1141 // Check if current form type exists on the page
1142 $current_form_exists = ( strpos( $post->post_content, $config['shortcode_pattern'] ) !== false || has_block( $config['block_name'] ) );
1143
1144 if( $current_form_exists ) {
1145 // Check if any other enabled form types also exist on the page
1146 foreach( $config['other_forms'] as $other_form ) {
1147 $other_form_exists = ( strpos( $post->post_content, $other_form['shortcode'] ) !== false || has_block( $other_form['block'] ) );
1148 $other_form_enabled = ( strpos( $recaptcha_field['captcha-pb-forms'], $other_form['form_type'] ) !== false );
1149
1150 if( $other_form_exists && $other_form_enabled ) {
1151 $wppb_recaptcha_v3 = true;
1152 break 2; // Break out of both loops since we found a match
1153 }
1154 }
1155 }
1156 }
1157
1158 // Cache the result
1159 $cache[ $cache_key ] = $wppb_recaptcha_v3;
1160
1161 return $wppb_recaptcha_v3;
1162
1163 }
1164