PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.7.0
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.7.0
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Frontend/Form/FrontendFormHandler.php +94 -320 3.2.12.7.0 View file →
@@ -1,28 +1,19 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Frontend\Form;
4 4
5 -if (!defined('ABSPATH')) {
6 - exit;
7 -}
8 -
9 -use BitCode\BitForm\Admin\Form\AdminFormHandler;
10 5 use BitCode\BitForm\Admin\Form\FrontEndScriptGenerator;
11 -use BitCode\BitForm\Admin\Form\Helpers;
12 6 use BitCode\BitForm\Core\Database\FormEntryMetaModel;
13 7 use BitCode\BitForm\Core\Database\FormModel;
14 8 use BitCode\BitForm\Core\Form\FormManager;
15 9 use BitCode\BitForm\Core\Integration\IntegrationHandler;
16 -use BitCode\BitForm\Core\Util\EscapingHelper;
17 10 use BitCode\BitForm\Core\Util\FieldValueHandler;
18 11 use BitCode\BitForm\Core\Util\FileDownloadProvider;
19 12 use BitCode\BitForm\Core\Util\FrontendHelpers;
20 -use BitCode\BitForm\Core\Util\Log;
21 -use BitCode\BitForm\Core\Util\SmartTagRegistry;
22 -use BitCode\BitForm\Core\Util\SmartTags;
23 13 use BitCode\BitForm\Core\Util\Utilities;
24 14 use BitCode\BitForm\Core\WorkFlow\WorkFlow;
15 +use BitCode\BitFormPro\Admin\FormSettings\FormAbandonment;
25 16
26 17 final class FrontendFormHandler
27 18 {
28 19 public function __construct()
@@ -56,23 +47,21 @@
56 47 }
57 48
58 49 private function getJSFileSrc($postId)
59 50 {
60 - $formUpdateVersion = get_option('bitform_form_update_version');
51 + $formUpdateVersion = get_option('bit-form_form_update_version');
61 52 $formScriptSrc = BITFORMS_UPLOAD_BASE_URL . "/form-scripts/$postId/bitform-js-$postId.js?bfv=$formUpdateVersion";
62 53
63 54 return $formScriptSrc;
64 55 }
65 56
66 - public function generateJs($formID = null, $entryID = null, $formType = null)
57 + public function generateJs($formID = null, $entryID = null)
67 58 {
68 - // return true;
69 59 $isFormPreview = get_transient('bitform_form_preview');
70 60 if ($isFormPreview && !$formID) {
71 61 delete_transient('bitform_form_preview');
72 62 return;
73 63 }
74 - $frontendScriptGenObj = new FrontEndScriptGenerator();
75 64 $isPageBuilder = FrontendHelpers::checkIsPageBuilder($_SERVER);
76 65 $bfFrontendFormIds = FrontendHelpers::$bfFrontendFormIds;
77 66 if ($isPageBuilder || empty($bfFrontendFormIds)) {
78 67 return;
@@ -81,16 +70,15 @@
81 70 $allFields = [];
82 71 $formContents = [];
83 72 $contentIds = [];
84 73 $formIDs = [];
85 - $previewMode = 'classic';
74 + $preview = false;
86 75 $postId = '';
87 76
88 - $formUpdateVersion = get_option('bitform_form_update_version');
77 + $formUpdateVersion = get_option('bit-form_form_update_version');
89 78 if ($formID) {
90 79 $formIDs[] = $formID;
91 - $FrontendFormManager = FrontendFormManager::getInstance($formID, 1);
92 - $formInfo = $FrontendFormManager->getFormInfo();
80 + $FrontendFormManager = new FrontendFormManager($formID, 1);
93 81 $FormIdentifier = esc_js($FrontendFormManager->getFormIdentifier());
94 82 $formContent = $FrontendFormManager->getFormContentWithValue($this->getValuesFromQueryParams());
95 83 $formContent->formId = $formID;
96 84 $formContents[] = $formContent;
@@ -100,17 +88,12 @@
100 88 $fields = $this->setFieldsValue($fields, $formID, $entryID);
101 89 }
102 90 $fields = $this->triggerWorkflowOnLoad($formID, 1, $fields, $workFlowRunType);
103 91 array_push($contentIds, $FormIdentifier);
104 -
105 92 foreach ($fields as $fk => $field) {
106 93 $allFields[$field->typ][] = ['fk' => $fk, 'field' => $field, 'formID' => $formID, 'contentId' => $FormIdentifier];
107 94 }
108 - //Generate JS file for conversational form
109 - if (!empty($formInfo->conversationalSettings->enable) && $formInfo->conversationalSettings->enable) {
110 - $frontendScriptGenObj->generateJsFile([$formContent], $allFields, [$FormIdentifier], $formID, [$formID], 'conversational');
111 - }
112 - $previewMode = 'preview';
95 + $preview = true;
113 96 $postId = $formID;
114 97 } else {
115 98 global $post;
116 99 if (!is_object($post) && !isset($post->ID)) {
@@ -129,12 +112,12 @@
129 112 if (!$regenerateScriptFlag && !$isJsGenerating && !empty($formIDs)) {
130 113 wp_enqueue_script('bit-form-all-script-test', $this->getJSFileSrc($postId), [], $formUpdateVersion, true);
131 114 return;
132 115 }
116 +
133 117 foreach ($bfFrontendFormIds as $index => $formId) {
134 118 $shortCodeCounter = $index + 1;
135 - $FrontendFormManager = FrontendFormManager::getInstance($formId, $shortCodeCounter);
136 - $formInfo = $FrontendFormManager->getFormInfo();
119 + $FrontendFormManager = new FrontendFormManager($formId, $shortCodeCounter);
137 120 $FormIdentifier = esc_js($FrontendFormManager->getFormIdentifier());
138 121 $formContent = $FrontendFormManager->getFormContentWithValue($this->getValuesFromQueryParams());
139 122 $formContent->formId = $formId;
140 123 $formContents[] = $formContent;
@@ -139,28 +122,17 @@
139 122 $formContent->formId = $formId;
140 123 $formContents[] = $formContent;
141 124 $fields = $this->triggerWorkflowOnLoad($formId, $shortCodeCounter, $formContent->fields);
142 125 $contentIds[] = $FormIdentifier;
143 - $formFields = []; // indivisual form fields array for conversational view
144 126 foreach ($fields as $fk => $field) {
145 - $fieldArr = ['fk' => $fk, 'field' => $field, 'formID' => $formId, 'contentId' => $FormIdentifier];
146 - $allFields[$field->typ][] = $fieldArr;
147 - $formFields[$field->typ][] = $fieldArr;
127 + $allFields[$field->typ][] = ['fk' => $fk, 'field' => $field, 'formID' => $formId, 'contentId' => $FormIdentifier];
148 128 }
149 - //Generate JS file for conversational form
150 - if (!empty($formInfo->conversationalSettings->enable) && $formInfo->conversationalSettings->enable) {
151 - $frontendScriptGenObj->generateJsFile([$formContent], $formFields, [$FormIdentifier], $formId, [$formId], 'conversational');
152 - }
153 129 }
154 130 }
155 131 if (empty($formIDs)) {
156 132 return;
157 133 }
158 -
159 - $frontendScriptGenObj->generateJsFile($formContents, $allFields, $contentIds, $postId, $formIDs, $previewMode);
160 - if ('preview' === $previewMode) {
161 - return;
162 - }
134 + (new FrontEndScriptGenerator())->generateJsFile($formContents, $allFields, $contentIds, $postId, $formIDs, $preview);
163 135 wp_enqueue_script('bit-form-all-script-test', $this->getJSFileSrc($postId), [], $formUpdateVersion, true);
164 136 }
165 137
166 138 private function deleteUnusedFormPageIds($postId, $formIDs)
@@ -188,15 +160,15 @@
188 160 $formModel->update(['generated_script_page_ids' => wp_json_encode($generatedScriptPageIdsDecoded)], ['id' => $formId]);
189 161 }
190 162 }
191 163 if ($regenerateScriptFlag) {
192 - $formUpdateVersion = get_option('bitform_form_update_version');
164 + $formUpdateVersion = get_option('bit-form_form_update_version');
193 165 if (!$formUpdateVersion) {
194 166 $formUpdateVersion = 1;
195 167 } else {
196 168 $formUpdateVersion = (int) $formUpdateVersion + 1;
197 169 }
198 - update_option('bitform_form_update_version', $formUpdateVersion);
170 + update_option('bit-form_form_update_version', $formUpdateVersion);
199 171 }
200 172 return $regenerateScriptFlag;
201 173 }
202 174
@@ -209,12 +181,9 @@
209 181 $postId = $post->ID;
210 182 $regenerateScriptFlag = false;
211 183 $formModel = new FormModel();
212 184 foreach ($formsIds as $formId) {
213 - $formInstance = FormManager::getInstance($formId);
214 - if (!$formInstance->isExist()) {
215 - continue;
216 - }
185 + $formInstance = new FormManager($formId);
217 186 $generatedPages = $formInstance->getFormData('generated_script_page_ids');
218 187 if (empty($generatedPages)) {
219 188 $regenerateScriptFlag = true;
220 189 } elseif (is_object($generatedPages) && (!isset($generatedPages->{$postId}) || (isset($generatedPages->{$postId}) && false === $generatedPages->{$postId}))) {
@@ -241,11 +210,10 @@
241 210
242 211 private function addInlineScript($code, $handle = '', $position = 'after')
243 212 {
244 213 $scriptHandle = !empty($handle) ? $handle : 'bf-inline-script';
245 - $formUpdateVersion = get_option('bitform_form_update_version');
246 214 if (!wp_script_is($scriptHandle)) {
247 - wp_register_script($scriptHandle, '', [], $formUpdateVersion, true);
215 + wp_register_script($scriptHandle, '', [], '', true);
248 216 wp_enqueue_script($scriptHandle);
249 217 }
250 218 wp_add_inline_script($scriptHandle, $code, $position);
251 219 }
@@ -252,11 +220,10 @@
252 220
253 221 private function addInlineStyle($code, $handle = '')
254 222 {
255 223 $styleHandle = !empty($handle) ? $handle : 'bf-inline-style';
256 - $formUpdateVersion = get_option('bitform_form_update_version');
257 224 if (!wp_style_is($styleHandle)) {
258 - wp_register_style($styleHandle, '', [], $formUpdateVersion);
225 + wp_register_style($styleHandle, '', [], '', true);
259 226 wp_enqueue_style($styleHandle);
260 227 }
261 228 wp_add_inline_style($styleHandle, $code);
262 229 }
@@ -282,37 +249,37 @@
282 249
283 250 return $fields;
284 251 }
285 252
286 - private function executeOnUserInput($formID, $shortCodeCounter, $workFlowRunType = 'create')
253 + private function executeOnUserInput($formID, $shortCodeCounter, $fields)
287 254 {
288 - $FrontendFormManager = FrontendFormManager::getInstance($formID, $shortCodeCounter);
255 + $FrontendFormManager = new FrontendFormManager($formID, $shortCodeCounter);
289 256 $previousValue = $this->getValuesFromQueryParams();
290 257 $formContent = $FrontendFormManager->getFormContentWithValue($previousValue);
291 258 $customCodesExist = strpos(FrontEndScriptGenerator::getCustomCodes($formID)['JavaScript'], 'bfVars');
292 259 if ($customCodesExist || (!empty($formContent->workFlowExist) && !empty($formContent->workFlowExist->oninput))) {
293 260 $workFlowRunHelper = new WorkFlow($formID);
294 - return $workFlowRunHelper->executeOnUserInput($workFlowRunType);
261 + return $workFlowRunHelper->executeOnUserInput('create', $fields);
295 262 }
296 263 }
297 264
298 265 private function getValuesFromQueryParams()
299 266 {
300 - // Read-only: query string parsed to pre-fill form fields. Values are sanitized per field before use.
267 + $reqField = $_SERVER['QUERY_STRING'];
301 268 $queryParamsValue = [];
302 - if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
303 - $reqField = wp_unslash($_SERVER['QUERY_STRING']);
269 + if (!empty($reqField)) {
304 270 foreach (explode('&', $reqField) as $keyValue) {
305 - if (false !== strpos($keyValue, '=')) {
271 + // $pattern = '/([a-zA-Z0-9])([a-zA-Z])\=+/';
272 + $pattern = '/([^.]+)=(.*?)([^.]+)/';
273 + $matches = preg_match($pattern, $keyValue, $matchFormat);
274 + if ($matches) {
306 275 list($field, $value) = explode('=', $keyValue, 2);
307 276
308 277 if (!trim($value)) {
309 278 continue;
310 279 }
311 - $field = sanitize_text_field(urldecode($field));
312 - if (!empty($field)) {
313 - $queryParamsValue[$field][] = sanitize_text_field(urldecode($value));
314 - }
280 +
281 + $queryParamsValue[$field][] = sanitize_text_field(urldecode($value));
315 282 }
316 283 }
317 284 }
318 285
@@ -320,9 +287,8 @@
320 287 }
321 288
322 289 public function handleFrontendRenderRequest($atts)
323 290 {
324 - $formType = isset($atts['type']) ? $atts['type'] : 'classic';
325 291 $formPreview = isset($atts['form_preview']) ? $atts['form_preview'] : false;
326 292 if (isset($atts['form_id'])) {
327 293 $formID = intval($atts['form_id']);
328 294 }
@@ -327,11 +293,8 @@
327 293 $formID = intval($atts['form_id']);
328 294 }
329 295 if (isset($atts['entry_id'])) {
330 296 $entryId = intval($atts['entry_id']);
331 - // Read-only: entry ID from query string for shortcode render. No state mutation.
332 - } elseif (isset($_GET['bf_entry_id']) && !is_array($_GET['bf_entry_id'])) {
333 - $entryId = intval(sanitize_text_field(wp_unslash($_GET['bf_entry_id'])));
334 297 } else {
335 298 $entryId = false;
336 299 }
337 300 if (isset($atts['id'])) {
@@ -343,26 +306,28 @@
343 306 return __('Form ID cannot be empty', 'bit-form');
344 307 }
345 308
346 309 if (!$this->isExist($formID)) {
347 - /* translators: %s: form ID */
348 310 return sprintf(__('#%s no. Form doesn\'t exists', 'bit-form'), $formID);
349 311 }
350 312
351 - // Add-ons may detect whether the current visitor is resuming an abandoned entry.
352 - $isAbandoned = (bool) apply_filters('bitform_is_abandoned_entry', false, $formID, $entryId, $atts);
313 + // check for abandoned form entry id
314 + $isAbandoned = false;
315 + if (empty($entryId) && Utilities::isPro() && class_exists('\BitCode\BitFormPro\Admin\FormSettings\FormAbandonment')) {
316 + $FormAbandonment = new FormAbandonment($formID);
317 + $isAbandoned = $FormAbandonment->checkAbandonedFormEntryId();
318 + }
353 319
354 320 FrontendHelpers::setBfFrontendFormIds($formID);
355 321 $bfFrontendFormIds = FrontendHelpers::$bfFrontendFormIds;
356 322 $shortCodeCounter = count($bfFrontendFormIds);
357 - $FrontendFormManager = FrontendFormManager::getInstance($formID, $shortCodeCounter);
323 + $FrontendFormManager = new FrontendFormManager($formID, $shortCodeCounter);
358 324
359 325 if (!$FrontendFormManager->checkStatus()) {
360 - /* translators: %s: form ID */
361 326 return sprintf(__('#%s no. Form is not active', 'bit-form'), $formID);
362 327 }
363 328 ob_start();
364 - $this->loadAssets($formID, $formType);
329 + $this->loadAssets($formID);
365 330
366 331 $font = $FrontendFormManager->getFont();
367 332
368 333 if ($font && !$formPreview) {
@@ -368,11 +333,10 @@
368 333 if ($font && !$formPreview) {
369 334 wp_enqueue_style('bf-google-font', $font, '1.0.0', true);
370 335 }
371 336
372 - // Read-only: password reset token from URL for display-time validation. No state written until form is submitted.
373 337 if (!empty($_GET['token']) && !empty($_GET['id'])) {
374 - $this->validPassowordResetToken(sanitize_text_field(wp_unslash($_GET['token'])), sanitize_text_field(wp_unslash($_GET['id'])), $formID);
338 + $this->validPassowordResetToken($_GET['token'], $_GET['id'], $formID);
375 339 }
376 340
377 341 $previousValue = $this->getValuesFromQueryParams();
378 342 $errorMessages = []; // delete
@@ -384,42 +348,24 @@
384 348
385 349 $formContent = $FrontendFormManager->getFormContentWithValue($previousValue);
386 350 $fields = $formContent->fields;
387 351 $layout = $formContent->layout;
388 - $nestedLayout = isset($formContent->nestedLayout) ? $formContent->nestedLayout : (object) [];
352 + $nestedLayout = $formContent->nestedLayout;
389 353 $buttons = !empty($formContent->buttons) ? $formContent->buttons : '';
390 354 $additional = $formContent->additional;
391 355
392 - // $workFlowRunType = $entryId ? 'edit' : 'create';
393 - if ($entryId && (FrontendHelpers::is_current_user_can_access($formID, 'entryEditAccess'))) {
394 - $workFlowRunType = 'edit';
395 - $adminFormHandler = new AdminFormHandler();
396 - $getEntry = $adminFormHandler->getSingleEntry($formID, $entryId);
397 - if (FrontendHelpers::is_current_user_can_access($formID, 'entryEditAccess', '', $getEntry->__user_id)) {
398 - $fields = $this->setFieldsValue($fields, $formID, $entryId);
399 - } elseif (!$isAbandoned) {
400 - $entryId = false;
401 - $workFlowRunType = 'create';
402 - }
403 - } else {
404 - $entryId = false;
405 - $workFlowRunType = 'create';
356 + $workFlowRunType = $entryId ? 'edit' : 'create';
357 + if ($entryId) {
358 + $fields = $this->setFieldsValue($fields, $formID, $entryId);
406 359 }
407 360
408 - // if ($entryId) {
409 - // $fields = $this->setFieldsValue($fields, $formID, $entryId);
410 - // }
411 -
412 - $fields = apply_filters('bitform_filter_before_workflow_onload_fields', $fields, $formID);
413 361 $fields = $this->triggerWorkflowOnLoad($formID, $shortCodeCounter, $fields, $workFlowRunType);
414 - $fields = apply_filters('bitform_filter_after_workflow_onload_fields', $fields, $formID);
415 - do_action('bitform_onload_fields', $fields, $formID);
416 - $workFlowreturnedOnUserInput = $this->executeOnUserInput($formID, $shortCodeCounter, $workFlowRunType);
362 + $workFlowreturnedOnUserInput = $this->executeOnUserInput($formID, $shortCodeCounter, $fields);
417 363
418 364 // test for form before remove
419 - $noLabelFieldTypes = ['decision-box', 'gdpr', 'html', 'shortcode', 'button', 'paypal', 'razorpay', 'recaptcha', 'turnstile', 'hcaptcha', 'stripe', 'spacer'];
365 + $noLabel = ['decision-box', 'html', 'button', 'paypal', 'razorpay', 'recaptcha'];
420 366 foreach ($fields as $fldKey => $field) {
421 - if (!in_array($field->typ, $noLabelFieldTypes) && isset($field->lbl)) {
367 + if (!in_array($field->typ, $noLabel) && isset($field->lbl)) {
422 368 $lblReplaceToBackslash = str_replace('$_bf_$', '\\', $field->lbl);
423 369 $fields->{$fldKey}->lbl = FieldValueHandler::replaceSmartTagWithValue($lblReplaceToBackslash);
424 370 }
425 371 }
@@ -425,9 +371,9 @@
425 371 }
426 372 $fieldsKey = $FrontendFormManager->getFieldsKey();
427 373
428 374 $captchaV3Settings = $FrontendFormManager->getCaptchaV3Settings();
429 - if ($FrontendFormManager->getCaptchaSettings() || $captchaV3Settings || $FrontendFormManager->getTurnstileSettings() || $FrontendFormManager->isFieldTypeExist('hcaptcha')) {
375 + if ($FrontendFormManager->getCaptchaSettings() || $captchaV3Settings) {
430 376 $integrationHandler = new IntegrationHandler(0);
431 377 $allFormIntegrations = $integrationHandler->getAllIntegration('app');
432 378 if (!is_wp_error($allFormIntegrations)) {
433 379 foreach ($allFormIntegrations as $integration) {
@@ -435,42 +381,20 @@
435 381 $FrontendFormManager->getCaptchaSettings()
436 382 && !is_null($integration->integration_type)
437 383 && 'gReCaptcha' === $integration->integration_type
438 384 ) {
439 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
440 - if ($integrationDetails) {
441 - $integrationDetails->id = $integration->id;
442 - $reCAPTCHA = $integrationDetails;
443 - $reCAPTCHAVersion = 'v2';
444 - }
385 + $integrationDetails = json_decode($integration->integration_details);
386 + $integrationDetails->id = $integration->id;
387 + $reCAPTCHA = $integrationDetails;
388 + $reCAPTCHAVersion = 'v2';
445 389 }
446 390
447 - if (
448 - $FrontendFormManager->getTurnstileSettings()
449 - && !is_null($integration->integration_type)
450 - && 'turnstileCaptcha' === $integration->integration_type
451 - ) {
452 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
453 - $turnstileSiteKey = $integrationDetails->siteKey ?? '';
454 - }
455 -
456 - if (
457 - $FrontendFormManager->isFieldTypeExist('hcaptcha')
458 - && !is_null($integration->integration_type)
459 - && 'hcaptcha' === $integration->integration_type
460 - ) {
461 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
462 - $hCaptchaSiteKey = $integrationDetails->siteKey ?? '';
463 - }
464 -
465 391 if ($captchaV3Settings) {
466 392 if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
467 - $integrationDetails = Utilities::jsonObj($integration->integration_details);
468 - if ($integrationDetails) {
469 - $integrationDetails->id = $integration->id;
470 - $reCAPTCHA = $integrationDetails;
471 - $reCAPTCHAVersion = 'v3';
472 - }
393 + $integrationDetails = json_decode($integration->integration_details);
394 + $integrationDetails->id = $integration->id;
395 + $reCAPTCHA = $integrationDetails;
396 + $reCAPTCHAVersion = 'v3';
473 397 }
474 398 }
475 399 }
476 400 }
@@ -478,11 +402,9 @@
478 402
479 403 if ($captchaV3Settings && !empty($reCAPTCHA->siteKey)) {
480 404 // DANGER: no matter what, DONT CHANGE THE SCRIPT ID OF THIS SCRIPT
481 405 $scriptId = BITFORMS_PREFIX . 'recaptcha';
482 - // External Google reCAPTCHA script; version managed by URL query param. Loaded in header because
483 - // standalone form views do not render wp_footer(), making footer enqueue unreliable.
484 - wp_enqueue_script($scriptId, "https://www.google.com/recaptcha/api.js?render={$reCAPTCHA->siteKey}", [], null, false);
406 + wp_enqueue_script($scriptId, "https://www.google.com/recaptcha/api.js?render={$reCAPTCHA->siteKey}");
485 407 }
486 408
487 409 $configs = [
488 410 'bf_separator' => BITFORMS_BF_SEPARATOR,
@@ -499,24 +421,22 @@
499 421
500 422 if (!empty($paymentFieldData)) {
501 423 $integrationHandler = new IntegrationHandler(0);
502 424 foreach ($paymentFieldData as $fldKey => $fldData) {
503 - $paymentIntegration = $integrationHandler->getAIntegration($fldData->payIntegID);
504 - if (is_wp_error($paymentIntegration)) {
505 - continue;
506 - }
507 - $paymentIntegrationRow = Utilities::firstRow($paymentIntegration);
508 425 if ('paypal' === $fldData->typ) {
509 - $integrationDetails = Utilities::jsonObj($paymentIntegrationRow->integration_details ?? '');
510 - $clientID = $integrationDetails->clientID ?? '';
426 + $paypalIntegration = $integrationHandler->getAIntegration($fldData->payIntegID);
427 + $integrationDetails = json_decode($paypalIntegration[0]->integration_details);
428 + $clientID = $integrationDetails->clientID;
511 429 $fields->{$fldKey}->clientId = $clientID;
512 430 } elseif ('razorpay' === $fldData->typ) {
513 - $integrationDetails = Utilities::jsonObj($paymentIntegrationRow->integration_details ?? '');
514 - $clientID = $integrationDetails->apiKey ?? '';
431 + $razorpayIntegration = $integrationHandler->getAIntegration($fldData->payIntegID);
432 + $integrationDetails = json_decode($razorpayIntegration[0]->integration_details);
433 + $clientID = $integrationDetails->apiKey;
515 434 $fields->{$fldKey}->clientId = $clientID;
516 435 } elseif ('stripe' === $fldData->typ) {
517 - $integrationDetails = Utilities::jsonObj($paymentIntegrationRow->integration_details ?? '');
518 - $publishableKey = $integrationDetails->publishableKey ?? '';
436 + $stripeIntegration = $integrationHandler->getAIntegration($fldData->payIntegID);
437 + $integrationDetails = json_decode($stripeIntegration[0]->integration_details);
438 + $publishableKey = $integrationDetails->publishableKey;
519 439 $fields->{$fldKey}->publishableKey = $publishableKey;
520 440 }
521 441 }
522 442 }
@@ -536,14 +456,12 @@
536 456 'appID' => "bitforms_{$formID}",
537 457 'GCLID' => $FrontendFormManager->isGCLIDEnabled(),
538 458 'assetUrl' => BITFORMS_ASSET_URI,
539 459 'onfieldCondition' => !empty($workFlowreturnedOnUserInput['onfield_input_conditions']) ? $workFlowreturnedOnUserInput['onfield_input_conditions'] : false,
540 - 'smartTags' => $this->buildFrontendSmartTags($formID, $workFlowreturnedOnUserInput, $fields),
460 + 'smartTags' => !empty($workFlowreturnedOnUserInput['smart_tags']) ? $workFlowreturnedOnUserInput['smart_tags'] : [],
541 461 'paymentCallbackUrl' => get_rest_url() . 'bitform/v1/payments/razorpay',
542 462 'gRecaptchaSiteKey' => !empty($reCAPTCHA->siteKey) ? $reCAPTCHA->siteKey : null,
543 463 'gRecaptchaVersion' => !empty($reCAPTCHAVersion) ? $reCAPTCHAVersion : null,
544 - 'turnstileSiteKey' => !empty($turnstileSiteKey) ? $turnstileSiteKey : null,
545 - 'hCaptchaSiteKey' => !empty($hCaptchaSiteKey) ? $hCaptchaSiteKey : null,
546 464 ];
547 465
548 466 if ($entryId) {
549 467 $bitFormFrontArr['entryId'] = $entryId;
@@ -555,15 +473,14 @@
555 473
556 474 if (!empty($isAbandoned)) {
557 475 $bitFormFrontArr['oldValues'] = $this->getFieldsValue($formID, $isAbandoned);
558 476 if (empty($entryId)) {
559 - $bitFormFrontArr['entryId'] = $entryId;
477 + $bitFormFrontArr['entryId'] = $isAbandoned;
560 478 }
561 479 }
562 480
563 - $formInfo = $FrontendFormManager->getFormInfo();
564 - $bitFormFrontArr['formName'] = $formInfo->formName ?? '';
565 481 if (is_array($layout) && count($layout) > 1) {
482 + $formInfo = $FrontendFormManager->getFormInfo();
566 483 $multiStepSettings = isset($formInfo->multiStepSettings) ? $formInfo->multiStepSettings : null;
567 484 $newTempSettings = (object) [
568 485 'validateOnStepChange' => isset($multiStepSettings->validateOnStepChange) ? $multiStepSettings->validateOnStepChange : false,
569 486 'maintainStepHistory' => isset($multiStepSettings->maintainStepHistory) ? $multiStepSettings->maintainStepHistory : false,
@@ -574,46 +491,30 @@
574 491 'multiStepSettings' => $newTempSettings
575 492 ];
576 493 }
577 494
578 - if (Helpers::property_exists_nested($formInfo, 'conversationalSettings->enable', true)) {
579 - if (!isset($bitFormFrontArr['formInfo'])) {
580 - $bitFormFrontArr['formInfo'] = new \stdClass();
581 - }
582 - $bitFormFrontArr['formInfo']->conversationalSettings = $formInfo->conversationalSettings;
583 - }
495 + $bitFormsFront = apply_filters(
496 + 'bitforms_localized_script',
497 + $bitFormFrontArr
498 + );
584 499
585 - $formAbandonmentSettings = $FrontendFormManager->getFormAbandonmentSettings();
586 - if (Helpers::property_exists_nested($formAbandonmentSettings, 'active', true)) {
587 - $bitFormFrontArr['formSettings'] = (object)[
588 - 'formAbandonment' => $formAbandonmentSettings
589 - ];
590 - }
500 + $layout = \json_encode($layout);
501 + $buttons = \json_encode($buttons);
502 + $frontArr = json_encode($bitFormFrontArr);
591 503
592 - $layout = wp_json_encode($layout);
593 - $buttons = wp_json_encode($buttons);
594 - $frontArr = wp_json_encode($bitFormFrontArr);
595 -
596 - $bfGlobals = sprintf('
504 + $bfGlobals = <<<BFGLOBALS
597 505 if(!window.bf_globals) {
598 506 window.bf_globals = {}
599 - } if(!window.bf_globals.%1$s) {
600 - window.bf_globals.%1$s = {}
507 + } if(!window.bf_globals.{$FormIdentifier}) {
508 + window.bf_globals.{$FormIdentifier} = {}
601 509 }
602 - if(document.getElementById("%1$s")) {
603 - window.bf_globals.%1$s = {
604 - ...window.bf_globals.%1$s,
605 - ...%2$s
606 - };
607 - }', $FormIdentifier, $frontArr);
510 + if(document.getElementById('{$FormIdentifier}')) {
511 + window.bf_globals.{$FormIdentifier} = {...window.bf_globals.{$FormIdentifier}, ...{$frontArr}};
512 + }
513 +BFGLOBALS;
514 + $this->addInlineScript($bfGlobals, 'bit-form-all-script', 'before');
608 515
609 - if ('conversational' === $formType
610 - && isset($formContent->formInfo->conversationalSettings->enable)
611 - && $formContent->formInfo->conversationalSettings->enable) {
612 - $html = $FrontendFormManager->conversationalFormView($fields, $file, $errorMessages);
613 - } else {
614 - $html = $FrontendFormManager->formView($fields, $file, $errorMessages);
615 - }
516 + $html = $FrontendFormManager->formView($fields, $file, $errorMessages);
616 517
617 518 // if form preview then return html otherwise echo with output buffer
618 519 if ($formPreview) {
619 520 ob_clean();
@@ -620,94 +521,14 @@
620 521 $formViewObject = new \stdClass();
621 522 $formViewObject->html = $html;
622 523 $formViewObject->font = $font;
623 524 $formViewObject->bfGlobals = $bfGlobals;
624 - $formViewObject->formContent = $formContent;
625 525 return $formViewObject;
626 526 }
627 -
628 - $bfGlobalsHandle = 'bitform-bf-globals-' . sanitize_key($FormIdentifier);
629 - $this->addInlineScript($bfGlobals, $bfGlobalsHandle, 'after');
630 - $this->emitShowPickerBridge();
631 -
632 - echo wp_kses(trim($html), EscapingHelper::getFormAllowedHtml($formContent));
527 + echo trim($html);
633 528 return ob_get_clean();
634 529 }
635 530
636 - /**
637 - * Build the smart-tag map exposed to the browser in window.bf_globals[formId].smartTags.
638 - *
639 - * Security: the legacy code shipped the ENTIRE ~43-tag map to every visitor, leaking
640 - * PII (admin/user/author email) and freezing per-visitor request data (IP, time,
641 - * browser, referer) into cacheable HTML. We now emit ONLY tags that are (a) actually
642 - * referenced by this form's client-evaluated surfaces — conditional logic, payment
643 - * notes, admin custom JS — AND (b) flagged frontend-safe in the registry (static/post
644 - * context only). Sensitive (identity) and request/visitor tags are never emitted; they
645 - * resolve server-side at submit time instead.
646 - *
647 - * @param int|string $formID
648 - * @param mixed $workflowConditions on-field input conditions (client-evaluated)
649 - * @param mixed $fields form fields object (carries payment notes, etc.)
650 - * @return array<string,string>
651 - */
652 - private function buildFrontendSmartTags($formID, $workflowConditions, $fields)
653 - {
654 - // Haystack = only surfaces the browser actually evaluates against smartTags.
655 - $haystack = wp_json_encode($workflowConditions) . ' ' . wp_json_encode($fields);
656 - $customJs = FrontEndScriptGenerator::getCustomCodes($formID)['JavaScript'];
657 - if (is_string($customJs) && '' !== $customJs) {
658 - $haystack .= ' ' . $customJs;
659 - }
660 -
661 - $ctx = SmartTags::getPostUserData();
662 - $frontendSmartTags = [];
663 - $referenced = [];
664 - foreach (SmartTags::smartTagFieldKeys() as $key) {
665 - if (!SmartTagRegistry::isFrontendExposable($key)) {
666 - continue; // identity / request / param tags never travel to the browser
667 - }
668 - // Match '${' . key prefix so keys containing spaces/slashes/commas are handled.
669 - if (false !== strpos($haystack, '${' . $key)) {
670 - $referenced[] = $key;
671 - $frontendSmartTags[$key] = SmartTagRegistry::resolve($key, $ctx);
672 - }
673 - }
674 -
675 - /**
676 - * Escape hatch: a site that genuinely needs an extra tag client-side can opt it
677 - * back in explicitly here, rather than core shipping everything by default.
678 - *
679 - * @param array<string,string> $frontendSmartTags resolved frontend-safe smart tags
680 - * @param int|string $formID
681 - * @param string[] $referenced keys detected in client surfaces
682 - */
683 - return apply_filters('bitform_frontend_smarttags', $frontendSmartTags, $formID, $referenced);
684 - }
685 -
686 - /**
687 - * Delegated listener that opens the native picker on date/time inputs marked
688 - * with data-bf-show-picker. Replaces the legacy hardcoded onclick attribute.
689 - * Registered as inline script once per request via wp_add_inline_script so
690 - * the markup never travels through wp_kses().
691 - */
692 - private function emitShowPickerBridge()
693 - {
694 - static $emitted = false;
695 - if ($emitted) {
696 - return;
697 - }
698 - $emitted = true;
699 - $code = 'if(!window.__bfShowPickerBound){window.__bfShowPickerBound=true;document.addEventListener("click",function(e){var t=e.target;if(t&&t.matches&&t.matches("input[data-bf-show-picker=\"1\"]")&&typeof t.showPicker==="function"){try{t.showPicker();}catch(_){}}});}';
700 - $this->addInlineScript($code, 'bitform-show-picker-bridge', 'after');
701 - }
702 -
703 - /**
704 - * Does this form row exist?
705 - *
706 - * @param int $formID
707 - *
708 - * @return bool
709 - */
710 531 private function isExist($formID)
711 532 {
712 533 $formModel = new FormModel();
713 534 $form = $formModel->get(
@@ -717,37 +538,16 @@
717 538 [
718 539 'id' => $formID,
719 540 ]
720 541 );
721 -
722 - if (is_wp_error($form)) {
723 - if ('result_empty' !== $form->get_error_code()) {
724 - Log::debug_log([
725 - 'message' => 'Form lookup failed — reported to the visitor as a missing form',
726 - 'formID' => $formID,
727 - 'code' => $form->get_error_code(),
728 - 'error' => $form->get_error_message(),
729 - ]);
730 - }
731 -
732 - return false;
542 + if (!is_wp_error($form)) {
543 + return true;
733 544 }
734 -
735 - if (empty($form)) {
736 - Log::debug_log([
737 - 'message' => 'Form lookup returned no rows without an error (is the form table present?)',
738 - 'formID' => $formID,
739 - ]);
740 -
741 - return false;
742 - }
743 -
744 - return true;
545 + return false;
745 546 }
746 547
747 548 private function getFieldsValue($formID, $entryID)
748 549 {
749 - $FrontendFormManager = FrontendFormManager::getInstance($formID, 1);
750 550 $formEntryModel = new FormEntryMetaModel();
751 551 $metaValues = $formEntryModel->get(
752 552 [
753 553 'meta_key',
@@ -756,9 +556,8 @@
756 556 [
757 557 'bitforms_form_entry_id' => $entryID,
758 558 ]
759 559 );
760 - $formFields = $FrontendFormManager->getFields();
761 560 $fldsData = (object) [];
762 561 if (!is_wp_error($metaValues)) {
763 562 foreach ($metaValues as $metaValue) {
764 563 $metaKey = $metaValue->meta_key;
@@ -776,11 +575,8 @@
776 575 if (!isset($fldsData->{$metaKey})) {
777 576 $fldsData->{$metaKey} = '';
778 577 }
779 578 $fldsData->{$metaKey} = $metaVal;
780 - if (isset($formFields[$metaKey]['type']) && in_array($formFields[$metaKey]['type'], ['file-up', 'advanced-file-up'])) {
781 - $fldsData->{$metaKey} = $metaValue->meta_value;
782 - }
783 579 }
784 580 }
785 581
786 582 return $fldsData;
@@ -798,23 +594,8 @@
798 594 'bitforms_form_entry_id' => $entryID,
799 595 ]
800 596 );
801 597 if (!is_wp_error($metaValues)) {
802 - $urlQuery = wp_parse_url(FileDownloadProvider::getBaseDownloadURL(), PHP_URL_QUERY);
803 - $baseDLURL = FileDownloadProvider::getBaseDownloadURL();
804 - $baseDLURL = empty($urlQuery) ? $baseDLURL . '?' : $baseDLURL . '&';
805 - $baseDLURL .= "formID={$formID}&entryID={$entryID}";
806 -
807 - foreach ($fields as $field) {
808 - if ('file-up' === $field->typ || 'advanced-file-up' === $field->typ) {
809 - if (!isset($field->config)) {
810 - $field->config = (object) [];
811 - } elseif (is_array($field->config)) {
812 - $field->config = (object) $field->config;
813 - }
814 - $field->config->baseDLURL = $baseDLURL;
815 - }
816 - }
817 598 foreach ($metaValues as $metaValue) {
818 599 $metaKey = $metaValue->meta_key;
819 600 $metaVal = $metaValue->meta_value;
820 601 // if meta value is array then convert to string
@@ -831,16 +612,21 @@
831 612 $fields->{$metaKey}->val = $metaVal;
832 613 if ('file-up' === $fields->{$metaKey}->typ || 'advanced-file-up' === $fields->{$metaKey}->typ) {
833 614 $fields->{$metaKey}->val = $metaValue->meta_value;
834 615 $fields->{$metaKey}->config->oldFiles = $metaValue->meta_value;
616 + $urlQuery = parse_url(FileDownloadProvider::getBaseDownloadURL(), PHP_URL_QUERY);
617 + $baseDLURL = FileDownloadProvider::getBaseDownloadURL();
618 + $baseDLURL = empty($urlQuery) ? $baseDLURL . '?' : $baseDLURL . '&';
619 + $fields->{$metaKey}->config->baseDLURL = $baseDLURL . "formID={$formID}&entryID={$entryID}";
835 620 }
836 621 }
837 622 }
838 623 }
624 +
839 625 return $fields;
840 626 }
841 627
842 - public function loadAssets($formID = 0, $fromType = 'classic')
628 + public function loadAssets($formID = 0)
843 629 {
844 630 $bfUniqFormIds = FrontendHelpers::getAllFormIdsInPage();
845 631 $isPageBuilder = FrontendHelpers::$isPageBuilder;
846 632 $bfMultipleFormsExists = $isPageBuilder ? true : count($bfUniqFormIds) > 1;
@@ -859,9 +645,9 @@
859 645 $newFormId = $formID . '-formid';
860 646 } else {
861 647 $newFormId = $formID;
862 648 }
863 - $formUpdateVersion = get_option('bitform_form_update_version');
649 + $formUpdateVersion = get_option('bit-form_form_update_version');
864 650 if (!wp_style_is('bitform-style-' . $newFormId) && is_readable(BITFORMS_CONTENT_DIR . '/form-styles/bitform-' . $newFormId . '.css')) {
865 651 wp_enqueue_style(
866 652 'bitform-style-' . $newFormId,
867 653 BITFORMS_UPLOAD_BASE_URL . "/form-styles/bitform-{$newFormId}.css",
@@ -869,9 +655,9 @@
869 655 $formUpdateVersion
870 656 );
871 657 if ($isPageBuilder) {
872 658 $formStyle = file_get_contents(BITFORMS_CONTENT_DIR . '/form-styles/bitform-' . $newFormId . '.css');
873 - echo '<style id="bitform-style-' . esc_attr((string) $newFormId) . '">' . wp_kses($formStyle, []) . '</style>';
659 + echo sprintf("<style id='bitform-style-{$newFormId}'>%s</style>", $formStyle);
874 660 }
875 661 }
876 662 if (!wp_style_is('bitform-style-custom-' . $formID) && is_readable(BITFORMS_CONTENT_DIR . '/form-styles/bitform-custom-' . $formID . '.css')) {
877 663 wp_enqueue_style(
@@ -881,21 +667,9 @@
881 667 $formUpdateVersion
882 668 );
883 669 if ($isPageBuilder) {
884 670 $formStyle = file_get_contents(BITFORMS_CONTENT_DIR . '/form-styles/bitform-custom-' . $formID . '.css');
885 - echo '<style id="bitform-style-custom-' . esc_attr((string) $formID) . '">' . wp_kses($formStyle, []) . '</style>';
886 - }
887 - }
888 - // load conversational form css
889 - if ('conversational' === $fromType) {
890 - if (!wp_style_is('bitform-conversational-style-' . $formID) &&
891 - is_readable(BITFORMS_CONTENT_DIR . "/form-styles/bitform-conversational-{$formID}.css")) {
892 - wp_enqueue_style(
893 - 'bitform-conversational-style',
894 - BITFORMS_UPLOAD_BASE_URL . "/form-styles/bitform-conversational-{$formID}.css",
895 - [],
896 - $formUpdateVersion
897 - );
671 + echo sprintf("<style id='bitform-style-custom-{$formID}'>%s</style>", $formStyle);
898 672 }
899 673 }
900 674 }
901 675 }