PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 5.2.9
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v5.2.9
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Modules / Form / FormHandler.php

FormHandler.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 5.2.9, at app/Modules/Form/FormHandler.php

1,029 lines 34.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Modules\Form;
4
5 use FluentForm\App\Databases\Migrations\SubmissionDetails;
6 use FluentForm\App\Helpers\Helper;
7 use FluentForm\App\Modules\Activator;
8 use FluentForm\App\Modules\ReCaptcha\ReCaptcha;
9 use FluentForm\App\Modules\HCaptcha\HCaptcha;
10 use FluentForm\App\Modules\Turnstile\Turnstile;
11 use FluentForm\App\Services\Browser\Browser;
12 use FluentForm\App\Services\FormBuilder\ShortCodeParser;
13 use FluentForm\App\Services\Submission\SubmissionService;
14 use FluentForm\Framework\Foundation\Application;
15 use FluentForm\Framework\Helpers\ArrayHelper as Arr;
16 use FluentForm\Framework\Helpers\ArrayHelper;
17
18 /* @deprecated Use class \FluentForm\App\Http\Controllers\SubmissionHandlerController */
19
20 class FormHandler
21 {
22 /**
23 * App instance
24 *
25 * @var \FluentForm\Framework\Foundation\Application
26 */
27 protected $app;
28
29 /**
30 * Request object
31 *
32 * @var \FluentForm\Framework\Request\Request
33 */
34 protected $request;
35
36 /**
37 * Form Data
38 *
39 * @var array $formData
40 */
41 protected $formData;
42
43 /**
44 * The Fluent Forms object.
45 *
46 * @var \stdClass
47 */
48 protected $form;
49
50 /**
51 * Form Handler constructor.
52 *
53 * @param \FluentForm\Framework\Foundation\Application $app
54 */
55 public function __construct(Application $app)
56 {
57 $this->app = $app;
58 $this->request = $app->request;
59 }
60
61 /**
62 * Set the form using it's ID.
63 *
64 * @param $formId
65 *
66 * @return $this
67 */
68 public function setForm($formId)
69 {
70 $this->form = wpFluent()->table('fluentform_forms')->find($formId);
71 return $this;
72 }
73
74 /**
75 * Handle form submition
76 */
77 public function onSubmit()
78 {
79 // Parse the url encoded data from the request object.
80 parse_str($this->app->request->get('data'), $data);
81
82 $data['_wp_http_referer'] = urldecode($data['_wp_http_referer']);
83
84 // Merge it back again to the request object.
85 $this->app->request->merge(['data' => $data]);
86
87 $formId = intval($this->app->request->get('form_id'));
88
89 $this->setForm($formId);
90
91 if (!$this->form) {
92 wp_send_json([
93 'errors' => [],
94 'message' => 'Sorry, No corresponding form found',
95 ], 423);
96 }
97
98 // Parse the form and get the flat inputs with validations.
99 $fields = FormFieldsParser::getInputs($this->form, ['rules', 'raw']);
100
101 // Sanitize the data properly.
102 $this->formData = fluentFormSanitizer($data, null, $fields);
103
104 // Now validate the data using the previous validations.
105 $this->validate($fields);
106
107 // Prepare the data to be inserted to the DB.
108 $insertData = $this->prepareInsertData();
109
110 if ($this->isAkismetSpam($this->formData, $this->form)) {
111 $insertData['status'] = 'spam';
112 $this->handleSpamError();
113 }
114
115 do_action_deprecated(
116 'fluentform_before_insert_submission',
117 [
118 $insertData,
119 $data,
120 $this->form
121 ],
122 FLUENTFORM_FRAMEWORK_UPGRADE,
123 'fluentform/before_insert_submission',
124 'Use fluentform/before_insert_submission instead of fluentform_before_insert_submission.'
125 );
126
127 do_action('fluentform/before_insert_submission', $insertData, $data, $this->form);
128
129 if ($this->form->has_payment) {
130 do_action_deprecated(
131 'fluentform_before_insert_payment_form',
132 [
133 $insertData,
134 $data,
135 $this->form
136 ],
137 FLUENTFORM_FRAMEWORK_UPGRADE,
138 'fluentform/before_insert_payment_form',
139 'Use fluentform/before_insert_payment_form instead of fluentform_before_insert_payment_form.'
140 );
141
142 do_action('fluentform/before_insert_payment_form', $insertData, $data, $this->form);
143 }
144
145 $insertId = wpFluent()->table('fluentform_submissions')->insertGetId($insertData);
146
147 do_action('fluentform/notify_on_form_submit', $insertId, $this->formData, $this->form);
148
149 $uidHash = md5(wp_generate_uuid4() . $insertId);
150 Helper::setSubmissionMeta($insertId, '_entry_uid_hash', $uidHash, $formId);
151
152 do_action_deprecated(
153 'fluentform_before_form_actions_processing',
154 [
155 $insertId,
156 $this->formData,
157 $this->form
158 ],
159 FLUENTFORM_FRAMEWORK_UPGRADE,
160 'fluentform/before_form_actions_processing',
161 'Use fluentform/before_form_actions_processing instead of fluentform_before_form_actions_processing.'
162 );
163
164 do_action('fluentform/before_form_actions_processing', $insertId, $this->formData, $this->form);
165
166 $result = $this->processFormSubmissionData($insertId, $this->formData, $this->form);
167
168 wp_send_json_success($result, 200);
169 }
170
171 public function processFormSubmissionData($insertId, $formData, $form)
172 {
173 if ($insertId) {
174 ob_start();
175 $submissionService = new SubmissionService();
176 $submissionService->recordEntryDetails($insertId, $form->id, $formData);
177 $isError = ob_get_clean();
178 if ($isError) {
179 SubmissionDetails::migrate();
180 }
181 }
182
183 $returnData = $this->getReturnData($insertId, $form, $formData);
184
185 $error = '';
186 try {
187
188 /*
189 * We will keep this old hook for backward compatability.
190 */
191 do_action('fluentform_submission_inserted', $insertId, $formData, $form);
192
193 do_action(
194 'fluentform/submission_inserted',
195 $insertId,
196 $formData,
197 $form
198 );
199
200 Helper::setSubmissionMeta($insertId, 'is_form_action_fired', 'yes');
201
202 do_action_deprecated(
203 'fluentform_submission_inserted_' . $form->type . '_form',
204 [
205 $insertId,
206 $formData,
207 $form
208 ],
209 FLUENTFORM_FRAMEWORK_UPGRADE,
210 'fluentform/submission_inserted',
211 'Use fluentform/submission_inserted_' . $form->type . '_form' . ' instead of fluentform_submission_inserted_' . $form->type . '_form'
212 );
213
214 do_action(
215 'fluentform/submission_inserted_' . $form->type . '_form',
216 $insertId,
217 $formData,
218 $form
219 );
220
221 } catch (\Exception $e) {
222 if (defined('WP_DEBUG') && WP_DEBUG) {
223 $error = $e->getMessage();
224 }
225 }
226
227 do_action_deprecated(
228 'fluentform_before_submission_confirmation',
229 [
230 $insertId,
231 $formData,
232 $form
233 ],
234 FLUENTFORM_FRAMEWORK_UPGRADE,
235 'fluentform/before_submission_confirmation',
236 'Use fluentform/before_submission_confirmation instead of fluentform_before_submission_confirmation.'
237 );
238
239 do_action('fluentform/before_submission_confirmation', $insertId, $formData, $form);
240
241 return [
242 'insert_id' => $insertId,
243 'result' => $returnData,
244 'error' => $error,
245 ];
246 }
247
248 public function getReturnData($insertId, $form, $formData)
249 {
250 if (empty($form->settings)) {
251 $formSettings = wpFluent()->table('fluentform_form_meta')
252 ->where('form_id', $form->id)
253 ->where('meta_key', 'formSettings')
254 ->first();
255
256 $form->settings = $formSettings ? json_decode($formSettings->value, true) : [];
257 }
258 $confirmation = $form->settings['confirmation'];
259 $confirmation = apply_filters_deprecated(
260 'fluentform_form_submission_confirmation',
261 [
262 $confirmation,
263 $formData,
264 $form
265 ],
266 FLUENTFORM_FRAMEWORK_UPGRADE,
267 'fluentform/form_submission_confirmation',
268 'Use fluentform/form_submission_confirmation instead of fluentform_form_submission_confirmation.'
269 );
270
271 $confirmation = $this->app->applyFilters(
272 'fluentform/form_submission_confirmation',
273 $confirmation,
274 $formData,
275 $form
276 );
277
278 if ('samePage' == $confirmation['redirectTo']) {
279
280 $confirmation['messageToShow'] = apply_filters_deprecated(
281 'fluentform_submission_message_parse',
282 [
283 $confirmation['messageToShow'],
284 $insertId,
285 $formData,
286 $form
287 ],
288 FLUENTFORM_FRAMEWORK_UPGRADE,
289 'fluentform/submission_message_parse',
290 'Use fluentform/submission_message_parse instead of fluentform_submission_message_parse.'
291 );
292 $confirmation['messageToShow'] = apply_filters('fluentform/submission_message_parse',
293 $confirmation['messageToShow'], $insertId, $formData, $form);
294
295 $message = ShortCodeParser::parse(
296 $confirmation['messageToShow'],
297 $insertId,
298 $formData,
299 $form,
300 false,
301 true
302 );
303
304 $message = $message ? $message : 'The form has been successfully submitted.';
305
306 $returnData = [
307 'message' => do_shortcode($message),
308 'action' => $confirmation['samePageFormBehavior'],
309 ];
310 } else {
311 $redirectUrl = Arr::get($confirmation, 'customUrl');
312
313 if ('customPage' == $confirmation['redirectTo']) {
314 $redirectUrl = get_permalink($confirmation['customPage']);
315 }
316
317 if (
318 ('yes' == Arr::get($confirmation, 'enable_query_string')) &&
319 Arr::get($confirmation, 'query_strings')
320 ) {
321 if (strpos($redirectUrl, '?')) {
322 $redirectUrl .= '&' . Arr::get($confirmation, 'query_strings');
323 } else {
324 $redirectUrl .= '?' . Arr::get($confirmation, 'query_strings');
325 }
326 }
327 $parseUrl = true;
328 $parseUrl = apply_filters_deprecated(
329 'fluentform_will_parse_url_value',
330 [
331 $parseUrl,
332 $form
333 ],
334 FLUENTFORM_FRAMEWORK_UPGRADE,
335 'fluentform/will_parse_url_value',
336 'Use fluentform/will_parse_url_value instead of fluentform_will_parse_url_value.'
337 );
338
339 $isUrlParser = apply_filters('fluentform/will_parse_url_value', $parseUrl, $form);
340
341 $redirectUrl = ShortCodeParser::parse(
342 $redirectUrl,
343 $insertId,
344 $formData,
345 $form,
346 $isUrlParser
347 );
348
349 if ($isUrlParser) {
350 /*
351 * For Empty Redirect Value
352 */
353 if (strpos($redirectUrl, '=&') || '=' == substr($redirectUrl, -1)) {
354 $urlArray = explode('?', $redirectUrl);
355 $baseUrl = array_shift($urlArray);
356
357 $query = wp_parse_url($redirectUrl)['query'];
358
359 $queryParams = explode('&', $query);
360
361 $params = [];
362 foreach ($queryParams as $queryParam) {
363 $paramArray = explode('=', $queryParam);
364 if (!empty($paramArray[1])) {
365 $params[$paramArray[0]] = $paramArray[1];
366 }
367 }
368
369 $redirectUrl = add_query_arg($params, $baseUrl);
370 }
371 }
372
373 $message = ShortCodeParser::parse(
374 ArrayHelper::get($confirmation, 'redirectMessage', ''),
375 $insertId,
376 $formData,
377 $form,
378 false,
379 true
380 );
381
382 $redirectUrl = wp_sanitize_redirect(urldecode($redirectUrl));
383 $returnData = [
384 'redirectUrl' => esc_url_raw($redirectUrl),
385 'message' => $message,
386 ];
387 }
388
389 $returnData = apply_filters_deprecated(
390 'fluentform_submission_confirmation',
391 [
392 $returnData,
393 $form,
394 $confirmation
395 ],
396 FLUENTFORM_FRAMEWORK_UPGRADE,
397 'fluentform/submission_confirmation',
398 'Use fluentform/submission_confirmation instead of fluentform_submission_confirmation.'
399 );
400
401 return $this->app->applyFilters(
402 'fluentform/submission_confirmation',
403 $returnData,
404 $form,
405 $confirmation
406 );
407 }
408
409 /**
410 * Validate form data.
411 *
412 * @param $fields
413 *
414 * @return bool
415 */
416 private function validate(&$fields)
417 {
418 $this->preventMaliciousAttacks();
419
420 $this->validateRestrictions($fields);
421
422 $this->validateNonce();
423
424 $this->validateReCaptcha();
425 $this->validateHCaptcha();
426 $this->validateTurnstile();
427
428 foreach ($fields as $fieldName => $field) {
429 if (isset($this->formData[$fieldName])) {
430 $element = $field['element'];
431
432 $this->formData[$fieldName] = apply_filters_deprecated(
433 'fluentform_input_data_' . $element,
434 [
435 $this->formData[$fieldName],
436 $field,
437 $this->formData,
438 $this->form
439 ],
440 FLUENTFORM_FRAMEWORK_UPGRADE,
441 'fluentform/input_data_' . $element,
442 'Use fluentform/input_data_' . $element . ' instead of fluentform_input_data_' . $element
443 );
444
445 $this->formData[$fieldName] = $this->app->applyFilters('fluentform/input_data_' . $element,
446 $this->formData[$fieldName], $field, $this->formData, $this->form);
447 }
448 }
449
450 $originalValidations = FormFieldsParser::getValidations($this->form, $this->formData, $fields);
451
452 $originalValidations = apply_filters_deprecated(
453 'fluentform_validations',
454 [
455 $originalValidations,
456 $this->form,
457 $this->formData
458 ],
459 FLUENTFORM_FRAMEWORK_UPGRADE,
460 'fluentform/validations',
461 'Use fluentform/validations instead of fluentform_validations.'
462 );
463 // Fire an event so that one can hook into it to work with the rules & messages.
464 $validations = apply_filters('fluentform/validations', $originalValidations, $this->form, $this->formData);
465
466 /*
467 * Clean talk fix for now
468 * They should not hook fluentform_validations and return nothing!
469 * We will remove this extra check once it's done
470 */
471 if ($originalValidations && (!$validations || !array_filter($validations))) {
472 $validations = $originalValidations;
473 }
474
475 $validator = wpFluentForm('validator')->make($this->formData, $validations[0], $validations[1]);
476
477 $errors = [];
478 if ($validator->validate()->fails()) {
479 foreach ($validator->errors() as $attribute => $rules) {
480 $position = strpos($attribute, ']');
481
482 if ($position) {
483 $attribute = substr($attribute, 0, strpos($attribute, ']') + 1);
484 }
485
486 $errors[$attribute] = $rules;
487 }
488
489 $errors = apply_filters_deprecated(
490 'fluentform_validation_error',
491 [
492 $errors,
493 $this->form,
494 $fields,
495 $this->formData
496 ],
497 FLUENTFORM_FRAMEWORK_UPGRADE,
498 'fluentform/validation_error',
499 'Use fluentform/validation_error instead of fluentform_validation_error.'
500 );
501 // Fire an event so that one can hook into it to work with the errors.
502 $errors = $this->app->applyFilters('fluentform/validation_error', $errors, $this->form, $fields,
503 $this->formData);
504 }
505
506 foreach ($fields as $fieldKey => $field) {
507 $field['data_key'] = $fieldKey;
508 $inputName = \FluentForm\Framework\Helpers\ArrayHelper::get($field, 'raw.attributes.name');
509 $field['name'] = $inputName;
510
511 $error = apply_filters_deprecated(
512 'fluentform_validate_input_item_' . $field['element'],
513 [
514 '',
515 $field,
516 $this->formData,
517 $fields,
518 $this->form,
519 $errors
520 ],
521 FLUENTFORM_FRAMEWORK_UPGRADE,
522 'fluentform_validate_input_item_' . $field['element'],
523 'Use fluentform/validate_input_item_' . $field['element'] . ' instead of fluentform_validate_input_item_' . $field['element']
524 );
525
526 $error = apply_filters('fluentform/validate_input_item_' . $field['element'], $error, $field, $this->formData, $fields, $this->form, $errors);
527 if ($error) {
528 if (empty($errors[$inputName])) {
529 $errors[$inputName] = [];
530 }
531
532 if (is_string($error)) {
533 $error = [$error];
534 }
535
536 $errors[$inputName] = array_merge($error, $errors[$inputName]);
537 }
538 }
539
540 $errors = apply_filters_deprecated(
541 'fluentform_validation_errors',
542 [
543 $errors,
544 $this->formData,
545 $this->form,
546 $fields
547 ],
548 FLUENTFORM_FRAMEWORK_UPGRADE,
549 'fluentform/validation_errors',
550 'Use fluentform/validation_errors instead of fluentform_validation_errors.'
551 );
552
553 $errors = apply_filters('fluentform/validation_errors', $errors, $this->formData, $this->form, $fields);
554
555 if ('yes' == Helper::getFormMeta($this->form->id, '_has_user_registration') && !get_current_user_id()) {
556 $errors = apply_filters_deprecated(
557 'fluentform_validation_user_registration_errors',
558 [
559 $errors,
560 $this->formData,
561 $this->form,
562 $fields
563 ],
564 FLUENTFORM_FRAMEWORK_UPGRADE,
565 'fluentform/validation_user_registration_errors',
566 'Use fluentform/validation_user_registration_errors instead of fluentform_validation_user_registration_errors.'
567 );
568
569 $errors = apply_filters('fluentform/validation_user_registration_errors', $errors, $this->formData,
570 $this->form, $fields);
571 }
572
573 if ('yes' == Helper::getFormMeta($this->form->id, '_has_user_update') && get_current_user_id()) {
574 $errors = apply_filters_deprecated(
575 'fluentform_validation_user_update_errors',
576 [
577 $errors,
578 $this->formData,
579 $this->form,
580 $fields
581 ],
582 FLUENTFORM_FRAMEWORK_UPGRADE,
583 'fluentform/validation_user_update_errors',
584 'Use fluentform/validation_user_update_errors instead of fluentform_validation_user_update_errors.'
585 );
586
587 $errors = apply_filters('fluentform/validation_user_update_errors', $errors, $this->formData, $this->form, $fields);
588 }
589
590 if ($errors) {
591 wp_send_json(['errors' => $errors], 423);
592 }
593
594 return true;
595 }
596
597 /**
598 * Validate nonce.
599 */
600 protected function validateNonce()
601 {
602 $formId = $this->form->id;
603 $nonceVerify = false;
604 /* This filter is deprecated and will be removed soon. */
605 $nonceVerify = $this->app->applyFilters('fluentform_nonce_verify', $nonceVerify, $formId);
606
607 $shouldVerifyNonce = $this->app->applyFilters('fluentform/nonce_verify', $nonceVerify, $formId);
608
609 if ($shouldVerifyNonce) {
610 $nonce = Arr::get($this->formData, '_fluentform_' . $formId . '_fluentformnonce');
611 if (!wp_verify_nonce($nonce, 'fluentform-submit-form')) {
612 $nonceMessage = apply_filters_deprecated(
613 'fluentForm_nonce_error',
614 [
615 '_fluentformnonce' => [
616 __('Nonce verification failed, please try again.', 'fluentform'),
617 ],
618 ],
619 FLUENTFORM_FRAMEWORK_UPGRADE,
620 'fluentform/nonce_error',
621 'Use fluentform/nonce_error instead of fluentForm_nonce_error.'
622 );
623
624 $errors = $this->app->applyFilters('fluentform/nonce_error', $nonceMessage);
625 wp_send_json(['errors' => $errors], 422);
626 }
627 }
628 }
629
630 protected function handleSpamError()
631 {
632 $settings = get_option('_fluentform_global_form_settings');
633 if (!$settings || 'validation_failed' != ArrayHelper::get($settings, 'misc.akismet_validation')) {
634 return;
635 }
636
637 $errors = [
638 '_fluentformakismet' => __('Submission marked as spammed. Please try again', 'fluentform'),
639 ];
640
641 wp_send_json(['errors' => $errors], 422);
642 }
643
644 protected function isAkismetSpam($formData, $form)
645 {
646 if (!AkismetHandler::isEnabled()) {
647 return false;
648 }
649 $isSpamCheck = true;
650 $isSpamCheck = apply_filters_deprecated(
651 'fluentform_akismet_check_spam',
652 [
653 true,
654 $form->id,
655 $formData
656 ],
657 FLUENTFORM_FRAMEWORK_UPGRADE,
658 'fluentform/akismet_check_spam',
659 'Use fluentform/akismet_check_spam instead of fluentform_akismet_check_spam.'
660 );
661
662 $isSpamCheck = apply_filters('fluentform/akismet_check_spam', $isSpamCheck, $form->id, $formData);
663 if (!$isSpamCheck) {
664 return false;
665 }
666 // Let's validate now
667 $isSpam = AkismetHandler::isSpamSubmission($formData, $form);
668
669 $isSpam = apply_filters_deprecated(
670 'fluentform_akismet_spam_result',
671 [
672 $isSpam,
673 $form->id,
674 $formData
675 ],
676 FLUENTFORM_FRAMEWORK_UPGRADE,
677 'fluentform/akismet_spam_result',
678 'Use fluentform/akismet_spam_result instead of fluentform_akismet_spam_result.'
679 );
680
681 return $this->app->applyFilters('fluentform/akismet_spam_result', $isSpam, $form->id, $formData);
682 }
683
684 /**
685 * Validate reCaptcha.
686 */
687 private function validateReCaptcha()
688 {
689 $hasAutoRecaptcha = false;
690 $hasAutoRecaptcha = apply_filters_deprecated(
691 'ff_has_auto_recaptcha',
692 [
693 $hasAutoRecaptcha
694 ],
695 FLUENTFORM_FRAMEWORK_UPGRADE,
696 'fluentform/has_recaptcha',
697 'Use fluentform/has_recaptcha instead of ff_has_auto_recaptcha.'
698 );
699 $autoInclude = apply_filters('fluentform/has_recaptcha', $hasAutoRecaptcha);
700 if (FormFieldsParser::hasElement($this->form, 'recaptcha') || $autoInclude) {
701 $keys = get_option('_fluentform_reCaptcha_details');
702 $token = Arr::get($this->formData, 'g-recaptcha-response');
703 $version = 'v2_visible';
704 if (!empty($keys['api_version'])) {
705 $version = $keys['api_version'];
706 }
707 $isValid = ReCaptcha::validate($token, $keys['secretKey'], $version);
708
709 if (!$isValid) {
710 wp_send_json([
711 'errors' => [
712 'g-recaptcha-response' => [
713 __('reCaptcha verification failed, please try again.', 'fluentform'),
714 ],
715 ],
716 ], 422);
717 }
718 }
719 }
720
721 /**
722 * Validate hCaptcha.
723 */
724 private function validateHCaptcha()
725 {
726 $hasAutoHcaptcha = false;
727
728 $hasAutoHcaptcha = apply_filters_deprecated(
729 'ff_has_auto_hcaptcha',
730 [
731 $hasAutoHcaptcha
732 ],
733 FLUENTFORM_FRAMEWORK_UPGRADE,
734 'fluentform/has_hcaptcha',
735 'Use fluentform/has_hcaptcha instead of ff_has_auto_hcaptcha.'
736 );
737 $autoInclude = apply_filters('fluentform/has_hcaptcha', $hasAutoHcaptcha);
738 FormFieldsParser::resetData();
739 if (FormFieldsParser::hasElement($this->form, 'hcaptcha') || $autoInclude) {
740 $keys = get_option('_fluentform_hCaptcha_details');
741 $token = Arr::get($this->formData, 'h-captcha-response');
742 $isValid = HCaptcha::validate($token, $keys['secretKey']);
743
744 if (!$isValid) {
745 wp_send_json([
746 'errors' => [
747 'h-captcha-response' => [
748 __('hCaptcha verification failed, please try again.', 'fluentform'),
749 ],
750 ],
751 ], 422);
752 }
753 }
754 }
755
756 /**
757 * Validate turnstile.
758 */
759 private function validateTurnstile()
760 {
761 $hasAutoTurnsTile = false;
762 $hasAutoTurnsTile = apply_filters_deprecated(
763 'ff_has_auto_turnstile',
764 [
765 $hasAutoTurnsTile
766 ],
767 FLUENTFORM_FRAMEWORK_UPGRADE,
768 'fluentform/has_turnstile',
769 'Use fluentform/has_turnstile instead of ff_has_auto_turnstile.'
770 );
771 $autoInclude = apply_filters('fluentform/has_turnstile', $hasAutoTurnsTile);
772 if (FormFieldsParser::hasElement($this->form, 'turnstile') || $autoInclude) {
773 $keys = get_option('_fluentform_turnstile_details');
774 $token = Arr::get($this->formData, 'cf-turnstile-response');
775
776 $isValid = Turnstile::validate($token, $keys['secretKey']);
777
778 if (!$isValid) {
779 wp_send_json([
780 'errors' => [
781 'cf-turnstile-response' => [
782 __('Turnstile verification failed, please try again.', 'fluentform'),
783 ],
784 ],
785 ], 422);
786 }
787 }
788 }
789
790 /**
791 * Validate form data based on the form restrictions settings.
792 *
793 * @param $fields
794 */
795 private function validateRestrictions(&$fields)
796 {
797 $formSettings = wpFluent()->table('fluentform_form_meta')
798 ->where('form_id', $this->form->id)
799 ->where('meta_key', 'formSettings')
800 ->first();
801
802 $this->form->settings = $formSettings ? json_decode($formSettings->value, true) : [];
803
804 $isAllowed = [
805 'status' => true,
806 'message' => '',
807 ];
808
809 // This will check the following restriction settings.
810 // 1. limitNumberOfEntries
811 // 2. scheduleForm
812 // 3. requireLogin
813
814 /* This filter is deprecated and will be removed soon */
815 $isAllowed = apply_filters('fluentform_is_form_renderable', $isAllowed, $this->form);
816
817 $isAllowed = apply_filters('fluentform/is_form_renderable', $isAllowed, $this->form);
818
819 if (!$isAllowed['status']) {
820 wp_send_json([
821 'errors' => [
822 'restricted' => [
823 $isAllowed['message'],
824 ],
825 ],
826 ], 422);
827 }
828
829 // Since we are here, we should now handle if the form should be allowed to submit empty.
830 $restrictions = Arr::get($this->form->settings, 'restrictions.denyEmptySubmission', []);
831
832 $this->handleDenyEmptySubmission($restrictions, $fields);
833 }
834
835 /**
836 * Handle response when empty form submission is not allowed.
837 *
838 * @param array $settings
839 * @param $fields
840 */
841 private function handleDenyEmptySubmission($settings, &$fields)
842 {
843 // Determine whether empty form submission is allowed or not.
844 if (Arr::get($settings, 'enabled')) {
845 // confirm this form has no required fields.
846 if (!FormFieldsParser::hasRequiredFields($this->form, $fields)) {
847 // Filter out the form data which doesn't have values.
848 $filteredFormData = array_filter(
849 // Filter out the other meta fields that aren't actual inputs.
850 array_intersect_key($this->formData, $fields)
851 );
852
853 // TODO: Extract this function into global functions file...
854 $arrayFilterRecursive = function ($array) use (&$arrayFilterRecursive) {
855 foreach ($array as $key => $item) {
856 is_array($item) && $array[$key] = $arrayFilterRecursive($item);
857 if (empty($array[$key])) {
858 unset($array[$key]);
859 }
860 }
861 return $array;
862 };
863
864 if (!count($arrayFilterRecursive($filteredFormData))) {
865 wp_send_json([
866 'errors' => [
867 'restricted' => [
868 __(
869 !($m = Arr::get($settings, 'message'))
870 ? 'Sorry! You can\'t submit an empty form.'
871 : $m,
872 'fluentform'
873 ),
874 ],
875 ],
876 ], 422);
877 }
878 }
879 }
880 }
881
882 /**
883 * Prepare the data to be inserted to the database.
884 *
885 * @param boolean $formData
886 *
887 * @return array
888 */
889 public function prepareInsertData($formData = false)
890 {
891 $formId = $this->form->id;
892
893 if (!$formData) {
894 $formData = $this->formData;
895 }
896
897 $previousItem = wpFluent()->table('fluentform_submissions')
898 ->where('form_id', $formId)
899 ->orderBy('id', 'DESC')
900 ->first();
901
902 $serialNumber = 1;
903
904 if ($previousItem) {
905 $serialNumber = $previousItem->serial_number + 1;
906 }
907
908 $browser = new Browser();
909
910 $inputConfigs = FormFieldsParser::getEntryInputs($this->form, ['admin_label', 'raw']);
911
912 $formData = apply_filters_deprecated(
913 'fluentform_insert_response_data',
914 [
915 $formData,
916 $formId,
917 $inputConfigs
918 ],
919 FLUENTFORM_FRAMEWORK_UPGRADE,
920 'fluentform/insert_response_data',
921 'Use fluentform/insert_response_data instead of fluentform_insert_response_data.'
922 );
923 $this->formData = apply_filters('fluentform/insert_response_data', $formData, $formId, $inputConfigs);
924
925 $ipAddress = $this->app->request->getIp();
926 $disableIpLogging = false;
927 $disableIpLogging = apply_filters_deprecated(
928 'fluentform_disable_ip_logging',
929 [
930 $disableIpLogging,
931 $formId
932 ],
933 FLUENTFORM_FRAMEWORK_UPGRADE,
934 'fluentform/disable_ip_logging',
935 'Use fluentform/disable_ip_logging instead of fluentform_disable_ip_logging.'
936 );
937
938 if ((defined('FLUENTFROM_DISABLE_IP_LOGGING') && FLUENTFROM_DISABLE_IP_LOGGING) || apply_filters('fluentform/disable_ip_logging',
939 $disableIpLogging, $formId)) {
940 $ipAddress = false;
941 }
942
943 $response = [
944 'form_id' => $formId,
945 'serial_number' => $serialNumber,
946 'response' => json_encode($this->formData, JSON_UNESCAPED_UNICODE),
947 'source_url' => site_url(Arr::get($formData, '_wp_http_referer')),
948 'user_id' => get_current_user_id(),
949 'browser' => $browser->getBrowser(),
950 'device' => $browser->getPlatform(),
951 'ip' => $ipAddress,
952 'created_at' => current_time('mysql'),
953 'updated_at' => current_time('mysql'),
954 ];
955
956 $response = apply_filters_deprecated(
957 'fluentform_filter_insert_data',
958 [
959 $response
960 ],
961 FLUENTFORM_FRAMEWORK_UPGRADE,
962 'fluentform/filter_insert_data',
963 'Use fluentform/filter_insert_data instead of fluentform_filter_insert_data.'
964 );
965
966 return apply_filters('fluentform/filter_insert_data', $response);
967 }
968
969 /**
970 * Delegate the validation rules & messages to the
971 * ones that the validation library recognizes.
972 *
973 * @param $rules
974 * @param $messages
975 *
976 * @return array
977 */
978 protected function delegateValidations($rules, $messages, $search = [], $replace = [])
979 {
980 $search = $search ?: ['max_file_size', 'allowed_file_types'];
981 $replace = $replace ?: ['max', 'mimes'];
982
983 foreach ($rules as &$rule) {
984 $rule = str_replace($search, $replace, $rule);
985 }
986
987 foreach ($messages as $key => $message) {
988 $newKey = str_replace($search, $replace, $key);
989 $messages[$newKey] = $message;
990 unset($messages[$key]);
991 }
992
993 return [$rules, $messages];
994 }
995
996 /**
997 * Prevents malicious attacks when the submission
998 * count exceeds in an allowed interval.
999 */
1000 public function preventMaliciousAttacks()
1001 {
1002 $prevent = apply_filters('fluentform/prevent_malicious_attacks', true, $this->form->id);
1003
1004 if ($prevent) {
1005 $maxSubmissionCount = apply_filters('fluentform/max_submission_count', 5, $this->form->id);
1006 $minSubmissionInterval = apply_filters('fluentform/min_submission_interval', 30, $this->form->id);
1007
1008 $interval = date('Y-m-d H:i:s', strtotime(current_time('mysql')) - $minSubmissionInterval);
1009
1010 $submissionCount = wpFluent()->table('fluentform_submissions')
1011 ->where('status', '!=', 'trashed')
1012 ->where('ip', $this->app->request->getIp())
1013 ->where('created_at', '>=', $interval)
1014 ->count();
1015
1016 if ($submissionCount >= $maxSubmissionCount) {
1017 wp_send_json([
1018 'errors' => [
1019 'restricted' => [
1020 __(apply_filters('fluentform/too_many_requests', 'Too Many Requests.', $this->form->id),
1021 'fluentform'),
1022 ],
1023 ],
1024 ], 429);
1025 }
1026 }
1027 }
1028 }
1029