PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.15.3
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.15.3
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Frontend/Form/FrontendFormManager.php +265 -279 3.1.12.15.3 View file →
@@ -9,9 +9,8 @@
9 9 /**
10 10 * FrontendFormManager class
11 11 */
12 12
13 -use BitCode\BitForm\Admin\Form\AdminFormHandler;
14 13 use BitCode\BitForm\Admin\Form\Helpers;
15 14 use BitCode\BitForm\Core\Database\FormEntryModel;
16 15 use BitCode\BitForm\Core\Form\FormManager;
17 16 use BitCode\BitForm\Core\Form\Validator\FormFieldValidator;
@@ -17,14 +16,15 @@
17 16 use BitCode\BitForm\Core\Form\Validator\FormFieldValidator;
18 17 use BitCode\BitForm\Core\Integration\IntegrationHandler;
19 18 use BitCode\BitForm\Core\Messages\SuccessMessageHandler;
20 19 use BitCode\BitForm\Core\Util\ApiResponse as UtilApiResponse;
20 +use BitCode\BitForm\Core\Util\DateTimeHelper;
21 21 use BitCode\BitForm\Core\Util\HttpHelper;
22 22 use BitCode\BitForm\Core\Util\IpTool;
23 23 use BitCode\BitForm\Core\WorkFlow\WorkFlow;
24 24 use BitCode\BitForm\Core\WorkFlow\WorkFlowHandler;
25 25 use BitCode\BitForm\Frontend\Form\View\FormViewer;
26 -use BitCode\BitForm\GlobalHelper;
26 +use BitCode\BitFormPro\Admin\FormSettings\FormAbandonment;
27 27 use WP_Error;
28 28
29 29 final class FrontendFormManager extends FormManager
30 30 {
@@ -32,9 +32,8 @@
32 32 private $_form_token;
33 33 private $_form_id;
34 34 private $_work_flows;
35 35 private $_conf_messages;
36 - private static $_instance = [];
37 36
38 37 // private $_has_upload = false;
39 38 public function __construct($form_id, $shortCodeCounter = null)
40 39 {
@@ -45,19 +44,8 @@
45 44 $this->_form_token = wp_create_nonce('bitforms_' . $form_id);
46 45 $this->_form_id = $form_id;
47 46 }
48 47
49 - public static function getInstance($form_id, $shortCodeCounter = null)
50 - {
51 - $key = $form_id . ':' . ($shortCodeCounter ?? 'default');
52 -
53 - if (!isset(self::$_instance[$key])) {
54 - self::$_instance[$key] = new self($form_id, $shortCodeCounter);
55 - }
56 -
57 - return self::$_instance[$key];
58 - }
59 -
60 48 public function getFormIdentifier()
61 49 {
62 50 return $this->_form_identifier;
63 51 }
@@ -71,8 +59,14 @@
71 59 {
72 60 return $this->_form_token;
73 61 }
74 62
63 + public function isSubmitted()
64 + {
65 + // return isset($_POST[$this->_form_identifier]) ? true : false;
66 + return (isset($_POST['bitforms_id']) && $_POST['bitforms_id'] === $this->_form_identifier) ? true : false;
67 + }
68 +
75 69 public function getSubmittedFields($submitted_data)
76 70 {
77 71 unset($submitted_data[$this->_form_identifier]);
78 72 // unset($submitted_data['bit-form-submit-btn']);
@@ -127,9 +121,9 @@
127 121 if ('button' === $fieldType) {
128 122 continue;
129 123 }
130 124 $fileUploadFieldTypes = ['file-up', 'advanced-file-up'];
131 - if ('decision-box' === $fieldType || 'gdpr' === $fieldType) {
125 + if ('decision-box' === $fieldType) {
132 126 continue;
133 127 }
134 128 $isFileType = in_array($fieldType, $fileUploadFieldTypes);
135 129 if ($this->isRepeatedField($key)) {
@@ -188,47 +182,13 @@
188 182 }
189 183 return $parameter;
190 184 }
191 185
192 - private function getFormFields($formID)
193 - {
194 - $adminFormHandler = new AdminFormHandler();
195 - $post = new \stdClass();
196 - $post = (object) [
197 - 'id' => $formID
198 - ];
199 - $getForm = $adminFormHandler->getAForm('', $post);
200 - $formContainer = $getForm['form_content'];
201 -
202 - return $formContainer['fields'];
203 - }
204 -
205 - private function transformDrpdwnValue($post)
206 - {
207 - $formFields = $this->getFormFields($this->_form_id);
208 -
209 - foreach ($post as $key => $value) {
210 - if (!str_starts_with($key, 'repeater') && isset($formFields->{$key}) && 'select' === $formFields->{$key}->typ) {
211 - if (is_array($value)) {
212 - foreach ($value as $k => $v) {
213 - $post[$key][$k] = !is_array($v) && is_string($v) ? explode(BITFORMS_BF_SEPARATOR, $v) : $v;
214 - }
215 - } else {
216 - $post[$key] = explode(BITFORMS_BF_SEPARATOR, $value);
217 - }
218 - };
219 - }
220 -
221 - return $post;
222 - }
223 -
224 186 public function handleSubmission()
225 187 {
226 - // CSRF verified via verifySubmissionNonce() before this method is called. All $_POST reads below occur after that verification.
227 188 $this->fieldNameReplaceOfPost();
228 189
229 190 $validated = $this->beforeSubmittedValidate();
230 -
231 191 $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);
232 192
233 193 if (true === $validated) {
234 194 do_action('bitform_validation_success', $this->_form_id);
@@ -237,30 +197,30 @@
237 197 $redirectPage = '';
238 198 $regSuccMsg = '';
239 199
240 200 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
241 - $unslashed_post = wp_unslash($_POST);
242 201 if (!is_wp_error($existAuth) && count($existAuth) > 0) {
243 202 $parameter = $this->getParams();
244 - $existAuthFilter = has_filter('bitform_wp_user_auth');
203 + $existAuthFilter = has_filter('bf_wp_user_auth');
245 204
246 205 if (true === $existAuthFilter) {
247 - $result = apply_filters('bitform_wp_user_auth', $existAuth[0], $unslashed_post, $parameter);
206 + $result = apply_filters('bf_wp_user_auth', $existAuth[0], $_POST, $parameter);
248 207
249 - $result = apply_filters('bitform_filter_wp_user_auth_response', $result, $this->_form_id, $unslashed_post, $parameter);
208 + $result = apply_filters('bitform_filter_wp_user_auth_response', $result, $_POST, $parameter);
250 209
251 - do_action('bitform_wp_user_auth_response', $result, $this->_form_id, $unslashed_post, $parameter);
210 + do_action('bitform_wp_user_auth_response', $result, $_POST, $parameter);
252 211
253 212 if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
254 213 if (!$result['success']) {
255 - return new WP_Error('errors', esc_html($result['message']));
214 + return new WP_Error('errors', __($result['message'], 'bit-form'));
256 215 } elseif (isset($result['success'])) {
257 216 $redirectPage = $result['redirectPage'];
258 217 $regSuccMsg = $result['message'];
218 + $newNonce = wp_create_nonce('bitforms_' . $this->_form_id);
259 219 }
260 220 } else {
261 221 if (!$result['success']) {
262 - return new WP_Error('errors', esc_html($result['message']));
222 + return new WP_Error('errors', __($result['message'], 'bit-form'));
263 223 } else {
264 224 return $result;
265 225 }
266 226 }
@@ -266,23 +226,30 @@
266 226 }
267 227 }
268 228 }
269 229
270 - $saveResponse = $this->saveFormEntry($unslashed_post);
230 + $saveResponse = $this->saveFormEntry($_POST);
271 231 if (is_wp_error($saveResponse)) {
272 232 return $saveResponse;
273 233 }
274 234
275 235 $entryID = $saveResponse['entry_id'];
236 + do_action('bitform_submit_success', $this->_form_id, $entryID, $_POST);
276 237
277 - // transformed dropdown value from string to array
278 - $newPost = $this->transformDrpdwnValue($unslashed_post);
279 - $filesData = GlobalHelper::sanitize_files_input($_FILES);
280 - do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $filesData);
238 + // check and replace signature field value
239 + $formFields = $this->getFields();
240 + $uploadPath = BITFORMS_UPLOAD_BASE_URL . "/uploads/{$this->_form_id}/{$entryID}";
281 241
242 + foreach ($formFields as $key => $field) {
243 + if ('signature' === $field['type']) {
244 + $saveResponse['fields'][$key] = $uploadPath . '/' . $saveResponse['fields'][$key];
245 + break;
246 + }
247 + }
248 +
282 249 $captchaV3Settings = $this->getCaptchaV3Settings();
283 250 if ($captchaV3Settings) {
284 - $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
251 + $token = $_POST['g-recaptcha-response'];
285 252 $integrationHandler = new IntegrationHandler(0);
286 253 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
287 254 if (!is_wp_error($allFormIntegrations)) {
288 255 foreach ($allFormIntegrations as $integration) {
@@ -312,14 +279,16 @@
312 279 }
313 280 if (!empty($regSuccMsg) && isset($saveResponse['dflt_message'])) {
314 281 $saveResponse['message'] = $regSuccMsg;
315 282 }
316 - $saveResponse['new_nonce'] = wp_create_nonce('bitforms_' . $this->_form_id);
317 283
318 284 $saveResponse = IntegrationHandler::maybeSetCronForIntegration($saveResponse, 'create');
319 285 $entryId = $saveResponse['entry_id'];
320 286
321 287 $responseMsg = is_array($saveResponse) && !empty($saveResponse) ? $saveResponse : __('Form Submitted Successfully', 'bit-form');
288 + if (isset($newNonce)) {
289 + $responseMsg['new_nonce'] = $newNonce;
290 + }
322 291 $_POST = [];
323 292 $responseMsg['entry_id'] = $entryId;
324 293 return $responseMsg;
325 294 }
@@ -328,15 +297,14 @@
328 297 }
329 298
330 299 public function handleUpdateEntry()
331 300 {
332 - // Entry token or capability verified by caller (FrontendAjax::update_entry). All $_POST reads occur after that check.
333 301 $this->fieldNameReplaceOfPost();
334 302 $validated = $this->beforeSubmittedValidate();
335 303 $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);
336 304
337 - $entryID = isset($_REQUEST['entryID']) ? sanitize_text_field(wp_unslash($_REQUEST['entryID'])) : null;
338 - $GLOBALS['bitform_entry_id'] = $entryID;
305 + $entryID = $_REQUEST['entryID'];
306 + $GLOBALS['bf_entry_id'] = $entryID;
339 307 if (is_null($entryID)) {
340 308 return new WP_Error('empty_form', __('Entries id is invalid', 'bit-form'));
341 309 }
342 310 if (true === $validated) {
@@ -344,28 +312,28 @@
344 312 unset($_POST['hidden_fields'], $_POST['entryID']);
345 313
346 314 $redirectPage = '';
347 315 $regSuccMsg = '';
348 - $postData = wp_unslash($_POST);
349 316
350 317 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
351 318 if (!is_wp_error($existAuth) && count($existAuth) > 0) {
352 319 $parameter = $this->getParams();
353 - $existAuthFilter = has_filter('bitform_wp_user_auth');
320 + $existAuthFilter = has_filter('bf_wp_user_auth');
354 321
355 322 if (true === $existAuthFilter) {
356 - $result = apply_filters('bitform_wp_user_auth', $existAuth[0], $postData, $parameter);
323 + $result = apply_filters('bf_wp_user_auth', $existAuth[0], $_POST, $parameter);
357 324
358 325 if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
359 326 if (!$result['success']) {
360 - return new WP_Error('errors', esc_html($result['message']));
327 + return new WP_Error('errors', __($result['message'], 'bit-form'));
361 328 } elseif (isset($result['success'])) {
362 329 $redirectPage = $result['redirectPage'];
363 330 $regSuccMsg = $result['message'];
331 + $newNonce = wp_create_nonce('bitforms_' . $this->_form_id);
364 332 }
365 333 } else {
366 334 if (!$result['success']) {
367 - return new WP_Error('errors', esc_html($result['message']));
335 + return new WP_Error('errors', __($result['message'], 'bit-form'));
368 336 } else {
369 337 return $result;
370 338 }
371 339 }
@@ -371,24 +339,18 @@
371 339 }
372 340 }
373 341 }
374 342
375 - $updateResponse = $this->updateFormEntry(wp_unslash($_POST), $this->getFormID(), $entryID);
343 + $updateResponse = $this->updateFormEntry($_POST, $this->getFormID(), $entryID);
376 344 if (is_wp_error($updateResponse)) {
377 345 return $updateResponse;
378 346 }
379 347
380 - // transformed dropdown value from string to array
381 - $newPost = $this->transformDrpdwnValue($postData);
382 - $filesData = GlobalHelper::sanitize_files_input($_FILES);
348 + do_action('bitform_submit_success', $this->_form_id, $entryID, $_POST);
383 349
384 - //TO DO:: submit success action temporarily added for solution of a issue
385 - do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $filesData);
386 - do_action('bitform_update_success', $this->_form_id, $entryID, $newPost, $filesData);
387 -
388 350 $captchaV3Settings = $this->getCaptchaV3Settings();
389 351 if ($captchaV3Settings) {
390 - $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
352 + $token = $_POST['g-recaptcha-response'];
391 353 $integrationHandler = new IntegrationHandler(0);
392 354 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
393 355 if (!is_wp_error($allFormIntegrations)) {
394 356 foreach ($allFormIntegrations as $integration) {
@@ -418,14 +380,15 @@
418 380 }
419 381 if (!empty($regSuccMsg) && isset($updateResponse['dflt_message'])) {
420 382 $updateResponse['message'] = $regSuccMsg;
421 383 }
422 - $updateResponse['new_nonce'] = wp_create_nonce('bitforms_' . $this->_form_id);
423 - $updateResponse = IntegrationHandler::maybeSetCronForIntegration($updateResponse, 'update');
384 + $updateResponse = IntegrationHandler::maybeSetCronForIntegration($updateResponse, 'create');
424 385 $entryId = $updateResponse['entry_id'];
425 386
426 387 $responseMsg = is_array($updateResponse) && !empty($updateResponse) ? $updateResponse : __('Entry Update Successfully', 'bit-form');
427 -
388 + if (isset($newNonce)) {
389 + $responseMsg['new_nonce'] = $newNonce;
390 + }
428 391 $_POST = [];
429 392 $responseMsg['entry_id'] = $entryId;
430 393 return $responseMsg;
431 394 }
@@ -449,9 +412,9 @@
449 412 }
450 413 return $submitted_data;
451 414 }
452 415
453 - public function beforeSubmittedValidate($verifyCaptcha = true)
416 + public function beforeSubmittedValidate()
454 417 {
455 418 if ($this->verifySubmissionNonce()) {
456 419 if ($this->isExist()) {
457 420 $isRestricted = $this->checkSubmissionRestriction();
@@ -457,31 +420,21 @@
457 420 $isRestricted = $this->checkSubmissionRestriction();
458 421 if ($isRestricted && !empty($isRestricted)) {
459 422 return new WP_Error('spam_detection', $isRestricted[0]);
460 423 }
461 - $postData = wp_unslash($_POST);
462 - $filesData = GlobalHelper::sanitize_files_input($_FILES);
463 - $isHoneypot = apply_filters('bitform_check_honeypot', false, $this->_form_id, $postData);
464 - if ($isHoneypot) {
424 + if ($this->isTrappedInHoneypot()) {
465 425 return new WP_Error('spam_detection', __('Token verification failed', 'bit-form'));
466 426 }
467 - $formCurrentStep = isset($_POST['form-current-step']) ? sanitize_text_field(wp_unslash($_POST['form-current-step'])) : null;
468 - // TODO: Temporary parameter to skip captcha verification in step change of multi step form
469 - if ($verifyCaptcha) {
470 - $verifyGRecaptchaResult = $this->verifyGRecaptcha();
471 - if (is_wp_error($verifyGRecaptchaResult)) {
472 - return $verifyGRecaptchaResult;
473 - }
474 - $verifyHCaptchaResult = $this->verifyHCaptcha();
475 - if (is_wp_error($verifyHCaptchaResult)) {
476 - return $verifyHCaptchaResult;
477 - }
478 - /* Implement Turnstile Captcha start */
479 - $verifyTurnstileCaptchaResult = $this->verifyTurnstileCaptcha();
480 - if (is_wp_error($verifyTurnstileCaptchaResult)) {
481 - return $verifyTurnstileCaptchaResult;
482 - }
427 + $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
428 +
429 + if (is_wp_error($this->verifyGRecaptcha())) {
430 + return $this->verifyGRecaptcha();
483 431 }
432 +
433 + /* Implement Turnstile Captcha start */
434 + if (is_wp_error($this->verifyTurnstileCaptcha())) {
435 + return $this->verifyTurnstileCaptcha();
436 + }
484 437 /* Implement Turnstile Captcha end */
485 438
486 439 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
487 440
@@ -489,14 +442,14 @@
489 442 do_action('bitform_checked_exist_auth', $this->_form_id, $existAuth);
490 443 if (!is_wp_error($existAuth) && count($existAuth) > 0 && is_user_logged_in()) {
491 444 return new WP_Error('auth_error', __('You are already logged in', 'bit-form'));
492 445 }
493 - $validateForm = $this->validateFormSubmission($postData);
494 - $validateFormFiles = $this->validateFormSubmission($filesData);
446 + $validateForm = $this->validateFormSubmission($_POST);
447 + $validateFormFiles = $this->validateFormSubmission($_FILES);
495 448 $validateForm = array_merge($validateForm, $validateFormFiles);
496 449 $form_fields = $this->getFields();
497 450 // check if form-current-step is set and form is multi-step
498 - $formCurrentStep = isset($_POST['form-current-step']) ? sanitize_text_field(wp_unslash($_POST['form-current-step'])) : null;
451 + $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
499 452 if (!is_null($formCurrentStep)) {
500 453 $formContents = $this->getFormContent();
501 454 $layout = $formContents->layout;
502 455 $stepIndex = (int) $formCurrentStep - 1;
@@ -515,13 +468,13 @@
515 468 $step_fields[$fk] = $form_fields[$fk];
516 469 }
517 470 $form_fields = $step_fields;
518 471 }
519 - $formFieldValidator = new FormFieldValidator($form_fields, $postData, $filesData);
472 + $formFieldValidator = new FormFieldValidator($form_fields, $_POST, $_FILES);
520 473 $validUniuqFields = [];
521 - $existFilter = has_filter('bitform_check_duplicate_entry');
474 + $existFilter = has_filter('bf_check_duplicate_entry');
522 475 if (true === $existFilter) {
523 - $validUniuqFields = apply_filters('bitform_check_duplicate_entry', $form_fields, $postData);
476 + $validUniuqFields = apply_filters('bf_check_duplicate_entry', $form_fields, $_POST);
524 477
525 478 $fieldKeys = array_keys($validUniuqFields);
526 479 $form_fields_keys = array_keys($form_fields);
527 480 $uniqueFields = [];
@@ -529,9 +482,9 @@
529 482 if (in_array($key, $form_fields_keys)) {
530 483 $uniqueFields[] = $form_fields[$key];
531 484 }
532 485 }
533 - do_action('bitform_Unique_entry', $uniqueFields, $validUniuqFields, $this->_form_id, $postData);
486 + do_action('bitform_Unique_entry', $uniqueFields, $validUniuqFields, $this->_form_id, $_POST);
534 487 }
535 488 $validateField = $formFieldValidator->validate('create', $this->_form_id);
536 489
537 490 if ($validateForm && $validateField && 0 === count($validUniuqFields)) {
@@ -558,11 +511,11 @@
558 511 {
559 512 $captchaSettings = $this->getCaptchaSettings();
560 513 $captchaV3Settings = $this->getCaptchaV3Settings();
561 514 if ($captchaSettings || $captchaV3Settings) {
562 - $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
515 + $token = $_POST['g-recaptcha-response'];
563 516 if (!isset($_POST['g-recaptcha-response'])) {
564 - return new WP_Error('spam_detection', __('Please recheck your reCaptcha Configuration', 'bit-form'));
517 + return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
565 518 }
566 519 $integrationHandler = new IntegrationHandler(0);
567 520 $allFormIntegrations = $integrationHandler->getAllIntegration('app', $captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3');
568 521 if (!is_wp_error($allFormIntegrations)) {
@@ -586,9 +539,12 @@
586 539 && !empty($gRecaptchaResponse->score)
587 540 && ((float) $gRecaptchaResponse->score < (float) $captchaV3Settings->score)
588 541 ) {
589 542 wp_send_json_error(
590 - sanitize_text_field((string) $captchaV3Settings->message)
543 + __(
544 + $captchaV3Settings->message,
545 + 'bit-form'
546 + )
591 547 );
592 548 }
593 549
594 550 $isgReCaptchaVerified = $gRecaptchaResponse->success;
@@ -599,61 +555,16 @@
599 555 }
600 556 }
601 557 }
602 558
603 - private function verifyHCaptcha()
604 - {
605 - $hCaptchaExist = $this->isFieldTypeExist('hcaptcha'); // You can rename this to getHCaptchaSettings() if needed
606 - if ($hCaptchaExist) {
607 - if (!isset($_POST['h-captcha-response'])) {
608 - return new WP_Error('spam_detection', __('Please verify hCaptcha', 'bit-form'));
609 - }
610 -
611 - $token = sanitize_text_field(wp_unslash($_POST['h-captcha-response']));
612 -
613 - $integrationHandler = new IntegrationHandler(0);
614 - $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'hcaptcha');
615 -
616 - if (!is_wp_error($allFormIntegrations)) {
617 - foreach ($allFormIntegrations as $integration) {
618 - if (!is_null($integration->integration_type) && 'hcaptcha' === $integration->integration_type) {
619 - $integrationDetails = json_decode($integration->integration_details);
620 - $integrationDetails->id = $integration->id;
621 - $hCaptcha = $integrationDetails;
622 - }
623 - }
624 - }
625 -
626 - if (!empty($hCaptcha->secretKey)) {
627 - $hCaptchaResponse = HttpHelper::post(
628 - 'https://api.hcaptcha.com/siteverify',
629 - [
630 - 'secret' => $hCaptcha->secretKey,
631 - 'response' => $token,
632 - 'remoteip' => (isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '')
633 - ]
634 - );
635 -
636 - $isVerified = false;
637 - if (!is_wp_error($hCaptchaResponse)) {
638 - $isVerified = $hCaptchaResponse->success;
639 - }
640 -
641 - if (!$isVerified) {
642 - return new WP_Error('spam_detection', __('hCaptcha verification failed', 'bit-form'));
643 - }
644 - }
645 - }
646 - }
647 -
648 559 private function verifyTurnstileCaptcha()
649 560 {
650 - $turnstileExist = $this->isFieldTypeExist('turnstile');
651 - if ($turnstileExist) {
561 + $turnstileSetting = $this->getTurnstileSettings();
562 + if ($turnstileSetting) {
652 563 if (!isset($_POST['cf-turnstile-response'])) {
653 564 return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
654 565 }
655 - $token = sanitize_text_field(wp_unslash($_POST['cf-turnstile-response']));
566 + $token = $_POST['cf-turnstile-response'];
656 567 $turnstileCaptcha = null;
657 568 $integrationHandler = new IntegrationHandler(0);
658 569 $turnstileIntegration = $integrationHandler->getAllIntegration('app', 'turnstileCaptcha')[0];
659 570 if (!is_wp_error($turnstileIntegration && !is_null($turnstileIntegration->integration_type))) {
@@ -668,14 +579,12 @@
668 579 ['secret' => $turnstileCaptcha->secretKey, 'response' => $token]
669 580 );
670 581 if (!is_wp_error($turnstileRecaptchaResponse)) {
671 582 if (!$turnstileRecaptchaResponse->success) {
672 - $errorCodes = implode(', ', (array) ($turnstileRecaptchaResponse->{'error-codes'} ?? []));
673 583 wp_send_json_error(
674 - sprintf(
675 - /* translators: %s: dynamic value. */
676 - __('Cloudflare Turnstile Validation Error: %s', 'bit-form'),
677 - $errorCodes
584 + __(
585 + 'Cloudflare Turnstile Validation Error: ' . implode(', ', $turnstileRecaptchaResponse->{'error-codes'}),
586 + 'bit-form'
678 587 )
679 588 );
680 589 }
681 590
@@ -689,13 +598,13 @@
689 598 }
690 599
691 600 public function verifySubmissionNonce()
692 601 {
693 - if (!isset($_POST['t_identity']) || !isset($_POST['csrf'])) {
602 + if (!isset($_POST['t_identity']) && !isset($_POST['csrf'])) {
694 603 return false;
695 604 }
696 - $tIdenty = sanitize_text_field(wp_unslash($_POST['t_identity']));
697 - $csrf = sanitize_text_field(wp_unslash($_POST['csrf']));
605 + $tIdenty = sanitize_text_field($_POST['t_identity']);
606 + $csrf = sanitize_text_field($_POST['csrf']);
698 607 unset($_POST['t_identity'], $_POST['action'], $_POST['bitforms_id'], $_POST['csrf']);
699 608 return Helpers::csrfDecrypted($tIdenty, $csrf);
700 609 }
701 610
@@ -715,106 +624,152 @@
715 624
716 625 public function checkSubmissionRestriction($checkedEmptySubmitted = true)
717 626 {
718 627 $formContents = $this->getFormContent();
719 - $additionalSettings = isset($formContents->additional) ? $formContents->additional : null;
720 - $fromRestrictionSetitingsEnabled = empty($additionalSettings->enabled) ? [] : $additionalSettings->enabled;
721 - $fromRestrictionSetitings = empty($additionalSettings->settings) ? null : $additionalSettings->settings;
722 -
723 - if (is_null($additionalSettings) || is_null($fromRestrictionSetitings) || empty((array) $fromRestrictionSetitingsEnabled)) {
628 + $fromRestrictionSetitingsEnabled = empty($formContents->additional->enabled) ? [] : $formContents->additional->enabled;
629 + $fromRestrictionSetitings = empty($formContents->additional->settings) ? null : $formContents->additional->settings;
630 + if (is_null($formContents->additional->enabled) || is_null($formContents->additional->settings)) {
724 631 return false;
725 632 }
726 -
727 633 $restrictionMessage = [];
728 634 $ipTool = new IpTool();
729 635 $ipAddress = $ipTool->getIP();
730 - $currentUserId = get_current_user_id();
731 -
732 636 foreach ($fromRestrictionSetitingsEnabled as $restrictionKey => $isEnabled) {
733 637 if ($isEnabled) {
734 - /**
735 - * Allow add-ons to handle any restriction key (Pro-only restrictions
736 - * should be implemented in the add-on, not shipped in the free plugin).
737 - *
738 - * Return a non-null string to block submission.
739 - */
740 - $addonMsg = apply_filters(
741 - 'bitform_submission_restriction',
742 - null,
743 - $restrictionKey,
744 - $this->form_id,
745 - $fromRestrictionSetitingsEnabled,
746 - $fromRestrictionSetitings,
747 - $ipAddress,
748 - $currentUserId
749 - );
750 -
751 - if (!is_null($addonMsg) && '' !== $addonMsg) {
752 - $restrictionMessage[] = $addonMsg;
753 - continue;
638 + if ('entry_limit' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
639 + $formEntry = new FormEntryModel();
640 + $countResult = $formEntry->count(
641 + [
642 + 'form_id' => $this->form_id
643 + ]
644 + );
645 + $count = !empty($countResult[0]) && !empty($countResult[0]->count) ? $countResult[0]->count : false;
646 + if ($count && $count >= intval($fromRestrictionSetitings->{$restrictionKey})) {
647 + $restrictionMessage[] = __('Sorry!! Entry limit exceeded', 'bit-form');
648 + }
754 649 }
755 -
756 650 if ('onePerIp' === $restrictionKey) {
757 651 $formEntry = new FormEntryModel();
758 -
759 - $getResult = $formEntry->get(
760 - ['user_ip', 'status'],
652 + $countResult = $formEntry->count(
761 653 [
762 654 'form_id' => $this->form_id,
763 - 'user_ip' => (int) ip2long((string) $ipAddress)
764 - ],
655 + 'user_ip' => ip2long($ipAddress)
656 + ]
765 657 );
658 + $count = !empty($countResult[0]) && !empty($countResult[0]->count) ? $countResult[0]->count : false;
766 659
767 - $count = 0;
768 - $status = 0;
660 + if ($count && $count > 0) {
661 + $restrictionMessage[] = __('Sorry!! You have already submitted', 'bit-form');
662 + }
663 + }
664 + if ('is_login' === $restrictionKey && 0 === get_current_user_id()) {
665 + $restrictionMessage[] = __($fromRestrictionSetitings->is_login->message, 'bit-form');
666 + }
667 + if ($checkedEmptySubmitted && 'empty_submission' === $restrictionKey) {
668 + $isEmpty = $this->checkEmptySubmission($_POST, $_FILES);
669 + if ($isEmpty) {
670 + $restrictionMessage[] = __($fromRestrictionSetitings->empty_submission->message, 'bit-form');
671 + }
672 + }
673 + if ('restrict_form' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
674 + $day = empty($fromRestrictionSetitings->{$restrictionKey}->day) ? null : $fromRestrictionSetitings->{$restrictionKey}->day;
675 + $date = empty($fromRestrictionSetitings->{$restrictionKey}->date) ? null : $fromRestrictionSetitings->{$restrictionKey}->date;
676 + $time = empty($fromRestrictionSetitings->{$restrictionKey}->time) ? null : $fromRestrictionSetitings->{$restrictionKey}->time;
769 677
770 - if (!is_wp_error($getResult) && count($getResult) > 0) {
771 - $count = count($getResult);
772 -
773 - foreach ($getResult as $row) {
774 - if (9 === (int) $row->status) {
775 - $status = 9;
776 - break;
678 + $isdayOk = $isdateOk = $istimeOk = true;
679 + $dayNotOkMsg = $dateNotOkMsg = $timeNotOkMsg = '';
680 + $dateTimeHelper = new DateTimeHelper();
681 + if (
682 + !empty($day)
683 + && is_array($day)
684 + && (in_array('Friday', $day)
685 + || in_array('Saturday', $day)
686 + || in_array('Sunday', $day)
687 + || in_array('Monday', $day)
688 + || in_array('Tuesday', $day)
689 + || in_array('Wednesday', $day)
690 + || in_array('Thursday', $day))
691 + && (!in_array($dateTimeHelper->getDay('full-name'), $day))
692 + ) {
693 + $isdayOk = false;
694 + $dayMsgVarsFormat = '';
695 + foreach ($day as $dayIndex => $dayValue) {
696 + if ($dayIndex > 0) {
697 + $dayMsgVarsFormat .= ', ';
777 698 }
699 + $dayMsgVarsFormat .= '%s';
778 700 }
701 + $dayNotOkMsg = vsprintf(__("in $dayMsgVarsFormat", 'bit-form'), $day);
779 702 }
703 + if (
704 + !empty($day)
705 + && is_array($day)
706 + && (in_array('Custom', $day))
707 + ) {
708 + $startDate = empty($date->from) ? '00-00-0000' : $date->from;
709 + $endDate = empty($date->to) ? '00-00-0000' : $date->to;
710 + $dateFormat = preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $startDate) ? 'Y-m-d' : 'm-d-Y';
711 + if (!empty($date->from) && false !== strpos($startDate, 'T')) {
712 + $startDate = $dateTimeHelper->getDate($startDate, false, null, $dateFormat);
713 + }
714 + if (!empty($date->to) && false !== strpos($endDate, 'T')) {
715 + $endDate = $dateTimeHelper->getDate($endDate, false, null, $dateFormat);
716 + }
717 + $currentDate = $dateTimeHelper->getDate(null, null, null, $dateFormat);
718 + if (!($currentDate >= $startDate && $currentDate <= $endDate)) {
719 + $isdateOk = false;
720 + $dateNotOkMsg = sprintf(__('within %s to %s', 'bit-form'), $startDate, $endDate);
721 + }
722 + }
780 723
781 - if ($count > 0 && 9 !== (int) $status) {
782 - $onePerIp = __('Sorry!! You have already submitted from this IP address', 'bit-form');
724 + if (!empty($time)) {
725 + $startTime = empty($time->from) ? '00:00' : $time->from;
726 + $endTime = empty($time->to) ? '23:59.999' : $time->to;
727 + $currentTime = $dateTimeHelper->getTime(null, null, null, 'H:i');
728 + if (!($currentTime >= $startTime && $currentTime <= $endTime)) {
729 + $istimeOk = false;
730 + $startTime = $dateTimeHelper->getTime($startTime, 'H:i', null);
731 + $endTime = $dateTimeHelper->getTime($endTime, 'H:i', null);
732 + $isTimeOk = false;
733 + $timeNotOkMsg = sprintf(__('%s to %s', 'bit-form'), $startTime, $endTime);
734 + }
735 + }
783 736
784 - $onePerIp = apply_filters(
785 - 'bitform_filter_restriction_one_per_ip_message',
786 - $onePerIp,
787 - $this->form_id
788 - );
789 -
790 - $restrictionMessage[] = $onePerIp;
737 + if (!($isdateOk && $isdayOk && $istimeOk)) {
738 + if (!$isdayOk) {
739 + $restrictionMessage[] = !empty($timeNotOkMsg) ? sprintf(__('Form is available %s From %s', 'bit-form'), $dayNotOkMsg, $timeNotOkMsg) :
740 + sprintf(__('Form is available %s', 'bit-form'), $dayNotOkMsg, $timeNotOkMsg);
741 + } elseif (!$isdateOk) {
742 + $restrictionMessage[] = !empty($timeNotOkMsg) ? sprintf(__('Form is available %s From %s', 'bit-form'), $dateNotOkMsg, $timeNotOkMsg) :
743 + sprintf(__('Form is available %s', 'bit-form'), $dateNotOkMsg, $timeNotOkMsg);
744 + } elseif (!$istimeOk) {
745 + $restrictionMessage[] = sprintf(__('Form is available on %s', 'bit-form'), $timeNotOkMsg);
746 + }
791 747 }
792 748 }
793 - if ('is_login' === $restrictionKey && 0 === get_current_user_id()) {
794 - $is_login_messages = $fromRestrictionSetitings->is_login->message;
795 -
796 - $is_login_messages = apply_filters(
797 - 'bitform_filter_restriction_is_login_message',
798 - $is_login_messages,
799 - $this->form_id
800 - );
801 -
802 - $restrictionMessage[] = $is_login_messages;
749 + if ('blocked_ip' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
750 + $isIpBlocked = false;
751 + foreach ($fromRestrictionSetitings->{$restrictionKey} as $ipIndex => $ipDetails) {
752 + if (!empty($ipDetails->status) && $ipDetails->status && !empty($ipDetails->ip) && $ipDetails->ip === $ipAddress) {
753 + $isIpBlocked = true;
754 + break;
755 + }
756 + }
757 + if ($isIpBlocked) {
758 + $restrictionMessage[] = sprintf(__('Sorry!! Your IP address is %s, Blocked from submitting the form', 'bit-form'), $ipAddress);
759 + }
803 760 }
804 - if ($checkedEmptySubmitted && 'empty_submission' === $restrictionKey) {
805 - $isEmpty = $this->checkEmptySubmission(wp_unslash($_POST), GlobalHelper::sanitize_files_input($_FILES));
806 - if ($isEmpty) {
807 - $restriction = $fromRestrictionSetitings->empty_submission->message;
808 -
809 - $restriction = apply_filters(
810 - 'bitform_filter_restriction_empty_submission_message',
811 - $restriction,
812 - $this->form_id
813 - );
814 -
815 - $restrictionMessage[] = $restriction;
761 + if ('private_ip' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
762 + $isIpWhiteListed = false;
763 + foreach ($fromRestrictionSetitings->{$restrictionKey} as $ipIndex => $ipDetails) {
764 + if (!empty($ipDetails->status) && $ipDetails->status && !empty($ipDetails->ip) && $ipDetails->ip === $ipAddress) {
765 + $isIpWhiteListed = true;
766 + break;
767 + }
816 768 }
769 + if (!$isIpWhiteListed) {
770 + $restrictionMessage[] = sprintf(__('Sorry!! Your IP address is %s, Blocked from submitting the form', 'bit-form'), $ipAddress);
771 + }
817 772 }
818 773 }
819 774 }
820 775 return $restrictionMessage;
@@ -820,21 +775,51 @@
820 775 return $restrictionMessage;
821 776 }
822 777
823 778 /**
824 - * Will check if form is submitted by a bot
825 - *
826 - * @return Boolean true - if submitted by bot else false
827 - */
779 + * Will check if form is submitted by a bot
780 + *
781 + * @return Boolean true - if submitted by bot else false
782 + */
828 783 public function isTrappedInHoneypot()
829 784 {
830 - // Honeypot is implemented by add-ons (e.g. Pro) via filter.
831 - return (bool) apply_filters('bitform_check_honeypot', false, $this->_form_id, wp_unslash($_POST));
785 + $isHoneyPot = false;
786 +
787 + if (!$this->isHoneypotActive()) {
788 + return false;
789 + }
790 +
791 + $token = $_POST['b_h_t'];
792 + $pattern = '/^([a-zA-Z0-9]*_[a-zA-Z0-9]*){4}$/';
793 + $decryptedToken = base64_decode(base64_decode($token));
794 +
795 + preg_match($pattern, $decryptedToken, $validToken);
796 +
797 + if ($validToken) {
798 + if (isset($_POST[$token]) && empty($_POST[$token])) {
799 + $isHoneyPot = false;
800 + } else {
801 + $isHoneyPot = true;
802 + }
803 + } else {
804 + $isHoneyPot = true;
805 + }
806 +
807 + if (isset($_POST[$token])) {
808 + unset($_POST[$token]);
809 + }
810 + unset($_POST['b_h_t']);
811 + return $isHoneyPot;
832 812 }
833 813
834 814 public function isHoneypotActive()
835 815 {
836 - return (bool) apply_filters('bitform_is_honeypot_active', false, $this->_form_id, $this->getFormContent());
816 + $formContents = $this->getFormContent();
817 + $enabled = empty($formContents->additional->enabled) ? null : $formContents->additional->enabled;
818 + if (!empty($enabled->honeypot) && $enabled->honeypot) {
819 + return true;
820 + }
821 + return false;
837 822 }
838 823
839 824 public function checkPaymentFields()
840 825 {
@@ -914,15 +899,26 @@
914 899 }
915 900
916 901 public function getFormAbandonmentMessage()
917 902 {
918 - $msg = apply_filters('bitform_form_abandonment_warning_markup', '', $this->form_id);
919 - return is_string($msg) ? $msg : '';
903 + if (class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
904 + $formAbandonmentSettings = FormAbandonment::getFormAbandonmentSettings($this->form_id);
905 + $msg = '';
906 + if (isset($formAbandonmentSettings->showWarningMsg) && $formAbandonmentSettings->showWarningMsg && !empty($formAbandonmentSettings->warningMsg)) {
907 + $msg = $formAbandonmentSettings->warningMsg;
908 + $msg = '<div class="bf-form-msg active warning">' . wp_kses_post($msg) . '</div>';
909 + }
910 + return $msg;
911 + }
920 912 }
921 913
922 914 public function getFormAbandonmentSettings()
923 915 {
924 - return apply_filters('bitform_form_abandonment_settings', null, $this->form_id);
916 + if (class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
917 + $formAbandonmentSettings = FormAbandonment::getFormAbandonmentSettings($this->form_id);
918 + return $formAbandonmentSettings;
919 + }
920 + return null;
925 921 }
926 922
927 923 private function messageMarkup($msg)
928 924 {
@@ -929,31 +925,21 @@
929 925 $msgId = $msg->id;
930 926 $msgConfig = json_decode($msg->message_config);
931 927 $scrollClass = 'below' === $msgConfig->msgType ? 'scroll' : '';
932 928
933 - return '<div
934 - role="dialog"
935 - aria-hidden="true"
936 - data-modal-backdrop="true"
937 - class="' . $this->getAtomicCls("msg-container-{$msgId}") . ' deactive ' . $scrollClass . '">
938 - <div
939 - data-contentid="' . $this->getFormIdentifier() . '"
940 - data-msgid="' . $msgId . '"
941 - role="button"
942 - class="' . $this->getAtomicCls("msg-background-{$msgId}") . ' msg-backdrop">
943 - <div class="bf-msg-content ' . $this->getAtomicCls("msg-content-{$msgId}") . '">
944 - <button
945 - data-contentid="' . $this->getFormIdentifier() . '"
946 - data-msgid="' . $msgId . '"
947 - class="' . $this->getAtomicCls("close-{$msgId}") . ' bf-msg-close"
948 - type="button">
949 - <svg class="' . $this->getAtomicCls("close-icn-{$msgId}") . '" viewBox="0 0 30 30">
950 - <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="4" y1="3.88" x2="26" y2="26.12"></line>
951 - <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="26" y1="3.88" x2="4" y2="26.12"></line>
952 - </svg>
953 - </button>
954 - <div class="msg-content"></div>
955 - </div>
956 - </div>
957 - </div>';
929 + return <<<SUCCESSMSG
930 + <div role="dialog" aria-hidden="true" data-modal-backdrop="true" class="{$this->getAtomicCls("msg-container-{$msgId}")} deactive {$scrollClass}">
931 + <div data-contentid="{$this->getFormIdentifier()}" data-msgid="{$msgId}" role="button" class="{$this->getAtomicCls("msg-background-{$msgId}")} msg-backdrop">
932 + <div class="bf-msg-content {$this->getAtomicCls("msg-content-{$msgId}")}">
933 + <button data-contentid="{$this->getFormIdentifier()}" data-msgid="{$msgId}" class="{$this->getAtomicCls("close-{$msgId}")} bf-msg-close" type="button">
934 + <svg class="{$this->getAtomicCls("close-icn-{$msgId}")}" viewBox="0 0 30 30">
935 + <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="4" y1="3.88" x2="26" y2="26.12"></line>
936 + <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="26" y1="3.88" x2="4" y2="26.12"></line>
937 + </svg>
938 + </button>
939 + <div class="msg-content"></div>
940 + </div>
941 + </div>
942 + </div>
943 +SUCCESSMSG;
958 944 }
959 945 }