PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.14
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.14
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
← All changes | app/Modules/Form/FormHandler.php +549 -107 3.6.506.2.14 View file →
@@ -1,38 +1,48 @@
1 1 <?php
2 2
3 3 namespace FluentForm\App\Modules\Form;
4 4
5 -use FluentForm\App\Databases\Migrations\FormSubmissionDetails;
5 +use FluentForm\Database\Migrations\SubmissionDetails;
6 6 use FluentForm\App\Helpers\Helper;
7 7 use FluentForm\App\Modules\Activator;
8 -use FluentForm\App\Modules\Entries\Entries;
9 8 use FluentForm\App\Modules\ReCaptcha\ReCaptcha;
9 +use FluentForm\App\Modules\HCaptcha\HCaptcha;
10 +use FluentForm\App\Modules\Turnstile\Turnstile;
10 11 use FluentForm\App\Services\Browser\Browser;
11 12 use FluentForm\App\Services\FormBuilder\ShortCodeParser;
13 +use FluentForm\App\Services\Submission\SubmissionService;
12 14 use FluentForm\Framework\Foundation\Application;
13 15 use FluentForm\Framework\Helpers\ArrayHelper as Arr;
14 16 use FluentForm\Framework\Helpers\ArrayHelper;
15 17
18 +/* @deprecated Use class \FluentForm\App\Http\Controllers\SubmissionHandlerController */
19 +
16 20 class FormHandler
17 21 {
18 22 /**
23 + * App instance
24 + *
19 25 * @var \FluentForm\Framework\Foundation\Application
20 26 */
21 27 protected $app;
22 28
23 29 /**
30 + * Request object
31 + *
24 32 * @var \FluentForm\Framework\Request\Request
25 33 */
26 34 protected $request;
27 35
28 36 /**
37 + * Form Data
38 + *
29 39 * @var array $formData
30 40 */
31 41 protected $formData;
32 42
33 43 /**
34 - * The fluent form object.
44 + * The Fluent Forms object.
35 45 *
36 46 * @var \stdClass
37 47 */
38 48 protected $form;
@@ -51,8 +61,9 @@
51 61 /**
52 62 * Set the form using it's ID.
53 63 *
54 64 * @param $formId
65 + *
55 66 * @return $this
56 67 */
57 68 public function setForm($formId)
58 69 {
@@ -67,8 +78,10 @@
67 78 {
68 79 // Parse the url encoded data from the request object.
69 80 parse_str($this->app->request->get('data'), $data);
70 81
82 + $data['_wp_http_referer'] = urldecode($data['_wp_http_referer']);
83 +
71 84 // Merge it back again to the request object.
72 85 $this->app->request->merge(['data' => $data]);
73 86
74 87 $formId = intval($this->app->request->get('form_id'));
@@ -74,8 +87,14 @@
74 87 $formId = intval($this->app->request->get('form_id'));
75 88
76 89 $this->setForm($formId);
77 90
91 + if (!$this->form) {
92 + wp_send_json([
93 + 'errors' => [],
94 + 'message' => 'Sorry, No corresponding form found',
95 + ], 423);
96 + }
78 97
79 98 // Parse the form and get the flat inputs with validations.
80 99 $fields = FormFieldsParser::getInputs($this->form, ['rules', 'raw']);
81 100
@@ -87,21 +106,64 @@
87 106
88 107 // Prepare the data to be inserted to the DB.
89 108 $insertData = $this->prepareInsertData();
90 109
91 - if ($this->isSpam($this->formData, $this->form)) {
110 + if ($this->isAkismetSpam($this->formData, $this->form)) {
92 111 $insertData['status'] = 'spam';
93 112 $this->handleSpamError();
94 113 }
95 114
96 - do_action('fluentform_before_insert_submission', $insertData, $data, $this->form);
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 + );
97 126
127 + do_action('fluentform/before_insert_submission', $insertData, $data, $this->form);
128 +
98 129 if ($this->form->has_payment) {
99 - do_action('fluentform_before_insert_payment_form', $insertData, $data, $this->form);
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);
100 143 }
101 144
102 - $insertId = wpFluent()->table('fluentform_submissions')->insert($insertData);
145 + $insertId = wpFluent()->table('fluentform_submissions')->insertGetId($insertData);
103 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 +
104 166 $result = $this->processFormSubmissionData($insertId, $this->formData, $this->form);
105 167
106 168 wp_send_json_success($result, 200);
107 169 }
@@ -109,13 +171,13 @@
109 171 public function processFormSubmissionData($insertId, $formData, $form)
110 172 {
111 173 if ($insertId) {
112 174 ob_start();
113 - $entries = new Entries();
114 - $entries->recordEntryDetails($insertId, $form->id, $formData);
175 + $submissionService = new SubmissionService();
176 + $submissionService->recordEntryDetails($insertId, $form->id, $formData);
115 177 $isError = ob_get_clean();
116 178 if ($isError) {
117 - FormSubmissionDetails::migrate();
179 + SubmissionDetails::migrate();
118 180 }
119 181 }
120 182
121 183 $returnData = $this->getReturnData($insertId, $form, $formData);
@@ -121,21 +183,42 @@
121 183 $returnData = $this->getReturnData($insertId, $form, $formData);
122 184
123 185 $error = '';
124 186 try {
125 - $this->app->doAction(
126 - 'fluentform_submission_inserted',
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',
127 195 $insertId,
128 196 $formData,
129 197 $form
130 198 );
131 199
132 - $this->app->doAction(
200 + Helper::setSubmissionMeta($insertId, 'is_form_action_fired', 'yes');
201 +
202 + do_action_deprecated(
133 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',
134 216 $insertId,
135 217 $formData,
136 218 $form
137 219 );
220 +
138 221 } catch (\Exception $e) {
139 222 if (defined('WP_DEBUG') && WP_DEBUG) {
140 223 $error = $e->getMessage();
141 224 }
@@ -140,14 +223,26 @@
140 223 $error = $e->getMessage();
141 224 }
142 225 }
143 226
144 - do_action('fluenform_before_submission_confirmation', $insertId, $formData, $form);
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 + );
145 238
239 + do_action('fluentform/before_submission_confirmation', $insertId, $formData, $form);
240 +
146 241 return [
147 242 'insert_id' => $insertId,
148 - 'result' => $returnData,
149 - 'error' => $error
243 + 'result' => $returnData,
244 + 'error' => $error,
150 245 ];
151 246 }
152 247
153 248 public function getReturnData($insertId, $form, $formData)
@@ -159,45 +254,61 @@
159 254 ->first();
160 255
161 256 $form->settings = $formSettings ? json_decode($formSettings->value, true) : [];
162 257 }
163 -
164 - $confirmation = apply_filters(
258 + $confirmation = $form->settings['confirmation'];
259 + $confirmation = apply_filters_deprecated(
165 260 'fluentform_form_submission_confirmation',
166 - $form->settings['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,
167 274 $formData,
168 275 $form
169 276 );
170 277
171 - if ($confirmation['redirectTo'] == 'samePage') {
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);
172 286
173 - $confirmation['messageToShow'] = apply_filters('fluentform_submission_message_parse', $confirmation['messageToShow'], $insertId, $formData, $form);
174 -
175 -
176 287 $message = ShortCodeParser::parse(
177 288 $confirmation['messageToShow'],
178 289 $insertId,
179 290 $formData,
180 - $form
291 + $form,
292 + false,
293 + true
181 294 );
182 295
183 -
184 296 $message = $message ? $message : 'The form has been successfully submitted.';
185 297
186 298 $returnData = [
187 - 'message' => do_shortcode($message),
188 - 'action' => $confirmation['samePageFormBehavior'],
299 + 'message' => $message,
300 + 'action' => $confirmation['samePageFormBehavior'],
189 301 ];
190 -
191 302 } else {
192 303 $redirectUrl = Arr::get($confirmation, 'customUrl');
193 304
194 - if ($confirmation['redirectTo'] == 'customPage') {
305 + if ('customPage' == $confirmation['redirectTo']) {
195 306 $redirectUrl = get_permalink($confirmation['customPage']);
196 307 }
197 308
198 309 if (
199 - (Arr::get($confirmation, 'enable_query_string') == 'yes') &&
310 + ('yes' == Arr::get($confirmation, 'enable_query_string')) &&
200 311 Arr::get($confirmation, 'query_strings')
201 312 ) {
202 313 if (strpos($redirectUrl, '?')) {
203 314 $redirectUrl .= '&' . Arr::get($confirmation, 'query_strings');
@@ -204,10 +315,21 @@
204 315 } else {
205 316 $redirectUrl .= '?' . Arr::get($confirmation, 'query_strings');
206 317 }
207 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 + );
208 330
209 - $isUrlParser = apply_filters('fluentform_will_parse_url_value', true, $form);
331 + $isUrlParser = apply_filters('fluentform/will_parse_url_value', $parseUrl, $form);
210 332
211 333 $redirectUrl = ShortCodeParser::parse(
212 334 $redirectUrl,
213 335 $insertId,
@@ -214,16 +336,14 @@
214 336 $formData,
215 337 $form,
216 338 $isUrlParser
217 339 );
218 -
219 - if($isUrlParser) {
220 - $redirectUrl = esc_url_raw($redirectUrl);
221 -
340 +
341 + if ($isUrlParser) {
222 342 /*
223 343 * For Empty Redirect Value
224 344 */
225 - if(strpos($redirectUrl, '=&') || substr($redirectUrl, -1) == '=') {
345 + if (strpos($redirectUrl, '=&') || '=' == substr($redirectUrl, -1)) {
226 346 $urlArray = explode('?', $redirectUrl);
227 347 $baseUrl = array_shift($urlArray);
228 348
229 349 $query = wp_parse_url($redirectUrl)['query'];
@@ -232,9 +352,9 @@
232 352
233 353 $params = [];
234 354 foreach ($queryParams as $queryParam) {
235 355 $paramArray = explode('=', $queryParam);
236 - if(!empty($paramArray[1])) {
356 + if (!empty($paramArray[1])) {
237 357 $params[$paramArray[0]] = $paramArray[1];
238 358 }
239 359 }
240 360
@@ -245,19 +365,34 @@
245 365 $message = ShortCodeParser::parse(
246 366 ArrayHelper::get($confirmation, 'redirectMessage', ''),
247 367 $insertId,
248 368 $formData,
249 - $form
369 + $form,
370 + false,
371 + true
250 372 );
251 -
373 +
374 + $redirectUrl = wp_sanitize_redirect(urldecode($redirectUrl));
252 375 $returnData = [
253 - 'redirectUrl' => $redirectUrl,
254 - 'message' => $message
376 + 'redirectUrl' => esc_url_raw($redirectUrl),
377 + 'message' => $message,
255 378 ];
256 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 + );
257 392
258 393 return $this->app->applyFilters(
259 - 'fluentform_submission_confirmation',
394 + 'fluentform/submission_confirmation',
260 395 $returnData,
261 396 $form,
262 397 $confirmation
263 398 );
@@ -266,22 +401,60 @@
266 401 /**
267 402 * Validate form data.
268 403 *
269 404 * @param $fields
405 + *
270 406 * @return bool
271 407 */
272 408 private function validate(&$fields)
273 409 {
410 + $this->preventMaliciousAttacks();
411 +
274 412 $this->validateRestrictions($fields);
275 413
276 414 $this->validateNonce();
277 415
278 416 $this->validateReCaptcha();
417 + $this->validateHCaptcha();
418 + $this->validateTurnstile();
279 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 +
280 442 $originalValidations = FormFieldsParser::getValidations($this->form, $this->formData, $fields);
281 -
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 + );
282 455 // Fire an event so that one can hook into it to work with the rules & messages.
283 - $validations = apply_filters('fluentform_validations', $originalValidations, $this->form, $this->formData);
456 + $validations = apply_filters('fluentform/validations', $originalValidations, $this->form, $this->formData);
284 457
285 458 /*
286 459 * Clean talk fix for now
287 460 * They should not hook fluentform_validations and return nothing!
@@ -290,9 +463,9 @@
290 463 if ($originalValidations && (!$validations || !array_filter($validations))) {
291 464 $validations = $originalValidations;
292 465 }
293 466
294 - $validator = \FluentValidator\Validator::make($this->formData, $validations[0], $validations[1]);
467 + $validator = wpFluentForm('validator')->make($this->formData, $validations[0], $validations[1]);
295 468
296 469 $errors = [];
297 470 if ($validator->validate()->fails()) {
298 471 foreach ($validator->errors() as $attribute => $rules) {
@@ -303,10 +476,24 @@
303 476 }
304 477
305 478 $errors[$attribute] = $rules;
306 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 + );
307 493 // Fire an event so that one can hook into it to work with the errors.
308 - $errors = $this->app->applyFilters('fluentform_validation_error', $errors, $this->form, $fields, $this->formData);
494 + $errors = $this->app->applyFilters('fluentform/validation_error', $errors, $this->form, $fields,
495 + $this->formData);
309 496 }
310 497
311 498 foreach ($fields as $fieldKey => $field) {
312 499 $field['data_key'] = $fieldKey;
@@ -311,15 +498,31 @@
311 498 foreach ($fields as $fieldKey => $field) {
312 499 $field['data_key'] = $fieldKey;
313 500 $inputName = \FluentForm\Framework\Helpers\ArrayHelper::get($field, 'raw.attributes.name');
314 501 $field['name'] = $inputName;
315 - $error = apply_filters('fluentform_validate_input_item_' . $field['element'], '', $field, $this->formData, $fields, $this->form, $errors);
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);
316 519 if ($error) {
317 520 if (empty($errors[$inputName])) {
318 521 $errors[$inputName] = [];
319 522 }
320 523
321 - if(is_string($error)) {
524 + if (is_string($error)) {
322 525 $error = [$error];
323 526 }
324 527
325 528 $errors[$inputName] = array_merge($error, $errors[$inputName]);
@@ -324,17 +527,61 @@
324 527
325 528 $errors[$inputName] = array_merge($error, $errors[$inputName]);
326 529 }
327 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 + );
328 544
329 - $errors = apply_filters('fluentform_validation_errors', $errors, $this->formData, $this->form, $fields);
545 + $errors = apply_filters('fluentform/validation_errors', $errors, $this->formData, $this->form, $fields);
330 546
331 - if(Helper::getFormMeta($this->form->id, '_has_user_registration') == 'yes') {
332 - $errors = apply_filters('fluentform_validation_user_registration_errors', $errors, $this->formData, $this->form, $fields);
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);
333 563 }
334 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 +
335 582 if ($errors) {
336 - wp_send_json(['errors' => $errors], 422);
583 + wp_send_json(['errors' => $errors], 423);
337 584 }
338 585
339 586 return true;
340 587 }
@@ -344,19 +591,30 @@
344 591 */
345 592 protected function validateNonce()
346 593 {
347 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);
348 598
349 - $shouldVerifyNonce = $this->app->applyFilters('fluentform_nonce_verify', false, $formId);
599 + $shouldVerifyNonce = $this->app->applyFilters('fluentform/nonce_verify', $nonceVerify, $formId);
350 600
351 601 if ($shouldVerifyNonce) {
352 602 $nonce = Arr::get($this->formData, '_fluentform_' . $formId . '_fluentformnonce');
353 603 if (!wp_verify_nonce($nonce, 'fluentform-submit-form')) {
354 - $errors = $this->app->applyFilters('fluentForm_nonce_error', [
355 - '_fluentformnonce' => [
356 - __('Nonce verification failed, please try again.', 'fluentform')
357 - ]
358 - ]);
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);
359 617 wp_send_json(['errors' => $errors], 422);
360 618 }
361 619 }
362 620 }
@@ -363,33 +621,61 @@
363 621
364 622 protected function handleSpamError()
365 623 {
366 624 $settings = get_option('_fluentform_global_form_settings');
367 - if (!$settings || ArrayHelper::get($settings, 'misc.akismet_validation') != 'validation_failed') {
625 + if (!$settings || 'validation_failed' != ArrayHelper::get($settings, 'misc.akismet_validation')) {
368 626 return;
369 627 }
370 628
371 - $errors = [
372 - '_fluentformakismet' => __('Submission marked as spammed. Please try again', 'fluentform')
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 + ),
373 635 ];
374 636
375 637 wp_send_json(['errors' => $errors], 422);
376 638 }
377 639
378 - protected function isSpam($formData, $form)
640 + protected function isAkismetSpam($formData, $form)
379 641 {
380 642 if (!AkismetHandler::isEnabled()) {
381 643 return false;
382 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 + );
383 657
384 - $isSpamCheck = apply_filters('fluentform_akismet_check_spam', true, $form->id, $formData);
658 + $isSpamCheck = apply_filters('fluentform/akismet_check_spam', $isSpamCheck, $form->id, $formData);
385 659 if (!$isSpamCheck) {
386 660 return false;
387 661 }
388 662 // Let's validate now
389 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 + );
390 676
391 - return apply_filters('fluentform_akismet_spam_result', $isSpam, $form->id, $formData);
677 + return $this->app->applyFilters('fluentform/akismet_spam_result', $isSpam, $form->id, $formData);
392 678 }
393 679
394 680 /**
395 681 * Validate reCaptcha.
@@ -395,24 +681,107 @@
395 681 * Validate reCaptcha.
396 682 */
397 683 private function validateReCaptcha()
398 684 {
399 - if (FormFieldsParser::hasElement($this->form, 'recaptcha')) {
400 - $isValid = ReCaptcha::validate(Arr::get($this->formData, 'g-recaptcha-response'));
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);
401 704
402 705 if (!$isValid) {
403 - wp_send_json([
404 - 'errors' => [
405 - 'g-recaptcha-response' => [
406 - __('reCaptcha verification failed, please try again.', 'fluentform')
407 - ]
408 - ]
409 - ], 422);
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);
410 712 }
411 713 }
412 714 }
413 715
414 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 + /**
415 784 * Validate form data based on the form restrictions settings.
416 785 *
417 786 * @param $fields
418 787 */
@@ -425,10 +794,10 @@
425 794
426 795 $this->form->settings = $formSettings ? json_decode($formSettings->value, true) : [];
427 796
428 797 $isAllowed = [
429 - 'status' => true,
430 - 'message' => ''
798 + 'status' => true,
799 + 'message' => '',
431 800 ];
432 801
433 802 // This will check the following restriction settings.
434 803 // 1. limitNumberOfEntries
@@ -433,17 +802,21 @@
433 802 // This will check the following restriction settings.
434 803 // 1. limitNumberOfEntries
435 804 // 2. scheduleForm
436 805 // 3. requireLogin
806 +
807 + /* This filter is deprecated and will be removed soon */
437 808 $isAllowed = apply_filters('fluentform_is_form_renderable', $isAllowed, $this->form);
809 +
810 + $isAllowed = apply_filters('fluentform/is_form_renderable', $isAllowed, $this->form);
438 811
439 812 if (!$isAllowed['status']) {
440 813 wp_send_json([
441 814 'errors' => [
442 815 'restricted' => [
443 - $isAllowed['message']
444 - ]
445 - ]
816 + $isAllowed['message'],
817 + ],
818 + ],
446 819 ], 422);
447 820 }
448 821
449 822 // Since we are here, we should now handle if the form should be allowed to submit empty.
@@ -457,9 +830,9 @@
457 830 *
458 831 * @param array $settings
459 832 * @param $fields
460 833 */
461 - private function handleDenyEmptySubmission($settings = [], &$fields)
834 + private function handleDenyEmptySubmission($settings, &$fields)
462 835 {
463 836 // Determine whether empty form submission is allowed or not.
464 837 if (Arr::get($settings, 'enabled')) {
465 838 // confirm this form has no required fields.
@@ -465,9 +838,9 @@
465 838 // confirm this form has no required fields.
466 839 if (!FormFieldsParser::hasRequiredFields($this->form, $fields)) {
467 840 // Filter out the form data which doesn't have values.
468 841 $filteredFormData = array_filter(
469 - // Filter out the other meta fields that aren't actual inputs.
842 + // Filter out the other meta fields that aren't actual inputs.
470 843 array_intersect_key($this->formData, $fields)
471 844 );
472 845
473 846 // TODO: Extract this function into global functions file...
@@ -481,19 +854,18 @@
481 854 return $array;
482 855 };
483 856
484 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 + }
485 862 wp_send_json([
486 863 'errors' => [
487 864 'restricted' => [
488 - __(
489 - !($m = Arr::get($settings, 'message'))
490 - ? 'Sorry! You can\'t submit an empty form.'
491 - : $m,
492 - 'fluentform'
493 - )
494 - ]
495 - ]
865 + $message,
866 + ],
867 + ],
496 868 ], 422);
497 869 }
498 870 }
499 871 }
@@ -502,13 +874,13 @@
502 874 /**
503 875 * Prepare the data to be inserted to the database.
504 876 *
505 877 * @param boolean $formData
878 + *
506 879 * @return array
507 880 */
508 881 public function prepareInsertData($formData = false)
509 882 {
510 -
511 883 $formId = $this->form->id;
512 884
513 885 if (!$formData) {
514 886 $formData = $this->formData;
@@ -524,34 +896,67 @@
524 896 if ($previousItem) {
525 897 $serialNumber = $previousItem->serial_number + 1;
526 898 }
527 899
528 - $browser = new Browser;
900 + $browser = new Browser();
529 901
530 - $inputConfigs = FormFieldsParser::getEntryInputs($this->form, array('admin_label', 'raw'));
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);
531 916
532 - $this->formData = apply_filters('fluentform_insert_response_data', $formData, $formId, $inputConfigs);
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 + );
533 929
534 - $ipAddress = $this->app->request->getIp();
535 -
536 - if ((defined('FLUENTFROM_DISABLE_IP_LOGGING') && FLUENTFROM_DISABLE_IP_LOGGING) || apply_filters('fluentform_disable_ip_logging', false, $formId)) {
930 + if ((defined('FLUENTFROM_DISABLE_IP_LOGGING') && FLUENTFROM_DISABLE_IP_LOGGING) || apply_filters('fluentform/disable_ip_logging',
931 + $disableIpLogging, $formId)) {
537 932 $ipAddress = false;
538 933 }
539 934
540 935 $response = [
541 - 'form_id' => $formId,
936 + 'form_id' => $formId,
542 937 'serial_number' => $serialNumber,
543 - 'response' => json_encode($this->formData),
544 - 'source_url' => site_url(Arr::get($this->formData, '_wp_http_referer')),
545 - 'user_id' => get_current_user_id(),
546 - 'browser' => $browser->getBrowser(),
547 - 'device' => $browser->getPlatform(),
548 - 'ip' => $ipAddress,
549 - 'created_at' => current_time('mysql'),
550 - 'updated_at' => current_time('mysql')
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'),
551 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 + );
552 957
553 - return apply_filters('fluentform_filter_insert_data', $response);
958 + return apply_filters('fluentform/filter_insert_data', $response);
554 959 }
555 960
556 961 /**
557 962 * Delegate the validation rules & messages to the
@@ -556,10 +961,11 @@
556 961 /**
557 962 * Delegate the validation rules & messages to the
558 963 * ones that the validation library recognizes.
559 964 *
560 - * @param $rules
561 - * @param $messages
965 + * @param $rules
966 + * @param $messages
967 + *
562 968 * @return array
563 969 */
564 970 protected function delegateValidations($rules, $messages, $search = [], $replace = [])
565 971 {
@@ -578,5 +984,41 @@
578 984
579 985 return [$rules, $messages];
580 986 }
581 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 + }
582 1024 }