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

949 lines 37.1 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, $_FILES);
237
238 // check and replace signature field value
239 $formFields = $this->getFields();
240 // $uploadPath = BITFORMS_UPLOAD_BASE_URL . "/uploads/{$this->_form_id}/{$entryID}"; // web root
241 // $uploadPath = BITFORMS_UPLOAD_DIR . "/{$this->_form_id}/{$entryID}"; // server root
242 $encrypt_directory = Helpers::getEncryptedEntryId($entryID);
243 $uploadPath = $this->_form_id . DIRECTORY_SEPARATOR . $encrypt_directory;
244
245 foreach ($formFields as $key => $field) {
246 if ('signature' === $field['type']) {
247 $saveResponse['fields'][$key] = $uploadPath . DIRECTORY_SEPARATOR . $saveResponse['fields'][$key];
248 break;
249 }
250 }
251
252 $captchaV3Settings = $this->getCaptchaV3Settings();
253 if ($captchaV3Settings) {
254 $token = $_POST['g-recaptcha-response'];
255 $integrationHandler = new IntegrationHandler(0);
256 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
257 if (!is_wp_error($allFormIntegrations)) {
258 foreach ($allFormIntegrations as $integration) {
259 if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
260 $integrationDetails = json_decode($integration->integration_details);
261 $integrationDetails->id = $integration->id;
262 $reCAPTCHA = $integrationDetails;
263 }
264 }
265 }
266 if (!empty($reCAPTCHA->secretKey)) {
267 $gRecaptchaResponse = HttpHelper::post(
268 'https://www.google.com/recaptcha/api/siteverify',
269 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
270 );
271 if ($captchaV3Settings && !empty($saveResponse['triggerData'])) {
272 $logID = $saveResponse['triggerData']['logID'];
273 $integId = $reCAPTCHA->id;
274 $saveApiResponse = new UtilApiResponse();
275 $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
276 }
277 }
278 unset($_POST['g-recaptcha-response']);
279 }
280 if (!empty($redirectPage) && empty($saveResponse['redirectPage']) || null === $saveResponse['redirectPage']) {
281 $saveResponse['redirectPage'] = $redirectPage;
282 }
283 if (!empty($regSuccMsg) && isset($saveResponse['dflt_message'])) {
284 $saveResponse['message'] = $regSuccMsg;
285 }
286
287 $saveResponse = IntegrationHandler::maybeSetCronForIntegration($saveResponse, 'create');
288 $entryId = $saveResponse['entry_id'];
289
290 $responseMsg = is_array($saveResponse) && !empty($saveResponse) ? $saveResponse : __('Form Submitted Successfully', 'bit-form');
291 if (isset($newNonce)) {
292 $responseMsg['new_nonce'] = $newNonce;
293 }
294 $_POST = [];
295 $responseMsg['entry_id'] = $entryId;
296 return $responseMsg;
297 }
298 do_action('bitform_validation_error', $this->_form_id, $validated);
299 return $validated;
300 }
301
302 public function handleUpdateEntry()
303 {
304 $this->fieldNameReplaceOfPost();
305 $validated = $this->beforeSubmittedValidate();
306 $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);
307
308 $entryID = $_REQUEST['entryID'];
309 $GLOBALS['bf_entry_id'] = $entryID;
310 if (is_null($entryID)) {
311 return new WP_Error('empty_form', __('Entries id is invalid', 'bit-form'));
312 }
313 if (true === $validated) {
314 do_action('bitform_validation_success', $this->_form_id);
315 unset($_POST['hidden_fields'], $_POST['entryID']);
316
317 $redirectPage = '';
318 $regSuccMsg = '';
319
320 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
321 if (!is_wp_error($existAuth) && count($existAuth) > 0) {
322 $parameter = $this->getParams();
323 $existAuthFilter = has_filter('bf_wp_user_auth');
324
325 if (true === $existAuthFilter) {
326 $result = apply_filters('bf_wp_user_auth', $existAuth[0], $_POST, $parameter);
327
328 if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
329 if (!$result['success']) {
330 return new WP_Error('errors', __($result['message'], 'bit-form'));
331 } elseif (isset($result['success'])) {
332 $redirectPage = $result['redirectPage'];
333 $regSuccMsg = $result['message'];
334 $newNonce = wp_create_nonce('bitforms_' . $this->_form_id);
335 }
336 } else {
337 if (!$result['success']) {
338 return new WP_Error('errors', __($result['message'], 'bit-form'));
339 } else {
340 return $result;
341 }
342 }
343 }
344 }
345
346 $updateResponse = $this->updateFormEntry($_POST, $this->getFormID(), $entryID);
347 if (is_wp_error($updateResponse)) {
348 return $updateResponse;
349 }
350
351 do_action('bitform_update_success', $this->_form_id, $entryID, $_POST, $_FILES);
352
353 $captchaV3Settings = $this->getCaptchaV3Settings();
354 if ($captchaV3Settings) {
355 $token = $_POST['g-recaptcha-response'];
356 $integrationHandler = new IntegrationHandler(0);
357 $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
358 if (!is_wp_error($allFormIntegrations)) {
359 foreach ($allFormIntegrations as $integration) {
360 if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
361 $integrationDetails = json_decode($integration->integration_details);
362 $integrationDetails->id = $integration->id;
363 $reCAPTCHA = $integrationDetails;
364 }
365 }
366 }
367 if (!empty($reCAPTCHA->secretKey)) {
368 $gRecaptchaResponse = HttpHelper::post(
369 'https://www.google.com/recaptcha/api/siteverify',
370 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
371 );
372 if ($captchaV3Settings && !empty($updateResponse['triggerData'])) {
373 $logID = $updateResponse['triggerData']['logID'];
374 $integId = $reCAPTCHA->id;
375 $saveApiResponse = new UtilApiResponse();
376 $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
377 }
378 }
379 unset($_POST['g-recaptcha-response']);
380 }
381 if (!empty($redirectPage) && empty($updateResponse['redirectPage']) || null === $updateResponse['redirectPage']) {
382 $updateResponse['redirectPage'] = $redirectPage;
383 }
384 if (!empty($regSuccMsg) && isset($updateResponse['dflt_message'])) {
385 $updateResponse['message'] = $regSuccMsg;
386 }
387 $updateResponse = IntegrationHandler::maybeSetCronForIntegration($updateResponse, 'create');
388 $entryId = $updateResponse['entry_id'];
389
390 $responseMsg = is_array($updateResponse) && !empty($updateResponse) ? $updateResponse : __('Entry Update Successfully', 'bit-form');
391 if (isset($newNonce)) {
392 $responseMsg['new_nonce'] = $newNonce;
393 }
394 $_POST = [];
395 $responseMsg['entry_id'] = $entryId;
396 return $responseMsg;
397 }
398 do_action('bitform_validation_error', $this->_form_id, $validated);
399 return $validated;
400 }
401
402 public function validateFormSubmission($submitted_data)
403 {
404 $hidden_fields = isset($submitted_data['hidden_fields']) ? $submitted_data['hidden_fields'] : '';
405 $submitted_fields = $this->getSubmittedFields($submitted_data);
406 $form_fields = $this->getFields();
407 $form_fields_names = array_keys($form_fields);
408 if ($this->isGCLIDEnabled()) {
409 array_push($form_fields_names, 'GCLID');
410 }
411 foreach ($submitted_fields as $field) {
412 if ('hidden_fields' !== $field && !in_array($field, $form_fields_names) || false !== strpos($hidden_fields, $field)) {
413 unset($submitted_data[$field]);
414 }
415 }
416 return $submitted_data;
417 }
418
419 public function beforeSubmittedValidate()
420 {
421 if ($this->verifySubmissionNonce()) {
422 if ($this->isExist()) {
423 $isRestricted = $this->checkSubmissionRestriction();
424 if ($isRestricted && !empty($isRestricted)) {
425 return new WP_Error('spam_detection', $isRestricted[0]);
426 }
427 if ($this->isTrappedInHoneypot()) {
428 return new WP_Error('spam_detection', __('Token verification failed', 'bit-form'));
429 }
430 $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
431
432 if (is_wp_error($this->verifyGRecaptcha())) {
433 return $this->verifyGRecaptcha();
434 }
435
436 /* Implement Turnstile Captcha start */
437 if (is_wp_error($this->verifyTurnstileCaptcha())) {
438 return $this->verifyTurnstileCaptcha();
439 }
440 /* Implement Turnstile Captcha end */
441
442 $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
443
444 // check if user is already logged in and form has auth integration
445 do_action('bitform_checked_exist_auth', $this->_form_id, $existAuth);
446 if (!is_wp_error($existAuth) && count($existAuth) > 0 && is_user_logged_in()) {
447 return new WP_Error('auth_error', __('You are already logged in', 'bit-form'));
448 }
449 $validateForm = $this->validateFormSubmission($_POST);
450 $validateFormFiles = $this->validateFormSubmission($_FILES);
451 $validateForm = array_merge($validateForm, $validateFormFiles);
452 $form_fields = $this->getFields();
453 // check if form-current-step is set and form is multi-step
454 $formCurrentStep = isset($_POST['form-current-step']) ? $_POST['form-current-step'] : null;
455 if (!is_null($formCurrentStep)) {
456 $formContents = $this->getFormContent();
457 $layout = $formContents->layout;
458 $stepIndex = (int) $formCurrentStep - 1;
459 $stepLayout = $layout[$stepIndex]->layout->lg;
460 $nestedLayout = $formContents->nestedLayout;
461 $step_fields = [];
462 foreach ($stepLayout as $lay) {
463 $fk = $lay->i;
464 if (isset($nestedLayout->{$fk})) {
465 $nestedLg = $nestedLayout->{$fk}->lg;
466 foreach ($nestedLg as $nestedLay) {
467 $nestedFk = $nestedLay->i;
468 $step_fields[$nestedFk] = $form_fields[$nestedFk];
469 }
470 }
471 $step_fields[$fk] = $form_fields[$fk];
472 }
473 $form_fields = $step_fields;
474 }
475 $formFieldValidator = new FormFieldValidator($form_fields, $_POST, $_FILES);
476 $validUniuqFields = [];
477 $existFilter = has_filter('bf_check_duplicate_entry');
478 if (true === $existFilter) {
479 $validUniuqFields = apply_filters('bf_check_duplicate_entry', $form_fields, $_POST);
480
481 $fieldKeys = array_keys($validUniuqFields);
482 $form_fields_keys = array_keys($form_fields);
483 $uniqueFields = [];
484 foreach ($fieldKeys as $key) {
485 if (in_array($key, $form_fields_keys)) {
486 $uniqueFields[] = $form_fields[$key];
487 }
488 }
489 do_action('bitform_Unique_entry', $uniqueFields, $validUniuqFields, $this->_form_id, $_POST);
490 }
491 $validateField = $formFieldValidator->validate('create', $this->_form_id);
492
493 if ($validateForm && $validateField && 0 === count($validUniuqFields)) {
494 return true;
495 } else {
496 $error = __('Please submit form with valid fields', 'bit-form');
497 if (!$validateForm) {
498 $errorMessages = $error;
499 } elseif (count($formFieldValidator->getMessage()) > 0) {
500 $errorMessages = $formFieldValidator->getMessage();
501 } else {
502 $errorMessages = 0 === count($validUniuqFields) ? $error : $validUniuqFields;
503 }
504 return new WP_Error('validation_error', $errorMessages);
505 }
506 }
507 return new WP_Error('unknown_form', __('Form does not exist', 'bit-form'));
508 } else {
509 return new WP_Error('token_expired', __('Token expired', 'bit-form'));
510 }
511 }
512
513 private function verifyGRecaptcha()
514 {
515 $captchaSettings = $this->getCaptchaSettings();
516 $captchaV3Settings = $this->getCaptchaV3Settings();
517 if ($captchaSettings || $captchaV3Settings) {
518 $token = $_POST['g-recaptcha-response'];
519 if (!isset($_POST['g-recaptcha-response'])) {
520 return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
521 }
522 $integrationHandler = new IntegrationHandler(0);
523 $allFormIntegrations = $integrationHandler->getAllIntegration('app', $captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3');
524 if (!is_wp_error($allFormIntegrations)) {
525 foreach ($allFormIntegrations as $integration) {
526 if (!is_null($integration->integration_type) && $integration->integration_type === ($captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3')) {
527 $integrationDetails = json_decode($integration->integration_details);
528 $integrationDetails->id = $integration->id;
529 $reCAPTCHA = $integrationDetails;
530 }
531 }
532 }
533 if (!empty($reCAPTCHA->secretKey)) {
534 $gRecaptchaResponse = HttpHelper::post(
535 'https://www.google.com/recaptcha/api/siteverify',
536 ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
537 );
538 $isgReCaptchaVerified = false;
539 if (!is_wp_error($gRecaptchaResponse)) {
540 if (
541 $captchaV3Settings
542 && !empty($gRecaptchaResponse->score)
543 && ((float) $gRecaptchaResponse->score < (float) $captchaV3Settings->score)
544 ) {
545 wp_send_json_error(
546 __(
547 $captchaV3Settings->message,
548 'bit-form'
549 )
550 );
551 }
552
553 $isgReCaptchaVerified = $gRecaptchaResponse->success;
554 }
555 if (!$isgReCaptchaVerified) {
556 return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
557 }
558 }
559 }
560 }
561
562 private function verifyTurnstileCaptcha()
563 {
564 $turnstileSetting = $this->getTurnstileSettings();
565 if ($turnstileSetting) {
566 if (!isset($_POST['cf-turnstile-response'])) {
567 return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
568 }
569 $token = $_POST['cf-turnstile-response'];
570 $turnstileCaptcha = null;
571 $integrationHandler = new IntegrationHandler(0);
572 $turnstileIntegration = $integrationHandler->getAllIntegration('app', 'turnstileCaptcha')[0];
573 if (!is_wp_error($turnstileIntegration && !is_null($turnstileIntegration->integration_type))) {
574 $turnstileCaptcha = json_decode($turnstileIntegration->integration_details);
575 // $integrationDetails->id = $turnstileIntegration->id;
576 // $turnstileCaptcha = $integrationDetails;
577 }
578 if (!is_null($turnstileCaptcha)) {
579 $isTurnstileCaptchaVerified = false;
580 $turnstileRecaptchaResponse = HttpHelper::post(
581 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
582 ['secret' => $turnstileCaptcha->secretKey, 'response' => $token]
583 );
584 if (!is_wp_error($turnstileRecaptchaResponse)) {
585 if (!$turnstileRecaptchaResponse->success) {
586 wp_send_json_error(
587 __(
588 'Cloudflare Turnstile Validation Error: ' . implode(', ', $turnstileRecaptchaResponse->{'error-codes'}),
589 'bit-form'
590 )
591 );
592 }
593
594 $isTurnstileCaptchaVerified = $turnstileRecaptchaResponse->success;
595 }
596 if (!$isTurnstileCaptchaVerified) {
597 return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
598 }
599 }
600 }
601 }
602
603 public function verifySubmissionNonce()
604 {
605 if (!isset($_POST['t_identity']) && !isset($_POST['csrf'])) {
606 return false;
607 }
608 $tIdenty = sanitize_text_field($_POST['t_identity']);
609 $csrf = sanitize_text_field($_POST['csrf']);
610 unset($_POST['t_identity'], $_POST['action'], $_POST['bitforms_id'], $_POST['csrf']);
611 return Helpers::csrfDecrypted($tIdenty, $csrf);
612 }
613
614 public function setViewCount()
615 {
616 if (!current_user_can('manage_options')) {
617 $update_status = $this->formModel->update(
618 [
619 'views' => intval(static::$form[0]->views) + 1
620 ],
621 [
622 'id' => $this->form_id
623 ]
624 );
625 }
626 }
627
628 public function checkSubmissionRestriction($checkedEmptySubmitted = true)
629 {
630 $formContents = $this->getFormContent();
631 $fromRestrictionSetitingsEnabled = empty($formContents->additional->enabled) ? [] : $formContents->additional->enabled;
632 $fromRestrictionSetitings = empty($formContents->additional->settings) ? null : $formContents->additional->settings;
633 if (is_null($formContents->additional->enabled) || is_null($formContents->additional->settings)) {
634 return false;
635 }
636 $restrictionMessage = [];
637 $ipTool = new IpTool();
638 $ipAddress = $ipTool->getIP();
639 foreach ($fromRestrictionSetitingsEnabled as $restrictionKey => $isEnabled) {
640 if ($isEnabled) {
641 if ('entry_limit' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
642 $formEntry = new FormEntryModel();
643 $countResult = $formEntry->count(
644 [
645 'form_id' => $this->form_id
646 ]
647 );
648 $count = !empty($countResult[0]) && !empty($countResult[0]->count) ? $countResult[0]->count : false;
649 if ($count && $count >= intval($fromRestrictionSetitings->{$restrictionKey})) {
650 $restrictionMessage[] = __('Sorry!! Entry limit exceeded', 'bit-form');
651 }
652 }
653 if ('onePerIp' === $restrictionKey) {
654 $formEntry = new FormEntryModel();
655 $countResult = $formEntry->count(
656 [
657 'form_id' => $this->form_id,
658 'user_ip' => ip2long($ipAddress)
659 ]
660 );
661 $count = !empty($countResult[0]) && !empty($countResult[0]->count) ? $countResult[0]->count : false;
662
663 if ($count && $count > 0) {
664 $restrictionMessage[] = __('Sorry!! You have already submitted', 'bit-form');
665 }
666 }
667 if ('is_login' === $restrictionKey && 0 === get_current_user_id()) {
668 $restrictionMessage[] = __($fromRestrictionSetitings->is_login->message, 'bit-form');
669 }
670 if ($checkedEmptySubmitted && 'empty_submission' === $restrictionKey) {
671 $isEmpty = $this->checkEmptySubmission($_POST, $_FILES);
672 if ($isEmpty) {
673 $restrictionMessage[] = __($fromRestrictionSetitings->empty_submission->message, 'bit-form');
674 }
675 }
676 if ('restrict_form' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
677 $day = empty($fromRestrictionSetitings->{$restrictionKey}->day) ? null : $fromRestrictionSetitings->{$restrictionKey}->day;
678 $date = empty($fromRestrictionSetitings->{$restrictionKey}->date) ? null : $fromRestrictionSetitings->{$restrictionKey}->date;
679 $time = empty($fromRestrictionSetitings->{$restrictionKey}->time) ? null : $fromRestrictionSetitings->{$restrictionKey}->time;
680
681 $isdayOk = $isdateOk = $istimeOk = true;
682 $dayNotOkMsg = $dateNotOkMsg = $timeNotOkMsg = '';
683 $dateTimeHelper = new DateTimeHelper();
684 if (
685 !empty($day)
686 && is_array($day)
687 && (in_array('Friday', $day)
688 || in_array('Saturday', $day)
689 || in_array('Sunday', $day)
690 || in_array('Monday', $day)
691 || in_array('Tuesday', $day)
692 || in_array('Wednesday', $day)
693 || in_array('Thursday', $day))
694 && (!in_array($dateTimeHelper->getDay('full-name'), $day))
695 ) {
696 $isdayOk = false;
697 $dayMsgVarsFormat = '';
698 foreach ($day as $dayIndex => $dayValue) {
699 if ($dayIndex > 0) {
700 $dayMsgVarsFormat .= ', ';
701 }
702 $dayMsgVarsFormat .= '%s';
703 }
704 $dayNotOkMsg = vsprintf(__("in $dayMsgVarsFormat", 'bit-form'), $day);
705 }
706 if (
707 !empty($day)
708 && is_array($day)
709 && (in_array('Custom', $day))
710 ) {
711 $startDate = empty($date->from) ? '00-00-0000' : $date->from;
712 $endDate = empty($date->to) ? '00-00-0000' : $date->to;
713 $dateFormat = preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $startDate) ? 'Y-m-d' : 'm-d-Y';
714 if (!empty($date->from) && false !== strpos($startDate, 'T')) {
715 $startDate = $dateTimeHelper->getDate($startDate, false, null, $dateFormat);
716 }
717 if (!empty($date->to) && false !== strpos($endDate, 'T')) {
718 $endDate = $dateTimeHelper->getDate($endDate, false, null, $dateFormat);
719 }
720 $currentDate = $dateTimeHelper->getDate(null, null, null, $dateFormat);
721 if (!($currentDate >= $startDate && $currentDate <= $endDate)) {
722 $isdateOk = false;
723 $dateNotOkMsg = sprintf(__('within %s to %s', 'bit-form'), $startDate, $endDate);
724 }
725 }
726
727 if (!empty($time)) {
728 $startTime = empty($time->from) ? '00:00' : $time->from;
729 $endTime = empty($time->to) ? '23:59.999' : $time->to;
730 $currentTime = $dateTimeHelper->getTime(null, null, null, 'H:i');
731 if (!($currentTime >= $startTime && $currentTime <= $endTime)) {
732 $istimeOk = false;
733 $startTime = $dateTimeHelper->getTime($startTime, 'H:i', null);
734 $endTime = $dateTimeHelper->getTime($endTime, 'H:i', null);
735 $isTimeOk = false;
736 $timeNotOkMsg = sprintf(__('%s to %s', 'bit-form'), $startTime, $endTime);
737 }
738 }
739
740 if (!($isdateOk && $isdayOk && $istimeOk)) {
741 if (!$isdayOk) {
742 $restrictionMessage[] = !empty($timeNotOkMsg) ? sprintf(__('Form is available %s From %s', 'bit-form'), $dayNotOkMsg, $timeNotOkMsg) :
743 sprintf(__('Form is available %s', 'bit-form'), $dayNotOkMsg, $timeNotOkMsg);
744 } elseif (!$isdateOk) {
745 $restrictionMessage[] = !empty($timeNotOkMsg) ? sprintf(__('Form is available %s From %s', 'bit-form'), $dateNotOkMsg, $timeNotOkMsg) :
746 sprintf(__('Form is available %s', 'bit-form'), $dateNotOkMsg, $timeNotOkMsg);
747 } elseif (!$istimeOk) {
748 $restrictionMessage[] = sprintf(__('Form is available on %s', 'bit-form'), $timeNotOkMsg);
749 }
750 }
751 }
752 if ('blocked_ip' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
753 $isIpBlocked = false;
754 foreach ($fromRestrictionSetitings->{$restrictionKey} as $ipIndex => $ipDetails) {
755 if (!empty($ipDetails->status) && $ipDetails->status && !empty($ipDetails->ip) && $ipDetails->ip === $ipAddress) {
756 $isIpBlocked = true;
757 break;
758 }
759 }
760 if ($isIpBlocked) {
761 $restrictionMessage[] = sprintf(__('Sorry!! Your IP address is %s, Blocked from submitting the form', 'bit-form'), $ipAddress);
762 }
763 }
764 if ('private_ip' === $restrictionKey && isset($fromRestrictionSetitings->{$restrictionKey})) {
765 $isIpWhiteListed = false;
766 foreach ($fromRestrictionSetitings->{$restrictionKey} as $ipIndex => $ipDetails) {
767 if (!empty($ipDetails->status) && $ipDetails->status && !empty($ipDetails->ip) && $ipDetails->ip === $ipAddress) {
768 $isIpWhiteListed = true;
769 break;
770 }
771 }
772 if (!$isIpWhiteListed) {
773 $restrictionMessage[] = sprintf(__('Sorry!! Your IP address is %s, Blocked from submitting the form', 'bit-form'), $ipAddress);
774 }
775 }
776 }
777 }
778 return $restrictionMessage;
779 }
780
781 /**
782 * Will check if form is submitted by a bot
783 *
784 * @return Boolean true - if submitted by bot else false
785 */
786 public function isTrappedInHoneypot()
787 {
788 $isHoneyPot = false;
789
790 if (!$this->isHoneypotActive()) {
791 return false;
792 }
793
794 $token = $_POST['b_h_t'];
795 $pattern = '/^([a-zA-Z0-9]*_[a-zA-Z0-9]*){4}$/';
796 $decryptedToken = base64_decode(base64_decode($token));
797
798 preg_match($pattern, $decryptedToken, $validToken);
799
800 if ($validToken) {
801 if (isset($_POST[$token]) && empty($_POST[$token])) {
802 $isHoneyPot = false;
803 } else {
804 $isHoneyPot = true;
805 }
806 } else {
807 $isHoneyPot = true;
808 }
809
810 if (isset($_POST[$token])) {
811 unset($_POST[$token]);
812 }
813 unset($_POST['b_h_t']);
814 return $isHoneyPot;
815 }
816
817 public function isHoneypotActive()
818 {
819 $formContents = $this->getFormContent();
820 $enabled = empty($formContents->additional->enabled) ? null : $formContents->additional->enabled;
821 if (!empty($enabled->honeypot) && $enabled->honeypot) {
822 return true;
823 }
824 return false;
825 }
826
827 public function checkPaymentFields()
828 {
829 $formContents = $this->getFormContent();
830 $fields = $formContents->fields;
831
832 $payments = [];
833 foreach ($fields as $fldData) {
834 if ('paypal' === $fldData->typ && property_exists($fldData, 'payIntegID')) {
835 $payments['paypalKey'] = $this->getClientKey($fldData->payIntegID, 'clientID');
836 } elseif ('razorpay' === $fldData->typ && property_exists($fldData->options, 'payIntegID')) {
837 $payments['razorpayKey'] = $this->getClientKey($fldData->options->payIntegID, 'apiKey');
838 }
839 }
840
841 return $payments;
842 }
843
844 private function getClientKey($integID, $keyName)
845 {
846 $client = '';
847 if (!empty($integID)) {
848 $integrationHandler = new IntegrationHandler(0);
849 $integration = $integrationHandler->getAIntegration($integID, 'app', 'payments');
850 if (!is_wp_error($integration)) {
851 $integration_details = json_decode($integration[0]->integration_details);
852 $client = base64_encode($integration_details->{$keyName});
853 }
854 }
855 return $client;
856 }
857
858 public function getSuccessMessageMarkups()
859 {
860 if (is_null($this->_work_flows)) {
861 $workFlowManager = new WorkFlowHandler($this->form_id);
862 $this->_work_flows = $workFlowManager->getAllworkFlow();
863 }
864
865 $ids = [];
866 foreach ($this->_work_flows as $msgItem) {
867 foreach ($msgItem['conditions'] as $condition) {
868 if (isset($condition->actions->success)) {
869 foreach ($condition->actions->success as $msg) {
870 if ('successMsg' === $msg->type && isset($msg->details->id)) {
871 $idObj = json_decode(stripslashes($msg->details->id));
872 if (is_object($idObj) && !empty($idObj->id)) {
873 array_push($ids, $idObj->id);
874 }
875 }
876 }
877 }
878 if (isset($condition->actions->failure)) {
879 $idObj = json_decode(stripslashes($condition->actions->failure));
880 if (is_object($idObj) && !empty($idObj->id)) {
881 array_push($ids, $idObj->id);
882 }
883 }
884 }
885 }
886 $ids = array_unique($ids);
887 if (is_null($this->_conf_messages)) {
888 $successMsgHandler = new SuccessMessageHandler($this->form_id);
889 $this->_conf_messages = $successMsgHandler->getMessages($ids);
890 }
891
892 $messageMarkups = '';
893 if (is_wp_error($this->_conf_messages)) {
894 return $messageMarkups;
895 }
896
897 foreach ($this->_conf_messages as $msgItem) {
898 $messageMarkups .= $this->messageMarkup($msgItem);
899 }
900
901 return $messageMarkups;
902 }
903
904 public function getFormAbandonmentMessage()
905 {
906 if (class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
907 $formAbandonmentSettings = FormAbandonment::getFormAbandonmentSettings($this->form_id);
908 $msg = '';
909 if (isset($formAbandonmentSettings->showWarningMsg) && $formAbandonmentSettings->showWarningMsg && !empty($formAbandonmentSettings->warningMsg)) {
910 $msg = $formAbandonmentSettings->warningMsg;
911 $msg = '<div class="bf-form-msg active warning">' . wp_kses_post($msg) . '</div>';
912 }
913 return $msg;
914 }
915 }
916
917 public function getFormAbandonmentSettings()
918 {
919 if (class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
920 $formAbandonmentSettings = FormAbandonment::getFormAbandonmentSettings($this->form_id);
921 return $formAbandonmentSettings;
922 }
923 return null;
924 }
925
926 private function messageMarkup($msg)
927 {
928 $msgId = $msg->id;
929 $msgConfig = json_decode($msg->message_config);
930 $scrollClass = 'below' === $msgConfig->msgType ? 'scroll' : '';
931
932 return <<<SUCCESSMSG
933 <div role="dialog" aria-hidden="true" data-modal-backdrop="true" class="{$this->getAtomicCls("msg-container-{$msgId}")} deactive {$scrollClass}">
934 <div data-contentid="{$this->getFormIdentifier()}" data-msgid="{$msgId}" role="button" class="{$this->getAtomicCls("msg-background-{$msgId}")} msg-backdrop">
935 <div class="bf-msg-content {$this->getAtomicCls("msg-content-{$msgId}")}">
936 <button data-contentid="{$this->getFormIdentifier()}" data-msgid="{$msgId}" class="{$this->getAtomicCls("close-{$msgId}")} bf-msg-close" type="button">
937 <svg class="{$this->getAtomicCls("close-icn-{$msgId}")}" viewBox="0 0 30 30">
938 <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="4" y1="3.88" x2="26" y2="26.12"></line>
939 <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="26" y1="3.88" x2="4" y2="26.12"></line>
940 </svg>
941 </button>
942 <div class="msg-content"></div>
943 </div>
944 </div>
945 </div>
946 SUCCESSMSG;
947 }
948 }
949