PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.8
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.8
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 6.2.8, at app/Modules/Form/FormHandler.php

1,025 lines 34.9 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\Database\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'] = fluentform_sanitize_html($confirmation['messageToShow']);
281
282 $confirmation['messageToShow'] = do_shortcode($confirmation['messageToShow']);
283
284 $confirmation['messageToShow'] = apply_filters('fluentform/submission_message_parse',
285 $confirmation['messageToShow'], $insertId, $formData, $form);
286
287 $message = ShortCodeParser::parse(
288 $confirmation['messageToShow'],
289 $insertId,
290 $formData,
291 $form,
292 false,
293 true
294 );
295
296 $message = $message ? $message : 'The form has been successfully submitted.';
297
298 $returnData = [
299 'message' => $message,
300 'action' => $confirmation['samePageFormBehavior'],
301 ];
302 } else {
303 $redirectUrl = Arr::get($confirmation, 'customUrl');
304
305 if ('customPage' == $confirmation['redirectTo']) {
306 $redirectUrl = get_permalink($confirmation['customPage']);
307 }
308
309 if (
310 ('yes' == Arr::get($confirmation, 'enable_query_string')) &&
311 Arr::get($confirmation, 'query_strings')
312 ) {
313 if (strpos($redirectUrl, '?')) {
314 $redirectUrl .= '&' . Arr::get($confirmation, 'query_strings');
315 } else {
316 $redirectUrl .= '?' . Arr::get($confirmation, 'query_strings');
317 }
318 }
319 $parseUrl = true;
320 $parseUrl = apply_filters_deprecated(
321 'fluentform_will_parse_url_value',
322 [
323 $parseUrl,
324 $form
325 ],
326 FLUENTFORM_FRAMEWORK_UPGRADE,
327 'fluentform/will_parse_url_value',
328 'Use fluentform/will_parse_url_value instead of fluentform_will_parse_url_value.'
329 );
330
331 $isUrlParser = apply_filters('fluentform/will_parse_url_value', $parseUrl, $form);
332
333 $redirectUrl = ShortCodeParser::parse(
334 $redirectUrl,
335 $insertId,
336 $formData,
337 $form,
338 $isUrlParser
339 );
340
341 if ($isUrlParser) {
342 /*
343 * For Empty Redirect Value
344 */
345 if (strpos($redirectUrl, '=&') || '=' == substr($redirectUrl, -1)) {
346 $urlArray = explode('?', $redirectUrl);
347 $baseUrl = array_shift($urlArray);
348
349 $query = wp_parse_url($redirectUrl)['query'];
350
351 $queryParams = explode('&', $query);
352
353 $params = [];
354 foreach ($queryParams as $queryParam) {
355 $paramArray = explode('=', $queryParam);
356 if (!empty($paramArray[1])) {
357 $params[$paramArray[0]] = $paramArray[1];
358 }
359 }
360
361 $redirectUrl = add_query_arg($params, $baseUrl);
362 }
363 }
364
365 $message = ShortCodeParser::parse(
366 ArrayHelper::get($confirmation, 'redirectMessage', ''),
367 $insertId,
368 $formData,
369 $form,
370 false,
371 true
372 );
373
374 $redirectUrl = wp_sanitize_redirect(urldecode($redirectUrl));
375 $returnData = [
376 'redirectUrl' => esc_url_raw($redirectUrl),
377 'message' => $message,
378 ];
379 }
380
381 $returnData = apply_filters_deprecated(
382 'fluentform_submission_confirmation',
383 [
384 $returnData,
385 $form,
386 $confirmation
387 ],
388 FLUENTFORM_FRAMEWORK_UPGRADE,
389 'fluentform/submission_confirmation',
390 'Use fluentform/submission_confirmation instead of fluentform_submission_confirmation.'
391 );
392
393 return $this->app->applyFilters(
394 'fluentform/submission_confirmation',
395 $returnData,
396 $form,
397 $confirmation
398 );
399 }
400
401 /**
402 * Validate form data.
403 *
404 * @param $fields
405 *
406 * @return bool
407 */
408 private function validate(&$fields)
409 {
410 $this->preventMaliciousAttacks();
411
412 $this->validateRestrictions($fields);
413
414 $this->validateNonce();
415
416 $this->validateReCaptcha();
417 $this->validateHCaptcha();
418 $this->validateTurnstile();
419
420 foreach ($fields as $fieldName => $field) {
421 if (isset($this->formData[$fieldName])) {
422 $element = $field['element'];
423
424 $this->formData[$fieldName] = apply_filters_deprecated(
425 'fluentform_input_data_' . $element,
426 [
427 $this->formData[$fieldName],
428 $field,
429 $this->formData,
430 $this->form
431 ],
432 FLUENTFORM_FRAMEWORK_UPGRADE,
433 'fluentform/input_data_' . $element,
434 'Use fluentform/input_data_' . $element . ' instead of fluentform_input_data_' . $element
435 );
436
437 $this->formData[$fieldName] = $this->app->applyFilters('fluentform/input_data_' . $element,
438 $this->formData[$fieldName], $field, $this->formData, $this->form);
439 }
440 }
441
442 $originalValidations = FormFieldsParser::getValidations($this->form, $this->formData, $fields);
443
444 $originalValidations = apply_filters_deprecated(
445 'fluentform_validations',
446 [
447 $originalValidations,
448 $this->form,
449 $this->formData
450 ],
451 FLUENTFORM_FRAMEWORK_UPGRADE,
452 'fluentform/validations',
453 'Use fluentform/validations instead of fluentform_validations.'
454 );
455 // Fire an event so that one can hook into it to work with the rules & messages.
456 $validations = apply_filters('fluentform/validations', $originalValidations, $this->form, $this->formData);
457
458 /*
459 * Clean talk fix for now
460 * They should not hook fluentform_validations and return nothing!
461 * We will remove this extra check once it's done
462 */
463 if ($originalValidations && (!$validations || !array_filter($validations))) {
464 $validations = $originalValidations;
465 }
466
467 $validator = wpFluentForm('validator')->make($this->formData, $validations[0], $validations[1]);
468
469 $errors = [];
470 if ($validator->validate()->fails()) {
471 foreach ($validator->errors() as $attribute => $rules) {
472 $position = strpos($attribute, ']');
473
474 if ($position) {
475 $attribute = substr($attribute, 0, strpos($attribute, ']') + 1);
476 }
477
478 $errors[$attribute] = $rules;
479 }
480
481 $errors = apply_filters_deprecated(
482 'fluentform_validation_error',
483 [
484 $errors,
485 $this->form,
486 $fields,
487 $this->formData
488 ],
489 FLUENTFORM_FRAMEWORK_UPGRADE,
490 'fluentform/validation_error',
491 'Use fluentform/validation_error instead of fluentform_validation_error.'
492 );
493 // Fire an event so that one can hook into it to work with the errors.
494 $errors = $this->app->applyFilters('fluentform/validation_error', $errors, $this->form, $fields,
495 $this->formData);
496 }
497
498 foreach ($fields as $fieldKey => $field) {
499 $field['data_key'] = $fieldKey;
500 $inputName = \FluentForm\Framework\Helpers\ArrayHelper::get($field, 'raw.attributes.name');
501 $field['name'] = $inputName;
502
503 $error = apply_filters_deprecated(
504 'fluentform_validate_input_item_' . $field['element'],
505 [
506 '',
507 $field,
508 $this->formData,
509 $fields,
510 $this->form,
511 $errors
512 ],
513 FLUENTFORM_FRAMEWORK_UPGRADE,
514 'fluentform_validate_input_item_' . $field['element'],
515 'Use fluentform/validate_input_item_' . $field['element'] . ' instead of fluentform_validate_input_item_' . $field['element']
516 );
517
518 $error = apply_filters('fluentform/validate_input_item_' . $field['element'], $error, $field, $this->formData, $fields, $this->form, $errors);
519 if ($error) {
520 if (empty($errors[$inputName])) {
521 $errors[$inputName] = [];
522 }
523
524 if (is_string($error)) {
525 $error = [$error];
526 }
527
528 $errors[$inputName] = array_merge($error, $errors[$inputName]);
529 }
530 }
531
532 $errors = apply_filters_deprecated(
533 'fluentform_validation_errors',
534 [
535 $errors,
536 $this->formData,
537 $this->form,
538 $fields
539 ],
540 FLUENTFORM_FRAMEWORK_UPGRADE,
541 'fluentform/validation_errors',
542 'Use fluentform/validation_errors instead of fluentform_validation_errors.'
543 );
544
545 $errors = apply_filters('fluentform/validation_errors', $errors, $this->formData, $this->form, $fields);
546
547 if ('yes' == Helper::getFormMeta($this->form->id, '_has_user_registration') && !get_current_user_id()) {
548 $errors = apply_filters_deprecated(
549 'fluentform_validation_user_registration_errors',
550 [
551 $errors,
552 $this->formData,
553 $this->form,
554 $fields
555 ],
556 FLUENTFORM_FRAMEWORK_UPGRADE,
557 'fluentform/validation_user_registration_errors',
558 'Use fluentform/validation_user_registration_errors instead of fluentform_validation_user_registration_errors.'
559 );
560
561 $errors = apply_filters('fluentform/validation_user_registration_errors', $errors, $this->formData,
562 $this->form, $fields);
563 }
564
565 if ('yes' == Helper::getFormMeta($this->form->id, '_has_user_update') && get_current_user_id()) {
566 $errors = apply_filters_deprecated(
567 'fluentform_validation_user_update_errors',
568 [
569 $errors,
570 $this->formData,
571 $this->form,
572 $fields
573 ],
574 FLUENTFORM_FRAMEWORK_UPGRADE,
575 'fluentform/validation_user_update_errors',
576 'Use fluentform/validation_user_update_errors instead of fluentform_validation_user_update_errors.'
577 );
578
579 $errors = apply_filters('fluentform/validation_user_update_errors', $errors, $this->formData, $this->form, $fields);
580 }
581
582 if ($errors) {
583 wp_send_json(['errors' => $errors], 423);
584 }
585
586 return true;
587 }
588
589 /**
590 * Validate nonce.
591 */
592 protected function validateNonce()
593 {
594 $formId = $this->form->id;
595 $nonceVerify = false;
596 /* This filter is deprecated and will be removed soon. */
597 $nonceVerify = $this->app->applyFilters('fluentform_nonce_verify', $nonceVerify, $formId);
598
599 $shouldVerifyNonce = $this->app->applyFilters('fluentform/nonce_verify', $nonceVerify, $formId);
600
601 if ($shouldVerifyNonce) {
602 $nonce = Arr::get($this->formData, '_fluentform_' . $formId . '_fluentformnonce');
603 if (!wp_verify_nonce($nonce, 'fluentform-submit-form')) {
604 $nonceMessage = apply_filters_deprecated(
605 'fluentForm_nonce_error',
606 [
607 '_fluentformnonce' => [
608 __('Nonce verification failed, please try again.', 'fluentform'),
609 ],
610 ],
611 FLUENTFORM_FRAMEWORK_UPGRADE,
612 'fluentform/nonce_error',
613 'Use fluentform/nonce_error instead of fluentForm_nonce_error.'
614 );
615
616 $errors = $this->app->applyFilters('fluentform/nonce_error', $nonceMessage);
617 wp_send_json(['errors' => $errors], 422);
618 }
619 }
620 }
621
622 protected function handleSpamError()
623 {
624 $settings = get_option('_fluentform_global_form_settings');
625 if (!$settings || 'validation_failed' != ArrayHelper::get($settings, 'misc.akismet_validation')) {
626 return;
627 }
628
629 $errors = [
630 '_fluentformakismet' => apply_filters(
631 'fluentform/akismet_spam_message',
632 __('Submission marked as spammed. Please try again', 'fluentform'),
633 $this->form->id
634 ),
635 ];
636
637 wp_send_json(['errors' => $errors], 422);
638 }
639
640 protected function isAkismetSpam($formData, $form)
641 {
642 if (!AkismetHandler::isEnabled()) {
643 return false;
644 }
645 $isSpamCheck = true;
646 $isSpamCheck = apply_filters_deprecated(
647 'fluentform_akismet_check_spam',
648 [
649 true,
650 $form->id,
651 $formData
652 ],
653 FLUENTFORM_FRAMEWORK_UPGRADE,
654 'fluentform/akismet_check_spam',
655 'Use fluentform/akismet_check_spam instead of fluentform_akismet_check_spam.'
656 );
657
658 $isSpamCheck = apply_filters('fluentform/akismet_check_spam', $isSpamCheck, $form->id, $formData);
659 if (!$isSpamCheck) {
660 return false;
661 }
662 // Let's validate now
663 $isSpam = AkismetHandler::isSpamSubmission($formData, $form);
664
665 $isSpam = apply_filters_deprecated(
666 'fluentform_akismet_spam_result',
667 [
668 $isSpam,
669 $form->id,
670 $formData
671 ],
672 FLUENTFORM_FRAMEWORK_UPGRADE,
673 'fluentform/akismet_spam_result',
674 'Use fluentform/akismet_spam_result instead of fluentform_akismet_spam_result.'
675 );
676
677 return $this->app->applyFilters('fluentform/akismet_spam_result', $isSpam, $form->id, $formData);
678 }
679
680 /**
681 * Validate reCaptcha.
682 */
683 private function validateReCaptcha()
684 {
685 $hasAutoRecaptcha = false;
686 $hasAutoRecaptcha = apply_filters_deprecated(
687 'ff_has_auto_recaptcha',
688 [
689 $hasAutoRecaptcha
690 ],
691 FLUENTFORM_FRAMEWORK_UPGRADE,
692 'fluentform/has_recaptcha',
693 'Use fluentform/has_recaptcha instead of ff_has_auto_recaptcha.'
694 );
695 $autoInclude = apply_filters('fluentform/has_recaptcha', $hasAutoRecaptcha);
696 if (FormFieldsParser::hasElement($this->form, 'recaptcha') || $autoInclude) {
697 $keys = get_option('_fluentform_reCaptcha_details');
698 $token = Arr::get($this->formData, 'g-recaptcha-response');
699 $version = 'v2_visible';
700 if (!empty($keys['api_version'])) {
701 $version = $keys['api_version'];
702 }
703 $isValid = ReCaptcha::validate($token, $keys['secretKey'], $version);
704
705 if (!$isValid) {
706 $message = apply_filters(
707 'fluentform/recaptcha_failed_message',
708 __('reCaptcha verification failed, please try again.', 'fluentform'),
709 $this->form
710 );
711 wp_send_json(['errors' => ['g-recaptcha-response' => [$message]]], 422);
712 }
713 }
714 }
715
716 /**
717 * Validate hCaptcha.
718 */
719 private function validateHCaptcha()
720 {
721 $hasAutoHcaptcha = false;
722
723 $hasAutoHcaptcha = apply_filters_deprecated(
724 'ff_has_auto_hcaptcha',
725 [
726 $hasAutoHcaptcha
727 ],
728 FLUENTFORM_FRAMEWORK_UPGRADE,
729 'fluentform/has_hcaptcha',
730 'Use fluentform/has_hcaptcha instead of ff_has_auto_hcaptcha.'
731 );
732 $autoInclude = apply_filters('fluentform/has_hcaptcha', $hasAutoHcaptcha);
733 FormFieldsParser::resetData();
734 if (FormFieldsParser::hasElement($this->form, 'hcaptcha') || $autoInclude) {
735 $keys = get_option('_fluentform_hCaptcha_details');
736 $token = Arr::get($this->formData, 'h-captcha-response');
737 $isValid = HCaptcha::validate($token, $keys['secretKey']);
738
739 if (!$isValid) {
740 $message = apply_filters(
741 'fluentform/hcaptcha_failed_message',
742 __('hCaptcha verification failed, please try again.', 'fluentform'),
743 $this->form
744 );
745 wp_send_json(['errors' => ['h-captcha-response' => [$message]]], 422);
746 }
747 }
748 }
749
750 /**
751 * Validate turnstile.
752 */
753 private function validateTurnstile()
754 {
755 $hasAutoTurnsTile = false;
756 $hasAutoTurnsTile = apply_filters_deprecated(
757 'ff_has_auto_turnstile',
758 [
759 $hasAutoTurnsTile
760 ],
761 FLUENTFORM_FRAMEWORK_UPGRADE,
762 'fluentform/has_turnstile',
763 'Use fluentform/has_turnstile instead of ff_has_auto_turnstile.'
764 );
765 $autoInclude = apply_filters('fluentform/has_turnstile', $hasAutoTurnsTile);
766 if (FormFieldsParser::hasElement($this->form, 'turnstile') || $autoInclude) {
767 $keys = get_option('_fluentform_turnstile_details');
768 $token = Arr::get($this->formData, 'cf-turnstile-response');
769
770 $isValid = Turnstile::validate($token, $keys['secretKey']);
771
772 if (!$isValid) {
773 $message = apply_filters(
774 'fluentform/turnstile_failed_message',
775 __('Turnstile verification failed, please try again.', 'fluentform'),
776 $this->form
777 );
778 wp_send_json(['errors' => ['cf-turnstile-response' => [$message]]], 422);
779 }
780 }
781 }
782
783 /**
784 * Validate form data based on the form restrictions settings.
785 *
786 * @param $fields
787 */
788 private function validateRestrictions(&$fields)
789 {
790 $formSettings = wpFluent()->table('fluentform_form_meta')
791 ->where('form_id', $this->form->id)
792 ->where('meta_key', 'formSettings')
793 ->first();
794
795 $this->form->settings = $formSettings ? json_decode($formSettings->value, true) : [];
796
797 $isAllowed = [
798 'status' => true,
799 'message' => '',
800 ];
801
802 // This will check the following restriction settings.
803 // 1. limitNumberOfEntries
804 // 2. scheduleForm
805 // 3. requireLogin
806
807 /* This filter is deprecated and will be removed soon */
808 $isAllowed = apply_filters('fluentform_is_form_renderable', $isAllowed, $this->form);
809
810 $isAllowed = apply_filters('fluentform/is_form_renderable', $isAllowed, $this->form);
811
812 if (!$isAllowed['status']) {
813 wp_send_json([
814 'errors' => [
815 'restricted' => [
816 $isAllowed['message'],
817 ],
818 ],
819 ], 422);
820 }
821
822 // Since we are here, we should now handle if the form should be allowed to submit empty.
823 $restrictions = Arr::get($this->form->settings, 'restrictions.denyEmptySubmission', []);
824
825 $this->handleDenyEmptySubmission($restrictions, $fields);
826 }
827
828 /**
829 * Handle response when empty form submission is not allowed.
830 *
831 * @param array $settings
832 * @param $fields
833 */
834 private function handleDenyEmptySubmission($settings, &$fields)
835 {
836 // Determine whether empty form submission is allowed or not.
837 if (Arr::get($settings, 'enabled')) {
838 // confirm this form has no required fields.
839 if (!FormFieldsParser::hasRequiredFields($this->form, $fields)) {
840 // Filter out the form data which doesn't have values.
841 $filteredFormData = array_filter(
842 // Filter out the other meta fields that aren't actual inputs.
843 array_intersect_key($this->formData, $fields)
844 );
845
846 // TODO: Extract this function into global functions file...
847 $arrayFilterRecursive = function ($array) use (&$arrayFilterRecursive) {
848 foreach ($array as $key => $item) {
849 is_array($item) && $array[$key] = $arrayFilterRecursive($item);
850 if (empty($array[$key])) {
851 unset($array[$key]);
852 }
853 }
854 return $array;
855 };
856
857 if (!count($arrayFilterRecursive($filteredFormData))) {
858 $message = Arr::get($settings, 'message');
859 if (!$message) {
860 $message = __('Sorry! You can\'t submit an empty form.', 'fluentform');
861 }
862 wp_send_json([
863 'errors' => [
864 'restricted' => [
865 $message,
866 ],
867 ],
868 ], 422);
869 }
870 }
871 }
872 }
873
874 /**
875 * Prepare the data to be inserted to the database.
876 *
877 * @param boolean $formData
878 *
879 * @return array
880 */
881 public function prepareInsertData($formData = false)
882 {
883 $formId = $this->form->id;
884
885 if (!$formData) {
886 $formData = $this->formData;
887 }
888
889 $previousItem = wpFluent()->table('fluentform_submissions')
890 ->where('form_id', $formId)
891 ->orderBy('id', 'DESC')
892 ->first();
893
894 $serialNumber = 1;
895
896 if ($previousItem) {
897 $serialNumber = $previousItem->serial_number + 1;
898 }
899
900 $browser = new Browser();
901
902 $inputConfigs = FormFieldsParser::getEntryInputs($this->form, ['admin_label', 'raw']);
903
904 $formData = apply_filters_deprecated(
905 'fluentform_insert_response_data',
906 [
907 $formData,
908 $formId,
909 $inputConfigs
910 ],
911 FLUENTFORM_FRAMEWORK_UPGRADE,
912 'fluentform/insert_response_data',
913 'Use fluentform/insert_response_data instead of fluentform_insert_response_data.'
914 );
915 $this->formData = apply_filters('fluentform/insert_response_data', $formData, $formId, $inputConfigs);
916
917 $ipAddress = sanitize_text_field($this->app->request->getIp());
918 $disableIpLogging = false;
919 $disableIpLogging = apply_filters_deprecated(
920 'fluentform_disable_ip_logging',
921 [
922 $disableIpLogging,
923 $formId
924 ],
925 FLUENTFORM_FRAMEWORK_UPGRADE,
926 'fluentform/disable_ip_logging',
927 'Use fluentform/disable_ip_logging instead of fluentform_disable_ip_logging.'
928 );
929
930 if ((defined('FLUENTFROM_DISABLE_IP_LOGGING') && FLUENTFROM_DISABLE_IP_LOGGING) || apply_filters('fluentform/disable_ip_logging',
931 $disableIpLogging, $formId)) {
932 $ipAddress = false;
933 }
934
935 $response = [
936 'form_id' => $formId,
937 'serial_number' => $serialNumber,
938 'response' => json_encode($this->formData, JSON_UNESCAPED_UNICODE),
939 'source_url' => site_url(Arr::get($formData, '_wp_http_referer')),
940 'user_id' => get_current_user_id(),
941 'browser' => $browser->getBrowser(),
942 'device' => $browser->getPlatform(),
943 'ip' => $ipAddress,
944 'created_at' => current_time('mysql'),
945 'updated_at' => current_time('mysql'),
946 ];
947
948 $response = apply_filters_deprecated(
949 'fluentform_filter_insert_data',
950 [
951 $response
952 ],
953 FLUENTFORM_FRAMEWORK_UPGRADE,
954 'fluentform/filter_insert_data',
955 'Use fluentform/filter_insert_data instead of fluentform_filter_insert_data.'
956 );
957
958 return apply_filters('fluentform/filter_insert_data', $response);
959 }
960
961 /**
962 * Delegate the validation rules & messages to the
963 * ones that the validation library recognizes.
964 *
965 * @param $rules
966 * @param $messages
967 *
968 * @return array
969 */
970 protected function delegateValidations($rules, $messages, $search = [], $replace = [])
971 {
972 $search = $search ?: ['max_file_size', 'allowed_file_types'];
973 $replace = $replace ?: ['max', 'mimes'];
974
975 foreach ($rules as &$rule) {
976 $rule = str_replace($search, $replace, $rule);
977 }
978
979 foreach ($messages as $key => $message) {
980 $newKey = str_replace($search, $replace, $key);
981 $messages[$newKey] = $message;
982 unset($messages[$key]);
983 }
984
985 return [$rules, $messages];
986 }
987
988 /**
989 * Prevents malicious attacks when the submission
990 * count exceeds in an allowed interval.
991 */
992 public function preventMaliciousAttacks()
993 {
994 $prevent = apply_filters('fluentform/prevent_malicious_attacks', true, $this->form->id);
995
996 if ($prevent) {
997 $maxSubmissionCount = apply_filters('fluentform/max_submission_count', 5, $this->form->id);
998 $minSubmissionInterval = apply_filters('fluentform/min_submission_interval', 30, $this->form->id);
999
1000 $interval = date('Y-m-d H:i:s', strtotime(current_time('mysql')) - $minSubmissionInterval);
1001
1002 $clientIp = sanitize_text_field($this->app->request->getIp());
1003 $submissionCount = wpFluent()->table('fluentform_submissions')
1004 ->where('status', '!=', 'trashed')
1005 ->where('ip', $clientIp ?: '0.0.0.0')
1006 ->where('created_at', '>=', $interval)
1007 ->count();
1008
1009 if ($submissionCount >= $maxSubmissionCount) {
1010 wp_send_json([
1011 'errors' => [
1012 'restricted' => [
1013 apply_filters(
1014 'fluentform/too_many_requests',
1015 __('Too Many Requests.', 'fluentform'),
1016 $this->form->id
1017 ),
1018 ],
1019 ],
1020 ], 429);
1021 }
1022 }
1023 }
1024 }
1025