PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 4.3.21
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v4.3.21
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 3.6.66 All 195 releases
fluentform / app / Modules / Form / Form.php

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

732 lines 23.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\App\Helpers\Helper;
6 use FluentForm\App\Modules\Acl\Acl;
7 use FluentForm\Framework\Foundation\Application;
8 use FluentForm\Framework\Helpers\ArrayHelper;
9
10 class Form
11 {
12 /**
13 * Request object
14 *
15 * @var \FluentForm\Framework\Request\Request $request
16 */
17 protected $request;
18
19 /**
20 * Set this value when we need predefined default settings.
21 *
22 * @var array $defaultSettings
23 */
24 protected $defaultSettings;
25
26 /**
27 * Set this value when we need predefined default notifications.
28 *
29 * @var array $defaultNotifications
30 */
31 protected $defaultNotifications;
32
33 /**
34 * Set this value when we need predefined form fields.
35 *
36 * @var array $formFields
37 */
38 protected $formFields;
39
40 protected $metas = [];
41
42 protected $formType = 'form';
43
44 protected $hasPayment = 0;
45
46 /**
47 * Form constructor.
48 *
49 * @param \FluentForm\Framework\Foundation\Application $application
50 *
51 * @throws \Exception
52 */
53 public function __construct(Application $application)
54 {
55 $this->request = $application->request;
56 $this->model = wpFluent()->table('fluentform_forms');
57 }
58
59 /**
60 * Get all forms from database
61 *
62 * @throws \Exception
63 */
64 public function index()
65 {
66 $forms = fluentFormApi('forms')->forms([
67 'search' => $this->request->get('search'),
68 'status' => $this->request->get('status'),
69 'filter_by' => $this->request->get('filter_by', 'all'),
70 'date_range' => $this->request->get('date_range', []),
71 'sort_column' => $this->request->get('sort_column', 'id'),
72 'sort_by' => $this->request->get('sort_by', 'DESC'),
73 'per_page' => $this->request->get('per_page', 10),
74 'page' => $this->request->get('page', 1),
75 ]);
76
77 wp_send_json($forms, 200);
78 }
79
80 /**
81 * Create a form from backend/editor
82 *
83 * @return void|array
84 */
85 public function store($returnJSON = true)
86 {
87 $type = $this->request->get('type', $this->formType);
88 $title = $this->request->get('title', 'My New Form');
89 $status = $this->request->get('status', 'published');
90 $createdBy = get_current_user_id();
91
92 $now = current_time('mysql');
93
94 $insertData = [
95 'title' => $title,
96 'type' => $type,
97 'status' => $status,
98 'created_by' => $createdBy,
99 'created_at' => $now,
100 'updated_at' => $now,
101 ];
102
103 if ($this->formFields) {
104 $insertData['form_fields'] = $this->formFields;
105 }
106
107 if ($this->hasPayment) {
108 $insertData['has_payment'] = $this->hasPayment;
109 }
110
111 $formId = $this->model->insert($insertData);
112
113 // Rename the form name here
114 wpFluent()->table('fluentform_forms')->where('id', $formId)->update([
115 'title' => $title . ' (#' . $formId . ')',
116 ]);
117
118 if ($this->metas && is_array($this->metas)) {
119 foreach ($this->metas as $meta) {
120 $meta['value'] = trim(preg_replace('/\s+/', ' ', $meta['value']));
121
122 wpFluent()->table('fluentform_form_meta')
123 ->insert([
124 'form_id' => $formId,
125 'meta_key' => $meta['meta_key'],
126 'value' => $meta['value'],
127 ]);
128 }
129 } else {
130 // add default form settings now
131 $defaultSettings = $this->defaultSettings ?: $this->getFormsDefaultSettings($formId);
132
133 $defaultSettings = apply_filters('fluentform_create_default_settings', $defaultSettings);
134
135 wpFluent()->table('fluentform_form_meta')
136 ->insert([
137 'form_id' => $formId,
138 'meta_key' => 'formSettings',
139 'value' => json_encode($defaultSettings),
140 ]);
141
142 if ($this->defaultNotifications) {
143 wpFluent()->table('fluentform_form_meta')
144 ->insert([
145 'form_id' => $formId,
146 'meta_key' => 'notifications',
147 'value' => json_encode($this->defaultNotifications),
148 ]);
149 }
150 }
151
152 do_action('fluentform_inserted_new_form', $formId, $insertData);
153
154 $data = [
155 'formId' => $formId,
156 'redirect_url' => admin_url('admin.php?page=fluent_forms&form_id=' . $formId . '&route=editor'),
157 'message' => __('Successfully created a form.', 'fluentform'),
158 ];
159
160 if ($returnJSON) {
161 wp_send_json_success($data, 200);
162 }
163
164 return $data;
165 }
166
167 public function getFormsDefaultSettings($formId = false)
168 {
169 $defaultSettings = [
170 'confirmation' => [
171 'redirectTo' => 'samePage',
172 'messageToShow' => __('Thank you for your message. We will get in touch with you shortly', 'fluentform'),
173 'customPage' => null,
174 'samePageFormBehavior' => 'hide_form',
175 'customUrl' => null,
176 ],
177 'restrictions' => [
178 'limitNumberOfEntries' => [
179 'enabled' => false,
180 'numberOfEntries' => null,
181 'period' => 'total',
182 'limitReachedMsg' => 'Maximum number of entries exceeded.',
183 ],
184 'scheduleForm' => [
185 'enabled' => false,
186 'start' => null,
187 'end' => null,
188 'selectedDays' => null,
189 'pendingMsg' => __('Form submission is not started yet.', 'fluentform'),
190 'expiredMsg' => __('Form submission is now closed.', 'fluentform'),
191 ],
192 'requireLogin' => [
193 'enabled' => false,
194 'requireLoginMsg' => 'You must be logged in to submit the form.',
195 ],
196 'denyEmptySubmission' => [
197 'enabled' => false,
198 'message' => __('Sorry, you cannot submit an empty form. Let\'s hear what you wanna say.', 'fluentform'),
199 ],
200 ],
201 'layout' => [
202 'labelPlacement' => 'top',
203 'helpMessagePlacement' => 'with_label',
204 'errorMessagePlacement' => 'inline',
205 'cssClassName' => '',
206 'asteriskPlacement' => 'asterisk-right',
207 ],
208 'delete_entry_on_submission' => 'no',
209 ];
210
211 if ($formId) {
212 $value = $this->getMeta($formId, 'formSettings', true);
213 if ($value) {
214 $defaultSettings = wp_parse_args($value, $defaultSettings);
215 }
216 } else {
217 $globalSettings = get_option('_fluentform_global_form_settings');
218 if (isset($globalSettings['layout'])) {
219 $defaultSettings['layout'] = $globalSettings['layout'];
220 }
221 }
222
223 return $defaultSettings;
224 }
225
226 public function getAdvancedValidationSettings($formId)
227 {
228 $settings = [
229 'status' => false,
230 'type' => 'all',
231 'conditions' => [
232 [
233 'field' => '',
234 'operator' => '=',
235 'value' => '',
236 ],
237 ],
238 'error_message' => '',
239 'validation_type' => 'fail_on_condition_met',
240 ];
241
242 $metaSettings = $this->getMeta($formId, 'advancedValidationSettings', true);
243
244 if ($metaSettings && is_array($metaSettings)) {
245 $settings = wp_parse_args($metaSettings, $settings);
246 }
247
248 return $settings;
249 }
250
251 public function getMeta($formId, $metaKey, $isJson = true)
252 {
253 $settingsMeta = wpFluent()->table('fluentform_form_meta')
254 ->where('form_id', $formId)
255 ->where('meta_key', $metaKey)
256 ->first();
257 if ($settingsMeta) {
258 if ($isJson) {
259 return \json_decode($settingsMeta->value, true);
260 } else {
261 return $settingsMeta->value;
262 }
263 }
264 return false;
265 }
266
267 public function updateMeta($formId, $metaKey, $metaValue)
268 {
269 $exist = wpFluent()->table('fluentform_form_meta')
270 ->where('form_id', $formId)
271 ->where('meta_key', $metaKey)
272 ->first();
273
274 if (is_array($metaValue) || is_object($metaValue)) {
275 $metaValue = \json_encode($metaValue);
276 }
277
278 if ($exist) {
279 return wpFluent()->table('fluentform_form_meta')
280 ->where('id', $exist->id)
281 ->update([
282 'value' => $metaValue,
283 ]);
284 }
285
286 return wpFluent()->table('fluentform_form_meta')->insert([
287 'form_id' => $formId,
288 'meta_key' => $metaKey,
289 'value' => $metaValue,
290 ]);
291 }
292
293 public function deleteMeta($formId, $metaKey)
294 {
295 return wpFluent()->table('fluentform_form_meta')
296 ->where('form_id', $formId)
297 ->where('meta_key', $metaKey)
298 ->delete();
299 }
300
301 /**
302 * Find/Read a from from the database
303 */
304 public function find()
305 {
306 $form = $this->fetchForm($this->request->get('formId'));
307 wp_send_json(['form' => $form, 'metas' => []], 200);
308 }
309
310 /**
311 * Fetch a from from the database
312 * Note: required for ninja-tables
313 *
314 * @return mixed
315 */
316 public function fetchForm($formId)
317 {
318 return $this->model->find($formId);
319 }
320
321 /**
322 * Save/update a form from backend/editor
323 *
324 * @throws \WpFluent\Exception
325 */
326 public function update()
327 {
328 $formId = $this->request->get('formId');
329 $title = sanitize_text_field($this->request->get('title'));
330 $status = $this->request->get('status', 'published');
331
332 $this->validate();
333
334 $data = [
335 'title' => $title,
336 'status' => $status,
337 'updated_at' => current_time('mysql'),
338 ];
339
340 if ($formFields = $this->request->get('formFields')) {
341 $formFields = apply_filters('fluentform_form_fields_update', $formFields, $formId);
342 $formFields = $this->sanitizeFields($formFields);
343 $data['form_fields'] = $formFields;
344 }
345
346 $this->model->where('id', $formId)->update($data);
347
348 $form = $this->fetchForm($formId);
349
350 if (FormFieldsParser::hasPaymentFields($form)) {
351 $this->model->where('id', $formId)->update([
352 'has_payment' => 1,
353 ]);
354 } elseif ($form->has_payment) {
355 $this->model->where('id', $formId)->update([
356 'has_payment' => 0,
357 ]);
358 }
359
360 $emailInputs = FormFieldsParser::getElement($form, ['input_email'], ['element', 'attributes']);
361 if ($emailInputs) {
362 $emailInput = array_shift($emailInputs);
363 $emailInputName = ArrayHelper::get($emailInput, 'attributes.name');
364 $this->updateMeta($formId, '_primary_email_field', $emailInputName);
365 } else {
366 $this->updateMeta($formId, '_primary_email_field', '');
367 }
368
369 wp_send_json([
370 'message' => __('The form is successfully updated.', 'fluentform'),
371 ], 200);
372 }
373
374 private function sanitizeFields($formFields)
375 {
376 if (fluentformCanUnfilteredHTML()) {
377 return $formFields;
378 }
379
380 $fieldsArray = json_decode($formFields, true);
381
382 if (isset($fieldsArray['submitButton'])) {
383 $fieldsArray['submitButton']['settings']['button_ui']['text'] = fluentform_sanitize_html($fieldsArray['submitButton']['settings']['button_ui']['text']);
384 if (!empty($fieldsArray['submitButton']['settings']['button_ui']['img_url'])) {
385 $fieldsArray['submitButton']['settings']['button_ui']['img_url'] = sanitize_url($fieldsArray['submitButton']['settings']['button_ui']['img_url']);
386 }
387 }
388
389 $fieldsArray['fields'] = $this->sanitizeFieldMaps($fieldsArray['fields']);
390
391 return json_encode($fieldsArray);
392 }
393
394 private function sanitizeFieldMaps($fields)
395 {
396 if (!is_array($fields)) {
397 return $fields;
398 }
399
400 $attributesMap = [
401 'name' => 'sanitize_key',
402 'value' => 'sanitize_textarea_field',
403 'id' => 'sanitize_key',
404 'class' => 'sanitize_text_field',
405 'placeholder' => 'sanitize_text_field',
406 ];
407 $attributesKeys = array_keys($attributesMap);
408 $settingsMap = [
409 'container_class' => 'sanitize_text_field',
410 'label' => 'wp_kses_post',
411 'label_placement' => 'sanitize_text_field',
412 'help_message' => 'wp_kses_post',
413 'admin_field_label' => 'sanitize_text_field',
414 'prefix_label' => 'sanitize_text_field',
415 'suffix_label' => 'sanitize_text_field',
416 'unique_validation_message' => 'sanitize_text_field',
417 'advanced_options' => 'fluentform_options_sanitize',
418 'html_codes' => 'fluentform_sanitize_html',
419 ];
420 $settingsKeys = array_keys($settingsMap);
421 $stylePrefMap = [
422 'layout' => 'sanitize_key',
423 'media' => 'sanitize_url',
424 'alt_text' => 'sanitize_text_field',
425 ];
426 $stylePrefKeys = array_keys($stylePrefMap);
427
428 foreach ($fields as $fieldIndex => $field) {
429 $element = ArrayHelper::get($field, 'element');
430
431 if ('container' == $element) {
432 $columns = $field['columns'];
433 foreach ($columns as $columnIndex => $column) {
434 $fields[$fieldIndex]['columns'][$columnIndex]['fields'] = $this->sanitizeFieldMaps($column['fields']);
435 }
436 return $fields;
437 }
438
439 /*
440 * Handle Name or address fields
441 */
442 if (!empty($field['fields'])) {
443 $fields[$fieldIndex]['fields'] = $this->sanitizeFieldMaps($field['fields']);
444 return $fields;
445 }
446
447 if (!empty($field['attributes'])) {
448 $attributes = array_filter(ArrayHelper::only($field['attributes'], $attributesKeys));
449 foreach ($attributes as $key => $value) {
450 $fields[$fieldIndex]['attributes'][$key] = call_user_func($attributesMap[$key], $value);
451 }
452 }
453
454 if (!empty($field['settings'])) {
455 $settings = array_filter(ArrayHelper::only($field['settings'], $settingsKeys));
456 foreach ($settings as $key => $value) {
457 $fields[$fieldIndex]['settings'][$key] = call_user_func($settingsMap[$key], $value);
458 }
459 }
460
461 if (!empty($field['style_pref'])) {
462 $settings = array_filter(ArrayHelper::only($field['style_pref'], $stylePrefKeys));
463 foreach ($settings as $key => $value) {
464 $fields[$fieldIndex]['style_pref'][$key] = call_user_func($stylePrefMap[$key], $value);
465 }
466 }
467 }
468
469 return $fields;
470 }
471
472 /**
473 * Delete a from from database
474 *
475 * @throws \WpFluent\Exception
476 */
477 public function delete()
478 {
479 $formId = $this->request->get('formId');
480
481 $this->model->where('id', $formId)->delete();
482
483 $maybeErrors = $this->deleteFormAssests($formId);
484
485 wp_send_json([
486 'message' => __('Successfully deleted the form.', 'fluentform'),
487 'errors' => $maybeErrors,
488 ], 200);
489 }
490
491 protected function deleteFormAssests($formId)
492 {
493 // Now Let's delete associate items
494 wpFluent()->table('fluentform_submissions')
495 ->where('form_id', $formId)
496 ->delete();
497
498 wpFluent()->table('fluentform_submission_meta')
499 ->where('form_id', $formId)
500 ->delete();
501
502 wpFluent()->table('fluentform_entry_details')
503 ->where('form_id', $formId)
504 ->delete();
505
506 wpFluent()->table('fluentform_form_meta')
507 ->where('form_id', $formId)
508 ->delete();
509
510 wpFluent()->table('fluentform_form_analytics')
511 ->where('form_id', $formId)
512 ->delete();
513
514 wpFluent()->table('fluentform_logs')
515 ->where('parent_source_id', $formId)
516 ->whereIn('source_type', ['submission_item', 'form_item', 'draft_submission_meta'])
517 ->delete();
518
519 ob_start();
520 if (defined('FLUENTFORMPRO')) {
521 try {
522 wpFluent()->table('fluentform_order_items')
523 ->where('form_id', $formId)
524 ->delete();
525
526 wpFluent()->table('fluentform_transactions')
527 ->where('form_id', $formId)
528 ->delete();
529 } catch (\Exception $exception) {
530 }
531 }
532 $errors = ob_get_clean();
533 return $errors;
534 }
535
536 /**
537 * Duplicate a from
538 *
539 * @throws \WpFluent\Exception
540 */
541 public function duplicate()
542 {
543 $formId = absint($this->request->get('formId'));
544 $form = $this->model->where('id', $formId)->first();
545
546 $data = [
547 'title' => $form->title,
548 'status' => $form->status,
549 'appearance_settings' => $form->appearance_settings,
550 'form_fields' => $form->form_fields,
551 'type' => $form->type,
552 'has_payment' => $form->has_payment,
553 'conditions' => $form->conditions,
554 'created_by' => get_current_user_id(),
555 'created_at' => current_time('mysql'),
556 'updated_at' => current_time('mysql'),
557 ];
558
559 $newFormId = $this->model->insert($data);
560
561 // Rename the form name here
562 wpFluent()->table('fluentform_forms')
563 ->where('id', $newFormId)
564 ->update([
565 'title' => $form->title . ' (#' . $newFormId . ')',
566 ]);
567
568 $formMetas = wpFluent()->table('fluentform_form_meta')
569 ->where('form_id', $formId)
570 ->whereNot('meta_key', ['_total_views'])
571 ->get();
572
573 // Required for duplicating PDF feeds
574 $extras = [];
575
576 foreach ($formMetas as $meta) {
577 if ('notifications' == $meta->meta_key || '_pdf_feeds' == $meta->meta_key) {
578 $extras[$meta->meta_key][] = $meta;
579 continue;
580 }
581 $metaData = [
582 'meta_key' => $meta->meta_key,
583 'value' => $meta->value,
584 'form_id' => $newFormId,
585 ];
586
587 wpFluent()->table('fluentform_form_meta')->insert($metaData);
588 }
589
590 $pdfFeedMap = $this->getPdfFeedMap($extras, $newFormId);
591 if (array_key_exists('notifications', $extras)) {
592 $extras = $this->notificationWithPdfMap($extras, $pdfFeedMap);
593 foreach ($extras['notifications'] as $notify) {
594 $notifyData = [
595 'meta_key' => $notify->meta_key,
596 'value' => $notify->value,
597 'form_id' => $newFormId,
598 ];
599 wpFluent()->table('fluentform_form_meta')->insert($notifyData);
600 }
601 }
602
603 do_action('flentform_form_duplicated', $newFormId);
604
605 wp_send_json([
606 'message' => __('Form has been successfully duplicated.', 'fluentform'),
607 'form_id' => $newFormId,
608 'redirect' => admin_url('admin.php?page=fluent_forms&route=editor&form_id=' . $newFormId),
609 ], 200);
610 }
611
612 /**
613 * Validate a form by form title & for duplicate name attributes
614 */
615 private function validate()
616 {
617 $fields = $this->request->get('formFields');
618 if ($fields) {
619 $duplicates = Helper::getDuplicateFieldNames($fields);
620 if ($duplicates) {
621 $duplicateString = implode(', ', $duplicates);
622 wp_send_json([
623 'title' => sprintf('Name attribute %s has duplicate value.', $duplicateString),
624 ], 422);
625 }
626 }
627
628 if (!sanitize_text_field($this->request->get('title'))) {
629 wp_send_json([
630 'title' => 'The title field is required.',
631 ], 422);
632 }
633 }
634
635 public function convertToConversational()
636 {
637 $formId = $this->request->get('form_id');
638 $form = $this->fetchForm($formId);
639
640 if (!$form) {
641 wp_send_json([
642 'message' => __('Form Not Found! Try Again.', 'fluentform'),
643 ], 422);
644 }
645 $formConverted['form_fields'] = \FluentForm\App\Services\FluentConversational\Classes\Converter\Converter::convertExistingForm($form);
646
647 $this->model->where('id', $formId)->update($formConverted);
648
649 $this->updateMeta($formId, 'is_conversion_form', 'yes');
650 wp_send_json_success([
651 'message' => __('Form has been successfully converted to conversational form.', 'fluentform'),
652 ], 200);
653 }
654
655 private function getAdminPermalink($route, $form)
656 {
657 $baseUrl = admin_url('admin.php?page=fluent_forms');
658 return $baseUrl . '&route=' . $route . '&form_id=' . $form->id;
659 }
660
661 private function getSettingsUrl($form)
662 {
663 $baseUrl = admin_url('admin.php?page=fluent_forms');
664 return $baseUrl . '&form_id=' . $form->id . '&route=settings&sub_route=form_settings#basic_settings';
665 }
666
667 public function getAllForms()
668 {
669 $fields = $this->request->get('fields');
670
671 if ($fields) {
672 $forms = $this->model
673 ->select($fields)
674 ->orderBy('created_at', 'DESC')->get();
675 } else {
676 $forms = $this->model->orderBy('created_at', 'DESC')->get();
677 }
678
679 wp_send_json($forms, 200);
680 }
681
682 /**
683 * Map pdf feed ID to replace with duplicated PDF feed ID when duplicating form
684 *
685 * @param array $extras
686 * @param array $newFormId
687 *
688 * @return array
689 */
690 private function getPdfFeedMap($extras, $newFormId)
691 {
692 $pdfFeedMap = [];
693 if (array_key_exists('_pdf_feeds', $extras)) {
694 foreach ($extras['_pdf_feeds'] as $pdf_feed) {
695 $pdfData = [
696 'meta_key' => $pdf_feed->meta_key,
697 'value' => $pdf_feed->value,
698 'form_id' => $newFormId,
699 ];
700 $pdfFeedMap[$pdf_feed->id] = wpFluent()->table('fluentform_form_meta')->insert($pdfData);
701 }
702 }
703 return $pdfFeedMap;
704 }
705
706 /**
707 * Map notification data with PDF feed map
708 *
709 * @param array $extras
710 * @param array $pdfFeedMap
711 *
712 * @return array
713 */
714 private function notificationWithPdfMap($extras, $pdfFeedMap)
715 {
716 foreach ($extras['notifications'] as $key => $notification) {
717 $notificationValue = json_decode($notification->value);
718 $pdf_attachments = [];
719 if (isset($notificationValue->pdf_attachments) && count($notificationValue->pdf_attachments)) {
720 foreach ($notificationValue->pdf_attachments as $attachment) {
721 $pdf_attachments[] = json_encode($pdfFeedMap[$attachment]);
722 }
723 }
724 $notificationValue->pdf_attachments = $pdf_attachments;
725 $notification->value = json_encode($notificationValue);
726
727 $extras['notifications'][$key] = $notification;
728 }
729 return $extras;
730 }
731 }
732