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

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