PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.21.4
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.21.4
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 2.10.2 All 137 releases
bit-form / includes / Frontend / Form / FrontendFormManager.php

FrontendFormManager.php in Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder 2.21.4, at includes/Frontend/Form/FrontendFormManager.php

1,105 lines 41.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Get set Form,fields
5 */
6
7 namespace BitCode\BitForm\Frontend\Form;
8
9 /**
10 * FrontendFormManager class
11 */
12
13 use BitCode\BitForm\Admin\Form\AdminFormHandler;
14 use BitCode\BitForm\Admin\Form\Helpers;
15 use BitCode\BitForm\Core\Database\FormEntryModel;
16 use BitCode\BitForm\Core\Form\FormManager;
17 use BitCode\BitForm\Core\Form\Validator\FormFieldValidator;
18 use BitCode\BitForm\Core\Integration\IntegrationHandler;
19 use BitCode\BitForm\Core\Messages\SuccessMessageHandler;
20 use BitCode\BitForm\Core\Util\ApiResponse as UtilApiResponse;
21 use BitCode\BitForm\Core\Util\DateTimeHelper;
22 use BitCode\BitForm\Core\Util\EntryLimitHelper;
23 use BitCode\BitForm\Core\Util\HttpHelper;
24 use BitCode\BitForm\Core\Util\IpTool;
25 use BitCode\BitForm\Core\WorkFlow\WorkFlow;
26 use BitCode\BitForm\Core\WorkFlow\WorkFlowHandler;
27 use BitCode\BitForm\Frontend\Form\View\FormViewer;
28 use BitCode\BitFormPro\Admin\FormSettings\FormAbandonment;
29 use WP_Error;
30
31 final class FrontendFormManager extends FormManager
32 {
33 private $_form_identifier;
34 private $_form_token;
35 private $_form_id;
36 private $_work_flows;
37 private $_conf_messages;
38 private static $_instance = [];
39
40 // private $_has_upload = false;
41 public function __construct($form_id, $shortCodeCounter = null)
42 {
43 parent::__construct($form_id);
44 $this->_form_identifier = 'bitforms_' . $form_id;
45 $this->_form_identifier .= !empty(get_post()->ID) ? '_' . get_post()->ID : '';
46 $this->_form_identifier .= !empty($shortCodeCounter) ? "_$shortCodeCounter" : '';
47 $this->_form_token = wp_create_nonce('bitforms_' . $form_id);
48 $this->_form_id = $form_id;
49 }
50
51 public static function getInstance($form_id, $shortCodeCounter = null)
52 {
53 $key = $form_id . ':' . ($shortCodeCounter ?? 'default');
54
55 if (!isset(self::$_instance[$key])) {
56 self::$_instance[$key] = new self($form_id, $shortCodeCounter);
57 }
58
59 return self::$_instance[$key];
60 }
61
62 public function getFormIdentifier()
63 {
64 return $this->_form_identifier;
65 }
66
67 public function getFormID()
68 {
69 return $this->_form_id;
70 }
71
72 public function getFormToken()
73 {
74 return $this->_form_token;
75 }
76
77 public function isSubmitted()
78 {
79 // return isset($_POST[$this->_form_identifier]) ? true : false;
80 return (isset($_POST['bitforms_id']) && $_POST['bitforms_id'] === $this->_form_identifier) ? true : false;
81 }
82
83 public function getSubmittedFields($submitted_data)
84 {
85 unset($submitted_data[$this->_form_identifier]);
86 // unset($submitted_data['bit-form-submit-btn']);
87 return array_keys($submitted_data);
88 }
89
90 public function formView($fields = null, $hasFile = false, $errorMessages = null, $previousValue = null)
91 {
92 $formContents = $this->getFormContent();
93 $formAtomicClsMap = $this->getAtomicClsMap();
94 if (!empty($fields)) {
95 $formContents->fields = is_string($fields) ? json_decode($fields) : $fields;
96 } else {
97 $workFlowRunHelper = new WorkFlow($this->form_id);
98 $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(
99 'create',
100 $formContents->fields
101 );
102 $formContents->fields = empty($workFlowreturnedOnLoad['fields']) ? $formContents->fields : $workFlowreturnedOnLoad['fields'];
103 }
104 $formViewer = new FormViewer($this, $formContents, $formAtomicClsMap, $errorMessages, $previousValue);
105 $isRestricted = $this->checkSubmissionRestriction(false);
106 $msg = !empty($isRestricted) ? $isRestricted[0] : '';
107 return $formViewer->getView($hasFile, $msg);
108 }
109
110 public function conversationalFormView($fields = null, $hasFile = false, $errorMessages = null, $previousValue = null)
111 {
112 $formContents = $this->getFormContent();
113 $formAtomicClsMap = $this->getAtomicClsMap();
114 if (!empty($fields)) {
115 $formContents->fields = is_string($fields) ? json_decode($fields) : $fields;
116 } else {
117 $workFlowRunHelper = new WorkFlow($this->form_id);
118 $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(
119 'create',
120 $formContents->fields
121 );
122 $formContents->fields = empty($workFlowreturnedOnLoad['fields']) ? $formContents->fields : $workFlowreturnedOnLoad['fields'];
123 }
124 $formViewer = new FormViewer($this, $formContents, $formAtomicClsMap, $errorMessages, $previousValue);
125 $isRestricted = $this->checkSubmissionRestriction(false);
126 $msg = !empty($isRestricted) ? $isRestricted[0] : '';
127 return $formViewer->getConversationalView($hasFile, $msg);
128 }
129
130 public function checkEmptySubmission($data, $file)
131 {
132 $formFields = $this->getFields();
133 foreach ($formFields as $key => $field) {
134 $fieldType = $field['type'];
135 if ('button' === $fieldType) {
136 continue;
137 }
138 $fileUploadFieldTypes = ['file-up', 'advanced-file-up'];
139 if ('decision-box' === $fieldType || 'gdpr' === $fieldType) {
140 continue;
141 }
142 $isFileType = in_array($fieldType, $fileUploadFieldTypes);
143 if ($this->isRepeatedField($key)) {
144 $fileData = !empty($file[$key]) ? $file[$key] : [];
145 $dataVal = !empty($data[$key]) ? $data[$key] : [];
146 if (!$this->checkRepeatedFieldEmptySubmission($isFileType, $dataVal, $fileData)) {
147 return false;
148 }
149 continue;
150 }
151 if (!$isFileType && (!empty($data[$key]) || (isset($data[$key]) && is_numeric($data[$key])))) {
152 return false;
153 }
154 if ($isFileType && !empty($file[$key]['name']) && is_string($file[$key]['name'])) {
155 return false;
156 }
157 if ($isFileType && !empty($file[$key]['name'][0])) {
158 return false;
159 }
160 }
161 return true;
162 }
163
164 private function checkRepeatedFieldEmptySubmission($isFileType, $data, $file = [])
165 {
166 if (!$isFileType) {
167 foreach ($data as $value) {
168 if (!empty($value)) {
169 return false;
170 }
171 }
172 }
173 if ($isFileType) {
174 foreach ($file['name'] as $value) {
175 if (!empty($value) && is_string($value)) {
176 return false;
177 }
178 if (is_array($value) && !empty($value[0])) {
179 return false;
180 }
181 }
182 }
183 return true;
184 }
185
186 private function getParams()
187 {
188 $url = wp_parse_url(wp_get_referer());
189 $parameter = [];
190 if (isset($url['query'])) {
191 $queries = explode('&', $url['query']);
192 foreach ($queries as $query) {
193 list($field, $value) = explode('=', $query);
194 $parameter[$field] = $value;
195 }
196 }
197 return $parameter;
198 }
199
200 private function getFormFields($formID)
201 {
202 $adminFormHandler = new AdminFormHandler();
203 $post = new \stdClass();
204 $post = (object) [
205 'id' => $formID
206 ];
207 $getForm = $adminFormHandler->getAForm('', $post);
208 $formContainer = $getForm['form_content'];
209
210 return $formContainer['fields'];
211 }
212
213 private function transformDrpdwnValue($post)
214 {
215 $formFields = $this->getFormFields($this->_form_id);
216
217 foreach ($post as $key => $value) {
218 if (!str_starts_with($key, 'repeater') && 'select' === $formFields->{$key}->typ) {
219 if (is_array($value)) {
220 foreach ($value as $k => $v) {
221 $post[$key][$k] = explode(BITFORMS_BF_SEPARATOR, $v);
222 }
223 } else {
224 $post[$key] = explode(BITFORMS_BF_SEPARATOR, $value);
225 }
226 };
227 }
228
229 return $post;
230 }
231
232 public function handleSubmission()
233 {
234 $this->fieldNameReplaceOfPost();
235
236 $validated = $this->beforeSubmittedValidate();
237
238 $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);
239
240 if (true === $validated) {
241 do_action('bitform_validation_success', $this->_form_id);
242 unset($_POST['hidden_fields']);
243
244 $redirectPage = '';
245 $regSuccMsg = '';
246
247 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
248 if (!is_wp_error($existAuth) && count($existAuth) > 0) {
249 $parameter = $this->getParams();
250 $existAuthFilter = has_filter('bf_wp_user_auth');
251
252 if (true === $existAuthFilter) {
253 $result = apply_filters('bf_wp_user_auth', $existAuth[0], $_POST, $parameter);
254
255 $result = apply_filters('bitform_filter_wp_user_auth_response', $result, $this->_form_id, $_POST, $parameter);
256
257 do_action('bitform_wp_user_auth_response', $result, $this->_form_id, $_POST, $parameter);
258
259 if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
260 if (!$result['success']) {
261 return new WP_Error('errors', __($result['message'], 'bit-form'));
262 } elseif (isset($result['success'])) {
263 $redirectPage = $result['redirectPage'];
264 $regSuccMsg = $result['message'];
265 }
266 } else {
267 if (!$result['success']) {
268 return new WP_Error('errors', __($result['message'], 'bit-form'));
269 } else {
270 return $result;
271 }
272 }
273 }
274 }
275
276 $saveResponse = $this->saveFormEntry($_POST);
277 if (is_wp_error($saveResponse)) {
278 return $saveResponse;
279 }
280
281 $entryID = $saveResponse['entry_id'];
282
283 // transformed dropdown value from string to array
284 $newPost = $this->transformDrpdwnValue($_POST);
285
286 do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $_FILES);
287
288 $captchaV3Settings = $this->getCaptchaV3Settings();
289 if ($captchaV3Settings) {
290 $token = $_POST['g-recaptcha-response'];
291 $integrationHandler = new IntegrationHandler(0);
292 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
293 if (!is_wp_error($allFormIntegrations)) {
294 foreach ($allFormIntegrations as $integration) {
295 if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
296 $integrationDetails = json_decode($integration->integration_details);
297 $integrationDetails->id = $integration->id;
298 $reCAPTCHA = $integrationDetails;
299 }
300 }
301 }
302 if (!empty($reCAPTCHA->secretKey)) {
303 $gRecaptchaResponse = HttpHelper::post(
304 'https://www.google.com/recaptcha/api/siteverify',
305 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
306 );
307 if ($captchaV3Settings && !empty($saveResponse['triggerData'])) {
308 $logID = $saveResponse['triggerData']['logID'];
309 $integId = $reCAPTCHA->id;
310 $saveApiResponse = new UtilApiResponse();
311 $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
312 }
313 }
314 unset($_POST['g-recaptcha-response']);
315 }
316 if (!empty($redirectPage) && empty($saveResponse['redirectPage']) || null === $saveResponse['redirectPage']) {
317 $saveResponse['redirectPage'] = $redirectPage;
318 }
319 if (!empty($regSuccMsg) && isset($saveResponse['dflt_message'])) {
320 $saveResponse['message'] = $regSuccMsg;
321 }
322 $saveResponse['new_nonce'] = wp_create_nonce('bitforms_' . $this->_form_id);
323
324 $saveResponse = IntegrationHandler::maybeSetCronForIntegration($saveResponse, 'create');
325 $entryId = $saveResponse['entry_id'];
326
327 $responseMsg = is_array($saveResponse) && !empty($saveResponse) ? $saveResponse : __('Form Submitted Successfully', 'bit-form');
328 $_POST = [];
329 $responseMsg['entry_id'] = $entryId;
330 return $responseMsg;
331 }
332 do_action('bitform_validation_error', $this->_form_id, $validated);
333 return $validated;
334 }
335
336 public function handleUpdateEntry()
337 {
338 $this->fieldNameReplaceOfPost();
339 $validated = $this->beforeSubmittedValidate();
340 $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);
341
342 $entryID = $_REQUEST['entryID'];
343 $GLOBALS['bf_entry_id'] = $entryID;
344 if (is_null($entryID)) {
345 return new WP_Error('empty_form', __('Entries id is invalid', 'bit-form'));
346 }
347 if (true === $validated) {
348 do_action('bitform_validation_success', $this->_form_id);
349 unset($_POST['hidden_fields'], $_POST['entryID']);
350
351 $redirectPage = '';
352 $regSuccMsg = '';
353
354 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
355 if (!is_wp_error($existAuth) && count($existAuth) > 0) {
356 $parameter = $this->getParams();
357 $existAuthFilter = has_filter('bf_wp_user_auth');
358
359 if (true === $existAuthFilter) {
360 $result = apply_filters('bf_wp_user_auth', $existAuth[0], $_POST, $parameter);
361
362 if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
363 if (!$result['success']) {
364 return new WP_Error('errors', __($result['message'], 'bit-form'));
365 } elseif (isset($result['success'])) {
366 $redirectPage = $result['redirectPage'];
367 $regSuccMsg = $result['message'];
368 }
369 } else {
370 if (!$result['success']) {
371 return new WP_Error('errors', __($result['message'], 'bit-form'));
372 } else {
373 return $result;
374 }
375 }
376 }
377 }
378
379 $updateResponse = $this->updateFormEntry($_POST, $this->getFormID(), $entryID);
380 if (is_wp_error($updateResponse)) {
381 return $updateResponse;
382 }
383
384 // transformed dropdown value from string to array
385 $newPost = $this->transformDrpdwnValue($_POST);
386
387 //TO DO:: submit success action temporarily added for solution of a issue
388 do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $_FILES);
389 do_action('bitform_update_success', $this->_form_id, $entryID, $newPost, $_FILES);
390
391 $captchaV3Settings = $this->getCaptchaV3Settings();
392 if ($captchaV3Settings) {
393 $token = $_POST['g-recaptcha-response'];
394 $integrationHandler = new IntegrationHandler(0);
395 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
396 if (!is_wp_error($allFormIntegrations)) {
397 foreach ($allFormIntegrations as $integration) {
398 if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
399 $integrationDetails = json_decode($integration->integration_details);
400 $integrationDetails->id = $integration->id;
401 $reCAPTCHA = $integrationDetails;
402 }
403 }
404 }
405 if (!empty($reCAPTCHA->secretKey)) {
406 $gRecaptchaResponse = HttpHelper::post(
407 'https://www.google.com/recaptcha/api/siteverify',
408 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
409 );
410 if ($captchaV3Settings && !empty($updateResponse['triggerData'])) {
411 $logID = $updateResponse['triggerData']['logID'];
412 $integId = $reCAPTCHA->id;
413 $saveApiResponse = new UtilApiResponse();
414 $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
415 }
416 }
417 unset($_POST['g-recaptcha-response']);
418 }
419 if (!empty($redirectPage) && empty($updateResponse['redirectPage']) || null === $updateResponse['redirectPage']) {
420 $updateResponse['redirectPage'] = $redirectPage;
421 }
422 if (!empty($regSuccMsg) && isset($updateResponse['dflt_message'])) {
423 $updateResponse['message'] = $regSuccMsg;
424 }
425 $updateResponse['new_nonce'] = wp_create_nonce('bitforms_' . $this->_form_id);
426 $updateResponse = IntegrationHandler::maybeSetCronForIntegration($updateResponse, 'create');
427 $entryId = $updateResponse['entry_id'];
428
429 $responseMsg = is_array($updateResponse) && !empty($updateResponse) ? $updateResponse : __('Entry Update Successfully', 'bit-form');
430
431 $_POST = [];
432 $responseMsg['entry_id'] = $entryId;
433 return $responseMsg;
434 }
435 do_action('bitform_validation_error', $this->_form_id, $validated);
436 return $validated;
437 }
438
439 public function validateFormSubmission($submitted_data)
440 {
441 $hidden_fields = isset($submitted_data['hidden_fields']) ? $submitted_data['hidden_fields'] : '';
442 $submitted_fields = $this->getSubmittedFields($submitted_data);
443 $form_fields = $this->getFields();
444 $form_fields_names = array_keys($form_fields);
445 if ($this->isGCLIDEnabled()) {
446 array_push($form_fields_names, 'GCLID');
447 }
448 foreach ($submitted_fields as $field) {
449 if ('hidden_fields' !== $field && !in_array($field, $form_fields_names) || false !== strpos($hidden_fields, $field)) {
450 unset($submitted_data[$field]);
451 }
452 }
453 return $submitted_data;
454 }
455
456 public function beforeSubmittedValidate()
457 {
458 if ($this->verifySubmissionNonce()) {
459 if ($this->isExist()) {
460 $isRestricted = $this->checkSubmissionRestriction();
461 if ($isRestricted && !empty($isRestricted)) {
462 return new WP_Error('spam_detection', $isRestricted[0]);
463 }
464 if ($this->isTrappedInHoneypot()) {
465 return new WP_Error('spam_detection', __('Token verification failed', 'bit-form'));
466 }
467 $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
468
469 $verifyGRecaptchaResult = $this->verifyGRecaptcha();
470 if (is_wp_error($verifyGRecaptchaResult)) {
471 return $verifyGRecaptchaResult;
472 }
473
474 $verifyHCaptchaResult = $this->verifyHCaptcha();
475 if (is_wp_error($verifyHCaptchaResult)) {
476 return $verifyHCaptchaResult;
477 }
478
479 /* Implement Turnstile Captcha start */
480 $verifyTurnstileCaptchaResult = $this->verifyTurnstileCaptcha();
481 if (is_wp_error($verifyTurnstileCaptchaResult)) {
482 return $verifyTurnstileCaptchaResult;
483 }
484 /* Implement Turnstile Captcha end */
485
486 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
487
488 // check if user is already logged in and form has auth integration
489 do_action('bitform_checked_exist_auth', $this->_form_id, $existAuth);
490 if (!is_wp_error($existAuth) && count($existAuth) > 0 && is_user_logged_in()) {
491 return new WP_Error('auth_error', __('You are already logged in', 'bit-form'));
492 }
493 $validateForm = $this->validateFormSubmission($_POST);
494 $validateFormFiles = $this->validateFormSubmission($_FILES);
495 $validateForm = array_merge($validateForm, $validateFormFiles);
496 $form_fields = $this->getFields();
497 // check if form-current-step is set and form is multi-step
498 $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
499 if (!is_null($formCurrentStep)) {
500 $formContents = $this->getFormContent();
501 $layout = $formContents->layout;
502 $stepIndex = (int) $formCurrentStep - 1;
503 $stepLayout = $layout[$stepIndex]->layout->lg;
504 $nestedLayout = $formContents->nestedLayout;
505 $step_fields = [];
506 foreach ($stepLayout as $lay) {
507 $fk = $lay->i;
508 if (isset($nestedLayout->{$fk})) {
509 $nestedLg = $nestedLayout->{$fk}->lg;
510 foreach ($nestedLg as $nestedLay) {
511 $nestedFk = $nestedLay->i;
512 $step_fields[$nestedFk] = $form_fields[$nestedFk];
513 }
514 }
515 $step_fields[$fk] = $form_fields[$fk];
516 }
517 $form_fields = $step_fields;
518 }
519 $formFieldValidator = new FormFieldValidator($form_fields, $_POST, $_FILES);
520 $validUniuqFields = [];
521 $existFilter = has_filter('bf_check_duplicate_entry');
522 if (true === $existFilter) {
523 $validUniuqFields = apply_filters('bf_check_duplicate_entry', $form_fields, $_POST);
524
525 $fieldKeys = array_keys($validUniuqFields);
526 $form_fields_keys = array_keys($form_fields);
527 $uniqueFields = [];
528 foreach ($fieldKeys as $key) {
529 if (in_array($key, $form_fields_keys)) {
530 $uniqueFields[] = $form_fields[$key];
531 }
532 }
533 do_action('bitform_Unique_entry', $uniqueFields, $validUniuqFields, $this->_form_id, $_POST);
534 }
535 $validateField = $formFieldValidator->validate('create', $this->_form_id);
536
537 if ($validateForm && $validateField && 0 === count($validUniuqFields)) {
538 return true;
539 } else {
540 $error = __('Please submit form with valid fields', 'bit-form');
541 if (!$validateForm) {
542 $errorMessages = $error;
543 } elseif (count($formFieldValidator->getMessage()) > 0) {
544 $errorMessages = $formFieldValidator->getMessage();
545 } else {
546 $errorMessages = 0 === count($validUniuqFields) ? $error : $validUniuqFields;
547 }
548 return new WP_Error('validation_error', $errorMessages);
549 }
550 }
551 return new WP_Error('unknown_form', __('Form does not exist', 'bit-form'));
552 } else {
553 return new WP_Error('token_expired', __('Token expired', 'bit-form'));
554 }
555 }
556
557 private function verifyGRecaptcha()
558 {
559 $captchaSettings = $this->getCaptchaSettings();
560 $captchaV3Settings = $this->getCaptchaV3Settings();
561 if ($captchaSettings || $captchaV3Settings) {
562 $token = $_POST['g-recaptcha-response'];
563 if (!isset($_POST['g-recaptcha-response'])) {
564 return new WP_Error('spam_detection', __('Please recheck your reCaptcha Configuration', 'bit-form'));
565 }
566 $integrationHandler = new IntegrationHandler(0);
567 $allFormIntegrations = $integrationHandler->getAllIntegration('app', $captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3');
568 if (!is_wp_error($allFormIntegrations)) {
569 foreach ($allFormIntegrations as $integration) {
570 if (!is_null($integration->integration_type) && $integration->integration_type === ($captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3')) {
571 $integrationDetails = json_decode($integration->integration_details);
572 $integrationDetails->id = $integration->id;
573 $reCAPTCHA = $integrationDetails;
574 }
575 }
576 }
577 if (!empty($reCAPTCHA->secretKey)) {
578 $gRecaptchaResponse = HttpHelper::post(
579 'https://www.google.com/recaptcha/api/siteverify',
580 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
581 );
582 $isgReCaptchaVerified = false;
583 if (!is_wp_error($gRecaptchaResponse)) {
584 if (
585 $captchaV3Settings
586 && !empty($gRecaptchaResponse->score)
587 && ((float) $gRecaptchaResponse->score < (float) $captchaV3Settings->score)
588 ) {
589 wp_send_json_error(
590 __(
591 $captchaV3Settings->message,
592 'bit-form'
593 )
594 );
595 }
596
597 $isgReCaptchaVerified = $gRecaptchaResponse->success;
598 }
599 if (!$isgReCaptchaVerified) {
600 return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
601 }
602 }
603 }
604 }
605
606 private function verifyHCaptcha()
607 {
608 $hCaptchaExist = $this->isFieldTypeExist('hcaptcha'); // You can rename this to getHCaptchaSettings() if needed
609 if ($hCaptchaExist) {
610 if (!isset($_POST['h-captcha-response'])) {
611 return new WP_Error('spam_detection', __('Please verify hCaptcha', 'bit-form'));
612 }
613
614 $token = sanitize_text_field($_POST['h-captcha-response']);
615
616 $integrationHandler = new IntegrationHandler(0);
617 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'hcaptcha');
618
619 if (!is_wp_error($allFormIntegrations)) {
620 foreach ($allFormIntegrations as $integration) {
621 if (!is_null($integration->integration_type) && 'hcaptcha' === $integration->integration_type) {
622 $integrationDetails = json_decode($integration->integration_details);
623 $integrationDetails->id = $integration->id;
624 $hCaptcha = $integrationDetails;
625 }
626 }
627 }
628
629 if (!empty($hCaptcha->secretKey)) {
630 $hCaptchaResponse = HttpHelper::post(
631 'https://api.hcaptcha.com/siteverify',
632 [
633 'secret' => $hCaptcha->secretKey,
634 'response' => $token,
635 'remoteip' => $_SERVER['REMOTE_ADDR'] ?? ''
636 ]
637 );
638
639 $isVerified = false;
640 if (!is_wp_error($hCaptchaResponse)) {
641 $isVerified = $hCaptchaResponse->success;
642 }
643
644 if (!$isVerified) {
645 return new WP_Error('spam_detection', __('hCaptcha verification failed', 'bit-form'));
646 }
647 }
648 }
649 }
650
651 private function verifyTurnstileCaptcha()
652 {
653 $turnstileExist = $this->isFieldTypeExist('turnstile');
654 if ($turnstileExist) {
655 if (!isset($_POST['cf-turnstile-response'])) {
656 return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
657 }
658 $token = sanitize_text_field($_POST['cf-turnstile-response']);
659 $turnstileCaptcha = null;
660 $integrationHandler = new IntegrationHandler(0);
661 $turnstileIntegration = $integrationHandler->getAllIntegration('app', 'turnstileCaptcha')[0];
662 if (!is_wp_error($turnstileIntegration && !is_null($turnstileIntegration->integration_type))) {
663 $turnstileCaptcha = json_decode($turnstileIntegration->integration_details);
664 // $integrationDetails->id = $turnstileIntegration->id;
665 // $turnstileCaptcha = $integrationDetails;
666 }
667 if (!is_null($turnstileCaptcha)) {
668 $isTurnstileCaptchaVerified = false;
669 $turnstileRecaptchaResponse = HttpHelper::post(
670 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
671 ['secret' => $turnstileCaptcha->secretKey, 'response' => $token]
672 );
673 if (!is_wp_error($turnstileRecaptchaResponse)) {
674 if (!$turnstileRecaptchaResponse->success) {
675 wp_send_json_error(
676 __(
677 'Cloudflare Turnstile Validation Error: ' . implode(', ', $turnstileRecaptchaResponse->{'error-codes'}),
678 'bit-form'
679 )
680 );
681 }
682
683 $isTurnstileCaptchaVerified = $turnstileRecaptchaResponse->success;
684 }
685 if (!$isTurnstileCaptchaVerified) {
686 return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
687 }
688 }
689 }
690 }
691
692 public function verifySubmissionNonce()
693 {
694 if (!isset($_POST['t_identity']) && !isset($_POST['csrf'])) {
695 return false;
696 }
697 $tIdenty = sanitize_text_field($_POST['t_identity']);
698 $csrf = sanitize_text_field($_POST['csrf']);
699 unset($_POST['t_identity'], $_POST['action'], $_POST['bitforms_id'], $_POST['csrf']);
700 return Helpers::csrfDecrypted($tIdenty, $csrf);
701 }
702
703 public function setViewCount()
704 {
705 if (!current_user_can('manage_options')) {
706 $update_status = $this->formModel->update(
707 [
708 'views' => intval(static::$form[0]->views) + 1
709 ],
710 [
711 'id' => $this->form_id
712 ]
713 );
714 }
715 }
716
717 public function checkSubmissionRestriction($checkedEmptySubmitted = true)
718 {
719 $formContents = $this->getFormContent();
720 $fromRestrictionSetitingsEnabled = empty($formContents->additional->enabled) ? [] : $formContents->additional->enabled;
721 $fromRestrictionSetitings = empty($formContents->additional->settings) ? null : $formContents->additional->settings;
722 if (is_null($formContents->additional->enabled) || is_null($formContents->additional->settings)) {
723 return false;
724 }
725 $restrictionMessage = [];
726 $ipTool = new IpTool();
727 $ipAddress = $ipTool->getIP();
728 $currentUserId = get_current_user_id();
729 foreach ($fromRestrictionSetitingsEnabled as $restrictionKey => $isEnabled) {
730 if ($isEnabled) {
731 if (('entry_limit' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) || ('entry_limit_by_user' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey}))) {
732 $entryLimitHelper = new EntryLimitHelper($this->form_id, $fromRestrictionSetitings, $fromRestrictionSetitingsEnabled);
733 $advancedLimitMessages = $entryLimitHelper->checkAllLimits($ipAddress, $currentUserId);
734 $restrictionMessage = array_merge($restrictionMessage, $advancedLimitMessages);
735 }
736
737 if ('onePerIp' === $restrictionKey) {
738 $formEntry = new FormEntryModel();
739
740 $getResult = $formEntry->get(
741 ['user_ip', 'status'],
742 [
743 'form_id' => $this->form_id,
744 'user_ip' => ip2long($ipAddress)
745 ],
746 );
747
748 $count = 0;
749 $status = 0;
750
751 if (!is_wp_error($getResult) && count($getResult) > 0) {
752 $count = count($getResult);
753
754 foreach ($getResult as $row) {
755 if (9 === (int) $row->status) {
756 $status = 9;
757 break;
758 }
759 }
760 }
761
762 if ($count > 0 && 9 !== (int) $status) {
763 $onePerIp = __('Sorry!! You have already submitted from this IP address', 'bit-form');
764
765 $onePerIp = apply_filters(
766 'bitform_filter_restriction_one_per_ip_message',
767 $onePerIp,
768 $this->form_id
769 );
770
771 $restrictionMessage[] = $onePerIp;
772 }
773 }
774 if ('is_login' === $restrictionKey && 0 === get_current_user_id()) {
775 $is_login_messages = $fromRestrictionSetitings->is_login->message;
776
777 $is_login_messages = apply_filters(
778 'bitform_filter_restriction_is_login_message',
779 $is_login_messages,
780 $this->form_id
781 );
782
783 $restrictionMessage[] = $is_login_messages;
784 }
785 if ($checkedEmptySubmitted && 'empty_submission' === $restrictionKey) {
786 $isEmpty = $this->checkEmptySubmission($_POST, $_FILES);
787 if ($isEmpty) {
788 $restriction = $fromRestrictionSetitings->empty_submission->message;
789
790 $restriction = apply_filters(
791 'bitform_filter_restriction_empty_submission_message',
792 $restriction,
793 $this->form_id
794 );
795
796 $restrictionMessage[] = $restriction;
797 }
798 }
799 if ('restrict_form' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
800 $day = empty($fromRestrictionSetitings->{$restrictionKey}->day) ? null : $fromRestrictionSetitings->{$restrictionKey}->day;
801 $date = empty($fromRestrictionSetitings->{$restrictionKey}->date) ? null : $fromRestrictionSetitings->{$restrictionKey}->date;
802 $time = empty($fromRestrictionSetitings->{$restrictionKey}->time) ? null : $fromRestrictionSetitings->{$restrictionKey}->time;
803
804 $isdayOk = $isdateOk = $istimeOk = true;
805 $dayNotOkMsg = $dateNotOkMsg = $timeNotOkMsg = '';
806 $dateTimeHelper = new DateTimeHelper();
807 if (
808 !empty($day)
809 && is_array($day)
810 && (in_array('Friday', $day)
811 || in_array('Saturday', $day)
812 || in_array('Sunday', $day)
813 || in_array('Monday', $day)
814 || in_array('Tuesday', $day)
815 || in_array('Wednesday', $day)
816 || in_array('Thursday', $day))
817 && (!in_array($dateTimeHelper->getDay('full-name'), $day))
818 ) {
819 $isdayOk = false;
820 $dayMsgVarsFormat = '';
821 foreach ($day as $dayIndex => $dayValue) {
822 if ($dayIndex > 0) {
823 $dayMsgVarsFormat .= ', ';
824 }
825 $dayMsgVarsFormat .= '%s';
826 }
827 $dayNotOkMsg = vsprintf(__("in $dayMsgVarsFormat", 'bit-form'), $day);
828 }
829 if (
830 !empty($day)
831 && is_array($day)
832 && (in_array('Custom', $day))
833 ) {
834 $startDate = empty($date->from) ? '00-00-0000' : $date->from;
835 $endDate = empty($date->to) ? '00-00-0000' : $date->to;
836 $dateFormat = preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $startDate) ? 'Y-m-d' : 'm-d-Y';
837 if (!empty($date->from) && false !== strpos($startDate, 'T')) {
838 $startDate = $dateTimeHelper->getDate($startDate, false, null, $dateFormat);
839 }
840 if (!empty($date->to) && false !== strpos($endDate, 'T')) {
841 $endDate = $dateTimeHelper->getDate($endDate, false, null, $dateFormat);
842 }
843 $currentDate = $dateTimeHelper->getDate(null, null, null, $dateFormat);
844 if (!($currentDate >= $startDate && $currentDate <= $endDate)) {
845 $isdateOk = false;
846 $dateNotOkMsg = sprintf(__('within %s to %s', 'bit-form'), $startDate, $endDate);
847 }
848 }
849
850 if (!empty($time)) {
851 $startTime = empty($time->from) ? '00:00' : $time->from;
852 $endTime = empty($time->to) ? '23:59.999' : $time->to;
853 $currentTime = $dateTimeHelper->getTime(null, null, null, 'H:i');
854 if (!($currentTime >= $startTime && $currentTime <= $endTime)) {
855 $istimeOk = false;
856 $startTime = $dateTimeHelper->getTime($startTime, 'H:i', null);
857 $endTime = $dateTimeHelper->getTime($endTime, 'H:i', null);
858 $isTimeOk = false;
859 $timeNotOkMsg = sprintf(__('%s to %s', 'bit-form'), $startTime, $endTime);
860 }
861 }
862
863 if (!($isdateOk && $isdayOk && $istimeOk)) {
864 $restrict_form_message = null;
865 if (!$isdayOk) {
866 $restrict_form_message = !empty($timeNotOkMsg) ? sprintf(__('Form is available %s From %s', 'bit-form'), $dayNotOkMsg, $timeNotOkMsg) :
867 sprintf(__('Form is available %s', 'bit-form'), $dayNotOkMsg, $timeNotOkMsg);
868 } elseif (!$isdateOk) {
869 $restrict_form_message = !empty($timeNotOkMsg) ? sprintf(__('Form is available %s From %s', 'bit-form'), $dateNotOkMsg, $timeNotOkMsg) :
870 sprintf(__('Form is available %s', 'bit-form'), $dateNotOkMsg, $timeNotOkMsg);
871 } elseif (!$istimeOk) {
872 $restrict_form_message = sprintf(__('Form is available on %s', 'bit-form'), $timeNotOkMsg);
873 }
874
875 if ($restrict_form_message) {
876 $restrict_form_message = apply_filters(
877 'bitform_filter_restrict_form_message',
878 $restrict_form_message,
879 $this->form_id
880 );
881
882 $restrictionMessage[] = $restrict_form_message;
883 }
884 }
885 }
886 if ('blocked_ip' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
887 $isIpBlocked = false;
888 foreach ($fromRestrictionSetitings->{$restrictionKey} as $ipIndex => $ipDetails) {
889 if (!empty($ipDetails->status) && $ipDetails->status && !empty($ipDetails->ip) && $ipDetails->ip === $ipAddress) {
890 $isIpBlocked = true;
891 break;
892 }
893 }
894 if ($isIpBlocked) {
895 $blocked_ip_message = sprintf(
896 __('Sorry!! Your IP address is %s, Blocked from submitting the form', 'bit-form'),
897 $ipAddress
898 );
899
900 $blocked_ip_message = apply_filters(
901 'bitform_filter_restricted_ip_message',
902 $blocked_ip_message,
903 $this->form_id
904 );
905
906 $restrictionMessage[] = $blocked_ip_message;
907 }
908 }
909 if ('private_ip' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
910 $isIpWhiteListed = false;
911 foreach ($fromRestrictionSetitings->{$restrictionKey} as $ipIndex => $ipDetails) {
912 if (!empty($ipDetails->status) && $ipDetails->status && !empty($ipDetails->ip) && $ipDetails->ip === $ipAddress) {
913 $isIpWhiteListed = true;
914 break;
915 }
916 }
917 if (!$isIpWhiteListed) {
918 $private_ip = sprintf(
919 __('Sorry!! Your IP address is %s, Blocked from submitting the form', 'bit-form'),
920 $ipAddress
921 );
922
923 $private_ip = apply_filters(
924 'bitform_filter_private_ip_message',
925 $private_ip,
926 $this->form_id
927 );
928
929 $restrictionMessage[] = $private_ip;
930 }
931 }
932 }
933 }
934 return $restrictionMessage;
935 }
936
937 /**
938 * Will check if form is submitted by a bot
939 *
940 * @return Boolean true - if submitted by bot else false
941 */
942 public function isTrappedInHoneypot()
943 {
944 $isHoneyPot = false;
945
946 if (!$this->isHoneypotActive()) {
947 return false;
948 }
949
950 $token = $_POST['b_h_t'];
951 $pattern = '/^([a-zA-Z0-9]*_[a-zA-Z0-9]*){4}$/';
952 $decryptedToken = base64_decode(base64_decode($token));
953
954 preg_match($pattern, $decryptedToken, $validToken);
955
956 if ($validToken) {
957 if (isset($_POST[$token]) && empty($_POST[$token])) {
958 $isHoneyPot = false;
959 } else {
960 $isHoneyPot = true;
961 }
962 } else {
963 $isHoneyPot = true;
964 }
965
966 if (isset($_POST[$token])) {
967 unset($_POST[$token]);
968 }
969 unset($_POST['b_h_t']);
970 return $isHoneyPot;
971 }
972
973 public function isHoneypotActive()
974 {
975 $formContents = $this->getFormContent();
976 $enabled = empty($formContents->additional->enabled) ? null : $formContents->additional->enabled;
977 if (!empty($enabled->honeypot) && $enabled->honeypot) {
978 return true;
979 }
980 return false;
981 }
982
983 public function checkPaymentFields()
984 {
985 $formContents = $this->getFormContent();
986 $fields = $formContents->fields;
987
988 $payments = [];
989 foreach ($fields as $fldData) {
990 if ('paypal' === $fldData->typ && property_exists($fldData, 'payIntegID')) {
991 $payments['paypalKey'] = $this->getClientKey($fldData->payIntegID, 'clientID');
992 } elseif ('razorpay' === $fldData->typ && property_exists($fldData->options, 'payIntegID')) {
993 $payments['razorpayKey'] = $this->getClientKey($fldData->options->payIntegID, 'apiKey');
994 }
995 }
996
997 return $payments;
998 }
999
1000 private function getClientKey($integID, $keyName)
1001 {
1002 $client = '';
1003 if (!empty($integID)) {
1004 $integrationHandler = new IntegrationHandler(0);
1005 $integration = $integrationHandler->getAIntegration($integID, 'app', 'payments');
1006 if (!is_wp_error($integration)) {
1007 $integration_details = json_decode($integration[0]->integration_details);
1008 $client = base64_encode($integration_details->{$keyName});
1009 }
1010 }
1011 return $client;
1012 }
1013
1014 public function getSuccessMessageMarkups()
1015 {
1016 if (is_null($this->_work_flows)) {
1017 $workFlowManager = new WorkFlowHandler($this->form_id);
1018 $this->_work_flows = $workFlowManager->getAllworkFlow();
1019 }
1020
1021 $ids = [];
1022 foreach ($this->_work_flows as $msgItem) {
1023 foreach ($msgItem['conditions'] as $condition) {
1024 if (isset($condition->actions->success)) {
1025 foreach ($condition->actions->success as $msg) {
1026 if ('successMsg' === $msg->type && isset($msg->details->id)) {
1027 $idObj = json_decode(stripslashes($msg->details->id));
1028 if (is_object($idObj) && !empty($idObj->id)) {
1029 array_push($ids, $idObj->id);
1030 }
1031 }
1032 }
1033 }
1034 if (isset($condition->actions->failure)) {
1035 $idObj = json_decode(stripslashes($condition->actions->failure));
1036 if (is_object($idObj) && !empty($idObj->id)) {
1037 array_push($ids, $idObj->id);
1038 }
1039 }
1040 }
1041 }
1042 $ids = array_unique($ids);
1043 if (is_null($this->_conf_messages)) {
1044 $successMsgHandler = new SuccessMessageHandler($this->form_id);
1045 $this->_conf_messages = $successMsgHandler->getMessages($ids);
1046 }
1047
1048 $messageMarkups = '';
1049 if (is_wp_error($this->_conf_messages)) {
1050 return $messageMarkups;
1051 }
1052
1053 foreach ($this->_conf_messages as $msgItem) {
1054 $messageMarkups .= $this->messageMarkup($msgItem);
1055 }
1056
1057 return $messageMarkups;
1058 }
1059
1060 public function getFormAbandonmentMessage()
1061 {
1062 if (class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
1063 $formAbandonmentSettings = FormAbandonment::getFormAbandonmentSettings($this->form_id);
1064 $msg = '';
1065 if (isset($formAbandonmentSettings->showWarningMsg) && $formAbandonmentSettings->showWarningMsg && !empty($formAbandonmentSettings->warningMsg)) {
1066 $msg = $formAbandonmentSettings->warningMsg;
1067 $msg = '<div class="bf-form-msg active warning">' . wp_kses_post($msg) . '</div>';
1068 }
1069 return $msg;
1070 }
1071 }
1072
1073 public function getFormAbandonmentSettings()
1074 {
1075 if (class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
1076 $formAbandonmentSettings = FormAbandonment::getFormAbandonmentSettings($this->form_id);
1077 return $formAbandonmentSettings;
1078 }
1079 return null;
1080 }
1081
1082 private function messageMarkup($msg)
1083 {
1084 $msgId = $msg->id;
1085 $msgConfig = json_decode($msg->message_config);
1086 $scrollClass = 'below' === $msgConfig->msgType ? 'scroll' : '';
1087
1088 return <<<SUCCESSMSG
1089 <div role="dialog" aria-hidden="true" data-modal-backdrop="true" class="{$this->getAtomicCls("msg-container-{$msgId}")} deactive {$scrollClass}">
1090 <div data-contentid="{$this->getFormIdentifier()}" data-msgid="{$msgId}" role="button" class="{$this->getAtomicCls("msg-background-{$msgId}")} msg-backdrop">
1091 <div class="bf-msg-content {$this->getAtomicCls("msg-content-{$msgId}")}">
1092 <button data-contentid="{$this->getFormIdentifier()}" data-msgid="{$msgId}" class="{$this->getAtomicCls("close-{$msgId}")} bf-msg-close" type="button">
1093 <svg class="{$this->getAtomicCls("close-icn-{$msgId}")}" viewBox="0 0 30 30">
1094 <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="4" y1="3.88" x2="26" y2="26.12"></line>
1095 <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="26" y1="3.88" x2="4" y2="26.12"></line>
1096 </svg>
1097 </button>
1098 <div class="msg-content"></div>
1099 </div>
1100 </div>
1101 </div>
1102 SUCCESSMSG;
1103 }
1104 }
1105