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

Form.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.6, at app/Services/FluentConversational/Classes/Form.php

1,009 lines 44.0 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\Services\FluentConversational\Classes;
4
5 defined('ABSPATH') or die;
6
7 use FluentForm\App\Helpers\Helper;
8 use FluentForm\App\Modules\Acl\Acl;
9 use FluentForm\App\Modules\Payments\PaymentHelper;
10 use FluentForm\App\Modules\Payments\PaymentMethods\Stripe\StripeSettings;
11 use FluentForm\Framework\Helpers\ArrayHelper;
12 use FluentForm\App\Modules\Form\Settings\FormCssJs;
13 use FluentForm\App\Services\FluentConversational\Classes\Converter\Converter;
14 use FluentForm\App\Services\FluentConversational\Classes\Elements\WelcomeScreen;
15
16 class Form
17 {
18 protected $addOnKey = 'conversational_forms';
19
20 protected $metaKey = 'ffc_form_settings';
21
22 public function boot()
23 {
24 add_action('wp', [$this, 'render'], 100);
25
26 add_filter('fluentform/editor_components', [$this, 'filterAcceptedFields'], 999, 2);
27
28 add_filter('fluentform/form_admin_menu', [$this, 'pushDesignTab'], 10, 2);
29
30 add_action('fluentform/form_application_view_conversational_design', [$this, 'renderDesignSettings'], 10, 1);
31
32 add_filter('fluentform/editor_element_settings_placement', [$this, 'maybeAlterPlacement'], 10, 2);
33
34 // elements
35 new WelcomeScreen();
36 }
37
38 public function pushDesignTab($menuItems, $formId)
39 {
40 if (!Helper::isConversionForm($formId)) {
41 return $menuItems;
42 }
43
44 $newItems = $menuItems;
45
46 if (Acl::hasPermission('fluentform_forms_manager')) {
47 $newItems = array_slice($menuItems, 0, 1, true) + [
48 'conversational_design' => [
49 'slug' => 'conversational_design',
50 'title' => __('Design', 'fluentform'),
51 'url' => admin_url('admin.php?page=fluent_forms&form_id=' . $formId . '&route=conversational_design'),
52 ],
53 ] + array_slice($menuItems, 1, count($menuItems) - 1, true);
54 }
55
56 return $newItems;
57 }
58
59 public function renderDesignSettings($formId)
60 {
61 if (!Helper::isConversionForm($formId)) {
62 echo 'Sorry! This is not a conversational form';
63 return;
64 }
65
66 if (function_exists('wp_enqueue_editor')) {
67 add_filter('user_can_richedit', '__return_true');
68 wp_enqueue_editor();
69 wp_enqueue_media();
70 }
71
72 wp_enqueue_script(
73 'fluent_forms_conversational_design',
74 fluentFormMix('js/conversational_design.js'),
75 ['jquery'],
76 FLUENTFORM_VERSION,
77 true
78 );
79
80 $slug = apply_filters_deprecated(
81 'fluentform_conversational_url_slug',
82 [
83 'fluent-form'
84 ],
85 FLUENTFORM_FRAMEWORK_UPGRADE,
86 'fluentform/conversational_url_slug',
87 'Use fluentform/conversational_url_slug instead of fluentform_conversational_url_slug.'
88 );
89
90 $paramKey = apply_filters('fluentform/conversational_url_slug', $slug);
91
92 if ('form' == $paramKey) {
93 $paramKey = 'fluent-form';
94 }
95
96 wp_localize_script('fluent_forms_conversational_design', 'ffc_conv_vars', [
97 'form_id' => $formId,
98 'preview_url' => Helper::getFrontendFacingUrl('?' . $paramKey . '=' . $formId),
99 'fonts' => Fonts::getFonts(),
100 'has_pro' => defined('FLUENTFORMPRO'),
101 'has_pro_share_page' => defined('FLUENTFORMPRO') && class_exists('\FluentFormPro\classes\SharePage\SharePage'),
102 ]);
103
104 wp_enqueue_style(
105 'fluent_forms_conversion_style',
106 fluentFormMix('css/conversational_design.css'),
107 [],
108 FLUENTFORM_VERSION
109 );
110
111 echo '<div id="ff_conversation_form_design_app"><design-skeleton><h1 style="text-align: center; margin: 60px 0px;">Loading App Please wait....</h1></design-skeleton><global-search></global-search></div>';
112 }
113
114 public function getDesignSettings($formId)
115 {
116 $settings = Helper::getFormMeta($formId, $this->metaKey . '_design', []);
117
118 $defaults = [
119 'background_color' => '#FFFFFF',
120 'question_color' => '#191919',
121 'answer_color' => '#0445AF',
122 'button_color' => '#0445AF',
123 'button_text_color' => '#FFFFFF',
124 'background_image' => '',
125 'background_brightness' => 0,
126 'disable_branding' => 'no',
127 'hide_media_on_mobile' => 'no',
128 'key_hint' => 'yes',
129 'enable_scroll_to_top' => 'no',
130 'asteriskPlacement' => $this->getAsteriskPlacement($formId)
131 ];
132
133 return wp_parse_args($settings, $defaults);
134 }
135
136 public function getMetaSettings($formId)
137 {
138 $settings = Helper::getFormMeta($formId, $this->metaKey . '_meta', []);
139 $defaults = [
140 'title' => '',
141 'description' => '',
142 'featured_image' => '',
143 'share_key' => '',
144 'google_font_href' => '',
145 'font_css' => '',
146 'i18n' => [
147 'skip_btn' => 'SKIP',
148 'confirm_btn' => 'OK',
149 'continue' => 'Continue',
150 'keyboard_instruction' => 'Press <b>Enter ↵</b>',
151 'multi_select_hint' => 'Choose as many as you like',
152 'single_select_hint' => 'Choose one option',
153 'progress_text' => '{percent}% completed',
154 'long_text_help' => '<b>Shift ⇧</b> + <b>Enter ↵</b> to make a line break.',
155 'invalid_prompt' => 'Please fill out the field correctly',
156 'errorMaxLength' => 'The maximum {maxLength} number of characters accept',
157 'default_placeholder' => 'Type Your answer here',
158 'key_hint_text' => 'Key',
159 'key_hint_tooltip' => 'Press the key to select',
160 'choose_file' => '<b>Choose file</b> or <b>drag here</b>',
161 'limit' => 'Size limit: ',
162 'ranking_reset' => 'Reset order'
163 ],
164 ];
165
166 if ($settings && !isset($settings['i18n']['key_hint_text'])) {
167 $settings['i18n']['key_hint_text'] = $defaults['i18n']['key_hint_text'];
168 $settings['i18n']['key_hint_tooltip'] = $defaults['i18n']['key_hint_tooltip'];
169 }
170
171 if ($settings && !isset($settings['i18n']['ranking_reset'])) {
172 $settings['i18n']['ranking_reset'] = $defaults['i18n']['ranking_reset'];
173 }
174
175 if (!$settings || empty($settings['title'])) {
176 $form = wpFluent()->table('fluentform_forms')->find($formId);
177 $settings['title'] = $form->title;
178 }
179
180 return wp_parse_args($settings, $defaults);
181 }
182
183 private function getGeneratedCss($formId)
184 {
185 $prefix = '.ff_conv_app_' . $formId;
186 if (defined('FLUENTFORMPRO')) {
187 $css = Helper::getFormMeta($formId, $this->metaKey . '_generated_css', '');
188 if ($css) {
189 return $css;
190 }
191 }
192
193 return $prefix . ' { background-color: #FFFFFF; }' . $prefix . ' .ffc-counter-div span { color: #0445AF; }' . $prefix . ' .ffc-counter-div .counter-icon-span svg { fill: #0445AF !important; }' . $prefix . ' .f-label-wrap, ' . $prefix . ' .f-answer { color: #0445AF !important; }' . $prefix . ' .f-label-wrap .f-key { border-color: #0445AF !important; }' . $prefix . ' .f-label-wrap .f-key-hint { border-color: #0445AF !important; }' . $prefix . ' .f-answer .f-radios-wrap ul li { background-color: rgba(4,69,175, 0.1) !important; border: 1px solid #0445AF; }' . $prefix . ' .f-answer .f-radios-wrap ul li:focus { background-color: rgba(4,69,175, 0.3) !important }' . $prefix . ' .f-answer .f-radios-wrap ul li:hover { background-color: rgba(4,69,175, 0.3) !important }' . $prefix . ' .f-answer .f-radios-wrap ul li.f-selected .f-key { background-color: #0445AF !important; color: white; }' . $prefix . ' .f-answer .f-radios-wrap ul li.f-selected .f-key-hint { background-color: #0445AF; }' . $prefix . ' .f-answer .f-radios-wrap ul li.f-selected svg { fill: #0445AF !important; }' . $prefix . ' .f-answer input, ' . $prefix . ' .f-answer textarea{ color: #0445AF !important; box-shadow: #0445AF 0px 1px; }' . $prefix . ' .f-answer input:focus, ' . $prefix . ' .f-answer textarea:focus { box-shadow: #0445AF 0px 2px !important; }' . $prefix . ' .f-answer textarea::placeholder, ' . $prefix . ' .f-answer input::placeholder { color: #0445AF !important; }' . $prefix . ' .text-success { color: #0445AF !important; }' . $prefix . ' .f-answer .f-matrix-table tbody td { background-color: rgba(4,69,175, 0.1); }' . $prefix . ' .f-answer .f-matrix-table input { border-color: rgba(4,69,175, 0.8); }' . $prefix . ' .f-answer .f-matrix-table input.f-radio-control:checked::after { background-color: #0445AF; }' . $prefix . ' .f-answer .f-matrix-table input:focus::before { border-color: #0445AF; }' . $prefix . ' .f-answer .f-matrix-table input.f-checkbox-control:checked { background-color: #0445AF; }' . $prefix . ' .f-answer .f-matrix-table tbody tr::after { border-right-color: #0445AF; }' . $prefix . ' .f-answer .f-matrix-table .f-table-cell.f-row-cell { box-shadow: rgba(4,69,175, 0.1) 0px 0px 0px 100vh inset; }' . $prefix . ' .f-answer .ff_file_upload_field_wrap { background-color: rgba(4,69,175, 0.1); border-color: rgba(4,69,175, 0.8); }' . $prefix . ' .f-answer .ff_file_upload_field_wrap:hover { background-color: rgba(4,69,175, 0.3);}' . $prefix . ' .f-answer .ff_file_upload_field_wrap:focus-within { background-color: rgba(4,69,175, 0.3); }' . $prefix . ' .f-answer .ff-upload-preview { border-color: rgba(4,69,175, 0.8); }' . $prefix . ' .f-answer .ff-upload-preview .ff-upload-thumb { background-color: rgba(4,69,175, 0.3); }' . $prefix . ' .f-answer .ff-upload-preview .ff-upload-details { border-left-color: rgba(4,69,175, 0.8); }' . $prefix . ' .f-answer .ff-upload-preview .ff-upload-details .ff-el-progress { border-left-color: rgba(4,69,175, 0.8); }' . $prefix . ' .f-answer .ff-upload-preview .ff-upload-details .ff-el-progress { background-color: rgba(4,69,175, 0.1); }' . $prefix . ' .f-answer .ff-upload-preview .ff-upload-details .ff-el-progress .ff-el-progress-bar { background-color: #0445AF; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap::before { background-color: #0445AF; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap .f-star-field .f-star-field-star .symbolOutline { fill: #0445AF; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap .f-star-field .f-star-field-rating { color: #0445AF; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap .f-star-field-star .ff-rating-icon-svg-holder { display: block; line-height: 0; width: 100%; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap .ff-rating-icon-svg { color: var(--ff-rating-inactive-color, rgba(4,69,175, 0.25)); display: block; height: auto; max-height: 64px; max-width: 64px; width: 100%; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap .ff-rating-icon-svg [fill]:not([fill="none"]) { fill: currentColor !important; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap .ff-rating-icon-svg [stroke]:not([stroke="none"]) { stroke: currentColor !important; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap.is-hovered .symbolFill { fill: rgba(4,69,175, 0.1); }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap.is-hovered .ff-rating-icon-svg { color: var(--ff-rating-hover-color, rgba(4,69,175, 0.4)); }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap.is-selected .symbolFill { fill: #0445AF; }' . $prefix . ' .f-answer .f-star-wrap .f-star-field-wrap.is-selected .ff-rating-icon-svg { color: var(--ff-rating-active-color, #0445AF); }' . $prefix . ' .f-answer .f-payment-summary-wrap tbody td { background-color: rgba(4,69,175, 0.1); }' . $prefix . ' .f-answer .f-payment-summary-wrap tfoot th { background-color: rgba(4,69,175, 0.1); }' . $prefix . ' .f-answer .stripe-inline-holder { border-bottom: 1px solid #0445AF; }' . $prefix . ' .f-answer .StripeElement--focus { border-bottom: 2.5px solid #0445AF; }' . $prefix . ' .ff_conv_input .f-info { color: #0445AF; }' . $prefix . ' .fh2 .f-text { color: #191919; }' . $prefix . ' .fh2 .f-tagline, ' . $prefix . ' .f-sub .f-help { color: rgba(25,25,25, 0.70); }' . $prefix . ' .fh2 .stripe-inline-header { color: #191919; }' . $prefix . ' .q-inner .o-btn-action, ' . $prefix . ' .footer-inner-wrap .f-nav { background-color: #0445AF; }' . $prefix . ' .q-inner .o-btn-action span, ' . $prefix . ' .footer-inner-wrap .f-nav a { color: #FFFFFF; } ' . $prefix . ' .f-enter .f-enter-desc { color: #0445AF; }' . $prefix . ' .footer-inner-wrap .f-nav a svg { fill: #FFFFFF; }' . $prefix . ' .vff-footer .f-progress-bar { background-color: rgba(4,69,175, 0.3); }' . $prefix . ' .vff-footer .f-progress-bar-inner { background-color: #0445AF; }' . $prefix . ' .q-inner .o-btn-action:hover { background-color: #0445AFD6; }' . $prefix . ' .q-inner .o-btn-action:focus::after { border-radius: 6px; inset: -3px; box-shadow: #0445AF 0px 0px 0px 2px; }' . $prefix . ' .f-answer .f-radios-wrap ul li.f-selected .f-key { color: #FFFFFF; }';
194 }
195
196 public function render()
197 {
198 $slug = 'fluent-form';
199 $paramKey = apply_filters_deprecated(
200 'fluentform_conversational_url_slug',
201 [
202 $slug
203 ],
204 FLUENTFORM_FRAMEWORK_UPGRADE,
205 'fluentform/conversational_url_slug',
206 'Use fluentform/conversational_url_slug instead of fluentform_conversational_url_slug.'
207 );
208
209 $paramKey = apply_filters('fluentform/conversational_url_slug', $paramKey);
210
211 if ('form' == $paramKey) {
212 $paramKey = $slug;
213 }
214
215 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public form display, no nonce needed
216 if(!isset($_REQUEST[$paramKey])) {
217 return;
218 }
219
220 $request = wpFluentForm('request')->get();
221
222 if ((isset($request[$paramKey])) && !wp_doing_ajax()) {
223 $formId = (int) ArrayHelper::get($request, $paramKey);
224 if (!Helper::isConversionForm($formId)) {
225 return;
226 }
227 $shareKey = ArrayHelper::get($request, 'form');
228 $this->renderFormHtml($formId, $shareKey);
229 }
230 }
231
232 public function isEnabled()
233 {
234 $globalModules = get_option('fluentform_global_modules_status');
235
236 $addOn = ArrayHelper::get($globalModules, $this->addOnKey);
237
238 if (!$addOn || 'yes' == $addOn) {
239 return true;
240 }
241
242 return false;
243 }
244
245 private function getSubmitBttnStyle($form)
246 {
247 $data = $form->submit_button;
248 $styles = '';
249
250 if ('' == ArrayHelper::get($data, 'settings.button_style')) {
251 // it's a custom button
252 $buttonActiveStyles = ArrayHelper::get($data, 'settings.normal_styles', []);
253 $buttonHoverStyles = ArrayHelper::get($data, 'settings.hover_styles', []);
254 $activeStates = '';
255 foreach ($buttonActiveStyles as $styleAtr => $styleValue) {
256 if ('0' != $styleValue && !$styleValue) {
257 continue;
258 }
259 if ('borderRadius' == $styleAtr) {
260 $styleValue .= 'px';
261 }
262 $activeStates .= ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '-$0', $styleAtr)), '_') . ':' . $styleValue . ';';
263 }
264 if ($activeStates) {
265 $styles .= ' .ff-btn-submit { ' . $activeStates . ' }';
266 }
267 $hoverStates = '';
268 foreach ($buttonHoverStyles as $styleAtr => $styleValue) {
269 if ('0' != $styleValue && !$styleValue) {
270 continue;
271 }
272 if ('borderRadius' == $styleAtr) {
273 $styleValue .= 'px';
274 }
275 $hoverStates .= ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '-$0', $styleAtr)), '-') . ':' . $styleValue . ';';
276 }
277 if ($hoverStates) {
278 $styles .= ' .wpf_has_custom_css.ff-btn-submit:hover { ' . $hoverStates . ' } ';
279 }
280 } else {
281 $styles .= ' .ff-btn-submit { background-color: ' . ArrayHelper::get($data, 'settings.background_color') . '; color: ' . ArrayHelper::get($data, 'settings.color') . '; }';
282 }
283
284 if (defined('FLUENTFORMPRO')) {
285 $customCssJsClass = new FormCssJs();
286 $customCss = $customCssJsClass->getCss($form->id);
287 $styles .= $customCss;
288 }
289
290 return $styles;
291 }
292
293 public function filterAcceptedFields($components, $formId)
294 {
295 if (!Helper::isConversionForm($formId)) {
296 return $components;
297 }
298
299 $generalFields = ArrayHelper::get($components, 'general', []);
300 $advancedFields = ArrayHelper::get($components, 'advanced', []);
301 $paymentFields = ArrayHelper::get($components, 'payments', []);
302
303 $acceptedFieldElements = [
304 'phone',
305 'select',
306 'select',
307 'ratings',
308 'textarea',
309 'address',
310 'input_name',
311 'input_url',
312 'input_text',
313 'input_date',
314 'input_file',
315 'input_email',
316 'input_radio',
317 'custom_html',
318 'input_image',
319 'input_hidden',
320 'input_number',
321 'tabular_grid',
322 'section_break',
323 'select_country',
324 'input_checkbox',
325 'input_password',
326 'terms_and_condition',
327 'gdpr_agreement',
328 'multi_payment_component',
329 'subscription_payment_component',
330 'custom_payment_component',
331 'item_quantity_component',
332 'payment_method',
333 'payment_summary_component',
334 'payment_coupon',
335 'recaptcha',
336 'hcaptcha',
337 'turnstile',
338 'quiz_score',
339 'save_progress_button',
340 'dynamic_field',
341 'rangeslider',
342 'net_promoter_score',
343 'input_ranking'
344 ];
345
346 if (defined('FLUENTFORM_SIGNATURE')) {
347 $acceptedFieldElements[] = 'signature';
348 }
349
350 $acceptedFieldElements = apply_filters(
351 'fluentform/conversational_accepted_field_elements',
352 $acceptedFieldElements,
353 $formId
354 );
355
356 $elements = [];
357
358 $allFields = [
359 'general' => $generalFields,
360 'advanced' => $advancedFields,
361 'payments' => $paymentFields,
362 ];
363
364 foreach ($allFields as $groupType => $group) {
365 foreach ($group as $field) {
366 $element = $field['element'];
367 if (in_array($element, $acceptedFieldElements)) {
368 $field['style_pref'] = [
369 'layout' => 'default',
370 'media' => fluentFormGetRandomPhoto(),
371 'brightness' => 0,
372 'alt_text' => '',
373 'media_x_position' => 50,
374 'media_y_position' => 50,
375 ];
376
377 if ('terms_and_condition' == $element || 'gdpr_agreement' == $element) {
378 $existingSettings = $field['settings'];
379 $existingSettings['tc_agree_text'] = __('I accept', 'fluentform');
380 if ('terms_and_condition' == $element) {
381 $existingSettings['tc_dis_agree_text'] = __('I don\'t accept', 'fluentform');
382 $existingSettings['hide_disagree'] = false;
383 }
384 $field['settings'] = $existingSettings;
385 }
386 //adding required settings for captcha in conversational form
387 if ('hcaptcha' == $element || 'recaptcha' == $element || 'turnstile' == $element) {
388 $existingSettings = $field['settings'];
389 if (empty($existingSettings['validation_rules'])) {
390 $existingSettings['validation_rules'] = [
391 'required' => [
392 'value' => true,
393 'message' => __('This field is required', 'fluentform'),
394 ],
395 ];
396 }
397 $field['settings'] = $existingSettings;
398 }
399
400 $elements[$groupType][] = $field;
401 }
402 }
403 }
404
405 $elements = apply_filters_deprecated(
406 'fluent_conversational_editor_elements',
407 [
408 $elements
409 ],
410 FLUENTFORM_FRAMEWORK_UPGRADE,
411 'fluentform/conversational_editor_elements',
412 'Use fluentform/conversational_editor_elements instead of fluent_conversational_editor_elements.'
413 );
414
415 $elements = apply_filters('fluentform/conversational_editor_elements', $elements, $formId);
416
417 return $elements;
418 }
419
420 public function printLoadedScripts()
421 {
422 $jsScripts = $this->getRegisteredScripts();
423 if ($jsScripts) {
424 add_action('fluentform/conversational_frame_footer', function () use ($jsScripts) {
425 foreach ($jsScripts as $handle => $jsScript) {
426 if (empty($jsScript->src)) {
427 continue;
428 }
429 if ($data = ArrayHelper::get($jsScript->extra, 'data')) {
430 printf("<script type='text/javascript' id='%s-js-extra'>\n", esc_attr($handle));
431 echo "$data\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $data is hardcoded localized data and escaped before being passed in.
432 echo "</script>\n";
433 }
434 $src = $jsScript->src;
435 $src = add_query_arg('ver', $jsScript->ver, $src);
436 echo "<script type='text/javascript' id='" . esc_attr($handle) . "' src='" . esc_url($src) . "'></script>\n";
437 }
438 }, 1);
439 }
440
441 $cssStyles = $this->getRegisteredStyles();
442 if ($cssStyles) {
443 add_action('fluentform/conversational_frame_head', function () use ($cssStyles) {
444 foreach ($cssStyles as $styleName => $cssStyle) {
445 if (empty($cssStyle->src)) {
446 continue;
447 }
448 $src = add_query_arg('ver', $cssStyle->ver, $cssStyle->src);
449 // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Completely a custom rendering page.
450 echo "<link rel='stylesheet' id='" . esc_attr($styleName) . "' href='" . esc_url($src) . "' type='text/css' media='all' />\n";
451 }
452 });
453 }
454 }
455
456 private function getRegisteredScripts()
457 {
458 global $wp_scripts;
459 if (!$wp_scripts) {
460 return [];
461 }
462
463 $jsScripts = [];
464
465 $pluginUrl = plugins_url() . '/fluentform';
466
467 foreach ($wp_scripts->queue as $script) {
468
469 if (!isset($wp_scripts->registered[$script])) {
470 continue;
471 }
472
473 $item = $wp_scripts->registered[$script];
474 $src = $wp_scripts->registered[$script]->src;
475
476 if (false === !strpos($src, $pluginUrl)) {
477 continue;
478 }
479
480 foreach ($item->deps as $dep) {
481 if (isset($wp_scripts->registered[$dep])) {
482 $child = $wp_scripts->registered[$dep];
483 if ($child->src) {
484 $jsScripts[$dep] = $child;
485 } else {
486 // this core file maybe
487 $childDependencies = $child->deps;
488 foreach ($childDependencies as $childDependency) {
489 $childX = $wp_scripts->registered[$childDependency];
490 if ($childX->src) {
491 $jsScripts[$childDependency] = $childX;
492 }
493 }
494 }
495 }
496 }
497 $jsScripts[$script] = $item;
498 }
499
500 return $jsScripts;
501 }
502
503 private function getRegisteredStyles()
504 {
505 $wp_styles = wp_styles();
506 if (!$wp_styles) {
507 return [];
508 }
509
510 $cssStyles = [];
511
512 $pluginUrl = plugins_url() . '/fluentform';
513
514 foreach ($wp_styles->queue as $style) {
515
516 if (!isset($wp_styles->registered[$style])) {
517 continue;
518 }
519
520 $item = $wp_styles->registered[$style];
521 $src = $wp_styles->registered[$style]->src;
522
523 if (false === !strpos($src, $pluginUrl)) {
524 continue;
525 }
526
527 if ($item->deps) {
528 foreach ($item->deps as $dep) {
529 if (isset($wp_styles->registered[$dep])) {
530 $child = $wp_styles->registered[$dep];
531 if ($child->src) {
532 $cssStyles[$dep] = $child;
533 } else {
534 // this core file maybe
535 $childDependencies = $child->deps;
536 if ($childDependencies && is_array($childDependencies)) {
537 foreach ($childDependencies as $childDependency) {
538 $childX = $wp_styles->registered[$childDependency];
539 if ($childX->src) {
540 $cssStyles[$childDependency] = $childX;
541 }
542 }
543 }
544 }
545 }
546 }
547 }
548
549 $cssStyles[$style] = $item;
550 }
551
552 return $cssStyles;
553 }
554
555 public function renderShortcode($form)
556 {
557 $formId = $form->id;
558 $fileUploadSettings = apply_filters('fluentform/file_upload_settings_for_js', [], $form);
559
560 $form = Converter::convert($form);
561
562 $this->enqueueScripts();
563 do_action('fluentform/conversational_enqueue_assets', $form, $fileUploadSettings);
564
565 $submitCss = $this->getSubmitBttnStyle($form);
566 $metaSettings = $this->getMetaSettings($formId);
567 $designSettings = $this->getDesignSettings($formId);
568 $instanceId = $form->instance_index;
569 $varName = 'fluent_forms_global_var_' . $instanceId;
570
571 $localizedVars = [
572 'fluent_forms_admin_nonce' => wp_create_nonce('fluent_forms_admin_nonce'),
573 'ajaxurl' => admin_url('admin-ajax.php'),
574 'nonce' => wp_create_nonce(),
575 'form' => $this->getLocalizedForm($form),
576 'assetBaseUrl' => FLUENT_CONVERSATIONAL_FORM_DIR_URL . 'public',
577 'i18n' => $metaSettings['i18n'],
578 'form_id' => $form->id,
579 'hasPro' => defined('FLUENTFORMPRO'),
580 'is_inline_form' => true,
581 'design' => $designSettings,
582 'extra_inputs' => $this->getExtraHiddenInputs($formId),
583 'uploading_txt' => __('Uploading', 'fluentform'),
584 'upload_completed_txt' => __('100% Completed', 'fluentform'),
585 'unknown_error_txt' => __('An unknown error occurred', 'fluentform'),
586 'request_error_txt' => __('An error occurred while processing your request', 'fluentform'),
587 'paymentConfig' => $this->getPaymentConfig($form),
588 'date_i18n' => \FluentForm\App\Modules\Component\Component::getDatei18n(),
589 'file_delete_nonce' => wp_create_nonce('fluentform_file_delete'),
590 'file_upload_settings' => $fileUploadSettings,
591 ];
592
593 wp_localize_script(
594 'fluent_forms_conversational_form',
595 $varName,
596 apply_filters('fluentform/global_form_vars', $localizedVars)
597 );
598
599 $hasSaveProgressButton = false;
600 $saveProgressButton = [];
601 foreach ($form->fields['fields'] as $item) {
602 if (isset($item['element']) && $item['element'] === 'save_progress_button') {
603 $hasSaveProgressButton = true;
604 $saveProgressButton = $item;
605 }
606 }
607
608 if ($hasSaveProgressButton && $saveProgressButton) {
609 $this->localizeSaveProgressButton($saveProgressButton, $formId);
610 }
611
612 /* This filter is deprecated and will be removed soon */
613 $disableAnalytics = apply_filters('fluentform-disabled_analytics', true);
614
615 if (!apply_filters('fluentform/disabled_analytics', $disableAnalytics)) {
616 if (!Acl::hasAnyFormPermission($form->id)) {
617 (new \FluentForm\App\Services\Analytics\AnalyticsService())->store($formId);
618 }
619 }
620
621 return wpFluentForm('view')->make('public.conversational-form-inline', [
622 'generated_css' => $this->getGeneratedCss($formId),
623 'design' => $designSettings,
624 'submit_css' => $submitCss,
625 'form_id' => $formId,
626 'meta' => $metaSettings,
627 'global_var_name' => $varName,
628 'instance_id' => $instanceId,
629 'is_inline' => 'yes',
630 ]);
631 }
632
633 public function maybeAlterPlacement($placements, $form)
634 {
635 if (!Helper::isConversionForm($form->id) || empty($placements['terms_and_condition']) || empty($placements['gdpr_agreement'])) {
636 return $placements;
637 }
638
639 $placements['terms_and_condition']['general'] = [
640 'admin_field_label',
641 'validation_rules',
642 'tnc_html',
643 'tc_agree_text',
644 'tc_dis_agree_text',
645 ];
646
647 $placements['terms_and_condition']['generalExtras'] = [
648 'tc_agree_text' => [
649 'template' => 'inputText',
650 'label' => 'Agree Button Text',
651 ],
652 'tc_dis_agree_text' => [
653 'template' => 'inputText',
654 'label' => 'Disagree Button Text',
655 ],
656 'hide_disagree' => [
657 'template' => 'inputCheckbox',
658 'options' => [
659 [
660 'value' => false,
661 'label' => __('Hide Disagree Button', 'fluentform'),
662 ],
663 ],
664 ],
665 ];
666
667 $placements['gdpr_agreement']['generalExtras'] = [
668 'tc_agree_text' => [
669 'template' => 'inputText',
670 'label' => 'Agree Option Text',
671 ],
672 ];
673
674 return $placements;
675 }
676
677 private function getExtraHiddenInputs($formId)
678 {
679 $inputs = [
680 '__fluent_form_embded_post_id' => get_the_ID(),
681 '_fluentform_' . $formId . '_fluentformnonce' => wp_create_nonce('fluentform-submit-form'),
682 '_wp_http_referer' => esc_attr(wp_unslash(wpFluentForm('request')->server('REQUEST_URI'))),
683 ];
684
685 return apply_filters('fluentform/conversational_extra_inputs', $inputs, $formId);
686 }
687
688 public function getRandomPhoto()
689 {
690 return fluentFormGetRandomPhoto();
691 }
692
693 public function renderFormHtml($formId, $providedKey = '')
694 {
695 $form = wpFluent()->table('fluentform_forms')->find($formId);
696
697 if (!$form) {
698 return '';
699 }
700
701 $formSettings = wpFluent()
702 ->table('fluentform_form_meta')
703 ->where('form_id', $formId)
704 ->where('meta_key', 'formSettings')
705 ->first();
706
707 if (!$formSettings) {
708 return '';
709 }
710
711 $form->fields = json_decode($form->form_fields, true);
712
713 if (!$form->fields['fields']) {
714 return '';
715 }
716
717 $form->settings = json_decode($formSettings->value, true);
718
719 if ($form->status == 'unpublished') {
720 global $wp_query;
721 $wp_query->set_404();
722 status_header(404);
723 nocache_headers();
724 include(get_query_template('404'));
725 exit();
726 }
727
728 $metaSettings = $this->getMetaSettings($formId);
729
730 $shareKey = ArrayHelper::get($metaSettings, 'share_key');
731 if ($shareKey) {
732 if ($providedKey != $shareKey && !Acl::hasAnyFormPermission($formId)) {
733 return '';
734 }
735 }
736
737 $isRenderable = apply_filters('fluentform/is_form_renderable', [
738 'status' => true,
739 'message' => '',
740 ], $form);
741
742 if (is_array($isRenderable) && !$isRenderable['status'] && !Acl::hasAnyFormPermission($formId)) {
743
744 echo wp_kses_post("<div style='text-align: center; font-size: 16px; margin: 100px 20px;' id='ff_form_{$form->id}' class='ff_form_not_render'>" . $isRenderable['message'] . "</div>");
745 exit(200);
746 }
747
748
749 /* This filter is deprecated and will be removed soon */
750 $form = wpFluentForm()->applyFilters('fluentform_rendering_form', $form);
751
752 $form = wpFluentForm()->applyFilters('fluentform/rendering_form', $form);
753 $fileUploadSettings = apply_filters('fluentform/file_upload_settings_for_js', [], $form);
754
755 $form = Converter::convert($form);
756
757 $this->enqueueScripts();
758 do_action('fluentform/conversational_enqueue_assets', $form, $fileUploadSettings);
759
760 $formSettings = wpFluent()
761 ->table('fluentform_form_meta')
762 ->where('form_id', $form->id)
763 ->where('meta_key', 'formSettings')
764 ->first();
765
766 if (!$formSettings) {
767 return '';
768 }
769
770 $form->settings = json_decode($formSettings->value, true);
771
772 $submitCss = $this->getSubmitBttnStyle($form);
773
774 $designSettings = $this->getDesignSettings($formId);
775
776 $localizedVars = [
777 'fluent_forms_admin_nonce' => wp_create_nonce('fluent_forms_admin_nonce'),
778 'ajaxurl' => admin_url('admin-ajax.php'),
779 'nonce' => wp_create_nonce(),
780 'form' => $this->getLocalizedForm($form),
781 'form_id' => $form->id,
782 'assetBaseUrl' => FLUENT_CONVERSATIONAL_FORM_DIR_URL . 'public',
783 'i18n' => $metaSettings['i18n'],
784 'design' => $designSettings,
785 'hasPro' => defined('FLUENTFORMPRO'),
786 'extra_inputs' => $this->getExtraHiddenInputs($formId),
787 'uploading_txt' => __('Uploading', 'fluentform'),
788 'upload_completed_txt' => __('100% Completed', 'fluentform'),
789 'unknown_error_txt' => __('An unknown error occurred', 'fluentform'),
790 'request_error_txt' => __('An error occurred while processing your request', 'fluentform'),
791 'paymentConfig' => $this->getPaymentConfig($form),
792 'date_i18n' => \FluentForm\App\Modules\Component\Component::getDatei18n(),
793 'rest' => Helper::getRestInfo(),
794 'file_delete_nonce' => wp_create_nonce('fluentform_file_delete'),
795 'file_upload_settings' => $fileUploadSettings,
796 ];
797
798 wp_localize_script(
799 'fluent_forms_conversational_form',
800 'fluent_forms_global_var',
801 apply_filters('fluentform/global_form_vars', $localizedVars)
802 );
803
804 $hasSaveProgressButton = false;
805 $saveProgressButton = [];
806 foreach ($form->fields['fields'] as $item) {
807 if (isset($item['element']) && $item['element'] === 'save_progress_button') {
808 $hasSaveProgressButton = true;
809 $saveProgressButton = $item;
810 }
811 }
812
813 if ($hasSaveProgressButton && $saveProgressButton) {
814 $this->localizeSaveProgressButton($saveProgressButton, $formId);
815 }
816
817 $this->printLoadedScripts();
818
819 $isRenderable = [
820 'status' => true,
821 'message' => '',
822 ];
823
824 /* This filter is deprecated and will be removed soon */
825 $isRenderable = apply_filters('fluentform_is_form_renderable', $isRenderable, $form);
826
827
828 $isRenderable = apply_filters('fluentform/is_form_renderable', $isRenderable, $form);
829
830 if (is_array($isRenderable) && !$isRenderable['status']) {
831 if (!Acl::hasAnyFormPermission($form->id)) {
832 echo wp_kses_post("<h1 style='width: 600px; margin: 200px auto; text-align: center;' id='ff_form_{$form->id}' class='ff_form_not_render'>" . $isRenderable['message'] . '</h1>');
833 exit();
834 }
835 }
836 /* This filter is deprecated and will be removed soon */
837 $status = apply_filters('fluentform-disabled_analytics', true);
838
839 if (!apply_filters('fluentform/disabled_analytics', $status)) {
840 if (!Acl::hasAnyFormPermission($form->id)) {
841 (new \FluentForm\App\Services\Analytics\AnalyticsService())->store($form->id);
842 }
843 }
844 wpFluentForm('view')->render('public.conversational-form', [
845 'generated_css' => $this->getGeneratedCss($formId),
846 'design' => $designSettings,
847 'submit_css' => $submitCss,
848 'form_id' => $formId,
849 'meta' => $metaSettings,
850 'form' => $form,
851 ]);
852
853 exit(200);
854 }
855
856 /**
857 * Enqueue proper stylesheet based on rtl & JS script.
858 */
859 private function enqueueScripts()
860 {
861 $cssFileName = 'conversationalForm';
862
863 if (is_rtl()) {
864 $cssFileName .= '-rtl';
865 }
866
867 wp_enqueue_style(
868 'fluent_forms_conversational_form',
869 FLUENT_CONVERSATIONAL_FORM_DIR_URL . 'public/css/' . $cssFileName . '.css',
870 [],
871 FLUENTFORM_VERSION
872 );
873
874 wp_enqueue_script(
875 'fluent_forms_conversational_form',
876 FLUENT_CONVERSATIONAL_FORM_DIR_URL . 'public/js/conversationalForm.js',
877 [],
878 FLUENTFORM_VERSION,
879 true
880 );
881 }
882
883 /**
884 * Get the payment configuration of this form.
885 *
886 * @param $form
887 */
888 private function getPaymentConfig($form)
889 {
890 $paymentConfig = null;
891
892 if ($form->has_payment) {
893 $publishableKeyStripe = StripeSettings::getPublishableKey($form->id);
894 $publishableKeyStripe = apply_filters_deprecated(
895 'fluentform-payment_stripe_publishable_key',
896 [
897 $publishableKeyStripe,
898 $form->id
899 ],
900 FLUENTFORM_FRAMEWORK_UPGRADE,
901 'fluentform/payment_stripe_publishable_key',
902 'Use fluentform/payment_stripe_publishable_key instead of fluentform-payment_stripe_publishable_key.'
903 );
904
905 $publishableKey = apply_filters(
906 'fluentform/payment_stripe_publishable_key',
907 $publishableKeyStripe,
908 $form->id
909 );
910
911 $paymentConfig = [
912 'currency_settings' => PaymentHelper::getCurrencyConfig($form->id),
913 'stripe' => [
914 'publishable_key' => $publishableKey,
915 'inlineConfig' => PaymentHelper::getStripeInlineConfig($form->id),
916 ],
917 'stripe_app_info' => [
918 'name' => 'Fluent Forms',
919 'version' => FLUENTFORM_VERSION,
920 'url' => site_url(),
921 'partner_id' => 'pp_partner_FN62GfRLM2Kx5d',
922 ],
923 'i18n' => [
924 'item' => __('Item', 'fluentform'),
925 'price' => __('Price', 'fluentform'),
926 'qty' => __('Qty', 'fluentform'),
927 'line_total' => __('Line Total', 'fluentform'),
928 'total' => __('Total', 'fluentform'),
929 'not_found' => __('No payment item selected yet', 'fluentform'),
930 'discount:' => __('Discount:', 'fluentform'),
931 'processing_text' => __('Processing payment. Please wait...', 'fluentform'),
932 'confirming_text' => __('Confirming payment. Please wait...', 'fluentform'),
933 'signup_fee_for' => __('Signup Fee for', 'fluentform'),
934 ],
935 ];
936
937 $paymentConfig['currency_settings']['currency_symbol'] = \html_entity_decode($paymentConfig['currency_settings']['currency_sign']);
938 }
939
940 return $paymentConfig;
941 }
942
943 protected function getAsteriskPlacement($formId)
944 {
945 $asteriskPlacement = 'asterisk-right';
946
947 $formSettings = wpFluent()
948 ->table('fluentform_form_meta')
949 ->where('form_id', $formId)
950 ->where('meta_key', 'formSettings')
951 ->first();
952
953 if (!$formSettings) {
954 return '';
955 }
956
957 $formSettings = json_decode($formSettings->value, true);
958
959 if (isset($formSettings['layout']['asteriskPlacement'])) {
960 $asteriskPlacement = $formSettings['layout']['asteriskPlacement'];
961 }
962
963 return $asteriskPlacement;
964 }
965
966 private function getLocalizedForm($form)
967 {
968 return [
969 'id' => $form->id,
970 'questions' => $form->questions,
971 'image_preloads' => $form->image_preloads,
972 'submit_button' => $form->submit_button,
973 'hasPayment' => (bool)$form->has_payment,
974 'hasCalculation' => (bool)$form->hasCalculation,
975 'reCaptcha' => $form->reCaptcha,
976 'hCaptcha' => $form->hCaptcha,
977 'turnstile' => $form->turnstile,
978 'has_per_step_save' => ArrayHelper::get($form->settings, 'conv_form_per_step_save', false),
979 'has_resume_from_last_step' => ArrayHelper::get($form->settings, 'conv_form_resume_from_last_step', false),
980 'has_save_link' => $form->save_state?? false,
981 'has_save_and_resume_button'=> $form->hasSaveAndResumeButton ?? false,
982 'step_completed' => $form->stepCompleted ?? 0
983 ];
984 }
985
986 public function localizeSaveProgressButton($field, $formId)
987 {
988 $vars = apply_filters('fluentform/save_progress_vars', [
989 'ajaxurl' => admin_url('admin-ajax.php'),
990 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in getFrontendFacingUrl()
991 'source_url' => Helper::getFrontendFacingUrl($_SERVER['REQUEST_URI']),
992 'form_id' => $formId,
993 'nonce' => wp_create_nonce(),
994 'copy_button' => fluentFormMix('img/copy.svg'),
995 'copy_success_button' => fluentFormMix('img/check.svg'),
996 'email_button' => fluentFormMix('img/email.svg'),
997 'email_placeholder_str' => __('Your Email Here', 'fluentform'),
998 'email_resume_link_enabled' => false,
999 'save_progress_btn_name' => ArrayHelper::get($field, 'attributes.name'),
1000 ]);
1001
1002 if (ArrayHelper::get($field, 'settings.email_resume_link_enabled')) {
1003 $vars['email_resume_link_enabled'] = true;
1004 }
1005
1006 wp_localize_script('fluent_forms_conversational_form', 'form_state_save_vars', $vars);
1007 }
1008 }
1009