PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.3
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.3, at front-end/default-fields/recaptcha/recaptcha.php

1,171 lines 57.8 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 that only pre-validates the
530 // credentials, so the same single use token is still accepted on the form submission that follows it
531 if( wppb_is_captcha_prevalidation_request() ){
532
533 $saved = wppb_prune_captcha_prevalidations( get_option( 'wppb_recaptcha_validations', array() ) );
534
535 if( $already_validated === true )
536 $saved[ $recaptcha_response_field ] = time();
537
538 update_option( 'wppb_recaptcha_validations', $saved, false );
539
540 }
541
542 return $already_validated;
543
544 }
545
546 /* the function to add reCAPTCHA to the registration form of PB */
547 function wppb_recaptcha_handler ( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
548 if ( $field['field'] == 'reCAPTCHA' ){
549 $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 ) );
550 $item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
551
552 wppb_recaptcha_set_default_values();
553
554 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 ) ) ) ) {
555 $error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
556
557 global $wppb_recaptcha_present;
558 $wppb_recaptcha_present = true;
559
560 if ( array_key_exists( $field['id'], $field_check_errors ) )
561 $error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
562
563 $publickey = trim( $field['public-key'] );
564 $privatekey = trim( $field['private-key'] );
565
566 if ( empty( $publickey ) || empty( $privatekey ) )
567 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>';
568
569 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
570 $output = '<label for="recaptcha_response_field">' . $item_title . $error_mark . '</label>' . wppb_recaptcha_get_html($publickey, 'pb_register');
571 if (!empty($item_description))
572 $output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
573 }
574 else {
575 // html for Invisible reCAPTCHA
576 $output = wppb_recaptcha_get_html($publickey, 'pb_register');
577 }
578
579
580 return $output;
581
582 }
583 }
584 }
585 add_filter( 'wppb_output_form_field_recaptcha', 'wppb_recaptcha_handler', 10, 6 );
586
587
588 /* handle reCAPTCHA field validation on PB Register form */
589 function wppb_check_recaptcha_value( $message, $field, $request_data, $form_location ){
590 if( $field['field'] == 'reCAPTCHA' ){
591 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 ) ) ) ) {
592 /* 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
593 so do not verify it again or it will fail */
594 global $wppb_recaptcha_response;
595 if (!isset($wppb_recaptcha_response)){
596 $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 );
597 }
598 /* reCAPTCHA must fail closed: whenever it is configured to display on this form it has to be
599 verified, regardless of the "required" toggle. A missing/empty token makes
600 wppb_validate_captcha_response() return false, so bots that omit g-recaptcha-response are blocked. */
601 if ( $wppb_recaptcha_response == false ){
602 return wppb_required_field_error($field["field-title"]);
603 }
604 }
605 }
606 return $message;
607 }
608 add_filter( 'wppb_check_form_field_recaptcha', 'wppb_check_recaptcha_value', 10, 4 );
609
610 // Get the reCAPTCHA field information
611 function wppb_get_recaptcha_field(){
612 $wppb_manage_fields = get_option( 'wppb_manage_fields', 'not_found' );
613 $field = array();
614 if ( $wppb_manage_fields != 'not_found' ) {
615 foreach ($wppb_manage_fields as $value) {
616 if ($value['field'] == 'reCAPTCHA'){
617 $field = $value;
618 break;
619 }
620 }
621 }
622 return $field;
623 }
624
625 /* Display reCAPTCHA on PB Recover Password form */
626 function wppb_display_recaptcha_recover_password( $output ){
627 $field = wppb_get_recaptcha_field();
628
629 if ( !empty($field) ) {
630 $publickey = trim($field['public-key']);
631 $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));
632 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
633
634 // check where reCAPTCHA should display and add reCAPTCHA html
635 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 ) ) ) ) {
636
637 global $wppb_recaptcha_present;
638 $wppb_recaptcha_present = true;
639
640 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
641 $recaptcha_output = '<label for="recaptcha_response_field">' . $item_title . '</label>' . wppb_recaptcha_get_html($publickey, 'pb_recover_password');
642 if (!empty($item_description))
643 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
644
645 $output = str_replace('</ul>', '<li class="wppb-form-field wppb-recaptcha wppb-recaptcha-'. $field['recaptcha-type'] .'">' . $recaptcha_output . '</li>' . '</ul>', $output);
646 }
647 else {
648 // output Invisible reCAPTCHA html
649 $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);
650 }
651 }
652 }
653 return $output;
654 }
655 add_filter('wppb_recover_password_generate_password_input','wppb_display_recaptcha_recover_password');
656
657 /* Function that changes the messageNo from the Recover Password form */
658 function wppb_recaptcha_change_recover_password_message_no($messageNo) {
659
660 if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'recover_password') {
661 $field = wppb_get_recaptcha_field();
662 if (!empty($field)) {
663
664 global $wppb_recaptcha_response;
665 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 );
666
667 if ( isset($field['captcha-pb-forms']) && (strpos($field['captcha-pb-forms'], 'pb_recover_password') !== false) ) {
668
669 if ( $wppb_recaptcha_response == false )
670 $messageNo = '';
671 }
672 }
673 }
674
675 return $messageNo;
676 }
677 add_filter('wppb_recover_password_message_no', 'wppb_recaptcha_change_recover_password_message_no');
678
679 /* Function that adds the reCAPTCHA error message on the Recover Password form */
680 function wppb_recaptcha_recover_password_displayed_message1( $message ) {
681 $field = wppb_get_recaptcha_field();
682
683 if ( !empty($field) ){
684 global $wppb_recaptcha_response;
685 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 );
686
687 if ( isset($field['captcha-pb-forms']) && ( strpos( $field['captcha-pb-forms'],'pb_recover_password' ) !== false ) && ( $wppb_recaptcha_response == false )) {
688
689 // This message is also altered by the plugin-compatibilities.php file, in regards to Captcha plugin ( function wppb_captcha_recover_password_displayed_message1 )
690 if (($message == '<p class="wppb-warning">wppb_recaptcha_error</p>') || ($message == '<p class="wppb-warning">wppb_captcha_error</p>'))
691 $message = '<p class="wppb-warning">' . wppb_recaptcha_field_error($field["field-title"]) . '</p>';
692 else
693 $message = $message . '<p class="wppb-warning">' . wppb_recaptcha_field_error($field["field-title"]) . '</p>';
694
695 }
696 }
697
698 return $message;
699 }
700 add_filter('wppb_recover_password_displayed_message1', 'wppb_recaptcha_recover_password_displayed_message1');
701
702 /* Function that changes the default success message to wppb_recaptcha_error if the reCAPTCHA doesn't validate
703 so that we can change the message displayed with the wppb_recover_password_displayed_message1 filter */
704 function wppb_recaptcha_recover_password_sent_message_1($message) {
705
706 if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'recover_password') {
707 $field = wppb_get_recaptcha_field();
708
709 if (!empty($field)) {
710 global $wppb_recaptcha_response;
711 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 );
712
713 if ( isset($field['captcha-pb-forms']) && ( strpos($field['captcha-pb-forms'], 'pb_recover_password') !== false ) && ( $wppb_recaptcha_response == false ) ){
714 $message = 'wppb_recaptcha_error';
715 }
716 }
717
718 }
719
720 return $message;
721 }
722 add_filter('wppb_recover_password_sent_message1', 'wppb_recaptcha_recover_password_sent_message_1');
723
724 /* Display reCAPTCHA html on PB Login form */
725 function wppb_display_recaptcha_login_form($form_part, $args) {
726
727 if( !isset( $args['form_id'] ) || $args['form_id'] != 'wppb-loginform' )
728 return $form_part;
729
730 $field = wppb_get_recaptcha_field();
731
732 if ( !empty($field) ) {
733 $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));
734 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
735
736 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
737
738 global $wppb_recaptcha_present;
739 $wppb_recaptcha_present = true;
740
741 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
742 $recaptcha_output = '<label for="recaptcha_response_field">' . $item_title . '</label>' . wppb_recaptcha_get_html(trim($field['public-key']), 'pb_login');
743 if (!empty($item_description))
744 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
745
746 $form_part .= '<div class="wppb-form-field wppb-recaptcha wppb-recaptcha-'. $field['recaptcha-type'] .'">' . $recaptcha_output . '</div>';
747 }
748 else {
749 //output Invisible reCAPTCHA html
750 // $form_part .= wppb_recaptcha_get_html(trim($field['public-key']), 'pb_login');
751 $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>';
752 }
753 }
754 }
755
756 return $form_part;
757 }
758 add_filter('login_form_middle', 'wppb_display_recaptcha_login_form', 10, 2);
759
760 /* Display reCAPTCHA html on default WP Login form */
761 function wppb_display_recaptcha_wp_login_form(){
762 $field = wppb_get_recaptcha_field();
763
764 if ( !empty($field) ) {
765 $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));
766 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
767
768 if ( isset($field['captcha-wp-forms']) && (strpos( $field['captcha-wp-forms'],'default_wp_login' ) !== false) ) { // check where reCAPTCHA should display and add reCAPTCHA html
769
770 global $wppb_recaptcha_present;
771 $wppb_recaptcha_present = true;
772
773 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
774 $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']));
775 if (!empty($item_description))
776 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
777
778 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 */
779 }
780 else {
781 // output Invisible reCAPTCHA html
782 echo wppb_recaptcha_get_html( trim($field['public-key'])); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
783 }
784 }
785 }
786 }
787 add_action( 'login_form', 'wppb_display_recaptcha_wp_login_form' );
788
789 //Show reCAPTCHA error on Login form (both default and PB one)
790 function wppb_recaptcha_login_wp_error_message($user){
791 //make sure you're on a Login form (WP or PB)
792 if ( isset( $_POST['log'] ) && !is_wp_error($user) && !isset( $_POST['pms_login'] ) ) {
793
794 $field = wppb_get_recaptcha_field();
795 if ( !empty($field) ){
796 /* Work out whether reCAPTCHA is enabled for the form that was actually submitted before verifying
797 anything. The token is single use, so verifying it on a form where our widget was never displayed
798 spends a token that belongs to whatever else protects that form, and that plugin's own check
799 then fails as a duplicate. */
800 if ( isset($_POST['wppb_login']) && ($_POST['wppb_login'] == true) ) {
801 // it's a PB login form, check if we have a reCAPTCHA on it
802 $recaptcha_enabled = ( 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 ) ) ) );
803 }
804 else {
805 // default WP login form
806 $recaptcha_enabled = ( isset($field['captcha-wp-forms']) && (strpos($field['captcha-wp-forms'], 'default_wp_login') !== false) );
807 }
808
809 if ( $recaptcha_enabled ) {
810 global $wppb_recaptcha_response;
811
812 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 );
813
814 $recaptcha_error_message = __('reCaptcha could not be verified. Please try again.','profile-builder');
815
816 if( isset( $field['recaptcha-type'] ) && $field['recaptcha-type'] === 'v2' ) {
817 $recaptcha_error_message = __('Please enter a (valid) reCAPTCHA value','profile-builder');
818 }
819
820 if ( $wppb_recaptcha_response == false ) {
821 $user = new WP_Error('wppb_recaptcha_error', $recaptcha_error_message);
822 remove_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 );
823 remove_filter( 'authenticate', 'wp_authenticate_email_password', 20, 3 );
824 }
825 }
826 }
827 }
828 return $user;
829 }
830 add_filter('authenticate','wppb_recaptcha_login_wp_error_message', 9);
831
832 /**
833 * Add a reCAPTCHA type–specific CSS class to the Register form field
834 *
835 * @param $classes - existing field classes
836 * @param $field - field data
837 * @return mixed|string
838 */
839 function wppb_register_form_recaptcha_type_class( $classes, $field ){
840
841 if ( isset( $field['field'] ) && $field['field'] == 'reCAPTCHA' && ! empty( $field['recaptcha-type'] ) )
842 $classes .= ' wppb-recaptcha-' . $field['recaptcha-type'];
843
844 return $classes;
845 }
846 add_filter( 'wppb_field_css_class', 'wppb_register_form_recaptcha_type_class', 20, 2);
847
848 // Display reCAPTCHA html on default WP Recover Password form
849 function wppb_display_recaptcha_default_wp_recover_password() {
850 $field = wppb_get_recaptcha_field();
851
852 if (!empty($field)) {
853 $publickey = trim($field['public-key']);
854 $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));
855 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
856
857 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
858
859 global $wppb_recaptcha_present;
860 $wppb_recaptcha_present = true;
861
862 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ){
863 $recaptcha_output = '<label for="recaptcha_response_field" style="padding-left:15px; padding-bottom:7px;">' . $item_title . '</label>' . wppb_recaptcha_get_html($publickey);
864 if (!empty($item_description))
865 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
866
867 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 */
868 }
869 else {
870 // output Invisible reCAPTCHA html
871 echo wppb_recaptcha_get_html($publickey); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
872 }
873 }
874 }
875 }
876 add_action('lostpassword_form','wppb_display_recaptcha_default_wp_recover_password');
877
878 // Verify and show reCAPTCHA errors for default WP Recover Password
879 function wppb_verify_recaptcha_default_wp_recover_password(){
880
881 // If field 'username or email' is empty - return
882 if( isset( $_REQUEST['user_login'] ) && "" === $_REQUEST['user_login'] )
883 return;
884
885 $field = wppb_get_recaptcha_field();
886 if ( !empty($field) ){
887 /* Only verify where the captcha is configured for the form being submitted. The token is single use,
888 so verifying it on a form our widget was never displayed on spends a token that another plugin
889 protecting that form still needs, and its own check then fails as a duplicate. */
890 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_recover_password' ) !== false ) ) {
891 global $wppb_recaptcha_response;
892 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 );
893
894 $recaptcha_error_message = esc_html__('reCaptcha could not be verified. Please try again.','profile-builder');
895
896 if( isset( $field['recaptcha-type'] ) && $field['recaptcha-type'] === 'v2' ) {
897 $recaptcha_error_message = esc_html__('Please enter a (valid) reCAPTCHA value','profile-builder');
898 }
899
900 // Fail closed: a missing token is treated as a failed verification.
901 if ( $wppb_recaptcha_response == false ) {
902 wp_die( esc_html( $recaptcha_error_message ) . '<br />' . esc_html__( "Click the BACK button on your browser, and try again.", 'profile-builder' ) ) ;
903 }
904 }
905 }
906 }
907 add_action('lostpassword_post','wppb_verify_recaptcha_default_wp_recover_password');
908
909 /* Display reCAPTCHA html on default WP Register form */
910 function wppb_display_recaptcha_default_wp_register(){
911 $field = wppb_get_recaptcha_field();
912
913 if (!empty($field)) {
914
915 $publickey = trim($field['public-key']);
916 $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));
917 $item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
918
919 wppb_recaptcha_set_default_values();
920 if (isset($field['captcha-wp-forms']) && (strpos($field['captcha-wp-forms'], 'default_wp_register') !== false)) { // check where reCAPTCHA should display and add reCAPTCHA html
921
922 global $wppb_recaptcha_present;
923 $wppb_recaptcha_present = true;
924
925 if ( empty($field['recaptcha-type']) || ($field['recaptcha-type'] == 'v2') ) {
926 $recaptcha_output = '<label for="recaptcha_response_field" style="padding-left:15px; padding-bottom:7px;">' . $item_title . '</label>' . wppb_recaptcha_get_html($publickey);
927 if (!empty($item_description))
928 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
929
930 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 */
931 }
932 else {
933 // output reCAPTCHA html
934 echo wppb_recaptcha_get_html($publickey); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
935 }
936 }
937 }
938 }
939 add_action( 'register_form', 'wppb_display_recaptcha_default_wp_register' );
940
941 // Verify and show reCAPTCHA errors for default WP Register form
942 function wppb_verify_recaptcha_default_wp_register( $errors ){
943
944 $field = wppb_get_recaptcha_field();
945 if ( !empty($field) ){
946 /* Only verify where the captcha is configured for the form being submitted. The token is single use,
947 so verifying it on a form our widget was never displayed on spends a token that another plugin
948 protecting that form still needs, and its own check then fails as a duplicate. */
949 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_register' ) !== false ) ) {
950 global $wppb_recaptcha_response;
951 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 );
952
953 $recaptcha_error_message = esc_html__('reCaptcha could not be verified. Please try again.','profile-builder');
954
955 if( isset( $field['recaptcha-type'] ) && $field['recaptcha-type'] === 'v2' ) {
956 $recaptcha_error_message = esc_html__('Please enter a (valid) reCAPTCHA value','profile-builder');
957 }
958
959 // Fail closed: a missing token is treated as a failed verification.
960 if ( $wppb_recaptcha_response == false ) {
961 $errors->add( 'wppb_recaptcha_error', $recaptcha_error_message );
962 }
963 }
964 }
965
966 return $errors;
967 }
968 add_filter('registration_errors','wppb_verify_recaptcha_default_wp_register');
969
970 /* Display reCAPTCHA html on default WP Comments form */
971 function wppb_display_recaptcha_default_wp_comments(){
972 $field = wppb_get_recaptcha_field();
973
974 if ( !empty( $field ) ) {
975 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_comments' ) !== false ) ) {
976 $publickey = trim( $field['public-key'] );
977 $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 ) );
978 $item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true );
979 $recaptcha_type = empty( $field['recaptcha-type'] ) ? 'v2' : $field['recaptcha-type'];
980
981 global $wppb_recaptcha_present;
982 $wppb_recaptcha_present = true;
983
984 if ( $recaptcha_type == 'v2' ) {
985 $recaptcha_output = '<label for="recaptcha_response_field">' . $item_title . '</label>' . wppb_recaptcha_get_html( $publickey, 'default_wp_comments' );
986 if ( !empty( $item_description ) )
987 $recaptcha_output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
988
989 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 */
990 }
991 else {
992 echo wppb_recaptcha_get_html( $publickey, 'default_wp_comments' ); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped when constructing the var */
993 }
994 }
995 }
996 }
997 add_action( 'comment_form_after_fields', 'wppb_display_recaptcha_default_wp_comments' );
998 add_action( 'comment_form_logged_in_after', 'wppb_display_recaptcha_default_wp_comments' );
999
1000 function wppb_display_recaptcha_default_wp_comments_error(){
1001 if ( !isset( $_GET['wppb_comment_recaptcha_error'] ) )
1002 return;
1003
1004 $field = wppb_get_recaptcha_field();
1005
1006 if ( empty( $field ) || !isset( $field['captcha-wp-forms'] ) || ( strpos( $field['captcha-wp-forms'], 'default_wp_comments' ) === false ) )
1007 return;
1008
1009 echo '<p class="wppb-error wppb-comment-captcha-error" id="wppb_comment_recaptcha_error">' . esc_html( wppb_recaptcha_field_error( $field['field-title'] ) ) . '</p>';
1010 }
1011 add_action( 'comment_form_top', 'wppb_display_recaptcha_default_wp_comments_error' );
1012
1013 // Verify reCAPTCHA for default WP Comments form
1014 function wppb_verify_recaptcha_default_wp_comments( $approved, $commentdata ){
1015 if ( !isset( $_POST['comment_post_ID'] ) )
1016 return $approved;
1017
1018 $field = wppb_get_recaptcha_field();
1019
1020 if ( !empty( $field ) ) {
1021 if ( isset( $field['captcha-wp-forms'] ) && ( strpos( $field['captcha-wp-forms'], 'default_wp_comments' ) !== false ) ) {
1022 global $wppb_recaptcha_response;
1023 if ( !isset( $wppb_recaptcha_response ) )
1024 $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 );
1025
1026 if ( $wppb_recaptcha_response == false ) {
1027 $redirect_to = wp_get_referer();
1028
1029 if ( empty( $redirect_to ) && isset( $commentdata['comment_post_ID'] ) )
1030 $redirect_to = get_permalink( absint( $commentdata['comment_post_ID'] ) );
1031
1032 if ( !empty( $redirect_to ) && !wp_doing_ajax() ) {
1033 $redirect_to = preg_replace( '/#.*$/', '', remove_query_arg( array( 'wppb_comment_recaptcha_error', 'wppb_comment_turnstile_error' ), $redirect_to ) );
1034 wp_safe_redirect( add_query_arg( 'wppb_comment_recaptcha_error', '1', $redirect_to ) . '#respond' );
1035 exit;
1036 }
1037
1038 return new WP_Error( 'wppb_recaptcha_error', wppb_recaptcha_field_error( $field['field-title'] ), 200 );
1039 }
1040 }
1041 }
1042
1043 return $approved;
1044 }
1045 add_filter( 'pre_comment_approved', 'wppb_verify_recaptcha_default_wp_comments', 10, 2 );
1046
1047 // set default values in case there's already an existing reCAPTCHA field in Manage fields (when upgrading)
1048 function wppb_recaptcha_set_default_values() {
1049 $manage_fields = get_option('wppb_manage_fields', 'not_set');
1050 if ($manage_fields != 'not_set') {
1051 foreach ($manage_fields as $key => $value) {
1052 if ($value['field'] == 'reCAPTCHA') {
1053 if ( !isset($value['captcha-pb-forms']) ) $manage_fields[$key]['captcha-pb-forms'] = 'pb_register';
1054 if ( !isset($value['captcha-wp-forms']) ) $manage_fields[$key]['captcha-wp-forms'] = 'default_wp_register';
1055 if ( !isset($value['recaptcha-type']) ) $manage_fields[$key]['recaptcha-type'] = 'v2';
1056 }
1057 }
1058 update_option('wppb_manage_fields', $manage_fields);
1059 }
1060 }
1061
1062 if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'paid-member-subscriptions/index.php' ) && defined( 'PMS_VERSION' ) && version_compare( PMS_VERSION, '2.12.9', '<' ) ) {
1063
1064 $notifications = WPPB_Plugin_Notifications::get_instance();
1065
1066 // this must be unique
1067 $notification_id = 'wppb_pms_recaptcha_compatibility';
1068
1069 $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>';
1070 $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>';
1071
1072 // add the notification (we need to add the "notice is-dismissible" classes for the dismiss button to be correctly positioned)
1073 $notifications->add_notification( $notification_id, $notification_message, 'wppb-notice notice notice-warning is-dismissible', false );
1074
1075 }
1076
1077 // Make sure the reCAPTCHA field score threshold is set correctly
1078 function wppb_check_recaptcha_fields_settings( $values ) {
1079 if( isset( $values['field'] ) && $values['field'] == 'reCAPTCHA' ) {
1080 if ( empty( $values['score-threshold'] ) || $values['score-threshold'] < 0 || $values['score-threshold'] > 1 ) {
1081 $values['score-threshold'] = 0.5;
1082 }
1083 }
1084
1085 return $values;
1086 }
1087 add_action( 'wck_update_meta_filter_values_wppb_manage_fields', 'wppb_check_recaptcha_fields_settings' );
1088
1089 function wppb_maybe_enable_recaptcha_v3_on_form( $recaptcha_field ){
1090
1091 // Static cache to avoid repeated calculations
1092 static $cache = array();
1093
1094 // Early validation checks
1095 if( empty( $recaptcha_field ) || empty( $recaptcha_field['captcha-pb-forms'] ) )
1096 return false;
1097
1098 $post_id = get_the_ID();
1099 $post = get_post( $post_id );
1100
1101 // Check if post is set, if not return false
1102 if( empty( $post ) || empty( $post->post_content ) )
1103 return false;
1104
1105 // Create cache key based on post ID and captcha forms configuration
1106 $cache_key = md5( $post_id . serialize( $recaptcha_field['captcha-pb-forms'] ) );
1107
1108 // Return cached result if available
1109 if( isset( $cache[ $cache_key ] ) )
1110 return $cache[ $cache_key ];
1111
1112 $wppb_recaptcha_v3 = false;
1113
1114 // Define form configurations for loop processing
1115 $form_configs = array(
1116 'pb_register' => array(
1117 'shortcode_pattern' => '[wppb-register',
1118 'block_name' => 'wppb/register',
1119 'other_forms' => array(
1120 array( 'shortcode' => '[wppb-login', 'block' => 'wppb/login', 'form_type' => 'pb_login' ),
1121 array( 'shortcode' => '[wppb-recover-password', 'block' => 'wppb/recover-password', 'form_type' => 'pb_recover_password' )
1122 )
1123 ),
1124 'pb_login' => array(
1125 'shortcode_pattern' => '[wppb-login',
1126 'block_name' => 'wppb/login',
1127 'other_forms' => array(
1128 array( 'shortcode' => '[wppb-register', 'block' => 'wppb/register', 'form_type' => 'pb_register' ),
1129 array( 'shortcode' => '[wppb-recover-password', 'block' => 'wppb/recover-password', 'form_type' => 'pb_recover_password' )
1130 )
1131 ),
1132 'pb_recover_password' => array(
1133 'shortcode_pattern' => '[wppb-recover-password',
1134 'block_name' => 'wppb/recover-password',
1135 'other_forms' => array(
1136 array( 'shortcode' => '[wppb-register', 'block' => 'wppb/register', 'form_type' => 'pb_register' ),
1137 array( 'shortcode' => '[wppb-login', 'block' => 'wppb/login', 'form_type' => 'pb_login' )
1138 )
1139 )
1140 );
1141
1142 // Process each form type using loop
1143 foreach( $form_configs as $form_type => $config ) {
1144 // Skip if this form type is already enabled in captcha-pb-forms
1145 if( strpos( $recaptcha_field['captcha-pb-forms'], $form_type ) !== false )
1146 continue;
1147
1148 // Check if current form type exists on the page
1149 $current_form_exists = ( strpos( $post->post_content, $config['shortcode_pattern'] ) !== false || has_block( $config['block_name'] ) );
1150
1151 if( $current_form_exists ) {
1152 // Check if any other enabled form types also exist on the page
1153 foreach( $config['other_forms'] as $other_form ) {
1154 $other_form_exists = ( strpos( $post->post_content, $other_form['shortcode'] ) !== false || has_block( $other_form['block'] ) );
1155 $other_form_enabled = ( strpos( $recaptcha_field['captcha-pb-forms'], $other_form['form_type'] ) !== false );
1156
1157 if( $other_form_exists && $other_form_enabled ) {
1158 $wppb_recaptcha_v3 = true;
1159 break 2; // Break out of both loops since we found a match
1160 }
1161 }
1162 }
1163 }
1164
1165 // Cache the result
1166 $cache[ $cache_key ] = $wppb_recaptcha_v3;
1167
1168 return $wppb_recaptcha_v3;
1169
1170 }
1171