PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.16.2
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.16.2
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.16.2, at includes/Frontend/Form/FrontendFormManager.php

946 lines 36.8 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\Helpers;
14 use BitCode\BitForm\Core\Database\FormEntryModel;
15 use BitCode\BitForm\Core\Form\FormManager;
16 use BitCode\BitForm\Core\Form\Validator\FormFieldValidator;
17 use BitCode\BitForm\Core\Integration\IntegrationHandler;
18 use BitCode\BitForm\Core\Messages\SuccessMessageHandler;
19 use BitCode\BitForm\Core\Util\ApiResponse as UtilApiResponse;
20 use BitCode\BitForm\Core\Util\DateTimeHelper;
21 use BitCode\BitForm\Core\Util\HttpHelper;
22 use BitCode\BitForm\Core\Util\IpTool;
23 use BitCode\BitForm\Core\WorkFlow\WorkFlow;
24 use BitCode\BitForm\Core\WorkFlow\WorkFlowHandler;
25 use BitCode\BitForm\Frontend\Form\View\FormViewer;
26 use BitCode\BitFormPro\Admin\FormSettings\FormAbandonment;
27 use WP_Error;
28
29 final class FrontendFormManager extends FormManager
30 {
31 private $_form_identifier;
32 private $_form_token;
33 private $_form_id;
34 private $_work_flows;
35 private $_conf_messages;
36
37 // private $_has_upload = false;
38 public function __construct($form_id, $shortCodeCounter = null)
39 {
40 parent::__construct($form_id);
41 $this->_form_identifier = 'bitforms_' . $form_id;
42 $this->_form_identifier .= !empty(get_post()->ID) ? '_' . get_post()->ID : '';
43 $this->_form_identifier .= !empty($shortCodeCounter) ? "_$shortCodeCounter" : '';
44 $this->_form_token = wp_create_nonce('bitforms_' . $form_id);
45 $this->_form_id = $form_id;
46 }
47
48 public function getFormIdentifier()
49 {
50 return $this->_form_identifier;
51 }
52
53 public function getFormID()
54 {
55 return $this->_form_id;
56 }
57
58 public function getFormToken()
59 {
60 return $this->_form_token;
61 }
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
69 public function getSubmittedFields($submitted_data)
70 {
71 unset($submitted_data[$this->_form_identifier]);
72 // unset($submitted_data['bit-form-submit-btn']);
73 return array_keys($submitted_data);
74 }
75
76 public function formView($fields = null, $hasFile = false, $errorMessages = null, $previousValue = null)
77 {
78 $formContents = $this->getFormContent();
79 $formAtomicClsMap = $this->getAtomicClsMap();
80 if (!empty($fields)) {
81 $formContents->fields = is_string($fields) ? json_decode($fields) : $fields;
82 } else {
83 $workFlowRunHelper = new WorkFlow($this->form_id);
84 $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(
85 'create',
86 $formContents->fields
87 );
88 $formContents->fields = empty($workFlowreturnedOnLoad['fields']) ? $formContents->fields : $workFlowreturnedOnLoad['fields'];
89 }
90 $formViewer = new FormViewer($this, $formContents, $formAtomicClsMap, $errorMessages, $previousValue);
91 $isRestricted = $this->checkSubmissionRestriction(false);
92 $msg = !empty($isRestricted) ? $isRestricted[0] : '';
93 return $formViewer->getView($hasFile, $msg);
94 }
95
96 public function conversationalFormView($fields = null, $hasFile = false, $errorMessages = null, $previousValue = null)
97 {
98 $formContents = $this->getFormContent();
99 $formAtomicClsMap = $this->getAtomicClsMap();
100 if (!empty($fields)) {
101 $formContents->fields = is_string($fields) ? json_decode($fields) : $fields;
102 } else {
103 $workFlowRunHelper = new WorkFlow($this->form_id);
104 $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(
105 'create',
106 $formContents->fields
107 );
108 $formContents->fields = empty($workFlowreturnedOnLoad['fields']) ? $formContents->fields : $workFlowreturnedOnLoad['fields'];
109 }
110 $formViewer = new FormViewer($this, $formContents, $formAtomicClsMap, $errorMessages, $previousValue);
111 $isRestricted = $this->checkSubmissionRestriction(false);
112 $msg = !empty($isRestricted) ? $isRestricted[0] : '';
113 return $formViewer->getConversationalView($hasFile, $msg);
114 }
115
116 public function checkEmptySubmission($data, $file)
117 {
118 $formFields = $this->getFields();
119 foreach ($formFields as $key => $field) {
120 $fieldType = $field['type'];
121 if ('button' === $fieldType) {
122 continue;
123 }
124 $fileUploadFieldTypes = ['file-up', 'advanced-file-up'];
125 if ('decision-box' === $fieldType || 'gdpr' === $fieldType) {
126 continue;
127 }
128 $isFileType = in_array($fieldType, $fileUploadFieldTypes);
129 if ($this->isRepeatedField($key)) {
130 $fileData = !empty($file[$key]) ? $file[$key] : [];
131 $dataVal = !empty($data[$key]) ? $data[$key] : [];
132 if (!$this->checkRepeatedFieldEmptySubmission($isFileType, $dataVal, $fileData)) {
133 return false;
134 }
135 continue;
136 }
137 if (!$isFileType && (!empty($data[$key]) || (isset($data[$key]) && is_numeric($data[$key])))) {
138 return false;
139 }
140 if ($isFileType && !empty($file[$key]['name']) && is_string($file[$key]['name'])) {
141 return false;
142 }
143 if ($isFileType && !empty($file[$key]['name'][0])) {
144 return false;
145 }
146 }
147 return true;
148 }
149
150 private function checkRepeatedFieldEmptySubmission($isFileType, $data, $file = [])
151 {
152 if (!$isFileType) {
153 foreach ($data as $value) {
154 if (!empty($value)) {
155 return false;
156 }
157 }
158 }
159 if ($isFileType) {
160 foreach ($file['name'] as $value) {
161 if (!empty($value) && is_string($value)) {
162 return false;
163 }
164 if (is_array($value) && !empty($value[0])) {
165 return false;
166 }
167 }
168 }
169 return true;
170 }
171
172 private function getParams()
173 {
174 $url = wp_parse_url(wp_get_referer());
175 $parameter = [];
176 if (isset($url['query'])) {
177 $queries = explode('&', $url['query']);
178 foreach ($queries as $query) {
179 list($field, $value) = explode('=', $query);
180 $parameter[$field] = $value;
181 }
182 }
183 return $parameter;
184 }
185
186 public function handleSubmission()
187 {
188 $this->fieldNameReplaceOfPost();
189
190 $validated = $this->beforeSubmittedValidate();
191 $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);
192
193 if (true === $validated) {
194 do_action('bitform_validation_success', $this->_form_id);
195 unset($_POST['hidden_fields']);
196
197 $redirectPage = '';
198 $regSuccMsg = '';
199
200 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
201 if (!is_wp_error($existAuth) && count($existAuth) > 0) {
202 $parameter = $this->getParams();
203 $existAuthFilter = has_filter('bf_wp_user_auth');
204
205 if (true === $existAuthFilter) {
206 $result = apply_filters('bf_wp_user_auth', $existAuth[0], $_POST, $parameter);
207
208 $result = apply_filters('bitform_filter_wp_user_auth_response', $result, $this->_form_id, $_POST, $parameter);
209
210 do_action('bitform_wp_user_auth_response', $result, $this->_form_id, $_POST, $parameter);
211
212 if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
213 if (!$result['success']) {
214 return new WP_Error('errors', __($result['message'], 'bit-form'));
215 } elseif (isset($result['success'])) {
216 $redirectPage = $result['redirectPage'];
217 $regSuccMsg = $result['message'];
218 $newNonce = wp_create_nonce('bitforms_' . $this->_form_id);
219 }
220 } else {
221 if (!$result['success']) {
222 return new WP_Error('errors', __($result['message'], 'bit-form'));
223 } else {
224 return $result;
225 }
226 }
227 }
228 }
229
230 $saveResponse = $this->saveFormEntry($_POST);
231 if (is_wp_error($saveResponse)) {
232 return $saveResponse;
233 }
234
235 $entryID = $saveResponse['entry_id'];
236 do_action('bitform_submit_success', $this->_form_id, $entryID, $_POST);
237
238 // check and replace signature field value
239 $formFields = $this->getFields();
240 $uploadPath = BITFORMS_UPLOAD_BASE_URL . "/uploads/{$this->_form_id}/{$entryID}";
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
249 $captchaV3Settings = $this->getCaptchaV3Settings();
250 if ($captchaV3Settings) {
251 $token = $_POST['g-recaptcha-response'];
252 $integrationHandler = new IntegrationHandler(0);
253 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
254 if (!is_wp_error($allFormIntegrations)) {
255 foreach ($allFormIntegrations as $integration) {
256 if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
257 $integrationDetails = json_decode($integration->integration_details);
258 $integrationDetails->id = $integration->id;
259 $reCAPTCHA = $integrationDetails;
260 }
261 }
262 }
263 if (!empty($reCAPTCHA->secretKey)) {
264 $gRecaptchaResponse = HttpHelper::post(
265 'https://www.google.com/recaptcha/api/siteverify',
266 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
267 );
268 if ($captchaV3Settings && !empty($saveResponse['triggerData'])) {
269 $logID = $saveResponse['triggerData']['logID'];
270 $integId = $reCAPTCHA->id;
271 $saveApiResponse = new UtilApiResponse();
272 $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
273 }
274 }
275 unset($_POST['g-recaptcha-response']);
276 }
277 if (!empty($redirectPage) && empty($saveResponse['redirectPage']) || null === $saveResponse['redirectPage']) {
278 $saveResponse['redirectPage'] = $redirectPage;
279 }
280 if (!empty($regSuccMsg) && isset($saveResponse['dflt_message'])) {
281 $saveResponse['message'] = $regSuccMsg;
282 }
283
284 $saveResponse = IntegrationHandler::maybeSetCronForIntegration($saveResponse, 'create');
285 $entryId = $saveResponse['entry_id'];
286
287 $responseMsg = is_array($saveResponse) && !empty($saveResponse) ? $saveResponse : __('Form Submitted Successfully', 'bit-form');
288 if (isset($newNonce)) {
289 $responseMsg['new_nonce'] = $newNonce;
290 }
291 $_POST = [];
292 $responseMsg['entry_id'] = $entryId;
293 return $responseMsg;
294 }
295 do_action('bitform_validation_error', $this->_form_id, $validated);
296 return $validated;
297 }
298
299 public function handleUpdateEntry()
300 {
301 $this->fieldNameReplaceOfPost();
302 $validated = $this->beforeSubmittedValidate();
303 $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);
304
305 $entryID = $_REQUEST['entryID'];
306 $GLOBALS['bf_entry_id'] = $entryID;
307 if (is_null($entryID)) {
308 return new WP_Error('empty_form', __('Entries id is invalid', 'bit-form'));
309 }
310 if (true === $validated) {
311 do_action('bitform_validation_success', $this->_form_id);
312 unset($_POST['hidden_fields'], $_POST['entryID']);
313
314 $redirectPage = '';
315 $regSuccMsg = '';
316
317 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
318 if (!is_wp_error($existAuth) && count($existAuth) > 0) {
319 $parameter = $this->getParams();
320 $existAuthFilter = has_filter('bf_wp_user_auth');
321
322 if (true === $existAuthFilter) {
323 $result = apply_filters('bf_wp_user_auth', $existAuth[0], $_POST, $parameter);
324
325 if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
326 if (!$result['success']) {
327 return new WP_Error('errors', __($result['message'], 'bit-form'));
328 } elseif (isset($result['success'])) {
329 $redirectPage = $result['redirectPage'];
330 $regSuccMsg = $result['message'];
331 $newNonce = wp_create_nonce('bitforms_' . $this->_form_id);
332 }
333 } else {
334 if (!$result['success']) {
335 return new WP_Error('errors', __($result['message'], 'bit-form'));
336 } else {
337 return $result;
338 }
339 }
340 }
341 }
342
343 $updateResponse = $this->updateFormEntry($_POST, $this->getFormID(), $entryID);
344 if (is_wp_error($updateResponse)) {
345 return $updateResponse;
346 }
347
348 do_action('bitform_submit_success', $this->_form_id, $entryID, $_POST);
349
350 $captchaV3Settings = $this->getCaptchaV3Settings();
351 if ($captchaV3Settings) {
352 $token = $_POST['g-recaptcha-response'];
353 $integrationHandler = new IntegrationHandler(0);
354 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
355 if (!is_wp_error($allFormIntegrations)) {
356 foreach ($allFormIntegrations as $integration) {
357 if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
358 $integrationDetails = json_decode($integration->integration_details);
359 $integrationDetails->id = $integration->id;
360 $reCAPTCHA = $integrationDetails;
361 }
362 }
363 }
364 if (!empty($reCAPTCHA->secretKey)) {
365 $gRecaptchaResponse = HttpHelper::post(
366 'https://www.google.com/recaptcha/api/siteverify',
367 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
368 );
369 if ($captchaV3Settings && !empty($updateResponse['triggerData'])) {
370 $logID = $updateResponse['triggerData']['logID'];
371 $integId = $reCAPTCHA->id;
372 $saveApiResponse = new UtilApiResponse();
373 $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
374 }
375 }
376 unset($_POST['g-recaptcha-response']);
377 }
378 if (!empty($redirectPage) && empty($updateResponse['redirectPage']) || null === $updateResponse['redirectPage']) {
379 $updateResponse['redirectPage'] = $redirectPage;
380 }
381 if (!empty($regSuccMsg) && isset($updateResponse['dflt_message'])) {
382 $updateResponse['message'] = $regSuccMsg;
383 }
384 $updateResponse = IntegrationHandler::maybeSetCronForIntegration($updateResponse, 'create');
385 $entryId = $updateResponse['entry_id'];
386
387 $responseMsg = is_array($updateResponse) && !empty($updateResponse) ? $updateResponse : __('Entry Update Successfully', 'bit-form');
388 if (isset($newNonce)) {
389 $responseMsg['new_nonce'] = $newNonce;
390 }
391 $_POST = [];
392 $responseMsg['entry_id'] = $entryId;
393 return $responseMsg;
394 }
395 do_action('bitform_validation_error', $this->_form_id, $validated);
396 return $validated;
397 }
398
399 public function validateFormSubmission($submitted_data)
400 {
401 $hidden_fields = isset($submitted_data['hidden_fields']) ? $submitted_data['hidden_fields'] : '';
402 $submitted_fields = $this->getSubmittedFields($submitted_data);
403 $form_fields = $this->getFields();
404 $form_fields_names = array_keys($form_fields);
405 if ($this->isGCLIDEnabled()) {
406 array_push($form_fields_names, 'GCLID');
407 }
408 foreach ($submitted_fields as $field) {
409 if ('hidden_fields' !== $field && !in_array($field, $form_fields_names) || false !== strpos($hidden_fields, $field)) {
410 unset($submitted_data[$field]);
411 }
412 }
413 return $submitted_data;
414 }
415
416 public function beforeSubmittedValidate()
417 {
418 if ($this->verifySubmissionNonce()) {
419 if ($this->isExist()) {
420 $isRestricted = $this->checkSubmissionRestriction();
421 if ($isRestricted && !empty($isRestricted)) {
422 return new WP_Error('spam_detection', $isRestricted[0]);
423 }
424 if ($this->isTrappedInHoneypot()) {
425 return new WP_Error('spam_detection', __('Token verification failed', 'bit-form'));
426 }
427 $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
428
429 if (is_wp_error($this->verifyGRecaptcha())) {
430 return $this->verifyGRecaptcha();
431 }
432
433 /* Implement Turnstile Captcha start */
434 if (is_wp_error($this->verifyTurnstileCaptcha())) {
435 return $this->verifyTurnstileCaptcha();
436 }
437 /* Implement Turnstile Captcha end */
438
439 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
440
441 // check if user is already logged in and form has auth integration
442 do_action('bitform_checked_exist_auth', $this->_form_id, $existAuth);
443 if (!is_wp_error($existAuth) && count($existAuth) > 0 && is_user_logged_in()) {
444 return new WP_Error('auth_error', __('You are already logged in', 'bit-form'));
445 }
446 $validateForm = $this->validateFormSubmission($_POST);
447 $validateFormFiles = $this->validateFormSubmission($_FILES);
448 $validateForm = array_merge($validateForm, $validateFormFiles);
449 $form_fields = $this->getFields();
450 // check if form-current-step is set and form is multi-step
451 $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
452 if (!is_null($formCurrentStep)) {
453 $formContents = $this->getFormContent();
454 $layout = $formContents->layout;
455 $stepIndex = (int) $formCurrentStep - 1;
456 $stepLayout = $layout[$stepIndex]->layout->lg;
457 $nestedLayout = $formContents->nestedLayout;
458 $step_fields = [];
459 foreach ($stepLayout as $lay) {
460 $fk = $lay->i;
461 if (isset($nestedLayout->{$fk})) {
462 $nestedLg = $nestedLayout->{$fk}->lg;
463 foreach ($nestedLg as $nestedLay) {
464 $nestedFk = $nestedLay->i;
465 $step_fields[$nestedFk] = $form_fields[$nestedFk];
466 }
467 }
468 $step_fields[$fk] = $form_fields[$fk];
469 }
470 $form_fields = $step_fields;
471 }
472 $formFieldValidator = new FormFieldValidator($form_fields, $_POST, $_FILES);
473 $validUniuqFields = [];
474 $existFilter = has_filter('bf_check_duplicate_entry');
475 if (true === $existFilter) {
476 $validUniuqFields = apply_filters('bf_check_duplicate_entry', $form_fields, $_POST);
477
478 $fieldKeys = array_keys($validUniuqFields);
479 $form_fields_keys = array_keys($form_fields);
480 $uniqueFields = [];
481 foreach ($fieldKeys as $key) {
482 if (in_array($key, $form_fields_keys)) {
483 $uniqueFields[] = $form_fields[$key];
484 }
485 }
486 do_action('bitform_Unique_entry', $uniqueFields, $validUniuqFields, $this->_form_id, $_POST);
487 }
488 $validateField = $formFieldValidator->validate('create', $this->_form_id);
489
490 if ($validateForm && $validateField && 0 === count($validUniuqFields)) {
491 return true;
492 } else {
493 $error = __('Please submit form with valid fields', 'bit-form');
494 if (!$validateForm) {
495 $errorMessages = $error;
496 } elseif (count($formFieldValidator->getMessage()) > 0) {
497 $errorMessages = $formFieldValidator->getMessage();
498 } else {
499 $errorMessages = 0 === count($validUniuqFields) ? $error : $validUniuqFields;
500 }
501 return new WP_Error('validation_error', $errorMessages);
502 }
503 }
504 return new WP_Error('unknown_form', __('Form does not exist', 'bit-form'));
505 } else {
506 return new WP_Error('token_expired', __('Token expired', 'bit-form'));
507 }
508 }
509
510 private function verifyGRecaptcha()
511 {
512 $captchaSettings = $this->getCaptchaSettings();
513 $captchaV3Settings = $this->getCaptchaV3Settings();
514 if ($captchaSettings || $captchaV3Settings) {
515 $token = $_POST['g-recaptcha-response'];
516 if (!isset($_POST['g-recaptcha-response'])) {
517 return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
518 }
519 $integrationHandler = new IntegrationHandler(0);
520 $allFormIntegrations = $integrationHandler->getAllIntegration('app', $captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3');
521 if (!is_wp_error($allFormIntegrations)) {
522 foreach ($allFormIntegrations as $integration) {
523 if (!is_null($integration->integration_type) && $integration->integration_type === ($captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3')) {
524 $integrationDetails = json_decode($integration->integration_details);
525 $integrationDetails->id = $integration->id;
526 $reCAPTCHA = $integrationDetails;
527 }
528 }
529 }
530 if (!empty($reCAPTCHA->secretKey)) {
531 $gRecaptchaResponse = HttpHelper::post(
532 'https://www.google.com/recaptcha/api/siteverify',
533 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
534 );
535 $isgReCaptchaVerified = false;
536 if (!is_wp_error($gRecaptchaResponse)) {
537 if (
538 $captchaV3Settings
539 && !empty($gRecaptchaResponse->score)
540 && ((float) $gRecaptchaResponse->score < (float) $captchaV3Settings->score)
541 ) {
542 wp_send_json_error(
543 __(
544 $captchaV3Settings->message,
545 'bit-form'
546 )
547 );
548 }
549
550 $isgReCaptchaVerified = $gRecaptchaResponse->success;
551 }
552 if (!$isgReCaptchaVerified) {
553 return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
554 }
555 }
556 }
557 }
558
559 private function verifyTurnstileCaptcha()
560 {
561 $turnstileSetting = $this->getTurnstileSettings();
562 if ($turnstileSetting) {
563 if (!isset($_POST['cf-turnstile-response'])) {
564 return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
565 }
566 $token = $_POST['cf-turnstile-response'];
567 $turnstileCaptcha = null;
568 $integrationHandler = new IntegrationHandler(0);
569 $turnstileIntegration = $integrationHandler->getAllIntegration('app', 'turnstileCaptcha')[0];
570 if (!is_wp_error($turnstileIntegration && !is_null($turnstileIntegration->integration_type))) {
571 $turnstileCaptcha = json_decode($turnstileIntegration->integration_details);
572 // $integrationDetails->id = $turnstileIntegration->id;
573 // $turnstileCaptcha = $integrationDetails;
574 }
575 if (!is_null($turnstileCaptcha)) {
576 $isTurnstileCaptchaVerified = false;
577 $turnstileRecaptchaResponse = HttpHelper::post(
578 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
579 ['secret' => $turnstileCaptcha->secretKey, 'response' => $token]
580 );
581 if (!is_wp_error($turnstileRecaptchaResponse)) {
582 if (!$turnstileRecaptchaResponse->success) {
583 wp_send_json_error(
584 __(
585 'Cloudflare Turnstile Validation Error: ' . implode(', ', $turnstileRecaptchaResponse->{'error-codes'}),
586 'bit-form'
587 )
588 );
589 }
590
591 $isTurnstileCaptchaVerified = $turnstileRecaptchaResponse->success;
592 }
593 if (!$isTurnstileCaptchaVerified) {
594 return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
595 }
596 }
597 }
598 }
599
600 public function verifySubmissionNonce()
601 {
602 if (!isset($_POST['t_identity']) && !isset($_POST['csrf'])) {
603 return false;
604 }
605 $tIdenty = sanitize_text_field($_POST['t_identity']);
606 $csrf = sanitize_text_field($_POST['csrf']);
607 unset($_POST['t_identity'], $_POST['action'], $_POST['bitforms_id'], $_POST['csrf']);
608 return Helpers::csrfDecrypted($tIdenty, $csrf);
609 }
610
611 public function setViewCount()
612 {
613 if (!current_user_can('manage_options')) {
614 $update_status = $this->formModel->update(
615 [
616 'views' => intval(static::$form[0]->views) + 1
617 ],
618 [
619 'id' => $this->form_id
620 ]
621 );
622 }
623 }
624
625 public function checkSubmissionRestriction($checkedEmptySubmitted = true)
626 {
627 $formContents = $this->getFormContent();
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)) {
631 return false;
632 }
633 $restrictionMessage = [];
634 $ipTool = new IpTool();
635 $ipAddress = $ipTool->getIP();
636 foreach ($fromRestrictionSetitingsEnabled as $restrictionKey => $isEnabled) {
637 if ($isEnabled) {
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 }
649 }
650 if ('onePerIp' === $restrictionKey) {
651 $formEntry = new FormEntryModel();
652 $countResult = $formEntry->count(
653 [
654 'form_id' => $this->form_id,
655 'user_ip' => ip2long($ipAddress)
656 ]
657 );
658 $count = !empty($countResult[0]) && !empty($countResult[0]->count) ? $countResult[0]->count : false;
659
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;
677
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 .= ', ';
698 }
699 $dayMsgVarsFormat .= '%s';
700 }
701 $dayNotOkMsg = vsprintf(__("in $dayMsgVarsFormat", 'bit-form'), $day);
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 }
723
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 }
736
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 }
747 }
748 }
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 }
760 }
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 }
768 }
769 if (!$isIpWhiteListed) {
770 $restrictionMessage[] = sprintf(__('Sorry!! Your IP address is %s, Blocked from submitting the form', 'bit-form'), $ipAddress);
771 }
772 }
773 }
774 }
775 return $restrictionMessage;
776 }
777
778 /**
779 * Will check if form is submitted by a bot
780 *
781 * @return Boolean true - if submitted by bot else false
782 */
783 public function isTrappedInHoneypot()
784 {
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;
812 }
813
814 public function isHoneypotActive()
815 {
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;
822 }
823
824 public function checkPaymentFields()
825 {
826 $formContents = $this->getFormContent();
827 $fields = $formContents->fields;
828
829 $payments = [];
830 foreach ($fields as $fldData) {
831 if ('paypal' === $fldData->typ && property_exists($fldData, 'payIntegID')) {
832 $payments['paypalKey'] = $this->getClientKey($fldData->payIntegID, 'clientID');
833 } elseif ('razorpay' === $fldData->typ && property_exists($fldData->options, 'payIntegID')) {
834 $payments['razorpayKey'] = $this->getClientKey($fldData->options->payIntegID, 'apiKey');
835 }
836 }
837
838 return $payments;
839 }
840
841 private function getClientKey($integID, $keyName)
842 {
843 $client = '';
844 if (!empty($integID)) {
845 $integrationHandler = new IntegrationHandler(0);
846 $integration = $integrationHandler->getAIntegration($integID, 'app', 'payments');
847 if (!is_wp_error($integration)) {
848 $integration_details = json_decode($integration[0]->integration_details);
849 $client = base64_encode($integration_details->{$keyName});
850 }
851 }
852 return $client;
853 }
854
855 public function getSuccessMessageMarkups()
856 {
857 if (is_null($this->_work_flows)) {
858 $workFlowManager = new WorkFlowHandler($this->form_id);
859 $this->_work_flows = $workFlowManager->getAllworkFlow();
860 }
861
862 $ids = [];
863 foreach ($this->_work_flows as $msgItem) {
864 foreach ($msgItem['conditions'] as $condition) {
865 if (isset($condition->actions->success)) {
866 foreach ($condition->actions->success as $msg) {
867 if ('successMsg' === $msg->type && isset($msg->details->id)) {
868 $idObj = json_decode(stripslashes($msg->details->id));
869 if (is_object($idObj) && !empty($idObj->id)) {
870 array_push($ids, $idObj->id);
871 }
872 }
873 }
874 }
875 if (isset($condition->actions->failure)) {
876 $idObj = json_decode(stripslashes($condition->actions->failure));
877 if (is_object($idObj) && !empty($idObj->id)) {
878 array_push($ids, $idObj->id);
879 }
880 }
881 }
882 }
883 $ids = array_unique($ids);
884 if (is_null($this->_conf_messages)) {
885 $successMsgHandler = new SuccessMessageHandler($this->form_id);
886 $this->_conf_messages = $successMsgHandler->getMessages($ids);
887 }
888
889 $messageMarkups = '';
890 if (is_wp_error($this->_conf_messages)) {
891 return $messageMarkups;
892 }
893
894 foreach ($this->_conf_messages as $msgItem) {
895 $messageMarkups .= $this->messageMarkup($msgItem);
896 }
897
898 return $messageMarkups;
899 }
900
901 public function getFormAbandonmentMessage()
902 {
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 }
912 }
913
914 public function getFormAbandonmentSettings()
915 {
916 if (class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
917 $formAbandonmentSettings = FormAbandonment::getFormAbandonmentSettings($this->form_id);
918 return $formAbandonmentSettings;
919 }
920 return null;
921 }
922
923 private function messageMarkup($msg)
924 {
925 $msgId = $msg->id;
926 $msgConfig = json_decode($msg->message_config);
927 $scrollClass = 'below' === $msgConfig->msgType ? 'scroll' : '';
928
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;
944 }
945 }
946