PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 4.3.19
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v4.3.19
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 / Component / Component.php

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

1,190 lines 45.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\Component;
4
5 use FluentForm\App\Helpers\Helper;
6 use FluentForm\App\Modules\Acl\Acl;
7 use FluentForm\App\Services\FormBuilder\EditorShortcodeParser;
8 use FluentForm\App\Services\FormBuilder\Notifications\EmailNotificationActions;
9 use FluentForm\Framework\Foundation\Application;
10 use FluentForm\Framework\Helpers\ArrayHelper;
11
12 class Component
13 {
14 /**
15 * FluentForm\Framework\Foundation\Application
16 *
17 * @var $app
18 */
19 protected $app = null;
20
21 /**
22 * Determine whether to register Elementor PopUp handler.
23 *
24 * @var bool
25 */
26 protected $elementorPopUpHandler = false;
27
28 /**
29 * Biuld the instance of this class
30 *
31 * @param \FluentForm\Framework\Foundation\Application $app
32 */
33 public function __construct(Application $app)
34 {
35 $this->app = $app;
36 }
37
38 public function registerScripts()
39 {
40 $app = $this->app;
41
42 // We will just register the scripts here. We will not load any scripts from here
43
44 $fluentFormPublicCss = $app->publicUrl('css/fluent-forms-public.css');
45 $fluentFormPublicDefaultCss = $app->publicUrl('css/fluentform-public-default.css');
46
47 if (is_rtl()) {
48 $fluentFormPublicCss = $app->publicUrl('css/fluent-forms-public-rtl.css');
49 $fluentFormPublicDefaultCss = $app->publicUrl('css/fluentform-public-default-rtl.css');
50 }
51
52 wp_register_style(
53 'fluent-form-styles',
54 $fluentFormPublicCss,
55 [],
56 FLUENTFORM_VERSION
57 );
58
59 wp_register_style(
60 'fluentform-public-default',
61 $fluentFormPublicDefaultCss,
62 [],
63 FLUENTFORM_VERSION
64 );
65
66 wp_register_script(
67 'fluent-form-submission',
68 $app->publicUrl('js/form-submission.js'),
69 ['jquery'],
70 FLUENTFORM_VERSION,
71 true
72 );
73
74 wp_register_script(
75 'fluentform-advanced',
76 $app->publicUrl('js/fluentform-advanced.js'),
77 ['jquery'],
78 FLUENTFORM_VERSION,
79 true
80 );
81
82 // Date Pickckr Style
83 //fix for essential addon event picker conflict
84 if (!wp_script_is('flatpickr', 'registered')) {
85 wp_register_style(
86 'flatpickr',
87 $app->publicUrl('libs/flatpickr/flatpickr.min.css')
88 );
89 }
90 // Date Pickckr Script
91 wp_register_script(
92 'flatpickr',
93 $app->publicUrl('libs/flatpickr/flatpickr.js'),
94 ['jquery'],
95 false,
96 true
97 );
98
99 wp_register_script(
100 'choices',
101 $app->publicUrl('libs/choices/choices.min.js'),
102 [],
103 '9.0.1',
104 true
105 );
106
107 wp_register_style(
108 'ff_choices',
109 $app->publicUrl('css/choices.css'),
110 [],
111 FLUENTFORM_VERSION
112 );
113
114 wp_register_script(
115 'form-save-progress',
116 $app->publicUrl('js/form-save-progress.js'),
117 ['jquery'],
118 FLUENTFORM_VERSION,
119 true
120 );
121
122 do_action('fluentform_scripts_registered');
123
124 $this->maybeLoadFluentFormStyles();
125 }
126
127 protected function maybeLoadFluentFormStyles()
128 {
129 global $post;
130
131 $postId = isset($post->ID) ? $post->ID : false;
132 if (!$postId) {
133 return;
134 }
135 $fluentFormIds = get_post_meta($postId, '_has_fluentform', true);
136 $hasFluentformMeta = is_a($post, 'WP_Post') && $fluentFormIds;
137
138 if ($hasFluentformMeta || apply_filters('fluentform_load_styles', false, $post)) {
139 wp_enqueue_style('fluent-form-styles');
140 wp_enqueue_style('fluentform-public-default');
141 do_action('fluentform_pre_load_scripts', $post);
142 wp_enqueue_script('fluent-form-submission');
143 }
144 }
145
146 /**
147 * Get all the available components
148 *
149 * @return void
150 *
151 * @throws \Exception
152 * @throws \FluentForm\Framework\Exception\UnResolveableEntityException
153 */
154 public function index()
155 {
156 $formId = intval($this->app->request->get('formId'));
157
158 $components = $this->app->make('components');
159
160 $this->app->doAction('fluent_editor_init', $components);
161
162 $editorComponents = $components->sort()->toArray();
163 $editorComponents = apply_filters('fluent_editor_components', $editorComponents, $formId);
164
165 $countries = $this->app->load($this->app->appPath('Services/FormBuilder/CountryNames.php'));
166
167 wp_send_json_success([
168 'countries' => $countries,
169 'components' => $editorComponents,
170 'disabled_components' => $this->getDisabledComponents(),
171 ]);
172 }
173
174 /**
175 * Get disabled components
176 *
177 * @return array
178 */
179 private function getDisabledComponents()
180 {
181 $isReCaptchaDisabled = !get_option('_fluentform_reCaptcha_keys_status', false);
182 $isHCaptchaDisabled = !get_option('_fluentform_hCaptcha_keys_status', false);
183 $isTurnstileDisabled = !get_option('_fluentform_turnstile_keys_status', false);
184
185 $disabled = [
186 'recaptcha' => [
187 'disabled' => $isReCaptchaDisabled,
188 'title' => __('reCaptcha', 'fluentform'),
189 'description' => __('Please enter a valid API key on FluentForms->Settings->reCaptcha', 'fluentform'),
190 'hidePro' => true,
191 ],
192 'hcaptcha' => [
193 'disabled' => $isHCaptchaDisabled,
194 'title' => __('hCaptcha', 'fluentform'),
195 'description' => __('Please enter a valid API key on FluentForms->Settings->hCaptcha', 'fluentform'),
196 'hidePro' => true,
197 ],
198 'turnstile' => [
199 'disabled' => $isTurnstileDisabled,
200 'title' => __('Turnstile', 'fluentform'),
201 'description' => __('Please enter a valid API key on FluentForms->Settings->Turnstile', 'fluentform'),
202 'hidePro' => true,
203 ],
204 'input_image' => [
205 'disabled' => true,
206 'title' => __('Image Upload', 'fluentform'),
207 'description' => __('Image Upload is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
208 'image' => '',
209 'video' => 'https://www.youtube.com/embed/Yb3FSoZl9Zg',
210 ],
211 'input_file' => [
212 'disabled' => true,
213 'title' => __('File Upload', 'fluentform'),
214 'description' => __('File Upload is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
215 'image' => '',
216 'video' => 'https://www.youtube.com/embed/bXbTbNPM_4k',
217 ],
218 'shortcode' => [
219 'disabled' => true,
220 'title' => __('Shortcode', 'fluentform'),
221 'description' => __('Shortcode is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
222 'image' => '',
223 'video' => 'https://www.youtube.com/embed/op3mEQxX1MM',
224 ],
225 'action_hook' => [
226 'disabled' => true,
227 'title' => __('Action Hook', 'fluentform'),
228 'description' => __('Action Hook is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
229 'image' => $this->app->publicUrl('img/pro-fields/Action Hook.png'),
230 'video' => '',
231 ],
232 'form_step' => [
233 'disabled' => true,
234 'title' => __('Form Step', 'fluentform'),
235 'description' => __('Form Step is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
236 'image' => '',
237 'video' => 'https://www.youtube.com/embed/VQTWnM6BbRU',
238 ],
239 ];
240
241 if (!defined('FLUENTFORMPRO')) {
242 $disabled['ratings'] = [
243 'disabled' => true,
244 'title' => __('Ratings', 'fluentform'),
245 'description' => __('Ratings is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
246 'image' => '',
247 'video' => 'https://www.youtube.com/embed/YGdkNspMaEs',
248 ];
249 $disabled['tabular_grid'] = [
250 'disabled' => true,
251 'title' => __('Checkable Grid', 'fluentform'),
252 'description' => __('Checkable Grid is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
253 'image' => '',
254 'video' => 'https://www.youtube.com/embed/ayI3TzXXANA',
255 ];
256 $disabled['chained_select'] = [
257 'disabled' => true,
258 'title' => __('Chained Select Field', 'fluentform'),
259 'description' => __('Chained Select Field is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
260 'image' => $this->app->publicUrl('img/pro-fields/Chained Select Field.png'),
261 'video' => '',
262 ];
263 $disabled['phone'] = [
264 'disabled' => true,
265 'title' => 'Phone Field',
266 'description' => __('Phone Field is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
267 'image' => $this->app->publicUrl('img/pro-fields/Phone Field.png'),
268 'video' => '',
269 ];
270 $disabled['rich_text_input'] = [
271 'disabled' => true,
272 'title' => __('Rich Text Input', 'fluentform'),
273 'description' => __('Rich Text Input is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
274 'image' => $this->app->publicUrl('img/pro-fields/Rich Text Input.png'),
275 'video' => '',
276 ];
277 $disabled['save_progress_button'] = [
278 'disabled' => true,
279 'title' => __('Save & Resume', 'fluentform'),
280 'description' => __('Save & Resume is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
281 'image' => $this->app->publicUrl('img/pro-fields/Save Progress Button.png'),
282 'video' => '',
283 ];
284 $disabled['cpt_selection'] = [
285 'disabled' => true,
286 'title' => __('Post/CPT Selection', 'fluentform'),
287 'description' => __('Post/CPT Selection is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
288 'image' => $this->app->publicUrl('img/pro-fields/Post_CPT Selection.png'),
289 'video' => '',
290 ];
291 $disabled['quiz_score'] = [
292 'disabled' => true,
293 'title' => __('Quiz Score', 'fluentform'),
294 'description' => __('Quiz Score is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
295 'image' => '',
296 'video' => 'https://www.youtube.com/embed/bPjDXR0y_Oo',
297 ];
298 $disabled['net_promoter_score'] = [
299 'disabled' => true,
300 'title' => __('Net Promoter Score', 'fluentform'),
301 'description' => __('Net Promoter Score is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
302 'image' => $this->app->publicUrl('img/pro-fields/Net Promoter Score.png'),
303 'video' => '',
304 ];
305 $disabled['repeater_field'] = [
306 'disabled' => true,
307 'title' => __('Repeat Field', 'fluentform'),
308 'description' => __('Repeat Field is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
309 'image' => '',
310 'video' => 'https://www.youtube.com/embed/BXo9Sk-OLnQ',
311 ];
312 $disabled['rangeslider'] = [
313 'disabled' => true,
314 'title' => __('Range Slider', 'fluentform'),
315 'description' => __('Range Slider is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
316 'image' => '',
317 'video' => 'https://www.youtube.com/embed/RaY2VcPWk6I',
318 ];
319 $disabled['color-picker'] = [
320 'disabled' => true,
321 'title' => __('Color Picker', 'fluentform'),
322 'description' => __('Color Picker is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
323 'image' => $this->app->publicUrl('img/pro-fields/Color Picker.png'),
324 'video' => '',
325 ];
326 $disabled['multi_payment_component'] = [
327 'disabled' => true,
328 'is_payment' => true,
329 'title' => __('Payment Field', 'fluentform'),
330 'description' => __('Payment Field is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
331 'image' => $this->app->publicUrl('img/pro-fields/Payment Field.png'),
332 'video' => '',
333 ];
334 $disabled['custom_payment_component'] = [
335 'disabled' => true,
336 'is_payment' => true,
337 'title' => 'Custom Payment Amount',
338 'description' => __('Custom Payment Amount is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
339 'image' => $this->app->publicUrl('img/pro-fields/Custom Payment Amount.png'),
340 'video' => '',
341 ];
342 $disabled['subscription_payment_component'] = [
343 'disabled' => true,
344 'is_payment' => true,
345 'title' => __('Subscription Field', 'fluentform'),
346 'description' => __('Subscription Field is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
347 'image' => $this->app->publicUrl('img/pro-fields/Subscription Field.png'),
348 'video' => '',
349 ];
350 $disabled['item_quantity_component'] = [
351 'disabled' => true,
352 'is_payment' => true,
353 'title' => __('Item Quantity', 'fluentform'),
354 'description' => __('Item Quantity is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
355 'image' => $this->app->publicUrl('img/pro-fields/Item Quantity.png'),
356 'video' => '',
357 ];
358 $disabled['payment_method'] = [
359 'disabled' => true,
360 'is_payment' => true,
361 'title' => __('Payment Method', 'fluentform'),
362 'description' => __('Payment Method is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
363 'image' => $this->app->publicUrl('img/pro-fields/Payment Method.png'),
364 'video' => '',
365 ];
366 $disabled['payment_summary_component'] = [
367 'disabled' => true,
368 'is_payment' => true,
369 'title' => __('Payment Summary', 'fluentform'),
370 'description' => __('Payment Summary is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
371 'image' => $this->app->publicUrl('img/pro-fields/Payment Summary.png'),
372 'video' => '',
373 ];
374 $disabled['payment_coupon'] = [
375 'disabled' => true,
376 'title' => __('Coupon', 'fluentform'),
377 'description' => __('Coupon is not available with the free version. Please upgrade to pro to get all the advanced features.', 'fluentform'),
378 'image' => $this->app->publicUrl('img/pro-fields/Coupon.png'),
379 'video' => '',
380 ];
381 }
382 return $this->app->applyFilters('fluentform_disabled_components', $disabled);
383 }
384
385 /**
386 * Get available shortcodes for editor
387 *
388 * @return void
389 *
390 * @throws \Exception
391 */
392 public function getEditorShortcodes()
393 {
394 $editor_shortcodes = fluentFormEditorShortCodes();
395 wp_send_json_success(['shortcodes' => $editor_shortcodes], 200);
396 }
397
398 /**
399 * Get all available shortcodes for editor
400 *
401 * @return void
402 *
403 * @throws \Exception
404 */
405 public function getAllEditorShortcodes()
406 {
407 wp_send_json(fluentFormGetAllEditorShortCodes(
408 $this->app->request->get('formId')
409 ), 200);
410 }
411
412 /**
413 * Register the form renderer shortcode
414 *
415 * @return void
416 */
417 public function addFluentFormShortCode()
418 {
419 add_action('wp_enqueue_scripts', [$this, 'registerScripts'], 9);
420
421 $this->app->addShortCode('fluentform', function ($atts, $content) {
422 $shortcodeDefaults = apply_filters('fluentform_shortcode_defaults', [
423 'id' => null,
424 'title' => null,
425 'css_classes' => '',
426 'permission' => '',
427 'type' => 'classic',
428 'permission_message' => __('Sorry, You do not have permission to view this form', 'fluentform'),
429 ], $atts);
430
431 $atts = shortcode_atts($shortcodeDefaults, $atts);
432
433 return $this->renderForm($atts);
434 });
435
436 $this->app->addShortCode('fluentform_info', function ($atts) {
437 $shortcodeDefaults = apply_filters('fluentform_info_shortcode_defaults', [
438 'id' => null, // This is the form id
439 'info' => 'submission_count', // submission_count | created_at | updated_at | payment_total
440 'status' => 'all', // get submission cound of a particular entry status favourites | unread | read
441 'with_trashed' => 'no', // yes | no
442 'substract_from' => 0, // [fluentform_info id="2" info="submission_count" substract_from="20"]
443 'hide_on_zero' => 'no',
444 'payment_status' => 'all', // it can be all / specific payment status
445 'currency_formatted' => 'yes',
446 'date_format' => '',
447 ], $atts);
448
449 $atts = shortcode_atts($shortcodeDefaults, $atts);
450 $formId = $atts['id'];
451 $form = wpFluent()->table('fluentform_forms')->find($formId);
452
453 if (!$form) {
454 return '';
455 }
456
457 if ('submission_count' == $atts['info']) {
458 $countQuery = wpFluent()->table('fluentform_submissions')
459 ->where('form_id', $formId);
460
461 if ('trashed' != $atts['status'] && 'no' == $atts['with_trashed']) {
462 $countQuery = $countQuery->where('status', '!=', 'trashed');
463 }
464
465 if ('all' == $atts['status']) {
466 // ...
467 } elseif ('favourites' == $atts['status']) {
468 $countQuery = $countQuery->where('is_favourite', '=', 1);
469 } else {
470 $countQuery = $countQuery->where('status', '=', sanitize_key($atts['status']));
471 }
472 if ($atts['payment_status'] && defined('FLUENTFORMPRO') && 'all' != $atts['payment_status']) {
473 $countQuery = $countQuery->where('payment_status', '=', sanitize_key($atts['payment_status']));
474 }
475
476 $total = $countQuery->count();
477
478 if ($atts['substract_from']) {
479 $total = intval($atts['substract_from']) - $total;
480 }
481
482 if ('yes' == $atts['hide_on_zero'] && !$total || $total < 0) {
483 return '';
484 }
485
486 return $total;
487 } elseif ('created_at' == $atts['info']) {
488 if ($atts['date_format']) {
489 $dateFormat = $atts['date_format'];
490 } else {
491 $dateFormat = get_option('date_format') . ' ' . get_option('time_format');
492 }
493 return date($dateFormat, strtotime($form->created_at));
494 } elseif ('updated_at' == $atts['info']) {
495 if ($atts['date_format']) {
496 $dateFormat = $atts['date_format'];
497 } else {
498 $dateFormat = get_option('date_format') . ' ' . get_option('time_format');
499 }
500 return date($dateFormat, strtotime($form->updated_at));
501 } elseif ('payment_total' == $atts['info']) {
502 if (!defined('FLUENTFORMPRO')) {
503 return '';
504 }
505
506 global $wpdb;
507 $countQuery = wpFluent()
508 ->table('fluentform_submissions')
509 ->select(wpFluent()->raw('SUM(payment_total) as payment_total'))
510 ->where('form_id', $formId);
511
512 if ('trashed' != $atts['status'] && 'no' == $atts['with_trashed']) {
513 $countQuery = $countQuery->where('status', '!=', 'trashed');
514 }
515
516 if ('all' == $atts['status']) {
517 // ...
518 } elseif ('favourites' == $atts['status']) {
519 $countQuery = $countQuery->where('is_favourite', '=', 1);
520 } else {
521 $countQuery = $countQuery->where('status', '=', sanitize_key($atts['status']));
522 }
523
524 if ('all' == $atts['payment_status']) {
525 // ...
526 } elseif ($atts['payment_status']) {
527 $countQuery = $countQuery->where('payment_status', '=', sanitize_key($atts['payment_status']));
528 }
529
530 $row = $countQuery->first();
531
532 $total = 0;
533 if ($row) {
534 $total = $row->payment_total;
535 }
536
537 if ($atts['substract_from']) {
538 $total = intval($atts['substract_from'] * 100) - $total;
539 }
540
541 if ('yes' == $atts['hide_on_zero'] && !$total) {
542 return '';
543 }
544
545 if ('yes' == $atts['currency_formatted']) {
546 $currency = \FluentFormPro\Payments\PaymentHelper::getFormCurrency($formId);
547 return \FluentFormPro\Payments\PaymentHelper::formatMoney($total, $currency);
548 }
549
550 if (!$total) {
551 return 0;
552 }
553
554 return $total / 100;
555 }
556
557 return '';
558 });
559
560 $this->app->addShortCode('ff_get', function ($atts) {
561 $atts = shortcode_atts([
562 'param' => '',
563 ], $atts);
564
565 $value = $this->app->request->get($atts['param']);
566
567 if ($atts['param'] && $value) {
568 if (is_array($value)) {
569 return implode(', ', $value);
570 }
571 return esc_html($value);
572 }
573 return '';
574 });
575 }
576
577 public function renderForm($atts)
578 {
579 $form_id = $atts['id'];
580 $query = wpFluent()->table('fluentform_forms');
581
582 if (! $this->app->request->get('preview_id')) {
583 $query->where('status', 'published');
584 }
585
586 if ($form_id) {
587 $form = $query->find($form_id);
588 } elseif ($formTitle = $atts['title']) {
589 $form = $query->where('title', $formTitle)->first();
590 } else {
591 return '';
592 }
593
594 if (!$form) {
595 return '';
596 }
597
598 if (!empty($atts['permission'])) {
599 if (!current_user_can($atts['permission'])) {
600 return "<div id='ff_form_{$form->id}' class='ff_form_not_render'>{$atts['permission_message']}</div>";
601 }
602 }
603
604 if (is_feed()) {
605 global $post;
606 $feedText = sprintf(__('The form can be filled in the actual <a href="%s">website url</a>.', 'fluentform'), get_permalink($post));
607 $feedText = apply_filters('fluentform_shortcode_feed_text', $feedText, $form);
608 return $feedText;
609 }
610
611 $formSettings = wpFluent()
612 ->table('fluentform_form_meta')
613 ->where('form_id', $form_id)
614 ->where('meta_key', 'formSettings')
615 ->first();
616
617 if (!$formSettings) {
618 return '';
619 }
620
621 $form->fields = json_decode($form->form_fields, true);
622
623 if (!$form->fields['fields']) {
624 return '';
625 }
626
627 $form->settings = json_decode($formSettings->value, true);
628 $form = $this->app->applyFilters('fluentform_rendering_form', $form);
629
630 $isRenderable = [
631 'status' => true,
632 'message' => '',
633 ];
634
635 $isRenderable = $this->app->applyFilters('fluentform_is_form_renderable', $isRenderable, $form);
636
637 if (is_array($isRenderable) && !$isRenderable['status']) {
638 return "<div id='ff_form_{$form->id}' class='ff_form_not_render'>{$isRenderable['message']}</div>";
639 }
640
641 $instanceCssClass = Helper::getFormInstaceClass($form->id);
642
643 $form->instance_css_class = $instanceCssClass;
644 $form->instance_index = Helper::$formInstance;
645
646 if ('conversational' == $atts['type']) {
647 $this->addInlineVars();
648 return (new \FluentForm\App\Services\FluentConversational\Classes\Form())->renderShortcode($form);
649 }
650
651 $formBuilder = $this->app->make('formBuilder');
652
653 $cssClasses = trim($instanceCssClass . ' ff-form-loading');
654
655 $output = $formBuilder->build($form, $cssClasses, $instanceCssClass, $atts);
656
657 $output = $this->replaceEditorSmartCodes($output, $form);
658
659 if (!wp_script_is('fluent-form-submission', 'registered')) {
660 $this->registerScripts();
661 }
662
663 wp_enqueue_style('fluent-form-styles');
664 if (apply_filters('fluentform_load_default_public', true, $form)) {
665 wp_enqueue_style('fluentform-public-default');
666 }
667 /*
668 * We will load fluentform-advanced if the form has certain fields or feature
669 */
670 $this->maybeHasAdvandedFields($form, $formBuilder);
671 wp_enqueue_script('fluent-form-submission');
672
673 $stepText = __('Step %activeStep% of %totalStep% - %stepTitle%', 'fluentform');
674 $stepText = apply_filters('fluentform_step_string', $stepText);
675 $vars = apply_filters('fluentform_global_form_vars', [
676 'ajaxUrl' => admin_url('admin-ajax.php'),
677 'forms' => [],
678 'step_text' => $stepText,
679 'is_rtl' => is_rtl(),
680 'date_i18n' => $this->getDatei18n(),
681 'pro_version' => (defined('FLUENTFORMPRO_VERSION')) ? FLUENTFORMPRO_VERSION : false,
682 'fluentform_version' => FLUENTFORM_VERSION,
683 'force_init' => false,
684 'stepAnimationDuration' => 350,
685 'upload_completed_txt' => __('100% Completed', 'fluentform'),
686 'upload_start_txt' => __('0% Completed', 'fluentform'),
687 'uploading_txt' => __('Uploading', 'fluentform'),
688 'choice_js_vars' => [
689 'noResultsText' => __('No results found', 'fluentform'),
690 'loadingText' => __('Loading...', 'fluentform'),
691 'noChoicesText' => __('No choices to choose from', 'fluentform'),
692 'itemSelectText' => __('Press to select', 'fluentform'),
693 'maxItemText' => __('Only %%maxItemCount%% options can be added', 'fluentform'),
694 ],
695 'input_mask_vars' => [
696 'clearIfNotMatch' => false,
697 ],
698 ]);
699
700 wp_localize_script('fluent-form-submission', 'fluentFormVars', $vars);
701
702 $formSettings = $form->settings;
703
704 $formSettings = ArrayHelper::only($formSettings, ['layout', 'id']);
705
706 $formSettings['restrictions']['denyEmptySubmission'] = [
707 'enabled' => false,
708 ];
709
710 $form_vars = [
711 'id' => $form->id,
712 'settings' => $formSettings,
713 'form_instance' => $instanceCssClass,
714 'form_id_selector' => 'fluentform_' . $form->id,
715 'rules' => $formBuilder->validationRules,
716 ];
717
718 if ($conditionals = $formBuilder->conditions) {
719 $form_vars['conditionals'] = $conditionals;
720 }
721
722 $form_vars = apply_filters('fluentform/form_vars_for_JS', $form_vars, $form);
723
724 if ($form->has_payment) {
725 do_action('fluentform_rendering_payment_form', $form);
726 }
727
728 $otherScripts = '';
729 ob_start();
730 ?>
731 <script type="text/javascript">
732 window.fluent_form_<?php echo esc_attr($instanceCssClass); ?> = <?php echo wp_json_encode($form_vars); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $form_vars is escaped before being passed in.?>;
733 <?php if (wp_doing_ajax()): ?>
734 function initFFInstance_<?php echo esc_attr($form_vars['id']); ?>() {
735 if (!window.fluentFormApp) {
736 console.log('No fluentFormApp found');
737 return;
738 }
739 var ajax_formInstance = window.fluentFormApp(jQuery('form.<?php echo esc_attr($form_vars['form_instance']); ?>'));
740 if (ajax_formInstance) {
741 ajax_formInstance.initFormHandlers();
742 }
743 }
744
745 initFFInstance_<?php echo esc_attr($form_vars['id']); ?>();
746 <?php endif; ?>
747 </script>
748 <?php
749 $this->addInlineVars();
750 $otherScripts .= ob_get_clean();
751
752 if (!apply_filters('fluentform-disabled_analytics', false)) {
753 if (!Acl::hasAnyFormPermission($form->id)) {
754 (new \FluentForm\App\Modules\Form\Analytics($this->app))->record($form->id);
755 }
756 }
757
758 return $output . $otherScripts;
759 }
760
761 /**
762 * Process the output HTML to generate the default values.
763 *
764 * @param string $output
765 * @param \stdClass $form
766 *
767 * @return string
768 */
769 public function replaceEditorSmartCodes($output, $form)
770 {
771 // Get the patterns for default values from the output HTML string.
772 // The example of a pattern would be for user ID: {user.ID}
773 preg_match_all('/{(.*?)}/', $output, $matches);
774 $patterns = array_unique($matches[0]);
775
776 $attrDefaultValues = [];
777
778 foreach ($patterns as $pattern) {
779 // The default value for each pattern will be resolved here.
780 $attrDefaultValues[$pattern] = apply_filters('fluentform_parse_default_value', $pattern, $form);
781 }
782
783 // Raising an event so that others can hook into it and modify the default values later.
784 $attrDefaultValues = (array) apply_filters('fluentform_parse_default_values', $attrDefaultValues);
785
786 if (isset($attrDefaultValues['{payment_total}'])) {
787 $attrDefaultValues['{payment_total}'] = '<span class="ff_order_total"></span>';
788 }
789
790 // Finally, replace the patterns with the replacements and return the output HTML.
791 return str_replace(array_keys($attrDefaultValues), array_values($attrDefaultValues), $output);
792 }
793
794 /**
795 * Register renderer actions for compiling each element
796 *
797 * @return void
798 */
799 public function addRendererActions()
800 {
801 $actionMappings = [
802 'Select@compile' => ['fluentform_render_item_select'],
803 'Rating@compile' => ['fluentform_render_item_ratings'],
804 'Address@compile' => ['fluentform_render_item_address'],
805 'Name@compile' => ['fluentform_render_item_input_name'],
806 'TextArea@compile' => ['fluentform_render_item_textarea'],
807 'DateTime@compile' => ['fluentform_render_item_input_date'],
808 'Recaptcha@compile' => ['fluentform_render_item_recaptcha'],
809 'Hcaptcha@compile' => ['fluentform_render_item_hcaptcha'],
810 'Turnstile@compile' => ['fluentform_render_item_turnstile'],
811 'Container@compile' => ['fluentform_render_item_container'],
812 'CustomHtml@compile' => ['fluentform_render_item_custom_html'],
813 'SectionBreak@compile' => ['fluentform_render_item_section_break'],
814 'SubmitButton@compile' => ['fluentform_render_item_submit_button'],
815 'SelectCountry@compile' => ['fluentform_render_item_select_country'],
816
817 'TermsAndConditions@compile' => [
818 'fluentform_render_item_terms_and_condition',
819 'fluentform_render_item_gdpr_agreement',
820 ],
821
822 'TabularGrid@compile' => [
823 'fluentform_render_item_tabular_grid',
824 ],
825
826 'Checkable@compile' => [
827 'fluentform_render_item_input_radio',
828 'fluentform_render_item_input_checkbox',
829 ],
830
831 'Text@compile' => [
832 'fluentform_render_item_input_url',
833 'fluentform_render_item_input_text',
834 'fluentform_render_item_input_email',
835 'fluentform_render_item_input_number',
836 'fluentform_render_item_input_hidden',
837 'fluentform_render_item_input_password',
838 ],
839 ];
840
841 $path = 'FluentForm\App\Services\FormBuilder\Components\\';
842 foreach ($actionMappings as $handler => $actions) {
843 foreach ($actions as $action) {
844 $this->app->addAction($action, function () use ($path, $handler) {
845 list($class, $method) = $this->app->parseHandler($path . $handler);
846 call_user_func_array([$class, $method], func_get_args());
847 }, 10, 2);
848 }
849 }
850 }
851
852 /**
853 * Register dynamic value shortcode parser (filter default value)
854 *
855 * @return void
856 */
857 public function addFluentFormDefaultValueParser()
858 {
859 $this->app->addFilter('fluentform_parse_default_value', function ($value, $form) {
860 return EditorShortcodeParser::filter($value, $form);
861 }, 10, 2);
862 }
863
864 /**
865 * Register filter to check whether the form is renderable
866 *
867 * @return mixed
868 */
869 public function addIsRenderableFilter()
870 {
871 $this->app->addFilter('fluentform_is_form_renderable', function ($isRenderable, $form) {
872 $checkables = ['limitNumberOfEntries', 'scheduleForm', 'requireLogin'];
873
874 foreach ($form->settings['restrictions'] as $key => $restrictions) {
875 if (in_array($key, $checkables)) {
876 $isRenderable['status'] = $this->{$key}($restrictions, $form, $isRenderable);
877 if (!$isRenderable['status']) {
878 $isRenderable['status'] = false;
879 return $isRenderable;
880 }
881 }
882 }
883
884 return $isRenderable;
885 }, 10, 2);
886 }
887
888 /**
889 * Check if limit is set on form submits and it's valid yet
890 *
891 * @param array $restrictions
892 *
893 * @return bool
894 */
895 private function limitNumberOfEntries($restrictions, $form, &$isRenderable)
896 {
897 if (!$restrictions['enabled']) {
898 return true;
899 }
900
901 $col = 'created_at';
902 $period = $restrictions['period'];
903 $maxAllowedEntries = $restrictions['numberOfEntries'];
904
905 if (!$maxAllowedEntries) {
906 return true;
907 }
908
909 $query = wpFluent()->table('fluentform_submissions')
910 ->where('form_id', $form->id)
911 ->where('status', '!=', 'trashed');
912
913 if ('day' == $period) {
914 $year = "YEAR(`{$col}`) = YEAR(NOW())";
915 $month = "MONTH(`{$col}`) = MONTH(NOW())";
916 $day = "DAY(`{$col}`) = DAY(NOW())";
917 $query->where(wpFluent()->raw("{$year} AND {$month} AND {$day}"));
918 } elseif ('week' == $period) {
919 $query->where(
920 wpFluent()->raw("YEARWEEK(`{$col}`, 1) = YEARWEEK(CURDATE(), 1)")
921 );
922 } elseif ('month' == $period) {
923 $year = "YEAR(`{$col}`) = YEAR(NOW())";
924 $month = "MONTH(`{$col}`) = MONTH(NOW())";
925 $query->where(wpFluent()->raw("{$year} AND {$month}"));
926 } elseif ('year' == $period) {
927 $query->where(wpFluent()->raw("YEAR(`{$col}`) = YEAR(NOW())"));
928 } elseif ('per_user_ip' == $period) {
929 $ip = $this->app->request->getIp();
930 $query->where('ip', $ip);
931 } elseif ('per_user_id' == $period) {
932 $userId = get_current_user_id();
933 if (!$userId) {
934 return true;
935 }
936 $query->where('user_id', $userId);
937 }
938
939 $count = $query->count();
940
941 if ($count >= $maxAllowedEntries) {
942 $isRenderable['message'] = $restrictions['limitReachedMsg'];
943 return false;
944 }
945
946 return true;
947 }
948
949 /**
950 * Check if form has scheduled date & weekdays and open for submission
951 *
952 * @param array $restrictions
953 *
954 * @return bool
955 */
956 private function scheduleForm($restrictions, $form, &$isRenderable)
957 {
958 if (!$restrictions['enabled']) {
959 return true;
960 }
961
962 $time = time();
963 $start = strtotime($restrictions['start']);
964 $end = strtotime($restrictions['end']);
965
966 if ($time < $start) {
967 $isRenderable['message'] = $restrictions['pendingMsg'];
968 return false;
969 }
970
971 if ($time >= $end) {
972 $isRenderable['message'] = $restrictions['expiredMsg'];
973
974 return false;
975 }
976
977 $weekDayToday = date('l'); //day of the week
978 $selectedWeekDays = ArrayHelper::get($restrictions, 'selectedDays');
979 //skip if it was not set initially and $selectedWeekDays is null
980 if ($selectedWeekDays && is_array($selectedWeekDays) && !in_array($weekDayToday, $selectedWeekDays)) {
981 $isRenderable['message'] = $restrictions['expiredMsg'];
982 return false;
983 }
984
985 return true;
986 }
987
988 /**
989 * * Check if form requires loged in user and user is logged in
990 *
991 * @param array $restrictions
992 *
993 * @return bool
994 */
995 private function requireLogin($restrictions, $form, &$isRenderable)
996 {
997 if (!$restrictions['enabled']) {
998 return true;
999 }
1000
1001 if (!($isLoggedIn = is_user_logged_in())) {
1002 $isRenderable['message'] = $restrictions['requireLoginMsg'];
1003 }
1004
1005 return $isLoggedIn;
1006 }
1007
1008 /**
1009 * Register fluentform_submission_inserted actions
1010 *
1011 * @return void
1012 */
1013 public function addFluentformSubmissionInsertedFilter()
1014 {
1015 (new EmailNotificationActions($this->app))->register();
1016 }
1017
1018 /**
1019 * Add inline scripts [Add localized script using same var]
1020 *
1021 * @return void
1022 */
1023 private function addInlineVars()
1024 {
1025 if (!defined('ELEMENTOR_PRO_VERSION') || $this->elementorPopUpHandler) {
1026 return '';
1027 }
1028
1029 // Previously this handler was registered for every form
1030 // in a single page. So multiple event handlers were
1031 // registered. This flag ensures the handler is
1032 // registered just once, since one is enough!
1033 $this->elementorPopUpHandler = true;
1034
1035 $actionName = 'wp_footer';
1036 if (is_admin()) {
1037 $actionName = 'admin_footer';
1038 }
1039
1040 add_action($actionName, function () {
1041 ?>
1042 <script type="text/javascript">
1043 <?php if (defined('ELEMENTOR_PRO_VERSION')): ?>
1044 jQuery(document).on('elementor/popup/show', function(event, id, instance) {
1045 var ffForms = jQuery('#elementor-popup-modal-' + id).find('form.frm-fluent-form');
1046
1047 /**
1048 * Support conversation form in elementor popup
1049 * No regular form found, check for conversational form
1050 */
1051 if (!ffForms.length) {
1052 const elements = document.getElementsByClassName('ffc_conv_form');
1053 if (elements.length) {
1054 let jsEvent = new CustomEvent('ff-elm-conv-form-event', {
1055 detail: elements
1056 });
1057 document.dispatchEvent(jsEvent);
1058 }
1059 }
1060 if (ffForms.length) {
1061 jQuery.each(ffForms, function(index, ffForm) {
1062 jQuery(ffForm).trigger('reInitExtras');
1063 jQuery(document).trigger('ff_reinit', [ffForm]);
1064 });
1065 }
1066 });
1067 <?php endif; ?>
1068 </script>
1069 <?php
1070 }, 999);
1071 return '';
1072 }
1073
1074 public static function getDatei18n()
1075 {
1076 $i18n = [
1077 'previousMonth' => __('Previous Month', 'fluentform'),
1078 'nextMonth' => __('Next Month', 'fluentform'),
1079 'months' => [
1080 'shorthand' => [
1081 __('Jan', 'fluentform'),
1082 __('Feb', 'fluentform'),
1083 __('Mar', 'fluentform'),
1084 __('Apr', 'fluentform'),
1085 __('May', 'fluentform'),
1086 __('Jun', 'fluentform'),
1087 __('Jul', 'fluentform'),
1088 __('Aug', 'fluentform'),
1089 __('Sep', 'fluentform'),
1090 __('Oct', 'fluentform'),
1091 __('Nov', 'fluentform'),
1092 __('Dec', 'fluentform'),
1093 ],
1094 'longhand' => [
1095 __('January', 'fluentform'),
1096 __('February', 'fluentform'),
1097 __('March', 'fluentform'),
1098 __('April', 'fluentform'),
1099 __('May', 'fluentform'),
1100 __('June', 'fluentform'),
1101 __('July', 'fluentform'),
1102 __('August', 'fluentform'),
1103 __('September', 'fluentform'),
1104 __('October', 'fluentform'),
1105 __('November', 'fluentform'),
1106 __('December', 'fluentform'),
1107 ],
1108 ],
1109 'weekdays' => [
1110 'longhand' => [
1111 __('Sunday', 'fluentform'),
1112 __('Monday', 'fluentform'),
1113 __('Tuesday', 'fluentform'),
1114 __('Wednesday', 'fluentform'),
1115 __('Thursday', 'fluentform'),
1116 __('Friday', 'fluentform'),
1117 __('Saturday', 'fluentform'),
1118 ],
1119 'shorthand' => [
1120 __('Sun', 'fluentform'),
1121 __('Mon', 'fluentform'),
1122 __('Tue', 'fluentform'),
1123 __('Wed', 'fluentform'),
1124 __('Thu', 'fluentform'),
1125 __('Fri', 'fluentform'),
1126 __('Sat', 'fluentform'),
1127 ],
1128 ],
1129 'daysInMonth' => [
1130 31,
1131 28,
1132 31,
1133 30,
1134 31,
1135 30,
1136 31,
1137 31,
1138 30,
1139 31,
1140 30,
1141 31,
1142 ],
1143 'rangeSeparator' => __(' to ', 'fluentform'),
1144 'weekAbbreviation' => __('Wk', 'fluentform'),
1145 'scrollTitle' => __('Scroll to increment', 'fluentform'),
1146 'toggleTitle' => __('Click to toggle', 'fluentform'),
1147 'amPM' => [
1148 __('AM', 'fluentform'),
1149 __('PM', 'fluentform'),
1150 ],
1151 'yearAriaLabel' => __('Year', 'fluentform'),
1152 ];
1153
1154 return apply_filters('fluentform/date_i18n', $i18n);
1155 }
1156
1157 protected function maybeHasAdvandedFields($form, $formBuilder)
1158 {
1159 $advancedFields = [
1160 'step_start',
1161 'repeater_field',
1162 'ratings',
1163 'form_step',
1164 'input_file',
1165 'input_image',
1166 'net_promoter_score',
1167 'featured_image',
1168 ];
1169
1170 if ($formBuilder->conditions || array_intersect($formBuilder->fieldLists, $advancedFields)) {
1171 wp_enqueue_script('fluentform-advanced');
1172 }
1173 }
1174
1175 public function registerInputSanitizers()
1176 {
1177 add_filter('fluentform_input_data_input_number', [$this, 'getNumericInputValue'], 10, 2);
1178 add_filter('fluentform_input_data_custom_payment_component', [$this, 'getNumericInputValue'], 10, 2);
1179 }
1180
1181 public function getNumericInputValue($value, $field)
1182 {
1183 $formatter = ArrayHelper::get($field, 'raw.settings.numeric_formatter');
1184 if (!$formatter) {
1185 return $value;
1186 }
1187 return Helper::getNumericValue($value, $formatter);
1188 }
1189 }
1190