PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / V3.3.2
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder vV3.3.2
V3.3.2 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 All 139 releases
← All changes | includes/Frontend/Form/FrontendFormManager.php +258 -202 3.3.1 → V3.3.2 View file →
@@ -22,8 +22,9 @@
22 22 use BitCode\BitForm\Core\Util\FieldValueHandler;
23 23 use BitCode\BitForm\Core\Util\FrontendHelpers;
24 24 use BitCode\BitForm\Core\Util\HttpHelper;
25 25 use BitCode\BitForm\Core\Util\IpTool;
26 +use BitCode\BitForm\Core\Util\Log;
26 27 use BitCode\BitForm\Core\Util\Utilities;
27 28 use BitCode\BitForm\Core\WorkFlow\WorkFlow;
28 29 use BitCode\BitForm\Frontend\Form\View\FormViewer;
29 30 use BitCode\BitForm\GlobalHelper;
@@ -31,8 +32,11 @@
31 32
32 33 final class FrontendFormManager extends FormManager
33 34 {
34 35 private $_form_identifier;
36 +
37 + /** @var array|null reCAPTCHA v3 verdict, reused for the log */
38 + private $recaptchaV3Verification;
35 39 private $_form_token;
36 40 private $_form_id;
37 41 private $_conf_messages;
38 42 private static $_instance = [];
@@ -189,13 +193,10 @@
189 193 {
190 194 $url = wp_parse_url(wp_get_referer());
191 195 $parameter = [];
192 196 if (isset($url['query'])) {
193 - $queries = explode('&', $url['query']);
194 - foreach ($queries as $query) {
195 - list($field, $value) = explode('=', $query);
196 - $parameter[$field] = $value;
197 - }
197 + // a bare key (?preview) made the old explode() split warn; this also URL-decodes values
198 + wp_parse_str($url['query'], $parameter);
198 199 }
199 200 return $parameter;
200 201 }
201 202
@@ -258,8 +259,9 @@
258 259 if (!is_array($postData)) {
259 260 return $postData;
260 261 }
261 262 $fields = $this->getFields();
263 + $postData = $this->resolveCompositeChildValues($postData, $fields);
262 264 foreach ($fields as $fieldKey => $fieldData) {
263 265 if (
264 266 empty($fieldData['childFields'])
265 267 || !isset($fieldData['type'])
@@ -296,8 +298,52 @@
296 298
297 299 return $postData;
298 300 }
299 301
302 + /**
303 + * Name/Address parts post as one array under the parent key ("parent[first_name]"), so a WP
304 + * auth mapping that points at a part's own key (First Name, Last Name) found nothing. Copy each
305 + * part's value to its key; the parent value is left as it was.
306 + *
307 + * @param array $postData
308 + * @param array $fields getFields() output
309 + *
310 + * @return array
311 + */
312 + private function resolveCompositeChildValues($postData, $fields)
313 + {
314 + foreach ($fields as $fieldKey => $fieldData) {
315 + if (
316 + empty($fieldData['childFields'])
317 + || !isset($fieldData['type'])
318 + || !in_array($fieldData['type'], ['name', 'address'], true)
319 + || !empty($fieldData['repeated'])
320 + || !isset($postData[$fieldKey])
321 + || !is_array($postData[$fieldKey])
322 + ) {
323 + continue;
324 + }
325 + foreach ((array) $fieldData['childFields'] as $childFieldRef) {
326 + $childKey = is_object($childFieldRef) && isset($childFieldRef->fldKey) ? $childFieldRef->fldKey : '';
327 + if (
328 + empty($childKey)
329 + || !isset($fields[$childKey])
330 + || !empty($fields[$childKey]['isDeactive'])
331 + || isset($postData[$childKey])
332 + || !preg_match('/\[([^\]]+)\]$/', (string) ($fields[$childKey]['name'] ?? ''), $matches)
333 + ) {
334 + continue;
335 + }
336 + $value = FieldValueHandler::extractChildValueFromParentValue($postData[$fieldKey], $matches[1], $childKey);
337 + if (null !== $value) {
338 + $postData[$childKey] = $value;
339 + }
340 + }
341 + }
342 +
343 + return $postData;
344 + }
345 +
300 346 public function handleSubmission()
301 347 {
302 348 // CSRF verified via verifySubmissionNonce() before this method is called. All $_POST reads below occur after that verification.
303 349 $this->fieldNameReplaceOfPost();
@@ -355,38 +401,9 @@
355 401 $newPost = $this->transformDrpdwnValue($unslashed_post);
356 402 $filesData = GlobalHelper::sanitize_files_input($_FILES);
357 403 do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $filesData);
358 404
359 - $captchaV3Settings = $this->getCaptchaV3Settings();
360 - if ($captchaV3Settings) {
361 - $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
362 - $integrationHandler = new IntegrationHandler(0);
363 - $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
364 - if (!is_wp_error($allFormIntegrations)) {
365 - foreach ($allFormIntegrations as $integration) {
366 - if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
367 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
368 - if ($integrationDetails) {
369 - $integrationDetails->id = $integration->id;
370 - $reCAPTCHA = $integrationDetails;
371 - }
372 - }
373 - }
374 - }
375 - if (!empty($reCAPTCHA->secretKey)) {
376 - $gRecaptchaResponse = HttpHelper::post(
377 - 'https://www.google.com/recaptcha/api/siteverify',
378 - ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
379 - );
380 - if ($captchaV3Settings && !empty($saveResponse['triggerData'])) {
381 - $logID = $saveResponse['triggerData']['logID'];
382 - $integId = $reCAPTCHA->id;
383 - $saveApiResponse = new UtilApiResponse();
384 - $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
385 - }
386 - }
387 - unset($_POST['g-recaptcha-response']);
388 - }
405 + $this->logRecaptchaV3Verification($saveResponse);
389 406 if (!empty($redirectPage) && empty($saveResponse['redirectPage']) || null === $saveResponse['redirectPage']) {
390 407 $saveResponse['redirectPage'] = $redirectPage;
391 408 }
392 409 if (!empty($regSuccMsg) && isset($saveResponse['dflt_message'])) {
@@ -465,38 +482,9 @@
465 482 //TO DO:: submit success action temporarily added for solution of a issue
466 483 do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $filesData);
467 484 do_action('bitform_update_success', $this->_form_id, $entryID, $newPost, $filesData);
468 485
469 - $captchaV3Settings = $this->getCaptchaV3Settings();
470 - if ($captchaV3Settings) {
471 - $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
472 - $integrationHandler = new IntegrationHandler(0);
473 - $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
474 - if (!is_wp_error($allFormIntegrations)) {
475 - foreach ($allFormIntegrations as $integration) {
476 - if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
477 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
478 - if ($integrationDetails) {
479 - $integrationDetails->id = $integration->id;
480 - $reCAPTCHA = $integrationDetails;
481 - }
482 - }
483 - }
484 - }
485 - if (!empty($reCAPTCHA->secretKey)) {
486 - $gRecaptchaResponse = HttpHelper::post(
487 - 'https://www.google.com/recaptcha/api/siteverify',
488 - ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
489 - );
490 - if ($captchaV3Settings && !empty($updateResponse['triggerData'])) {
491 - $logID = $updateResponse['triggerData']['logID'];
492 - $integId = $reCAPTCHA->id;
493 - $saveApiResponse = new UtilApiResponse();
494 - $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
495 - }
496 - }
497 - unset($_POST['g-recaptcha-response']);
498 - }
486 + $this->logRecaptchaV3Verification($updateResponse);
499 487 if (!empty($redirectPage) && empty($updateResponse['redirectPage']) || null === $updateResponse['redirectPage']) {
500 488 $updateResponse['redirectPage'] = $redirectPage;
501 489 }
502 490 if (!empty($regSuccMsg) && isset($updateResponse['dflt_message'])) {
@@ -762,144 +750,236 @@
762 750 return new WP_Error('token_expired', __('Token expired', 'bit-form'));
763 751 }
764 752 }
765 753
754 + /**
755 + * Whether the IP already has a completed entry; drafts (status 9) don't count.
756 + *
757 + * @param string $ipAddress Visitor IP
758 + *
759 + * @return bool
760 + */
761 + private function hasCompletedEntryFromIp($ipAddress)
762 + {
763 + $storedIp = IpTool::storageValue($ipAddress);
764 + if ('' === $storedIp) {
765 + return false;
766 + }
767 +
768 + $entries = (new FormEntryModel())->get(
769 + ['status'],
770 + [
771 + 'form_id' => $this->form_id,
772 + 'user_ip' => $storedIp,
773 + ]
774 + );
775 +
776 + if (is_wp_error($entries) || !is_array($entries)) {
777 + return false;
778 + }
779 +
780 + foreach ($entries as $entry) {
781 + if (9 !== (int) $entry->status) {
782 + return true;
783 + }
784 + }
785 +
786 + return false;
787 + }
788 +
766 789 private function verifyGRecaptcha()
767 790 {
768 791 $captchaSettings = $this->getCaptchaSettings();
769 792 $captchaV3Settings = $this->getCaptchaV3Settings();
770 - if ($captchaSettings || $captchaV3Settings) {
771 - $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
772 - if (!isset($_POST['g-recaptcha-response'])) {
773 - return new WP_Error('spam_detection', __('Please recheck your reCaptcha Configuration', 'bit-form'));
793 + if (!$captchaSettings && !$captchaV3Settings) {
794 + return;
795 + }
796 +
797 + if (!isset($_POST['g-recaptcha-response'])) {
798 + return new WP_Error('spam_detection', __('Please recheck your reCaptcha Configuration', 'bit-form'));
799 + }
800 + $token = sanitize_text_field(wp_unslash($_POST['g-recaptcha-response']));
801 +
802 + $appType = $captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3';
803 + $reCAPTCHA = $this->getCaptchaApp($appType);
804 +
805 + // Fail closed: no secret key must not skip verification.
806 + if (empty($reCAPTCHA->secretKey)) {
807 + Log::debug_log("reCAPTCHA ({$appType}) is enabled on form {$this->form_id}, but no secret key is set in App Settings.");
808 + return new WP_Error('spam_detection', __('reCAPTCHA is not configured. Please contact the site administrator.', 'bit-form'));
809 + }
810 +
811 + $gRecaptchaResponse = HttpHelper::post(
812 + 'https://www.google.com/recaptcha/api/siteverify',
813 + ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
814 + );
815 + if (is_wp_error($gRecaptchaResponse)) {
816 + return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
817 + }
818 +
819 + // Tokens are single-use; keep this verdict for the log.
820 + if ('gReCaptchaV3' === $appType) {
821 + $this->recaptchaV3Verification = ['integId' => $reCAPTCHA->id, 'response' => $gRecaptchaResponse];
822 + }
823 +
824 + if ($captchaV3Settings && self::isRecaptchaScoreTooLow($gRecaptchaResponse, $captchaV3Settings)) {
825 + wp_send_json_error(
826 + sanitize_text_field((string) $captchaV3Settings->message)
827 + );
828 + }
829 +
830 + if (!is_object($gRecaptchaResponse) || empty($gRecaptchaResponse->success)) {
831 + return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
832 + }
833 + }
834 +
835 + /**
836 + * Connected captcha app from App Settings, with its integration id.
837 + *
838 + * @param string $appType gReCaptcha, gReCaptchaV3, hcaptcha or turnstileCaptcha
839 + *
840 + * @return object|null
841 + */
842 + private function getCaptchaApp($appType)
843 + {
844 + $apps = (new IntegrationHandler(0))->getAllIntegration('app', $appType);
845 + if (is_wp_error($apps) || !is_array($apps)) {
846 + return null;
847 + }
848 +
849 + $reCAPTCHA = null;
850 + foreach ($apps as $integration) {
851 + if ($appType !== $integration->integration_type) {
852 + continue;
774 853 }
775 - $integrationHandler = new IntegrationHandler(0);
776 - $allFormIntegrations = $integrationHandler->getAllIntegration('app', $captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3');
777 - if (!is_wp_error($allFormIntegrations)) {
778 - foreach ($allFormIntegrations as $integration) {
779 - if (!is_null($integration->integration_type) && $integration->integration_type === ($captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3')) {
780 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
781 - if ($integrationDetails) {
782 - $integrationDetails->id = $integration->id;
783 - $reCAPTCHA = $integrationDetails;
784 - }
785 - }
786 - }
854 + $details = Utilities::jsonObj($integration->integration_details);
855 + if (is_object($details)) {
856 + $details->id = $integration->id;
857 + $reCAPTCHA = $details;
787 858 }
788 - if (!empty($reCAPTCHA->secretKey)) {
789 - $gRecaptchaResponse = HttpHelper::post(
790 - 'https://www.google.com/recaptcha/api/siteverify',
791 - ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
792 - );
793 - $isgReCaptchaVerified = false;
794 - if (!is_wp_error($gRecaptchaResponse)) {
795 - if (
796 - $captchaV3Settings
797 - && !empty($gRecaptchaResponse->score)
798 - && ((float) $gRecaptchaResponse->score < (float) $captchaV3Settings->score)
799 - ) {
800 - wp_send_json_error(
801 - sanitize_text_field((string) $captchaV3Settings->message)
802 - );
803 - }
859 + }
804 860
805 - $isgReCaptchaVerified = $gRecaptchaResponse->success;
806 - }
807 - if (!$isgReCaptchaVerified) {
808 - return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
809 - }
810 - }
861 + return $reCAPTCHA;
862 + }
863 +
864 + /**
865 + * Log the reCAPTCHA v3 verdict for this submission.
866 + *
867 + * @param array $saveResponse Entry save/update response
868 + *
869 + * @return void
870 + */
871 + private function logRecaptchaV3Verification($saveResponse)
872 + {
873 + if (!$this->getCaptchaV3Settings()) {
874 + return;
811 875 }
876 + unset($_POST['g-recaptcha-response']);
877 +
878 + if (empty($this->recaptchaV3Verification) || empty($saveResponse['triggerData']['logID'])) {
879 + return;
880 + }
881 +
882 + $response = $this->recaptchaV3Verification['response'];
883 + $status = is_object($response) && !empty($response->success) ? 'success' : 'error';
884 + (new UtilApiResponse())->apiResponse(
885 + $saveResponse['triggerData']['logID'],
886 + $this->recaptchaV3Verification['integId'],
887 + ['type_name' => 'ReCaptcha', 'type' => 'v3'],
888 + $status,
889 + $response
890 + );
812 891 }
813 892
814 - private function verifyHCaptcha()
893 + /**
894 + * Whether the v3 score is below tolerance; 0.0 counts, blank tolerance means 0.6.
895 + *
896 + * @param mixed $response Decoded siteverify response
897 + * @param mixed $settings Form's recaptchav3 settings
898 + *
899 + * @return bool
900 + */
901 + private static function isRecaptchaScoreTooLow($response, $settings)
815 902 {
816 - $hCaptchaExist = $this->isFieldTypeExist('hcaptcha'); // You can rename this to getHCaptchaSettings() if needed
817 - if ($hCaptchaExist) {
818 - if (!isset($_POST['h-captcha-response'])) {
819 - return new WP_Error('spam_detection', __('Please verify hCaptcha', 'bit-form'));
820 - }
903 + if (!is_object($response) || !isset($response->score) || !is_numeric($response->score)) {
904 + return false;
905 + }
821 906
822 - $token = sanitize_text_field(wp_unslash($_POST['h-captcha-response']));
907 + $threshold = is_object($settings) && isset($settings->score) && is_numeric($settings->score)
908 + ? (float) $settings->score
909 + : 0.6;
823 910
824 - $integrationHandler = new IntegrationHandler(0);
825 - $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'hcaptcha');
911 + return (float) $response->score < $threshold;
912 + }
826 913
827 - if (!is_wp_error($allFormIntegrations)) {
828 - foreach ($allFormIntegrations as $integration) {
829 - if (!is_null($integration->integration_type) && 'hcaptcha' === $integration->integration_type) {
830 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
831 - if ($integrationDetails) {
832 - $integrationDetails->id = $integration->id;
833 - $hCaptcha = $integrationDetails;
834 - }
835 - }
836 - }
837 - }
914 + private function verifyHCaptcha()
915 + {
916 + if (!$this->isFieldTypeExist('hcaptcha')) {
917 + return;
918 + }
838 919
839 - if (!empty($hCaptcha->secretKey)) {
840 - $hCaptchaResponse = HttpHelper::post(
841 - 'https://api.hcaptcha.com/siteverify',
842 - [
843 - 'secret' => $hCaptcha->secretKey,
844 - 'response' => $token,
845 - 'remoteip' => (isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '')
846 - ]
847 - );
920 + if (!isset($_POST['h-captcha-response'])) {
921 + return new WP_Error('spam_detection', __('Please verify hCaptcha', 'bit-form'));
922 + }
923 + $token = sanitize_text_field(wp_unslash($_POST['h-captcha-response']));
924 + $hCaptcha = $this->getCaptchaApp('hcaptcha');
848 925
849 - $isVerified = false;
850 - if (!is_wp_error($hCaptchaResponse)) {
851 - $isVerified = $hCaptchaResponse->success;
852 - }
926 + // Fail closed: no secret key must not skip verification.
927 + if (empty($hCaptcha->secretKey)) {
928 + Log::debug_log("hCaptcha is enabled on form {$this->form_id}, but no secret key is set in App Settings.");
929 + return new WP_Error('spam_detection', __('hCaptcha is not configured. Please contact the site administrator.', 'bit-form'));
930 + }
853 931
854 - if (!$isVerified) {
855 - return new WP_Error('spam_detection', __('hCaptcha verification failed', 'bit-form'));
856 - }
857 - }
932 + $hCaptchaResponse = HttpHelper::post(
933 + 'https://api.hcaptcha.com/siteverify',
934 + [
935 + 'secret' => $hCaptcha->secretKey,
936 + 'response' => $token,
937 + 'remoteip' => (isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '')
938 + ]
939 + );
940 +
941 + if (is_wp_error($hCaptchaResponse) || !is_object($hCaptchaResponse) || empty($hCaptchaResponse->success)) {
942 + return new WP_Error('spam_detection', __('hCaptcha verification failed', 'bit-form'));
858 943 }
859 944 }
860 945
861 946 private function verifyTurnstileCaptcha()
862 947 {
863 - $turnstileExist = $this->isFieldTypeExist('turnstile');
864 - if ($turnstileExist) {
865 - if (!isset($_POST['cf-turnstile-response'])) {
866 - return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
867 - }
868 - $token = sanitize_text_field(wp_unslash($_POST['cf-turnstile-response']));
869 - $turnstileCaptcha = null;
870 - $integrationHandler = new IntegrationHandler(0);
871 - $turnstileIntegration = $integrationHandler->getAllIntegration('app', 'turnstileCaptcha')[0];
872 - if (!is_wp_error($turnstileIntegration && !is_null($turnstileIntegration->integration_type))) {
873 - $turnstileCaptcha = json_decode($turnstileIntegration->integration_details);
874 - // $integrationDetails->id = $turnstileIntegration->id;
875 - // $turnstileCaptcha = $integrationDetails;
876 - }
877 - if (!is_null($turnstileCaptcha)) {
878 - $isTurnstileCaptchaVerified = false;
879 - $turnstileRecaptchaResponse = HttpHelper::post(
880 - 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
881 - ['secret' => $turnstileCaptcha->secretKey, 'response' => $token]
882 - );
883 - if (!is_wp_error($turnstileRecaptchaResponse)) {
884 - if (!$turnstileRecaptchaResponse->success) {
885 - $errorCodes = implode(', ', (array) ($turnstileRecaptchaResponse->{'error-codes'} ?? []));
886 - wp_send_json_error(
887 - sprintf(
888 - /* translators: %s: dynamic value. */
889 - __('Cloudflare Turnstile Validation Error: %s', 'bit-form'),
890 - $errorCodes
891 - )
892 - );
893 - }
948 + if (!$this->isFieldTypeExist('turnstile')) {
949 + return;
950 + }
894 951
895 - $isTurnstileCaptchaVerified = $turnstileRecaptchaResponse->success;
896 - }
897 - if (!$isTurnstileCaptchaVerified) {
898 - return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
899 - }
900 - }
952 + if (!isset($_POST['cf-turnstile-response'])) {
953 + return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
901 954 }
955 + $token = sanitize_text_field(wp_unslash($_POST['cf-turnstile-response']));
956 + $turnstileCaptcha = $this->getCaptchaApp('turnstileCaptcha');
957 +
958 + // Fail closed: no secret key must not skip verification.
959 + if (empty($turnstileCaptcha->secretKey)) {
960 + Log::debug_log("Turnstile is enabled on form {$this->form_id}, but no secret key is set in App Settings.");
961 + return new WP_Error('spam_detection', __('Cloudflare Turnstile is not configured. Please contact the site administrator.', 'bit-form'));
962 + }
963 +
964 + $turnstileResponse = HttpHelper::post(
965 + 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
966 + ['secret' => $turnstileCaptcha->secretKey, 'response' => $token]
967 + );
968 + if (is_wp_error($turnstileResponse) || !is_object($turnstileResponse)) {
969 + return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
970 + }
971 +
972 + if (empty($turnstileResponse->success)) {
973 + $errorCodes = implode(', ', (array) ($turnstileResponse->{'error-codes'} ?? []));
974 + wp_send_json_error(
975 + sprintf(
976 + /* translators: %s: dynamic value. */
977 + __('Cloudflare Turnstile Validation Error: %s', 'bit-form'),
978 + $errorCodes
979 + )
980 + );
981 + }
902 982 }
903 983
904 984 public function verifySubmissionNonce()
905 985 {
@@ -983,33 +1063,9 @@
983 1063 continue;
984 1064 }
985 1065
986 1066 if ('onePerIp' === $restrictionKey) {
987 - $formEntry = new FormEntryModel();
988 -
989 - $getResult = $formEntry->get(
990 - ['user_ip', 'status'],
991 - [
992 - 'form_id' => $this->form_id,
993 - 'user_ip' => (int) ip2long((string) $ipAddress)
994 - ],
995 - );
996 -
997 - $count = 0;
998 - $status = 0;
999 -
1000 - if (!is_wp_error($getResult) && count($getResult) > 0) {
1001 - $count = count($getResult);
1002 -
1003 - foreach ($getResult as $row) {
1004 - if (9 === (int) $row->status) {
1005 - $status = 9;
1006 - break;
1007 - }
1008 - }
1009 - }
1010 -
1011 - if ($count > 0 && 9 !== (int) $status) {
1067 + if ($this->hasCompletedEntryFromIp($ipAddress)) {
1012 1068 $onePerIp = __('Sorry!! You have already submitted from this IP address', 'bit-form');
1013 1069
1014 1070 $onePerIp = apply_filters(
1015 1071 'bitform_filter_restriction_one_per_ip_message',