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

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

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