PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
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/Admin/Form/AdminFormHandler.php +855 -238 2.10.13.3.1 View file →
@@ -13,24 +13,24 @@
13 13 use BitCode\BitForm\Core\Database\FormEntryMetaModel;
14 14 use BitCode\BitForm\Core\Database\FormEntryModel;
15 15 use BitCode\BitForm\Core\Database\FormModel;
16 16 use BitCode\BitForm\Core\Database\IntegrationModel;
17 -use BitCode\BitForm\Core\Database\PdfTemplateModel;
18 17 use BitCode\BitForm\Core\Database\ReportsModel;
19 18 use BitCode\BitForm\Core\Database\SuccessMessageModel;
20 19 use BitCode\BitForm\Core\Database\WorkFlowModel;
20 +use BitCode\BitForm\Core\Form\FormManager;
21 21 use BitCode\BitForm\Core\Integration\IntegrationHandler;
22 22 use BitCode\BitForm\Core\Messages\EmailTemplateHandler;
23 -use BitCode\BitForm\Core\Messages\PdfTemplateHandler;
24 23 use BitCode\BitForm\Core\Messages\SuccessMessageHandler;
24 +use BitCode\BitForm\Core\Migration\MigrationHelper;
25 25 use BitCode\BitForm\Core\Util\FileHandler;
26 26 use BitCode\BitForm\Core\Util\FormDuplicateHelper;
27 27 use BitCode\BitForm\Core\Util\HttpHelper;
28 28 use BitCode\BitForm\Core\Util\IpTool;
29 29 use BitCode\BitForm\Core\Util\Log;
30 +use BitCode\BitForm\Core\Util\Utilities;
30 31 use BitCode\BitForm\Core\WorkFlow\WorkFlow;
31 32 use BitCode\BitForm\Core\WorkFlow\WorkFlowHandler;
32 -use BitCode\BitFormPro\Admin\AppSetting\Pdf;
33 33 use WP_Error;
34 34
35 35 class AdminFormHandler
36 36 {
@@ -71,14 +71,12 @@
71 71 $this->createFormStylesDirIfNotExists();
72 72
73 73 if (
74 74 file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
75 - && is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
75 + && wp_is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
76 76 ) {
77 - $createStyleFile = fopen(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName, 'w');
78 - fwrite($createStyleFile, $styles);
79 - fclose($createStyleFile);
80 - return true;
77 + $filePath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName;
78 + return FileHandler::writeFile($filePath, $styles);
81 79 }
82 80 return false;
83 81 }
84 82
@@ -88,9 +86,9 @@
88 86 $sm_width = 400;
89 87 $lg_styles = '';
90 88 $md_styles = '';
91 89 $sm_styles = '';
92 - for ($i = 0; $i < count($layout->lg); $i++) {
90 + for ($i = 0; $i < count($layout->lg ?? []); $i++) {
93 91 $fld = $layout->lg[$i];
94 92
95 93 if ($fld->w < 10) {
96 94 $lay_row_height = 43;
@@ -154,14 +152,12 @@
154 152 $this->createFormStylesDirIfNotExists();
155 153
156 154 if (
157 155 file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
158 - && is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
156 + && wp_is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
159 157 ) {
160 - $createStyleFile = fopen(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName, 'w');
161 - fwrite($createStyleFile, $formStyle);
162 - fclose($createStyleFile);
163 - return true;
158 + $filePath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName;
159 + return FileHandler::writeFile($filePath, $formStyle);
164 160 }
165 161 return false;
166 162 }
167 163
@@ -177,9 +173,9 @@
177 173
178 174 private function pdfFontDownload($fontName)
179 175 {
180 176 if (class_exists('\BitCode\BitFormPro\Admin\AppSetting\Pdf')) {
181 - Pdf::getInstance()->fontsDownload($fontName);
177 + \BitCode\BitFormPro\Admin\AppSetting\Pdf::getInstance()->fontsDownload($fontName);
182 178 }
183 179 return false;
184 180 }
185 181
@@ -195,17 +191,8 @@
195 191 if ($formManager->isExist()) {
196 192 return new WP_Error('empty_form', __('Error Occurred, Please Reload', 'bit-form'));
197 193 }
198 194
199 - if (!class_exists('BitCode\\BitFormPro\\Plugin')) {
200 - if (!empty($workFlows) && count($workFlows) > 2) {
201 - return new WP_Error(
202 - 'free_limit',
203 - __('You are allowed to add maximum 2 workflows ', 'bit-form')
204 - );
205 - }
206 - }
207 -
208 195 if (isset($post->formStyle) && $post->formStyle) {
209 196 $sheetName = 'bitform-' . $post->form_id . '.css';
210 197 $this->saveStyleSheet($post->formStyle, $sheetName);
211 198 }
@@ -211,18 +198,20 @@
211 198 }
212 199
213 200 if (isset($post->layoutChanged) && $post->layoutChanged) {
214 201 $sheetName = 'bitform-layout-' . $post->form_id . '.css';
215 - $rowHeight = (int)$post->rowHeight;
202 + $rowHeight = (int) $post->rowHeight;
216 203 $this->saveLayoutStyleSheet($post->layout, $sheetName, $rowHeight);
217 204 }
218 205
219 206 $fields = wp_unslash($post->fields);
207 + $fields = Helpers::replaceFieldsDefaultErrorMsg($fields);
220 208 // $fields = $post->fields;
221 209 $layout = wp_unslash($post->layout);
222 - $nestedLayout = isset($post->nestedLayout) ? wp_unslash($post->nestedLayout) : (object) [];
210 + $nestedLayout = isset($post->nestedLayouts) ? wp_unslash($post->nestedLayouts) : (object) [];
223 211 $formInfo = wp_unslash($post->formInfo);
224 212 $form_name = wp_unslash($post->form_name);
213 + $form_name = strlen($form_name) > 50 ? substr($form_name, 0, 50) : $form_name;
225 214 $formSettings = (object) wp_unslash($post->formSettings);
226 215 $atomic_class_map = isset($post->atomicClassMap) ? wp_unslash($post->atomicClassMap) : [];
227 216 $breakpointSize = wp_unslash($post->breakpointSize);
228 217 $style = $post->style;
@@ -236,8 +225,9 @@
236 225
237 226 $mailTem = $formSettings->mailTem;
238 227 $pdfTem = $formSettings->pdfTem;
239 228 $integrations = $formSettings->integrations;
229 + $formPermissions = $formSettings->formPermissions;
240 230
241 231 $user_details = static::$ipTool->getUserDetail();
242 232 if (empty($fields) || empty($layout)) {
243 233 return new WP_Error(
@@ -250,8 +240,12 @@
250 240 'empty_form',
251 241 __('Form Name Should Be Within 50 Characters', 'bit-form')
252 242 );
253 243 }
244 + // Guard: never persist orphan fields (in fields but in no layout) — they
245 + // render nothing yet their validation rules block submission. Covers old
246 + // clients and hand-edited imports (importAForm funnels through here).
247 + $this->pruneOrphanFields($fields, $nestedLayout, $layout, null, $post);
254 248 $form_content = [
255 249 'fields' => $fields,
256 250 'layout' => $layout,
257 251 'nestedLayout' => $nestedLayout,
@@ -264,8 +258,12 @@
264 258 if (!empty($formSettings->theme)) {
265 259 $form_content = array_merge($form_content, ['theme' => $formSettings->theme]);
266 260 }
267 261
262 + if (!empty($formSettings->formPermissions)) {
263 + $form_content = array_merge($form_content, ['formPermissions' => $formSettings->formPermissions]);
264 + }
265 +
268 266 if (!empty($additional)) {
269 267 $form_content = array_merge($form_content, ['additional' => $additional]);
270 268 }
271 269 if (!empty($workFlows)) {
@@ -313,10 +311,11 @@
313 311 return $save_status;
314 312 } elseif (!$save_status) {
315 313 return false;
316 314 } else {
317 - wp_mkdir_p(BITFORMS_UPLOAD_DIR . "/$save_status");
315 + FileHandler::createIndexFile(BITFORMS_UPLOAD_DIR . "/$save_status");
318 316 $formID = $save_status;
317 + do_action('bitform_admin_form_changed');
319 318 $integartionIDForWorkflow = [];
320 319 // Confiramtion Message [start]
321 320 if (!empty($formSettings->confirmation->type->successMsg)) {
322 321 $successMessages = $formSettings->confirmation->type->successMsg;
@@ -325,8 +324,11 @@
325 324 $savedID = $successMessageHandler->saveMessage($messageDetail);
326 325 if ($savedID) {
327 326 $msgIdx = empty($messageDetail->id) ? $messageKey : wp_json_encode(['id' => $messageDetail->id]);
328 327 $integartionIDForWorkflow['successMsg'][$msgIdx] = $savedID;
328 + if (!empty($messageDetail->clRef)) {
329 + $integartionIDForWorkflow['confirmationCLRef'][$messageDetail->clRef] = $savedID;
330 + }
329 331 $messageDetail->id = $savedID;
330 332 $style = $this->updateSuccessMessageClassName($style, $messageKey, $savedID);
331 333 $atomic_class_map = $this->updateSuccessMessageClassName($atomic_class_map, $messageKey, $savedID);
332 334 }
@@ -339,10 +341,10 @@
339 341 'builderSettings' => $builderSettings
340 342 ];
341 343 $update_status = static::$formModel->update(
342 344 [
343 - 'builder_helper_state' => \wp_json_encode($builder_helper_state),
344 - 'atomic_class_map' => $atomic_class_map,
345 + 'builder_helper_state' => \wp_json_encode($builder_helper_state),
346 + 'atomic_class_map' => $atomic_class_map,
345 347 ],
346 348 [
347 349 'id' => $formID,
348 350 ]
@@ -359,31 +361,55 @@
359 361 $emailTemplateHandler
360 362 = new EmailTemplateHandler($formID, $user_details);
361 363 foreach ($mailTem as $templateKey => $templateDetail) {
362 364 $savedID = $emailTemplateHandler->saveTemplate($templateDetail);
363 - if ($savedID) {
365 + if ($savedID && !is_wp_error($savedID)) {
364 366 $tempIdx = empty($templateDetail->id) ? $templateKey : wp_json_encode(['id' => $templateDetail->id]);
365 367 $integartionIDForWorkflow['mailNotify'][$tempIdx] = $savedID;
368 + if (!empty($templateDetail->clRef)) {
369 + $integartionIDForWorkflow['emailCLRef'][$templateDetail->clRef] = $savedID;
370 + }
371 + // return the new id (and drop the temp ref) so the frontend reconciles the template
372 + $templateDetail->id = $savedID;
373 + unset($templateDetail->clRef);
366 374 }
367 375 }
368 376 }
369 377 // Email Template [end] */
370 - // if (!empty($pdfTem)) {
371 - // $pdfTemplateHandler = new PdfTemplateHandler($formID);
372 - // foreach ($pdfTem as $templateKey => $templateDetail) {
373 - // $savedID = $pdfTemplateHandler->save($templateDetail);
374 378
375 - // $pdfSetting = $templateDetail->setting;
379 + // PDF Template [start]
380 + if (!empty($pdfTem) && class_exists('\BitCode\BitFormPro\Core\Messages\PdfTemplateHandler')) {
381 + $pdfTemplateHandler = new \BitCode\BitFormPro\Core\Messages\PdfTemplateHandler($formID);
382 + $workFlowString = '';
383 + if (!empty($workFlows)) {
384 + $workFlowString = wp_json_encode($workFlows);
385 + }
386 + foreach ($pdfTem as $templateDetail) {
387 + $savedID = $pdfTemplateHandler->save($templateDetail);
376 388
377 - // if( isset($pdfSetting->font->name) && !empty($pdfSetting->font->name)) {
378 - // $this->pdfFontDownload($pdfSetting->font->name);
379 - // }
380 - // // if ($savedID) {
381 - // // $tempIdx = empty($templateDetail->id) ? $templateKey : wp_json_encode(['id' => $templateDetail->id]);
382 - // // $integartionIDForWorkflow['mailNotify'][$tempIdx] = $savedID;
383 - // // }
384 - // }
385 - // }
389 + $pdfSetting = $templateDetail->setting;
390 +
391 + if (isset($pdfSetting->font->name) && !empty($pdfSetting->font->name)) {
392 + $this->pdfFontDownload($pdfSetting->font->name);
393 + }
394 + if (!empty($workFlowString)) {
395 + $workFlowString = str_replace('"pdfId":"{\"id\":\"' . $templateDetail->id . '\"}"', '"pdfId":"{\"id\":\"' . $savedID . '\"}"', $workFlowString);
396 + // Multi-PDF: remap ids inside "pdfIds":[...] segments only — email-template ids use
397 + // the same {\"id\":N} token shape, so a global replace would corrupt them.
398 + $workFlowString = preg_replace_callback(
399 + '/"pdfIds":\[[^\]]*\]/',
400 + function ($matches) use ($templateDetail, $savedID) {
401 + return str_replace('{\"id\":\"' . $templateDetail->id . '\"}', '{\"id\":\"' . $savedID . '\"}', $matches[0]);
402 + },
403 + $workFlowString
404 + );
405 + }
406 + }
407 + if (!empty($workFlowString) && !empty($workFlows)) {
408 + $workFlows = json_decode($workFlowString);
409 + }
410 + }
411 + // PDF Template [end] */
386 412 //Integration [start] */
387 413 $integrationHandler = new IntegrationHandler($formID, $user_details);
388 414 if (!empty($formSettings->confirmation->type)) {
389 415 $allIntegrations = $formSettings->confirmation->type;
@@ -388,8 +414,14 @@
388 414 if (!empty($formSettings->confirmation->type)) {
389 415 $allIntegrations = $formSettings->confirmation->type;
390 416 foreach ($allIntegrations as $integrationType => $integrationGroup) {
391 417 foreach ($integrationGroup as $singleIntegrationKey => $singleIntegrationDetail) {
418 + $integrationStatus = isset($singleIntegrationDetail->status) ? (int) $singleIntegrationDetail->status : 1;
419 + // Inline-CL temp ref for a brand-new redirect (reconciled to the real id for workflow rows).
420 + $clRef = isset($singleIntegrationDetail->clRef) ? $singleIntegrationDetail->clRef : null;
421 + if (is_object($singleIntegrationDetail)) {
422 + unset($singleIntegrationDetail->status, $singleIntegrationDetail->clRef);
423 + }
392 424 if (empty($singleIntegrationDetail->details)) {
393 425 $integrationName = $singleIntegrationDetail->title;
394 426 unset($singleIntegrationDetail->title, $singleIntegrationDetail->id);
395 427
@@ -399,14 +431,25 @@
399 431 $integrationName = $singleIntegrationDetail->title;
400 432 $integrationDetails = !is_string($singleIntegrationDetail->details) ?
401 433 wp_json_encode($singleIntegrationDetail->details) : $singleIntegrationDetail->details;
402 434 }
403 - $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
435 + $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form', $integrationStatus);
404 436 $integIdx = empty($singleIntegrationDetail->id) ? $savedID : wp_json_encode(['id' => $singleIntegrationDetail->id]);
405 437 $integartionIDForWorkflow[$integrationType][$integIdx] = $savedID;
438 + if (!empty($clRef) && 'redirectPage' === $integrationType) {
439 + $integartionIDForWorkflow['redirectCLRef'][$clRef] = $savedID;
440 + }
406 441 }
407 442 }
408 443 }
444 + if (isset($formSettings->formAbandonment)) {
445 + $formAbandonment = $formSettings->formAbandonment;
446 +
447 + $integrationName = 'formAbandonment';
448 + $integrationType = 'formAbandonment';
449 + $abandonmentIntegDetails = wp_json_encode($formAbandonment);
450 + $integrationHandler->saveIntegration($integrationName, $integrationType, $abandonmentIntegDetails, 'formAbandonment');
451 + }
409 452 if (!empty($integrations)) {
410 453 foreach ($integrations as $singleIntegrationKey => $singleIntegrationDetail) {
411 454 $integrationName = $singleIntegrationDetail->name;
412 455 unset($singleIntegrationDetail->name);
@@ -413,8 +456,11 @@
413 456 $integrationType = $singleIntegrationDetail->type;
414 457 unset($singleIntegrationDetail->type);
415 458 $singleIntegrationDetailID = empty($singleIntegrationDetail->id) ? null : $singleIntegrationDetail->id;
416 459 unset($singleIntegrationDetail->id);
460 + // Inline-CL temp ref for a brand-new integration (reconciled to the real id for workflow rows).
461 + $clRef = isset($singleIntegrationDetail->__bf_cl_ref) ? $singleIntegrationDetail->__bf_cl_ref : null;
462 + unset($singleIntegrationDetail->__bf_cl_ref);
417 463 if (empty($singleIntegrationDetail->details)) {
418 464 $integrationDetails = !is_string($singleIntegrationDetail) ?
419 465 wp_json_encode($singleIntegrationDetail) : $singleIntegrationDetail;
420 466 } else {
@@ -423,8 +469,11 @@
423 469 }
424 470 $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
425 471 $integIdx = empty($singleIntegrationDetailID) ? $singleIntegrationKey : wp_json_encode(['id' => $singleIntegrationDetailID]);
426 472 $integartionIDForWorkflow['integ'][$integIdx] = $savedID;
473 + if (!empty($clRef) && !is_wp_error($savedID)) {
474 + $integartionIDForWorkflow['integrationCLRef'][$clRef] = $savedID;
475 + }
427 476 }
428 477 }
429 478 //Integrations [end]
430 479 //wrokFlows [start]
@@ -444,9 +493,9 @@
444 493 // }
445 494 }
446 495 //wrokFlows [end]
447 496 $details = [
448 - 'report_name' => 'All Entries',
497 + 'report_name' => __('All Entries', 'bit-form'),
449 498 'hiddenColumns' => ['__user_id', '__user_ip', '__referer', '__user_device', '__created_at', '__updated_at'],
450 499 'pageSize' => 10,
451 500 'sortBy' => [],
452 501 'filters' => [],
@@ -475,8 +524,12 @@
475 524 $updated_data['form_content']['is_default'] = 1;
476 525 }
477 526 $updated_data['message'] = __('Form Saved successfully', 'bit-form');
478 527
528 + // Form and dependent rows are persisted; multilingual providers register
529 + // the form's translatable strings here.
530 + do_action('bitform_form_saved', $save_status);
531 +
479 532 return $updated_data;
480 533 }
481 534 }
482 535
@@ -506,13 +559,14 @@
506 559 if (property_exists($post, 'builderSettings')) {
507 560 $builder_helper_state->builderSettings = $post->builderSettings;
508 561 }
509 562 if (property_exists($post, 'atomicClassMap')) {
510 - $atomic_class_map = \wp_json_encode((object)$post->atomicClassMap);
563 + $atomic_class_map = \wp_json_encode((object) $post->atomicClassMap);
511 564 }
512 565
513 566 if (property_exists($post, 'fields') && null !== $post->fields && property_exists($post, 'layout') && $post->layout) {
514 567 $fields = wp_unslash($post->fields);
568 + $fields = Helpers::replaceFieldsDefaultErrorMsg($fields);
515 569 $layout = wp_unslash($post->layout);
516 570 $nestedLayout = wp_unslash($post->nestedLayouts);
517 571 $formInfo = wp_unslash($post->formInfo);
518 572 $formID = wp_unslash($post->id);
@@ -532,16 +586,10 @@
532 586 if (empty($fields) || empty($layout) || is_null($formID)) {
533 587 return new WP_Error('empty_form', 'Can not update empty form.');
534 588 }
535 589
536 - if (!class_exists('BitCode\\BitFormPro\\Plugin')) {
537 - if (count($workFlows) > 2) {
538 - return new WP_Error(
539 - 'free_limit',
540 - __('You are allowed to add maximum 2 workflows ', 'bit-form')
541 - );
542 - }
543 - }
590 + // Guard: never persist orphan fields (see createNewForm). Fail closed.
591 + $this->pruneOrphanFields($fields, $nestedLayout, $layout, $formID, $post);
544 592
545 593 $form_content = [
546 594 'fields' => $fields,
547 595 'layout' => $layout,
@@ -558,8 +606,12 @@
558 606 } */
559 607 if (!empty($formSettings->theme)) {
560 608 $form_content = array_merge($form_content, ['theme' => $formSettings->theme]);
561 609 }
610 +
611 + if (!empty($formSettings->formPermissions)) {
612 + $form_content = array_merge($form_content, ['formPermissions' => $formSettings->formPermissions]);
613 + }
562 614 if (!empty($additional)) {
563 615 $form_content = array_merge($form_content, ['additional' => $additional]);
564 616 }
565 617 $integartionIDForWorkflow = [];
@@ -567,8 +619,9 @@
567 619 $newData['integation'] = 0;
568 620 $newData['mailTemplate'] = 0;
569 621 $newData['workflow'] = 0;
570 622 $newData['reports'] = 0;
623 + $newData['pdfTemplate'] = 0;
571 624 // Confiramtion Message [start]
572 625 if (!empty($formSettings->confirmation->type->successMsg)) {
573 626 $successMessages = $formSettings->confirmation->type->successMsg;
574 627 $successMessageHandler
@@ -578,8 +631,11 @@
578 631 $savedID = $successMessageHandler->saveMessage($messageDetail);
579 632 if ($savedID) {
580 633 $newData['settingConfiramation'] = 1;
581 634 $integartionIDForWorkflow['successMsg'][$messageKey] = $savedID;
635 + if (!empty($messageDetail->clRef)) {
636 + $integartionIDForWorkflow['confirmationCLRef'][$messageDetail->clRef] = $savedID;
637 + }
582 638 $messageDetail->id = $savedID;
583 639 $builder_helper_state->style = $this->updateSuccessMessageClassName($builder_helper_state->style, $messageKey, $savedID);
584 640 if (isset($atomic_class_map)) {
585 641 $atomic_class_map = $this->updateSuccessMessageClassName($atomic_class_map, $messageKey, $savedID);
@@ -604,18 +660,29 @@
604 660 $newData['mailTemplate'] = 2;
605 661 } else {
606 662 $newData['mailTemplate'] = 1;
607 663 $integartionIDForWorkflow['mailTem'][$templateKey] = $savedID;
664 + if (!empty($templateDetail->clRef)) {
665 + $integartionIDForWorkflow['emailCLRef'][$templateDetail->clRef] = $savedID;
666 + }
667 + // return the new id (and drop the temp ref) so the frontend reconciles the template
668 + $templateDetail->id = $savedID;
669 + unset($templateDetail->clRef);
608 670 }
609 671 } else {
610 - $emailTemplateHandler->updateTemplate($templateDetail);
672 + $updated = $emailTemplateHandler->updateTemplate($templateDetail);
673 + if (is_wp_error($updated) && 'db_error' === $updated->get_error_code()) {
674 + $newData['mailTemplate'] = 2;
675 + } elseif (0 === $newData['mailTemplate']) {
676 + $newData['mailTemplate'] = 1;
677 + }
611 678 }
612 679 }
613 680 }
614 681 // return $formSettings;
615 682 // Email Template [end] */
616 - if (!empty($pdfTem)) {
617 - $pdfTemplateHandler = new PdfTemplateHandler($formID);
683 + if (!empty($pdfTem) && class_exists('\BitCode\BitFormPro\Core\Messages\PdfTemplateHandler')) {
684 + $pdfTemplateHandler = new \BitCode\BitFormPro\Core\Messages\PdfTemplateHandler($formID);
618 685
619 686 foreach ($pdfTem as $templateKey => $templateDetail) {
620 687 if (isset($templateDetail->setting->font->name)) {
621 688 $this->pdfFontDownload($templateDetail->setting->font->name);
@@ -635,8 +702,15 @@
635 702 if (!empty($formSettings->confirmation->type)) {
636 703 $allIntegrations = $formSettings->confirmation->type;
637 704 foreach ($allIntegrations as $integrationType => $integrationGroup) {
638 705 foreach ($integrationGroup as $singleIntegrationKey => $singleIntegrationDetail) {
706 + // Redirect (and webhook) enable/disable toggle persists to the integration status column.
707 + $integrationStatus = isset($singleIntegrationDetail->status) ? (int) $singleIntegrationDetail->status : 1;
708 + // Inline-CL temp ref for a brand-new redirect (reconciled to the real id for workflow rows).
709 + $clRef = isset($singleIntegrationDetail->clRef) ? $singleIntegrationDetail->clRef : null;
710 + if (is_object($singleIntegrationDetail)) {
711 + unset($singleIntegrationDetail->status, $singleIntegrationDetail->clRef);
712 + }
639 713 if (empty($singleIntegrationDetail->details)) {
640 714 $integrationName = $singleIntegrationDetail->title;
641 715 unset($singleIntegrationDetail->title);
642 716 $singleIntegrationDetailID = empty($singleIntegrationDetail->id) ? null : $singleIntegrationDetail->id;
@@ -650,13 +724,16 @@
650 724 $integrationDetails = !is_string($singleIntegrationDetail->details) ?
651 725 wp_json_encode($singleIntegrationDetail->details) : $singleIntegrationDetail->details;
652 726 }
653 727 if (empty($singleIntegrationDetailID)) {
654 - $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
728 + $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form', $integrationStatus);
655 729 $newData['settingConfiramation'] = 1;
656 730 $integartionIDForWorkflow[$integrationType][$singleIntegrationKey] = $savedID;
731 + if (!empty($clRef) && 'redirectPage' === $integrationType) {
732 + $integartionIDForWorkflow['redirectCLRef'][$clRef] = $savedID;
733 + }
657 734 } else {
658 - $integrationHandler->updateIntegration($singleIntegrationDetailID, $integrationName, $integrationType, $integrationDetails, 'form');
735 + $integrationHandler->updateIntegration($singleIntegrationDetailID, $integrationName, $integrationType, $integrationDetails, 'form', $integrationStatus);
659 736 }
660 737 }
661 738 }
662 739 }
@@ -667,8 +744,11 @@
667 744 $integrationType = $singleIntegrationDetail->type;
668 745 unset($singleIntegrationDetail->type);
669 746 $singleIntegrationDetailID = empty($singleIntegrationDetail->id) ? null : $singleIntegrationDetail->id;
670 747 unset($singleIntegrationDetail->id);
748 + // Inline-CL temp ref for a brand-new integration (reconciled to the real id for workflow rows).
749 + $clRef = isset($singleIntegrationDetail->__bf_cl_ref) ? $singleIntegrationDetail->__bf_cl_ref : null;
750 + unset($singleIntegrationDetail->__bf_cl_ref);
671 751 if (empty($singleIntegrationDetail->details)) {
672 752 $integrationDetails = !is_string($singleIntegrationDetail) ?
673 753 wp_json_encode($singleIntegrationDetail) : $singleIntegrationDetail;
674 754 } else {
@@ -677,8 +757,11 @@
677 757 }
678 758 if (empty($singleIntegrationDetailID)) {
679 759 $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
680 760 $integartionIDForWorkflow[$integrationType][$singleIntegrationKey] = $savedID;
761 + if (!empty($clRef) && !is_wp_error($savedID)) {
762 + $integartionIDForWorkflow['integrationCLRef'][$clRef] = $savedID;
763 + }
681 764 if (is_wp_error($savedID) && 'result_empty' !== $savedID->get_error_code()) {
682 765 $newData['integation'] = 2;
683 766 } else {
684 767 $newData['integation'] = 1;
@@ -702,8 +785,9 @@
702 785 }
703 786 if ('oninput' === $workFlow->action_type || ('always' === $workFlow->action_type && 'cond' === $workFlow->action_behaviour)) {
704 787 $workFlowExist['oninput'] = true;
705 788 }
789 + // $integartionIDForWorkflow = apply_filters('bitform_filter_integration_id', $workFlow, $formID, $index);
706 790 if (empty($workFlow->id)) {
707 791 $savedID = $workFlowHandler->saveworkFlow($workFlow, $integartionIDForWorkflow, $index);
708 792 if (is_wp_error($savedID) && 'result_empty' !== $savedID->get_error_code()) {
709 793 $newData['workflow'] = 2;
@@ -717,8 +801,11 @@
717 801 }
718 802 $form_content = array_merge($form_content, ['workFlowExist' => $workFlowExist]);
719 803 //wrokFlows [end]
720 804 //reports [start] */
805 + // read unconditionally after the reports block; a save without currentReport
806 + // must not trip an undefined-variable warning
807 + $reportIsDefault = null;
721 808 if (!empty($reports)) {
722 809 $reportsModel = new ReportsModel();
723 810 $fieldNames = [];
724 811 foreach ($fields as $key => $field) {
@@ -730,15 +817,19 @@
730 817
731 818 $fieldNames['__user_id'] = __('User', 'bit-form');
732 819 $fieldNames['__user_ip'] = __('IP address', 'bit-form');
733 820 // $fieldNames['__user_location'] = __('');
734 - $fieldNames['__referer'] = __('Refer URL');
735 - $fieldNames['__user_device'] = __('Device');
736 - $fieldNames['__created_at'] = __('Created Time');
737 - $fieldNames['__updated_at'] = __('Modified Time');
821 + $fieldNames['__referer'] = __('Refer URL', 'bit-form');
822 + $fieldNames['__user_device'] = __('Device', 'bit-form');
823 + $fieldNames['__created_at'] = __('Created Time', 'bit-form');
824 + $fieldNames['__updated_at'] = __('Modified Time', 'bit-form');
738 825
739 826 $reportIsDefault = null;
740 - if (isset($form_content['report_id'])) {
827 + // The client posts currentReport as {} whenever its report atom has not resolved yet. That
828 + // is an empty stdClass, which empty() above treats as non-empty, so we reach here with
829 + // nothing to save — and writing the validated result would replace the stored report's
830 + // column order, hidden columns, page size and name with an empty list. Leave the row alone.
831 + if (isset($form_content['report_id']) && ReportsModel::isValidatableReport($reports)) {
741 832 $validDateReport = $reportsModel->validateReportFields($reports, $fieldNames);
742 833 if (isset($reports->isDefault)) {
743 834 $reportIsDefault = $reports->isDefault;
744 835 } else {
@@ -787,9 +878,9 @@
787 878 'form_name' => $form_name,
788 879 'form_content' => $form_content,
789 880 'user_id' => $user_details['id'],
790 881 'user_ip' => $user_details['ip'],
791 - 'builder_helper_state' => \wp_json_encode((object)$builder_helper_state),
882 + 'builder_helper_state' => \wp_json_encode((object) $builder_helper_state),
792 883 'generated_script_page_ids' => \wp_json_encode((object) []),
793 884 'user_device' => $user_details['device'],
794 885 'updated_at' => $user_details['time'],
795 886 ];
@@ -795,15 +886,16 @@
795 886 ];
796 887 if (isset($atomic_class_map)) {
797 888 $updateData['atomic_class_map'] = $atomic_class_map;
798 889 }
799 - $formUpdateVersion = get_option('bit-form_form_update_version');
890 + $formUpdateVersion = get_option('bitform_form_update_version');
800 891 if (!$formUpdateVersion) {
801 892 $formUpdateVersion = 1;
802 893 } else {
803 894 $formUpdateVersion = (int) $formUpdateVersion + 1;
804 895 }
805 - update_option('bit-form_form_update_version', $formUpdateVersion);
896 + update_option('bitform_form_update_version', $formUpdateVersion);
897 + do_action('bitform_admin_form_changed');
806 898
807 899 $update_status = static::$formModel->update(
808 900 $updateData,
809 901 [
@@ -821,9 +913,9 @@
821 913 }
822 914
823 915 $updated_data = $this->getAForm(['id' => $formID], null);
824 916
825 - if (0 === $newData['settingConfiramation'] && 0 === $newData['mailTemplate'] && 0 === $newData['integation']) {
917 + if (0 === $newData['settingConfiramation'] && 0 === $newData['mailTemplate'] && 0 === $newData['integation'] && 0 === $newData['pdfTemplate']) {
826 918 unset($updated_data['formSettings']);
827 919 }
828 920 if (0 === $newData['integation']) {
829 921 unset($updated_data['integrations']);
@@ -835,13 +927,13 @@
835 927 unset($updated_data['mailTem']);
836 928 } elseif (2 === $newData['mailTemplate']) {
837 929 $errorIN .= empty($errorIN) ? 'mailTemplate' : ', mailTemplate';
838 930 }
839 - // if (0 === $newData['pdfTemplate']) {
840 - // unset($updated_data['pdfTem']);
841 - // } elseif (2 === $newData['pdfTemplate']) {
842 - // $errorIN .= empty($errorIN) ? 'pdfTemplate' : ', pdfTemplate';
843 - // }
931 + if (0 === $newData['pdfTemplate']) {
932 + unset($updated_data['pdfTem']);
933 + } elseif (2 === $newData['pdfTemplate']) {
934 + $errorIN .= empty($errorIN) ? 'pdfTemplate' : ', pdfTemplate';
935 + }
844 936 if (0 === $newData['workflow']) {
845 937 unset($updated_data['workFlows']);
846 938 } elseif (2 === $newData['workflow']) {
847 939 $errorIN .= empty($errorIN) ? 'workflow' : ', workflow';
@@ -848,9 +940,14 @@
848 940 }
849 941 if (2 === $newData['reports']) {
850 942 $errorIN .= empty($errorIN) ? 'reports' : ', reports';
851 943 }
944 + // See createNewForm(). Fired before the partial-failure return: the rows
945 + // that did save are already persisted, so their strings still need registering.
946 + do_action('bitform_form_saved', $formId);
947 +
852 948 if (!empty($errorIN)) {
949 + /* translators: %s: comma-separated list of areas where error occurred */
853 950 $updated_data['message'] = sprintf(__('Error Occured in saving %s', 'bit-form'), $errorIN);
854 951 return new WP_Error('Form update Error.', $updated_data);
855 952 }
856 953 if (null !== $reportIsDefault) {
@@ -855,30 +952,77 @@
855 952 }
856 953 if (null !== $reportIsDefault) {
857 954 $updated_data['form_content']['is_default'] = $reportIsDefault;
858 955 }
859 - $updated_data['message'] = __('Form updated successfully.', 'bitform');
956 + $updated_data['message'] = __('Form updated successfully.', 'bit-form');
957 +
860 958 return $updated_data;
861 959 }
862 960 }
863 961
962 + /**
963 + * Drop orphan fields (present in `fields` but in no layout) from form_content
964 + * before persisting, and remove now-childless nestedLayout entries.
965 + *
966 + * Entry meta is NEVER touched here — only explicit user deletes clear meta
967 + * (see setEmptyMetaValue). Fail closed: an unusable/partial layout, or a
968 + * client that flagged a failed pre-save layout sync (->skipOrphanPrune),
969 + * prunes nothing. Pruned keys are logged, never silently dropped.
970 + *
971 + * @param object|array $fields by ref; orphan keys removed
972 + * @param object|null $nestedLayout by ref; stale container entries removed
973 + * @param array|object $layout raw layout (single or multi-step)
974 + * @param int|string|null $formID for logging ('new' when absent)
975 + * @param object $post raw request; honors ->skipOrphanPrune
976 + */
977 + private function pruneOrphanFields(&$fields, &$nestedLayout, $layout, $formID, $post)
978 + {
979 + $formLabel = $formID ? $formID : 'new';
980 + if (isset($post->skipOrphanPrune) && $post->skipOrphanPrune) {
981 + Log::debug_log('Bit Form: orphan prune skipped (client reported failed layout sync). FormID=' . $formLabel);
982 + return;
983 + }
984 + $orphanFldKeys = FormManager::computeOrphanFieldKeys($layout, $nestedLayout, $fields);
985 + // null = unusable/partial layout (fail closed); [] = nothing to prune
986 + if (!is_array($orphanFldKeys) || empty($orphanFldKeys)) {
987 + return;
988 + }
989 + Log::debug_log('Bit Form: pruning orphan fields (absent from every layout). FormID=' . $formLabel . ' keys=' . implode(',', $orphanFldKeys));
990 + foreach ($orphanFldKeys as $orphanKey) {
991 + if (is_object($fields)) {
992 + unset($fields->{$orphanKey});
993 + } elseif (is_array($fields)) {
994 + unset($fields[$orphanKey]);
995 + }
996 + }
997 + // prune stale nestedLayout entries — containers that no longer exist in fields
998 + if (is_object($nestedLayout)) {
999 + foreach (array_keys(get_object_vars($nestedLayout)) as $nKey) {
1000 + $parentExists = is_object($fields) ? isset($fields->{$nKey}) : isset($fields[$nKey]);
1001 + if (!$parentExists) {
1002 + unset($nestedLayout->{$nKey});
1003 + }
1004 + }
1005 + }
1006 + }
1007 +
864 1008 public function setEmptyMetaValue($fieldkeys)
865 1009 {
866 - $sqlEscaped = array_map(function ($fld) {
867 - return "'" . esc_sql($fld) . "'";
868 - }, $fieldkeys);
869 - $deletedFldKey = implode(',', $sqlEscaped);
870 1010 global $wpdb;
871 - $tablename = $wpdb->prefix . 'bitforms_form_entrymeta';
872 - $sql = $wpdb->prepare("UPDATE $tablename SET meta_value = %s WHERE meta_key IN ( " . $deletedFldKey . ')', '');
873 - $wpdb->query($sql);
1011 + // Placeholders and arguments built dynamically from $fieldkeys; count matches at runtime.
1012 + $placeholders = implode(',', array_fill(0, \count($fieldkeys), '%s'));
1013 + $wpdb->query($wpdb->prepare(
1014 + "UPDATE `{$wpdb->prefix}bitforms_form_entrymeta` SET meta_value = %s WHERE meta_key IN ($placeholders)",
1015 + '',
1016 + ...$fieldkeys
1017 + ));
874 1018 }
875 1019
876 1020 public function changeFormStatus($Request, $post)
877 1021 {
878 1022 if (isset($Request['status']) && $Request['id']) {
879 - $status = wp_unslash($Request['status']);
880 - $id = wp_unslash($Request['id']);
1023 + $status = sanitize_text_field(wp_unslash($Request['status']));
1024 + $id = absint(wp_unslash($Request['id']));
881 1025 } else {
882 1026 $status = wp_unslash($post->status);
883 1027 $id = wp_unslash($post->id);
884 1028 }
@@ -912,10 +1056,10 @@
912 1056
913 1057 public function changeBulkFormStatus($Request, $post)
914 1058 {
915 1059 if (isset($Request['status']) && $Request['formID']) {
916 - $status = wp_unslash($Request['status']);
917 - $formID = wp_unslash($Request['formID']);
1060 + $status = sanitize_text_field(wp_unslash($Request['status']));
1061 + $formID = array_map('absint', wp_unslash($Request['formID']));
918 1062 } else {
919 1063 $status = wp_unslash($post->status);
920 1064 $formID = wp_unslash($post->formID);
921 1065 }
@@ -950,9 +1094,13 @@
950 1094 public function getAllForm()
951 1095 {
952 1096 global $wpdb;
953 1097
954 - $allForms = $wpdb->get_results("SELECT forms.id,forms.entries as fm_entries,forms.form_name,forms.status,forms.views,forms.created_at,COUNT(entries.id) as entries FROM `{$wpdb->prefix}bitforms_form` as forms LEFT JOIN `{$wpdb->prefix}bitforms_form_entries` as entries ON forms.id = entries.form_id GROUP BY forms.id");
1098 + // Direct query: table name interpolation only (no user input). $wpdb->prepare() cannot parameterize table names.
1099 + $allForms = $wpdb->get_results("SELECT forms.id,forms.entries as fm_entries,forms.form_name,forms.status,forms.views,forms.created_at,COUNT(entries.id) as entries
1100 + FROM `{$wpdb->prefix}bitforms_form` as forms
1101 + LEFT JOIN `{$wpdb->prefix}bitforms_form_entries` as entries ON forms.id = entries.form_id
1102 + GROUP BY forms.id");
955 1103
956 1104 if (is_wp_error($allForms)) {
957 1105 return $allForms;
958 1106 } elseif (!$allForms) {
@@ -963,10 +1111,10 @@
963 1111 }
964 1112
965 1113 public function getAForm($Request, $post)
966 1114 {
967 - if (isset($Request['id'])) {
968 - $formID = wp_unslash($Request['id']);
1115 + if (!empty($Request['id'])) {
1116 + $formID = absint(wp_unslash($Request['id']));
969 1117 } else {
970 1118 $formID = wp_unslash($post->id);
971 1119 }
972 1120 // return $post->fields;
@@ -985,9 +1133,9 @@
985 1133 'nestedLayout' => !empty($form_content->nestedLayout) ? $form_content->nestedLayout : (object) [],
986 1134 'formInfo' => !empty($form_content->formInfo) ? $form_content->formInfo : (object) ['formName' => $formManager->getFormName()],
987 1135 'fields' => $form_content->fields,
988 1136 'form_name' => $formManager->getFormName(),
989 - 'workFlowExist' => $form_content->workFlowExist,
1137 + 'workFlowExist' => isset($form_content->workFlowExist) ? $form_content->workFlowExist : [],
990 1138 'report_id' => isset($form_content->report_id) ? $form_content->report_id : null
991 1139 ];
992 1140 $successMessageHandler
993 1141 = new SuccessMessageHandler($formID);
@@ -1010,28 +1158,37 @@
1010 1158 if (!is_wp_error($emailTemplates)) {
1011 1159 foreach ($emailTemplates as $emailTemplatekey => $emailTemplatevalue) {
1012 1160 $mailTem[] =
1013 1161 [
1014 - 'id' => $emailTemplatevalue->id,
1015 - 'title' => $emailTemplatevalue->title,
1016 - 'sub' => $emailTemplatevalue->sub,
1017 - 'body' => $emailTemplatevalue->body,
1162 + 'id' => $emailTemplatevalue->id,
1163 + 'title' => $emailTemplatevalue->title,
1164 + 'sub' => $emailTemplatevalue->sub,
1165 + 'body' => $emailTemplatevalue->body,
1166 + 'status' => isset($emailTemplatevalue->status) ? (int) $emailTemplatevalue->status : 1,
1167 + 'config' => EmailTemplateHandler::normalizeConfig($emailTemplatevalue->config ?? null),
1018 1168 ];
1019 1169 }
1020 1170 }
1021 1171 // get all pdf template
1022 - $pdfTemplateHandler = new PdfTemplateHandler($formID);
1023 - $pdfTemplates = $pdfTemplateHandler->getAll();
1172 + if (class_exists('\BitCode\BitFormPro\Core\Messages\PdfTemplateHandler')) {
1173 + $pdfTemplateHandler = new \BitCode\BitFormPro\Core\Messages\PdfTemplateHandler($formID);
1174 + $pdfTemplates = $pdfTemplateHandler->getAll();
1024 1175
1025 - if (!is_wp_error($pdfTemplates)) {
1026 - foreach ($pdfTemplates as $value) {
1027 - $pdfTem[] =
1028 - [
1029 - 'id' => $value->id,
1030 - 'title' => $value->title,
1031 - 'setting' => json_decode($value->setting),
1032 - 'body' => $value->body,
1033 - ];
1176 + if (!is_wp_error($pdfTemplates)) {
1177 + foreach ($pdfTemplates as $value) {
1178 + $pdfTem[] =
1179 + [
1180 + 'id' => $value->id,
1181 + 'title' => $value->title,
1182 + 'setting' => json_decode($value->setting),
1183 + 'pdf_config' => !empty($value->pdf_config) ? json_decode($value->pdf_config) : (object) [
1184 + 'status' => 1,
1185 + 'header' => '',
1186 + 'footer' => '',
1187 + ],
1188 + 'body' => $value->body,
1189 + ];
1190 + }
1034 1191 }
1035 1192 }
1036 1193
1037 1194 $integrationHandler = new IntegrationHandler($formID);
@@ -1039,10 +1196,11 @@
1039 1196 if (!is_wp_error($formIntegrations)) {
1040 1197 foreach ($formIntegrations as $integrationkey => $integrationValue) {
1041 1198 if ('redirectPage' === $integrationValue->integration_type || 'webHooks' === $integrationValue->integration_type) {
1042 1199 $integrationData = [
1043 - 'id' => $integrationValue->id,
1044 - 'title' => $integrationValue->integration_name,
1200 + 'id' => $integrationValue->id,
1201 + 'title' => $integrationValue->integration_name,
1202 + 'status' => isset($integrationValue->status) ? (int) $integrationValue->status : 1,
1045 1203 ];
1046 1204 if (!empty($integrationValue->integration_details)) {
1047 1205 $integration_details = (array) json_decode($integrationValue->integration_details);
1048 1206 $integrationData = array_merge($integration_details, $integrationData);
@@ -1068,11 +1226,26 @@
1068 1226 );
1069 1227 }
1070 1228 }
1071 1229 }
1230 +
1231 + // get form abandonment integration if exist
1232 + $formAbandonmentInteg = $integrationHandler->getAllIntegration('formAbandonment', 'formAbandonment');
1233 + if (!is_wp_error($formAbandonmentInteg) && !empty($formAbandonmentInteg)) {
1234 + $formAbandonmentInteg = $formAbandonmentInteg[0];
1235 + if (!empty($formAbandonmentInteg->integration_details)) {
1236 + $formAbandonment = json_decode($formAbandonmentInteg->integration_details);
1237 + } else {
1238 + $formAbandonment = (object) [];
1239 + }
1240 + } else {
1241 + $formAbandonment = (object) [];
1242 + }
1243 +
1072 1244 $settingsContent = [
1073 - 'formName' => $formManager->getFormName(),
1074 - 'theme' => isset($form_content->theme) ? $form_content->theme : 'default',
1245 + 'formName' => $formManager->getFormName(),
1246 + 'theme' => isset($form_content->theme) ? $form_content->theme : 'default',
1247 + 'formPermissions' => isset($form_content->formPermissions) ? $form_content->formPermissions : (object) [],
1075 1248 // 'submitBtn' => $form_content->buttons,
1076 1249 ];
1077 1250
1078 1251 if (!empty($allConfirmation)) {
@@ -1131,8 +1304,24 @@
1131 1304 'pdfTem' => [],
1132 1305 ]
1133 1306 );
1134 1307 }
1308 +
1309 + if (!empty($formAbandonment)) {
1310 + $settingsContent = array_merge(
1311 + $settingsContent,
1312 + [
1313 + 'formAbandonment' => $formAbandonment,
1314 + ]
1315 + );
1316 + } else {
1317 + $settingsContent = array_merge(
1318 + $settingsContent,
1319 + [
1320 + 'formAbandonment' => (object) [],
1321 + ]
1322 + );
1323 + }
1135 1324 $formSettings = [
1136 1325 'formSettings' => $settingsContent,
1137 1326 ];
1138 1327 $data = [
@@ -1143,12 +1332,14 @@
1143 1332 'style' => $helper_states->style,
1144 1333 'themeVars' => $helper_states->themeVars,
1145 1334 'themeColors' => $helper_states->themeColors,
1146 1335 'builderSettings' => $helper_states->builderSettings,
1336 + 'customCode' => FrontEndScriptGenerator::getCustomCodes($formID),
1147 1337 'additional' => empty($form_content->additional) ? [
1148 1338 'enabled' => [
1149 1339 'blocked_ip' => false,
1150 1340 'restrict_form' => false,
1341 + 'onePerIp' => false,
1151 1342 ],
1152 1343 'settings' => [
1153 1344 'restrict_form' => [
1154 1345 'day' => [],
@@ -1163,9 +1354,8 @@
1163 1354 ],
1164 1355 'entry_limit' => null,
1165 1356 'blocked_ip' => [],
1166 1357 ],
1167 - 'onePerIp' => false,
1168 1358 ] : $form_content->additional,
1169 1359 'created_at' => $formManager->getFormMetaData()['created_at'],
1170 1360 'views' => $formManager->getFormMetaData()['views'],
1171 1361 'entries' => $formManager->getFormMetaData()['entries'],
@@ -1206,9 +1396,9 @@
1206 1396 } else {
1207 1397 $returnedReportData[$reportKey]->details = [];
1208 1398 }
1209 1399 if ('1' === (string) $reportData->isDefault) {
1210 - $returnedReportData[$reportKey]->details->report_name = 'All Entries';
1400 + $returnedReportData[$reportKey]->details->report_name = __('All Entries', 'bit-form');
1211 1401 }
1212 1402 }
1213 1403 $reports = ['reports' => $returnedReportData];
1214 1404 $data = array_merge($data, $reports);
@@ -1220,12 +1410,233 @@
1220 1410 return $data;
1221 1411 }
1222 1412 }
1223 1413
1414 + public function getAFormV1($Request, $post)
1415 + {
1416 + if (!empty($Request['id'])) {
1417 + $formID = absint(wp_unslash($Request['id']));
1418 + } else {
1419 + $formID = wp_unslash($post->id);
1420 + }
1421 + // return $post->fields;
1422 + if (is_null($formID)) {
1423 + return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1424 + }
1425 + $formManager = new AdminFormManager($formID);
1426 + if (!$formManager->isExist()) {
1427 + return new WP_Error('not_exists', __('Form is not exists.', 'bit-form'));
1428 + }
1429 + $form_content = $formManager->getFormContent();
1430 + if ($formManager->isExist()) {
1431 + $form_content_arr = [
1432 + 'layout' => $form_content->layout,
1433 + 'fields' => $form_content->fields,
1434 + 'form_name' => $formManager->getFormName(),
1435 + 'workFlowExist' => isset($form_content->workFlowExist) ? $form_content->workFlowExist : [],
1436 + 'report_id' => isset($form_content->report_id) ? $form_content->report_id : null
1437 + ];
1438 + $successMessageHandler
1439 + = new SuccessMessageHandler($formID);
1440 + $successMessages = $successMessageHandler->getAllMessage();
1441 + if (!is_wp_error($successMessages)) {
1442 + foreach ($successMessages as $sucessMessagekey => $sucessMessagevalue) {
1443 + $allConfirmation['type']['successMsg'][$sucessMessagekey] =
1444 + [
1445 + 'id' => $sucessMessagevalue->id,
1446 + 'title' => $sucessMessagevalue->message_title,
1447 + 'msg' => $sucessMessagevalue->message_content,
1448 + 'config' => !empty($sucessMessagevalue->message_config) ? json_decode($sucessMessagevalue->message_config) : (object) [],
1449 + ];
1450 + }
1451 + }
1452 + $emailTemplateHandler
1453 + = new EmailTemplateHandler($formID);
1454 + $emailTemplates = $emailTemplateHandler->getAllTemplate();
1455 + if (!is_wp_error($emailTemplates)) {
1456 + foreach ($emailTemplates as $emailTemplatekey => $emailTemplatevalue) {
1457 + $mailTem[] =
1458 + [
1459 + 'id' => $emailTemplatevalue->id,
1460 + 'title' => $emailTemplatevalue->title,
1461 + 'sub' => $emailTemplatevalue->sub,
1462 + 'body' => $emailTemplatevalue->body,
1463 + 'status' => isset($emailTemplatevalue->status) ? (int) $emailTemplatevalue->status : 1,
1464 + 'config' => EmailTemplateHandler::normalizeConfig($emailTemplatevalue->config ?? null),
1465 + ];
1466 + }
1467 + }
1468 + $integrationHandler = new IntegrationHandler($formID);
1469 + $formIntegrations = $integrationHandler->getAllIntegration('form');
1470 + if (!is_wp_error($formIntegrations)) {
1471 + foreach ($formIntegrations as $integrationkey => $integrationValue) {
1472 + if ('redirectPage' === $integrationValue->integration_type || 'webHooks' === $integrationValue->integration_type) {
1473 + $integrationData = [
1474 + 'id' => $integrationValue->id,
1475 + 'title' => $integrationValue->integration_name,
1476 + 'status' => isset($integrationValue->status) ? (int) $integrationValue->status : 1,
1477 + ];
1478 + if (!empty($integrationValue->integration_details)) {
1479 + // var_dump(json_decode($integrationValue->integration_details));
1480 + $integration_details = (array) json_decode($integrationValue->integration_details);
1481 + $integrationData = array_merge($integration_details, $integrationData);
1482 + } else {
1483 + $integration_details = [
1484 + 'url' => '',
1485 + ];
1486 + $integrationData = array_merge($integrationData, $integration_details);
1487 + }
1488 + $allConfirmation['type'][$integrationValue->integration_type][]
1489 + = $integrationData;
1490 + } else {
1491 + $integrationData = [
1492 + 'id' => $integrationValue->id,
1493 + 'name' => $integrationValue->integration_name,
1494 + 'type' => $integrationValue->integration_type,
1495 + ];
1496 + $integrations[] = array_merge(
1497 + $integrationData,
1498 + is_string($integrationValue->integration_details) ?
1499 + (array) json_decode($integrationValue->integration_details) :
1500 + $integrationValue->integration_details
1501 + );
1502 + }
1503 + }
1504 + }
1505 + $settingsContent = [
1506 + 'formName' => $formManager->getFormName(),
1507 + 'theme' => $form_content->theme,
1508 + // 'submitBtn' => $form_content->buttons,
1509 + ];
1510 +
1511 + if (!empty($allConfirmation)) {
1512 + $confirmation = [
1513 + 'confirmation' => $allConfirmation,
1514 + ];
1515 + } else {
1516 + $confirmation = [
1517 + 'confirmation' => ['type' => []],
1518 + ];
1519 + }
1520 + $settingsContent = array_merge($settingsContent, $confirmation);
1521 + if (!empty($integrations)) {
1522 + $settingsContent = array_merge(
1523 + $settingsContent,
1524 + [
1525 + 'integrations' => $integrations,
1526 + ]
1527 + );
1528 + } else {
1529 + $settingsContent = array_merge(
1530 + $settingsContent,
1531 + [
1532 + 'integrations' => []
1533 + ]
1534 + );
1535 + }
1536 + if (!empty($mailTem)) {
1537 + $settingsContent = array_merge(
1538 + $settingsContent,
1539 + [
1540 + 'mailTem' => $mailTem,
1541 + ]
1542 + );
1543 + } else {
1544 + $settingsContent = array_merge(
1545 + $settingsContent,
1546 + [
1547 + 'mailTem' => []
1548 + ]
1549 + );
1550 + }
1551 + $formSettings = [
1552 + 'formSettings' => $settingsContent,
1553 + ];
1554 + $data = [
1555 + 'id' => $formID,
1556 + 'form_name' => $formManager->getFormName(),
1557 + 'form_content' => $form_content_arr,
1558 + 'additional' => empty($form_content->additional) ? [
1559 + 'enabled' => [
1560 + 'blocked_ip' => false,
1561 + 'restrict_form' => false,
1562 + 'onePerIp' => false,
1563 + ],
1564 + 'settings' => [
1565 + 'restrict_form' => [
1566 + 'day' => [],
1567 + 'date' => [
1568 + 'from' => null,
1569 + 'to' => null,
1570 + ],
1571 + 'time' => [
1572 + 'from' => null,
1573 + 'to' => null,
1574 + ],
1575 + ],
1576 + 'entry_limit' => null,
1577 + 'blocked_ip' => [],
1578 + ],
1579 + ] : $form_content->additional,
1580 + 'created_at' => $formManager->getFormMetaData()['created_at'],
1581 + 'views' => $formManager->getFormMetaData()['views'],
1582 + 'entries' => $formManager->getFormMetaData()['entries'],
1583 + 'status' => $formManager->getFormMetaData()['status'],
1584 + ];
1585 + $data = array_merge($data, $formSettings);
1586 + $workFlowHandler = new WorkFlowHandler($formID);
1587 + $workFlows = ['workFlows' => $workFlowHandler->getAllworkFlowV1()];
1588 + $data = array_merge($data, $workFlows);
1589 + $reportsModel = new ReportsModel();
1590 + $returnedReportData = $reportsModel->get(
1591 + [
1592 + 'id',
1593 + 'details',
1594 + 'isDefault',
1595 + 'type',
1596 + ],
1597 + [
1598 + 'category' => 'form',
1599 + 'context' => $formID,
1600 + ]
1601 + );
1602 +
1603 + if (!is_wp_error($returnedReportData)) {
1604 + $fieldNames = [];
1605 + foreach ($formManager->getFieldLabel() as $key => $field) {
1606 + $fieldNames[$field['key']] = isset($field->lbl) ? $field->lbl : '';
1607 + }
1608 + if ($formManager->isGCLIDEnabled()) {
1609 + $fieldNames['GCLID'] = 'GCLID';
1610 + }
1611 + foreach ($returnedReportData as $reportKey => $reportData) {
1612 + $reportDetails = $reportsModel->validateReportFields($reportData, $fieldNames);
1613 + if (!empty($reportDetails) && is_string($reportDetails)) {
1614 + $returnedReportData[$reportKey]->details = json_decode($reportDetails);
1615 + } elseif (!empty($reportDetails)) {
1616 + $returnedReportData[$reportKey]->details = $reportDetails;
1617 + } else {
1618 + $returnedReportData[$reportKey]->details = [];
1619 + }
1620 + if ('1' === (string) $reportData->isDefault) {
1621 + $returnedReportData[$reportKey]->details->report_name = __('All Entries', 'bit-form');
1622 + }
1623 + }
1624 + $reports = ['reports' => $returnedReportData];
1625 + $data = array_merge($data, $reports);
1626 + }
1627 +
1628 + $formFields = $formManager->getFieldLabel();
1629 + $data = array_merge($data, ['Labels' => $formFields]);
1630 +
1631 + return $data;
1632 + }
1633 + }
1634 +
1224 1635 public function deleteAForm($Request, $post)
1225 1636 {
1226 - if (isset($Request['id'])) {
1227 - $formID = wp_unslash($Request['id']);
1637 + if (!empty($Request['id'])) {
1638 + $formID = absint(wp_unslash($Request['id']));
1228 1639 } else {
1229 1640 $formID = wp_unslash($post->id);
1230 1641 }
1231 1642 if (is_null($formID)) {
@@ -1230,24 +1641,30 @@
1230 1641 }
1231 1642 if (is_null($formID)) {
1232 1643 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1233 1644 }
1645 +
1646 + if (!filter_var($formID, FILTER_VALIDATE_INT)) {
1647 + return new WP_Error('invalid_form', __('Form id is invalid.', 'bit-form'));
1648 + }
1649 +
1234 1650 $delete_status = static::$formModel->delete(
1235 1651 [
1236 1652 'id' => $formID,
1237 1653 ]
1238 1654 );
1655 + do_action('bitform_admin_form_changed');
1239 1656
1240 - $successMessageModel
1241 - = new SuccessMessageModel();
1657 + $successMessageModel = new SuccessMessageModel();
1242 1658 $deleteMsgStatus = $successMessageModel->delete(['form_id' => $formID]);
1243 1659
1244 - $emailTemplateModel
1245 - = new EmailTemplateModel();
1660 + $emailTemplateModel = new EmailTemplateModel();
1246 1661 $deleteTemplateStatus = $emailTemplateModel->delete(['form_id' => $formID]);
1247 1662
1248 - $pdfTemplateModel = new PdfTemplateModel();
1249 - $deletePdfTemplateStatus = $pdfTemplateModel->delete(['form_id' => $formID]);
1663 + if (class_exists('\BitCode\BitFormPro\Core\Database\PdfTemplateModel')) {
1664 + $pdfTemplateModel = new \BitCode\BitFormPro\Core\Database\PdfTemplateModel();
1665 + $deletePdfTemplateStatus = $pdfTemplateModel->delete(['form_id' => $formID]);
1666 + }
1250 1667
1251 1668 $integrationModel = new IntegrationModel();
1252 1669 $deleteIntegrationStatus = $integrationModel->delete(['form_id' => $formID]);
1253 1670
@@ -1260,22 +1677,11 @@
1260 1677 $formEntryModel = new FormEntryModel();
1261 1678 $returnedEntries = $formEntryModel->get('id', ['form_id' => $formID]);
1262 1679 $entries = [];
1263 1680
1264 - if (!is_wp_error($returnedEntries)) {
1265 - foreach ($returnedEntries as $entryKey => $entryInfo) {
1266 - $entries[] = $entryInfo->id;
1267 - }
1268 - global $wpdb;
1269 - $prefix = $wpdb->prefix;
1270 - if (count($entries) > 0) {
1271 - $deleteEntriesStatus = $formEntryModel->bulkDelete(
1272 - [
1273 - "`{$prefix}bitforms_form_entries`.`id`" => $entries,
1274 - "`{$prefix}bitforms_form_entries`.`form_id`" => $formID,
1275 - ]
1276 - );
1277 - }
1681 + // checked namespace exist
1682 + if (class_exists('\BitCode\BitFormPro\Admin\BfTable\Table')) {
1683 + \BitCode\BitFormPro\Admin\BfTable\Table::deleteTableByFormId($formID);
1278 1684 }
1279 1685
1280 1686 $fileHandler = new FileHandler();
1281 1687 if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID)) {
@@ -1283,45 +1689,54 @@
1283 1689 }
1284 1690 if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID)) {
1285 1691 $fileHandler->rmrf(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID);
1286 1692 }
1287 - if (file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-' . $formID . '.css')) {
1288 - unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-' . $formID . '.css');
1289 - }
1290 - if (file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-layout-' . $formID . '.css')) {
1291 - unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-layout-' . $formID . '.css');
1292 - }
1293 - if (file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.css')) {
1294 - unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.css');
1295 - }
1296 - if (file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-scripts' . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.js')) {
1297 - unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-scripts' . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.js');
1298 - }
1299 - if (file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-conversational-' . $formID . '.css')) {
1300 - unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-conversational-' . $formID . '.css');
1301 - }
1693 + $formStylePath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles';
1694 +
1695 + $formCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-' . $formID . '.css';
1696 + FileHandler::deleteIsFileExists($formCssPath);
1697 +
1698 + $layoutCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-layout-' . $formID . '.css';
1699 + FileHandler::deleteIsFileExists($layoutCssPath);
1700 +
1701 + $customCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.css';
1702 + FileHandler::deleteIsFileExists($customCssPath);
1703 +
1704 + $customJSPath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-scripts' . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.js';
1705 + FileHandler::deleteIsFileExists($customJSPath);
1706 +
1707 + $conversationCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-conversational-' . $formID . '.css';
1708 + FileHandler::deleteIsFileExists($conversationCssPath);
1709 +
1302 1710 return is_wp_error($delete_status) ? $delete_status : __('Form deleted successfully.', 'bit-form');
1303 1711 }
1304 1712
1305 1713 public function deleteBlukForm($Request, $post)
1306 1714 {
1307 - if (isset($Request['formID'])) {
1308 - $formID = wp_unslash($Request['formID']);
1715 + if (!empty($Request['formID'])) {
1716 + $formID = array_map('absint', wp_unslash($Request['formID']));
1309 1717 } else {
1310 1718 $formID = wp_unslash($post->formID);
1311 1719 }
1720 +
1312 1721 if (is_null($formID) || !is_array($formID)) {
1313 1722 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1314 1723 }
1724 +
1725 + if (!Helpers::checkIsIntArr($formID)) {
1726 + return new WP_Error('invalid_form', __('Form id is invalid', 'bit-form'));
1727 + }
1728 +
1315 1729 $cssPath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR;
1316 1730 foreach ($formID as $id) {
1317 - unlink($cssPath . 'bitform-' . $id . '.css');
1318 - if (file_exists($cssPath . 'bitform-layout-' . $id . '.css')) {
1319 - unlink($cssPath . 'bitform-layout-' . $id . '.css');
1320 - }
1321 - if (file_exists($cssPath . 'bitform-' . $id . '-formid' . '.css')) {
1322 - unlink($cssPath . 'bitform-' . $id . '-formid' . '.css');
1323 - }
1731 + $formCssPath = $cssPath . 'bitform-' . $id . '.css';
1732 + FileHandler::deleteIsFileExists($formCssPath);
1733 +
1734 + $layoutPath = $cssPath . 'bitform-layout-' . $id . '.css';
1735 + FileHandler::deleteIsFileExists($layoutPath);
1736 +
1737 + $bitformCustomPath = $cssPath . 'bitform-' . $id . '-formid' . '.css';
1738 + FileHandler::deleteIsFileExists($bitformCustomPath);
1324 1739 }
1325 1740 $delete_status = static::$formModel->bulkDelete(
1326 1741 [
1327 1742 'id' => $formID,
@@ -1326,16 +1741,26 @@
1326 1741 [
1327 1742 'id' => $formID,
1328 1743 ]
1329 1744 );
1745 + do_action('bitform_admin_form_changed');
1746 +
1747 + if (is_wp_error($delete_status)) {
1748 + wp_send_json_error($delete_status->get_error_message(), 400);
1749 + }
1750 +
1330 1751 $successMessageModel
1331 1752 = new SuccessMessageModel();
1332 1753 $deleteMsgStatus = $successMessageModel->bulkDelete(['form_id' => $formID]);
1333 1754
1334 - $emailTemplateModel
1335 - = new EmailTemplateModel();
1755 + $emailTemplateModel = new EmailTemplateModel();
1336 1756 $deleteTemplateStatus = $emailTemplateModel->bulkDelete(['form_id' => $formID]);
1337 1757
1758 + if (class_exists('\BitCode\BitFormPro\Core\Database\PdfTemplateModel')) {
1759 + $pdfTemplateModel = new \BitCode\BitFormPro\Core\Database\PdfTemplateModel();
1760 + $deletePdfTemplateStatus = $pdfTemplateModel->bulkDelete(['form_id' => $formID]);
1761 + }
1762 +
1338 1763 $integrationModel = new IntegrationModel();
1339 1764 $deleteIntegrationStatus = $integrationModel->bulkDelete(['form_id' => $formID]);
1340 1765
1341 1766 $workFlowModel = new WorkFlowModel();
@@ -1345,8 +1770,13 @@
1345 1770 $deleteReportStatus = $reportsModel->bulkDelete(['context' => $formID]);
1346 1771
1347 1772 $formEntryModel = new FormEntryModel();
1348 1773 $returnedEntries = $formEntryModel->get('id', ['form_id' => $formID]);
1774 +
1775 + if (class_exists('\BitCode\BitFormPro\Admin\BfTable\Table')) {
1776 + \BitCode\BitFormPro\Admin\BfTable\Table::deleteTableByBulkFormId($formID);
1777 + }
1778 +
1349 1779 $entries = [];
1350 1780 foreach ($returnedEntries as $entryKey => $entryInfo) {
1351 1781 if (!empty($entryInfo->id)) {
1352 1782 $entries[] = $entryInfo->id;
@@ -1362,16 +1792,19 @@
1362 1792 ]
1363 1793 );
1364 1794 }
1365 1795
1796 + // delete entry log and details; table name interpolation only — $wpdb->prepare() parameterizes the form_id value.
1797 + $wpdb->query($wpdb->prepare(
1798 + "DELETE em, e FROM `{$prefix}bitforms_form_log_details` AS em JOIN `{$prefix}bitforms_form_entry_log` AS e ON em.log_id = e.id WHERE e.form_id = %d",
1799 + $formID
1800 + ));
1801 +
1366 1802 $fileHandler = new FileHandler();
1367 1803 foreach ($formID as $fId) {
1368 1804 if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $fId)) {
1369 1805 $fileHandler->rmrf(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $fId);
1370 1806 }
1371 -
1372 - // unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-' . $fId . '.css');
1373 - // unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-layout-' . $fId . '.css');
1374 1807 }
1375 1808
1376 1809 return is_wp_error($delete_status) ? $delete_status : __('Forms deleted successfully.', 'bit-form');
1377 1810 }
@@ -1413,21 +1846,15 @@
1413 1846 if (!is_wp_error($duplicateResponse) || (is_wp_error($duplicateResponse) && 'result_empty' === $duplicateResponse->get_error_code())) {
1414 1847 // duplicate stylesheet
1415 1848 $style_dir = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR;
1416 1849 // layout style copy
1417 - $mainFormLayStyle = fopen($style_dir . 'bitform-layout-' . $oldFormId . '.css', 'r');
1418 - $styleStr = fread($mainFormLayStyle, filesize($style_dir . 'bitform-layout-' . $oldFormId . '.css'));
1850 + $styleStr = FileHandler::readFile($style_dir . 'bitform-layout-' . $oldFormId . '.css');
1419 1851 $newStyle = str_replace(".bf$oldFormId-", ".bf$newFormId-", $styleStr);
1420 - $createStyleFile = fopen($style_dir . "bitform-layout-$newFormId.css", 'w');
1421 - fwrite($createStyleFile, $newStyle);
1422 - fclose($createStyleFile);
1852 + FileHandler::writeFile($style_dir . "bitform-layout-$newFormId.css", $newStyle);
1423 1853 // style copy
1424 - $mainFormStyle = fopen($style_dir . 'bitform-' . $oldFormId . '.css', 'r');
1425 - $styleStr = fread($mainFormStyle, filesize($style_dir . 'bitform-' . $oldFormId . '.css'));
1854 + $styleStr = FileHandler::readFile($style_dir . 'bitform-' . $oldFormId . '.css');
1426 1855 $newStyle = str_replace("-$oldFormId", "-$newFormId", $styleStr);
1427 - $createStyleFile = fopen($style_dir . "bitform-$newFormId.css", 'w');
1428 - fwrite($createStyleFile, $newStyle);
1429 - fclose($createStyleFile);
1856 + FileHandler::writeFile($style_dir . "bitform-$newFormId.css", $newStyle);
1430 1857 $duplicatedForm = $this->getAForm(null, (object) ['id' => $newFormId]);
1431 1858 $duplicatedForm['message'] = __('Form duplicated successfully', 'bit-form');
1432 1859 return $duplicatedForm;
1433 1860 }
@@ -1440,15 +1867,15 @@
1440 1867 $newFormId = intval($post->newFormId);
1441 1868 if (!$oldFormId || empty($post->formDetail->fields) || empty($post->formDetail->layout)) {
1442 1869 return new WP_Error('invalid_form', __('Please import a valid json.', 'bit-form'));
1443 1870 }
1444 - $importtedForm = (array)$post->formDetail;
1871 + $importtedForm = (array) $post->formDetail;
1445 1872 $importtedForm['form_content']['fields'] = $importtedForm['fields'];
1446 1873 $importtedForm['form_content']['layout'] = $importtedForm['layout'];
1447 - $importtedForm['form_content']['nestedLayout'] = $importtedForm['nestedLayout'];
1874 + $importtedForm['form_content']['nestedLayout'] = $importtedForm['nestedLayouts'];
1448 1875 $importtedForm['form_content']['formInfo'] = $importtedForm['formInfo'];
1449 1876 unset($importtedForm['fields'], $importtedForm['layout'], $importtedForm['nestedLayout'], $importtedForm['formInfo']);
1450 - $formData = FormDuplicateHelper::createReplica((array)$importtedForm, $oldFormId, $newFormId);
1877 + $formData = FormDuplicateHelper::createReplica((array) $importtedForm, $oldFormId, $newFormId);
1451 1878
1452 1879 if (!empty($importtedForm['rowHeight'])) {
1453 1880 $formData->rowHeight = $importtedForm['rowHeight'];
1454 1881 }
@@ -1473,9 +1900,11 @@
1473 1900 $fileName = 'bitform-custom-' . $newFormId . '.css';
1474 1901 $customCode['CSS'] = $formData->customCode->customCss;
1475 1902 Helpers::saveFile($customCssPath, $fileName, $formData->customCode->customCss, 'w');
1476 1903 }
1477 - $importResponse['customCode'] = $customCode;
1904 + if (!is_wp_error($importResponse)) {
1905 + $importResponse['customCode'] = $customCode;
1906 + }
1478 1907 if (!is_wp_error($importResponse) || (is_wp_error($importResponse) && 'result_empty' === $importResponse->get_error_code())) {
1479 1908 $responseData = $this->getAForm(null, (object) ['id' => $newFormId]);
1480 1909 $responseData['customCode'] = $customCode;
1481 1910 $responseData['message'] = __('Form imported successfully.', 'bit-form');
@@ -1533,9 +1962,9 @@
1533 1962 return new WP_Error('empty_form', __('Form does not exists.', 'bit-form'));
1534 1963 }
1535 1964
1536 1965 $newFormId = $oldFormId;
1537 - $duplicatedForm = $this->getAForm($Request, (object)['id' => $oldFormId]);
1966 + $duplicatedForm = $this->getAForm($Request, (object) ['id' => $oldFormId]);
1538 1967 $formData = FormDuplicateHelper::createReplica($duplicatedForm, $oldFormId, $newFormId);
1539 1968 // for custom css
1540 1969 $customCssPath = 'form-styles/bitform-custom-' . $newFormId . '.css';
1541 1970 $cssPath = Helpers::generatePathDirOrFile($customCssPath);
@@ -1555,10 +1984,10 @@
1555 1984 }
1556 1985
1557 1986 public function getFormEntryLabelAndCount($Request, $post)
1558 1987 {
1559 - if (isset($Request['id'])) {
1560 - $id = wp_unslash($Request['id']);
1988 + if (!empty($Request['id'])) {
1989 + $id = absint(wp_unslash($Request['id']));
1561 1990 } else {
1562 1991 $id = wp_unslash($post->id);
1563 1992 }
1564 1993 if (is_null($id)) {
@@ -1611,16 +2040,74 @@
1611 2040 $response['fieldDetails'] = $labels;
1612 2041 return $response;
1613 2042 }
1614 2043
2044 + /**
2045 + * Sanitize a request-supplied entry query condition before it reaches the Model
2046 + * layer. Only known entry-table columns are allowed as condition keys, operators
2047 + * are restricted to a safe allow-list ['form_id' => $id] when nothing valid remains.
2048 + *
2049 + * @param mixed $queryCondition
2050 + * @param int $id
2051 + * @return array
2052 + */
2053 + private function sanitizeEntryQueryCondition($queryCondition, $id)
2054 + {
2055 + $allowedColumns = [
2056 + 'id', 'form_id', 'status', 'created_at', 'updated_at',
2057 + 'user_id', 'user_ip', 'user_device', 'user_location', 'referer',
2058 + ];
2059 + $allowedOperators = ['=', '!=', '<>', '<', '>', '<=', '>=', 'LIKE', 'IN'];
2060 +
2061 + if (!is_array($queryCondition)) {
2062 + return ['form_id' => $id];
2063 + }
2064 +
2065 + $safe = [];
2066 + foreach ($queryCondition as $key => $value) {
2067 + if (!in_array($key, $allowedColumns, true)) {
2068 + continue;
2069 + }
2070 +
2071 + if (is_array($value)) {
2072 + // Never honour a request-supplied raw SQL fragment.
2073 + unset($value['raw']);
2074 +
2075 + if (array_key_exists('operator', $value) || array_key_exists('value', $value)) {
2076 + $operator = isset($value['operator']) && is_string($value['operator'])
2077 + ? strtoupper(trim($value['operator']))
2078 + : '=';
2079 + if (!in_array($operator, $allowedOperators, true)) {
2080 + $operator = '=';
2081 + }
2082 + $condValue = isset($value['value']) ? $value['value'] : '';
2083 + if (is_scalar($condValue)) {
2084 + $safe[$key] = ['operator' => $operator, 'value' => $condValue];
2085 + }
2086 + } else {
2087 + // IN-style list: keep scalar members only (bound by the Model).
2088 + $list = array_values(array_filter($value, 'is_scalar'));
2089 + if (!empty($list)) {
2090 + $safe[$key] = $list;
2091 + }
2092 + }
2093 + } elseif (is_scalar($value)) {
2094 + $safe[$key] = $value;
2095 + }
2096 + }
2097 +
2098 + return empty($safe) ? ['form_id' => $id] : $safe;
2099 + }
2100 +
1615 2101 public function getFormEntry($Request, $post)
1616 2102 {
1617 - if (isset($Request['id'])) {
1618 - $id = wp_unslash($Request['id']);
2103 + if (!empty($Request['id'])) {
2104 + $id = absint(wp_unslash($Request['id']));
1619 2105 $offset = isset($Request['offset']) ?
1620 2106 wp_unslash($Request['offset']) : 0;
1621 2107 $pageSize = isset($Request['pageSize']) ?
1622 2108 wp_unslash($Request['pageSize']) : 10;
2109 + $queryCondition = isset($Request['queryCondition']) ? wp_unslash($Request['queryCondition']) : ['form_id' => $id];
1623 2110 } else {
1624 2111 $id = wp_unslash($post->id);
1625 2112 $conditions = isset($post->conditions) ? wp_unslash($post->conditions) : [];
1626 2113 $dateBetweenFilter = isset($post->entriesFilterByDate) ? wp_unslash($post->entriesFilterByDate) : [];
@@ -1629,9 +2116,11 @@
1629 2116 $filters = isset($post->filters) ? wp_unslash($post->filters) : null;
1630 2117 $globalFilter = isset($post->globalFilter) ? wp_unslash($post->globalFilter) : null;
1631 2118 $pageSize = isset($post->pageSize) ?
1632 2119 wp_unslash($post->pageSize) : 10;
2120 + $queryCondition = isset($post->queryCondition) ? wp_unslash($post->queryCondition) : ['form_id' => $id];
1633 2121 }
2122 + $queryCondition = $this->sanitizeEntryQueryCondition($queryCondition, $id);
1634 2123 if (is_null($id)) {
1635 2124 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1636 2125 }
1637 2126 $formManager = new AdminFormManager($id);
@@ -1637,15 +2126,12 @@
1637 2126 $formManager = new AdminFormManager($id);
1638 2127 if (!$formManager->isExist()) {
1639 2128 return new WP_Error('empty_form', __('Form does not exists', 'bit-form'));
1640 2129 }
1641 -
1642 2130 $formEntry = new FormEntryModel();
1643 2131 $entries = $formEntry->get(
1644 2132 'id',
1645 - [
1646 - 'form_id' => $id,
1647 - ],
2133 + $queryCondition,
1648 2134 null,
1649 2135 null,
1650 2136 'created_at',
1651 2137 'DESC'
@@ -1671,12 +2157,36 @@
1671 2157 $formEntries = $customFieldHandler->updatedEntries($formEntries, $fieldDetails);
1672 2158 return $formEntries;
1673 2159 }
1674 2160
2161 + public function getSingleEntry($formId, $entryId)
2162 + {
2163 + $formManager = new AdminFormManager($formId);
2164 + if (!$formManager->isExist()) {
2165 + return new WP_Error('empty_form', __('Form does not exists', 'bit-form'));
2166 + }
2167 +
2168 + $formFields = $formManager->getFieldLabel(true);
2169 + $fieldDetails = $formManager->getFields();
2170 + $entryMeta = new FormEntryMetaModel();
2171 + // $formEntry = new FormEntryModel();
2172 +
2173 + $entries = $entryMeta->getSingleEntryMeta($formFields, $entryId);
2174 +
2175 + if (!is_wp_error($entries)) {
2176 + return $entries[0];
2177 + }
2178 + return new WP_Error('entry_not_fonud', __('Entry Not Found!', 'bit-form'));
2179 +
2180 + // $customFieldHandler = new CustomFieldHandler();
2181 + // $formEntries = $customFieldHandler->updatedEntries($formEntries, $fieldDetails);
2182 + // return $formEntries;
2183 + }
2184 +
1675 2185 public function getEntriesForReport($Request, $post)
1676 2186 {
1677 - if (isset($Request['id'])) {
1678 - $id = wp_unslash($Request['id']);
2187 + if (!empty($Request['id'])) {
2188 + $id = absint(wp_unslash($Request['id']));
1679 2189 $offset = isset($Request['offset']) ?
1680 2190 wp_unslash($Request['offset']) : null;
1681 2191 $pageSize = isset($Request['pageSize']) ?
1682 2192 wp_unslash($Request['pageSize']) : null;
@@ -1737,8 +2247,9 @@
1737 2247 {
1738 2248 $formId = wp_unslash($post->formId);
1739 2249 $fileFormate = isset($post->fileFormate) ? wp_unslash($post->fileFormate) : null;
1740 2250 $limit = isset($post->limit) ? wp_unslash($post->limit) : null;
2251 + $entryCondition = isset($post->filter) ? wp_unslash($post->filter) : null;
1741 2252 $sortBy = isset($post->sort) ? wp_unslash($post->sort) : 'ASC';
1742 2253 $sortByField = isset($post->sortField) ? wp_unslash($post->sortField) : 'bitforms_form_entry_id';
1743 2254 $formFields = json_decode($post->selectedField);
1744 2255 if (is_null($formId)) {
@@ -1771,17 +2282,17 @@
1771 2282 return $entries;
1772 2283 }
1773 2284 $entryMeta = new FormEntryMetaModel();
1774 2285 $filter = [];
1775 - $formEntries = $entryMeta->getExportEntry($formFields, $entries, $formId, $fieldLabels, $limit, $sortBy, $sortByField);
2286 + $formEntries = $entryMeta->getExportEntry($formFields, $entries, $formId, $fieldLabels, $limit, $sortBy, $sortByField, $offset, $entryCondition);
1776 2287 return $formEntries;
1777 2288 }
1778 2289
1779 2290 public function deleteBlukFormEntries($Request, $post)
1780 2291 {
1781 - if (isset($Request['formID'])) {
1782 - $formID = wp_unslash($Request['formID']);
1783 - $entries = wp_unslash($Request['entries']);
2292 + if (!empty($Request['formID'])) {
2293 + $formID = absint(wp_unslash($Request['formID']));
2294 + $entries = array_map('absint', wp_unslash($Request['entries']));
1784 2295 } else {
1785 2296 $formID = wp_unslash($post->formID);
1786 2297 $entries = wp_unslash($post->entries);
1787 2298 }
@@ -1787,21 +2298,32 @@
1787 2298 }
1788 2299 if (is_null($formID) || !is_array($entries) || 0 === count($entries)) {
1789 2300 return new WP_Error('empty_form', __('Invalid Form ID or Entries ID.', 'bit-form'));
1790 2301 }
2302 +
2303 + if (!filter_var($formID, FILTER_VALIDATE_INT)) {
2304 + return new WP_Error('invalid_form', __('Form id is invalid.', 'bit-form'));
2305 + }
2306 +
2307 + if (!Helpers::checkIsIntArr($entries)) {
2308 + return new WP_Error('invalid_entries', __('Entries id is invalid', 'bit-form'));
2309 + }
2310 +
1791 2311 $formManager = new AdminFormManager($formID);
1792 2312 if (!$formManager->isExist()) {
1793 2313 return new WP_Error('empty_form', __('Form does not exist.', 'bit-form'));
1794 2314 }
1795 2315 $workFlowRunHelper = new WorkFlow($formID);
2316 +
1796 2317 $workFlowreturnedOnDelete = $workFlowRunHelper->executeOnDelete(
1797 2318 $formManager,
1798 2319 $formID,
1799 2320 $entries
1800 2321 );
2322 +
1801 2323 if (isset($workFlowreturnedOnDelete['entries'])) {
1802 2324 if (0 === count($workFlowreturnedOnDelete['entries'])) {
1803 - return ['message' => __('Entry Deletetion prevented by workflow', 'bit-form')];
2325 + return ['message' => __('Entry Deletetion prevented by workflow', 'bit-form')];
1804 2326 } elseif (count($workFlowreturnedOnDelete['entries']) === count($entries)) {
1805 2327 $message = __('Entry Deleted successfully', 'bit-form');
1806 2328 } else {
1807 2329 $result['prevented'] = array_diff($entries, $workFlowreturnedOnDelete['entries']);
@@ -1822,9 +2344,9 @@
1822 2344 );
1823 2345 if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID)) {
1824 2346 $fileHandler = new FileHandler();
1825 2347 foreach ($entries as $enrtyKey => $entryID) {
1826 - $fileEntries = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID . DIRECTORY_SEPARATOR . $entryID;
2348 + $fileEntries = $fileHandler->getEntriesFileUploadDir($formID, $entryID);
1827 2349 if (file_exists($fileEntries)) {
1828 2350 $fileHandler->rmrf($fileEntries);
1829 2351 }
1830 2352 }
@@ -1843,11 +2365,11 @@
1843 2365 }
1844 2366
1845 2367 public function duplicateFormEntry($Request, $post)
1846 2368 {
1847 - if (isset($Request['formID'])) {
1848 - $formID = wp_unslash($Request['formID']);
1849 - $entries = wp_unslash($Request['entries']);
2369 + if (!empty($Request['formID'])) {
2370 + $formID = absint(wp_unslash($Request['formID']));
2371 + $entries = array_map('absint', wp_unslash($Request['entries']));
1850 2372 } else {
1851 2373 $formID = wp_unslash($post->formID);
1852 2374 $entries = wp_unslash($post->entries);
1853 2375 }
@@ -1854,8 +2376,16 @@
1854 2376 if (is_null($formID) || !is_array($entries) || empty($entries)) {
1855 2377 return new WP_Error('empty_form', __('Form id or entries id is invalid', 'bit-form'));
1856 2378 }
1857 2379
2380 + if (!filter_var($formID, FILTER_VALIDATE_INT)) {
2381 + return new WP_Error('invalid_form', __('Form id is invalid', 'bit-form'));
2382 + }
2383 +
2384 + if (!Helpers::checkIsIntArr($entries)) {
2385 + return new WP_Error('invalid_entries', __('Entries id is invalid', 'bit-form'));
2386 + }
2387 +
1858 2388 $formManager = new AdminFormManager($formID);
1859 2389 if (!$formManager->isExist()) {
1860 2390 return new WP_Error('empty_form', __('Form does not exist', 'bit-form'));
1861 2391 }
@@ -1888,12 +2418,12 @@
1888 2418 );
1889 2419 if ($duplicate_status) {
1890 2420 $result['details'][$entryID] = $duplicatedEntryId;
1891 2421 $duplicate_count = $duplicate_count + 1;
1892 - if (file_exists(BITFORMS_UPLOAD_DIR . "/$formID/$entryID")) {
2422 + if (file_exists(FileHandler::getEntriesFileUploadDir($formID, $entryID))) {
1893 2423 $fileHandler->cpyr(
1894 - BITFORMS_UPLOAD_DIR . "/$formID/$entryID",
1895 - BITFORMS_UPLOAD_DIR . "/$formID/$duplicatedEntryId"
2424 + FileHandler::getEntriesFileUploadDir($formID, $entryID),
2425 + FileHandler::getEntriesFileUploadDir($formID, $duplicatedEntryId)
1896 2426 );
1897 2427 }
1898 2428 }
1899 2429 }
@@ -1910,11 +2440,11 @@
1910 2440 }
1911 2441
1912 2442 public function editFormEntry($Request, $post)
1913 2443 {
1914 - if (isset($Request['formID'])) {
1915 - $formID = wp_unslash($Request['formID']);
1916 - $entryID = wp_unslash($Request['entryID']);
2444 + if (!empty($Request['formID'])) {
2445 + $formID = absint(wp_unslash($Request['formID']));
2446 + $entryID = absint(wp_unslash($Request['entryID']));
1917 2447 } else {
1918 2448 $formID = wp_unslash($post->formID);
1919 2449 $entryID = wp_unslash($post->entryID);
1920 2450 }
@@ -1989,15 +2519,16 @@
1989 2519
1990 2520 public function updateFormEntry($Request, $post)
1991 2521 {
1992 2522 if (isset($Request['formID'], $Request['entryID'])) {
1993 - $formID = wp_unslash($Request['formID']);
1994 - $entryID = wp_unslash($Request['entryID']);
2523 + $formID = absint(wp_unslash($Request['formID']));
2524 + $entryID = absint(wp_unslash($Request['entryID']));
1995 2525 unset($Request['action'], $Request['formID'], $Request['entryID']);
1996 2526 }
1997 2527 $formManager = new AdminFormManager($formID);
1998 2528 $formManager->fieldNameReplaceOfPost();
1999 - $updatedValue = wp_unslash($_POST);
2529 + // Called from AdminAjax handler; nonce already verified at AJAX entry before dispatch.
2530 + $updatedValue = is_array($post) ? $post : wp_unslash($_POST);
2000 2531
2001 2532 if (is_null($updatedValue) || !is_array($updatedValue)) {
2002 2533 return new WP_Error('empty_data', __('Failed to update, Data is empty.', 'bit-form'));
2003 2534 }
@@ -2013,11 +2544,11 @@
2013 2544 }
2014 2545
2015 2546 public function getLogHistory($Request, $post)
2016 2547 {
2017 - if (isset($Request['formID'])) {
2018 - $formID = wp_unslash($Request['formID']);
2019 - $entryID = wp_unslash($Request['entryID']);
2548 + if (!empty($Request['formID'])) {
2549 + $formID = absint(wp_unslash($Request['formID']));
2550 + $entryID = absint(wp_unslash($Request['entryID']));
2020 2551 } else {
2021 2552 $formID = wp_unslash($post->formID);
2022 2553 $entryID = wp_unslash($post->entryID);
2023 2554 }
@@ -2037,10 +2568,10 @@
2037 2568 }
2038 2569
2039 2570 public function importDataStore($Request, $post)
2040 2571 {
2041 - if (isset($Request['formID'])) {
2042 - $formID = wp_unslash($Request['formID']);
2572 + if (!empty($Request['formID'])) {
2573 + $formID = absint(wp_unslash($Request['formID']));
2043 2574 } else {
2044 2575 $formID = wp_unslash($post->formID);
2045 2576 }
2046 2577 if (is_null($formID)) {
@@ -2052,10 +2583,14 @@
2052 2583 {
2053 2584 $pages = get_pages(['post_status' => 'publish', 'sort_column' => 'post_date', 'sort_order' => 'desc']);
2054 2585 $allPages = [];
2055 2586 foreach ($pages as $pageKey => $pageDetails) {
2056 - $allPages[$pageKey]['title'] = $pageDetails->post_title;
2057 - $allPages[$pageKey]['url'] = get_page_link($pageDetails->ID);
2587 + // $allPages[$pageKey]['title'] = $pageDetails->post_title;
2588 + // $allPages[$pageKey]['url'] = get_page_link($pageDetails->ID);
2589 + $allPages[] = (object) [
2590 + 'title' => $pageDetails->post_title,
2591 + 'url' => get_page_link($pageDetails->ID)
2592 + ];
2058 2593 }
2059 2594 return $allPages;
2060 2595 }
2061 2596
@@ -2060,11 +2595,11 @@
2060 2595 }
2061 2596
2062 2597 public function deleteAIntegration($Request, $post)
2063 2598 {
2064 - if (isset($Request['formID']) && $Request['id']) {
2065 - $formID = json_decode(wp_unslash($Request['formID']));
2066 - $id = wp_unslash($Request['id']);
2599 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2600 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2601 + $id = absint(wp_unslash($Request['id']));
2067 2602 } else {
2068 2603 $formID = wp_unslash($post->formID);
2069 2604 $id = wp_unslash($post->id);
2070 2605 }
@@ -2084,11 +2619,11 @@
2084 2619 }
2085 2620
2086 2621 public function deleteSuccessMessage($Request, $post)
2087 2622 {
2088 - if (isset($Request['formID']) && $Request['id']) {
2089 - $formID = json_decode(wp_unslash($Request['formID']));
2090 - $id = wp_unslash($Request['id']);
2623 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2624 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2625 + $id = absint(wp_unslash($Request['id']));
2091 2626 } else {
2092 2627 $formID = wp_unslash($post->formID);
2093 2628 $id = wp_unslash($post->id);
2094 2629 }
@@ -2107,11 +2642,11 @@
2107 2642 }
2108 2643
2109 2644 public function deleteAWorkflow($Request, $post)
2110 2645 {
2111 - if (isset($Request['formID']) && $Request['id']) {
2112 - $formID = json_decode(wp_unslash($Request['formID']));
2113 - $id = wp_unslash($Request['id']);
2646 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2647 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2648 + $id = absint(wp_unslash($Request['id']));
2114 2649 } else {
2115 2650 $formID = wp_unslash($post->formID);
2116 2651 $id = wp_unslash($post->id);
2117 2652 }
@@ -2129,11 +2664,11 @@
2129 2664 }
2130 2665
2131 2666 public function deleteAMailTemplate($Request, $post)
2132 2667 {
2133 - if (isset($Request['formID']) && $Request['id']) {
2134 - $formID = json_decode(wp_unslash($Request['formID']));
2135 - $id = wp_unslash($Request['id']);
2668 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2669 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2670 + $id = absint(wp_unslash($Request['id']));
2136 2671 } else {
2137 2672 $formID = wp_unslash($post->formID);
2138 2673 $id = wp_unslash($post->id);
2139 2674 }
@@ -2151,11 +2686,11 @@
2151 2686 }
2152 2687
2153 2688 public function duplicateAMailTemplate($Request, $post)
2154 2689 {
2155 - if (isset($Request['formID']) && $Request['id']) {
2156 - $formID = json_decode(wp_unslash($Request['formID']));
2157 - $id = wp_unslash($Request['id']);
2690 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2691 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2692 + $id = absint(wp_unslash($Request['id']));
2158 2693 } else {
2159 2694 $formID = wp_unslash($post->formID);
2160 2695 $id = wp_unslash($post->id);
2161 2696 }
@@ -2161,8 +2696,17 @@
2161 2696 }
2162 2697 if (empty($formID) || empty($id)) {
2163 2698 return new WP_Error('empty_form', 'Invalid Form ID or Email Template ID.');
2164 2699 }
2700 +
2701 + if (false === filter_var($id, FILTER_VALIDATE_INT)) {
2702 + return new WP_Error('empty_form', 'Invalid Email Template ID.');
2703 + }
2704 +
2705 + if (false === filter_var($formID, FILTER_VALIDATE_INT)) {
2706 + return new WP_Error('empty_form', 'Invalid Form ID.');
2707 + }
2708 +
2165 2709 $emailTemplateHandler = new EmailTemplateHandler($formID);
2166 2710 $duplicate_status = $emailTemplateHandler->duplicateTemplate($id);
2167 2711 if (is_wp_error($duplicate_status)) {
2168 2712 return $duplicate_status;
@@ -2303,22 +2847,34 @@
2303 2847 }
2304 2848 if (is_string($paySetting)) {
2305 2849 $paySetting = json_decode($paySetting);
2306 2850 }
2851 +
2307 2852 $canSave = null;
2308 -
2309 - if ('PayPal' === $paySetting->type) {
2310 - $canSave = $this->addWebhookToPaypal($paySetting);
2311 - } elseif ('Razorpay' === $paySetting->type) {
2312 - $canSave = true;
2313 - } elseif ('Stripe' === $paySetting->type) {
2314 - $canSave = $this->addWebhookToStripe($paySetting);
2853 + switch ($paySetting->type) {
2854 + case 'PayPal':
2855 + $canSave = $this->addWebhookToPaypal($paySetting);
2856 + break;
2857 + case 'Razorpay':
2858 + $canSave = true;
2859 + break;
2860 + case 'Stripe':
2861 + $canSave = $this->addWebhookToStripe($paySetting);
2862 + break;
2863 + case 'Mollie':
2864 + $canSave = true;
2865 + break;
2866 + default:
2867 + $canSave = null;
2315 2868 }
2316 2869 if (is_null($canSave)) {
2317 2870 return new WP_Error('stripe', __('Please re-check your Client ID & Secret', 'bit-form'));
2318 2871 }
2319 - $integrationID = $paySetting->id;
2320 - unset($paySetting->id);
2872 + $integrationID = null;
2873 + if (isset($paySetting->id)) {
2874 + $integrationID = $paySetting->id;
2875 + unset($paySetting->id);
2876 + }
2321 2877 $integrationName = $paySetting->name;
2322 2878 $integrationType = 'payments';
2323 2879 $paySetting = wp_json_encode($paySetting);
2324 2880 $integrationDetails = $paySetting;
@@ -2396,15 +2952,16 @@
2396 2952 }
2397 2953 }
2398 2954
2399 2955 $data = [
2400 - 'enabled_events[]' => 'payment_intent.succeeded',
2401 - 'url' => $webhook_url,
2956 + 'enabled_events[]' => 'payment_intent.succeeded',
2957 + 'url' => $webhook_url,
2402 2958 ];
2403 2959
2404 2960 if (!$webhook_already_exist) {
2405 2961 $webhook_response = HttpHelper::post($create_webhook_url, $data, $headers);
2406 2962 if (is_wp_error($webhook_response) || (isset($webhook_response->error) && $webhook_response->error)) {
2963 + Log::debug_log(['Stripe webhook creation failed' => $webhook_response]);
2407 2964 return null;
2408 2965 }
2409 2966 }
2410 2967 return true;
@@ -2447,10 +3004,70 @@
2447 3004 return true;
2448 3005 }
2449 3006 }
2450 3007
3008 + public function replaceAllFormsErrorMessagesByGlobalMessages()
3009 + {
3010 + try {
3011 + $formModel = new FormModel();
3012 + $allForms = $formModel->get(['id', 'form_content']);
3013 +
3014 + foreach ($allForms as $form) {
3015 + // Decode form_content JSON to stdClass
3016 + $formContent = \json_decode($form->form_content);
3017 + $fields = $formContent->fields ?? null;
3018 + // If decode fails, skip to next
3019 + if (!$fields) {
3020 + Log::debug_log("Form ID {$form->id}: invalid JSON in form_content");
3021 + continue;
3022 + }
3023 +
3024 + // Replace default error messages using your helper
3025 + $updatedFields = Helpers::replaceFieldsDefaultErrorMsg($fields);
3026 +
3027 + $formContent->fields = $updatedFields;
3028 + // Encode back to JSON
3029 + $updatedContent = \json_encode($formContent);
3030 +
3031 + if (false === $updatedContent) {
3032 + Log::debug_log("Form ID {$form->id}: JSON encoding failed");
3033 + continue;
3034 + }
3035 + // Update form content in DB
3036 + $formModel->update(
3037 + ['form_content' => $updatedContent], // data to update
3038 + ['id' => $form->id] // condition for update
3039 + );
3040 + }
3041 + } catch (\Exception $e) {
3042 + Log::debug_log([
3043 + 'message' => 'Error in updateAllFormsErrorMessagesWithGlobalMessages',
3044 + 'error' => $e->getMessage(),
3045 + ]);
3046 + }
3047 + }
3048 +
2451 3049 public function updateSuccessMessageClassName($str, $msgIdx, $msgId)
2452 3050 {
2453 3051 $TEMP_CONF_ID = '_tmp_' . $msgIdx . '_conf_id';
2454 3052 return str_replace($TEMP_CONF_ID, $msgId, $str);
3053 + }
3054 +
3055 + public function startMigrationProcess()
3056 + {
3057 + Utilities::duplicateDbTable('form', 'form_v1');
3058 + Utilities::duplicateDbTable('workflows', 'workflows_v1');
3059 + Utilities::duplicateDbTable('success_messages', 'success_messages_v1');
3060 + MigrationHelper::duplicateV1StyleFiles();
3061 + $all_forms = $this->getAllForm();
3062 + $v1FormContents = [];
3063 + $defaultStyleClass = MigrationHelper::getBitformDefaultStyleClass();
3064 + foreach ($all_forms as $form) {
3065 + $formContents = $this->getAFormV1(['id' => $form->id], ['id' => $form->id]);
3066 + $v1FormContents[] = $formContents;
3067 + FrontEndScriptGenerator::saveCssFile("{$form->id}", $defaultStyleClass);
3068 + FrontEndScriptGenerator::saveCssFile("{$form->id}-formid", $defaultStyleClass);
3069 + }
3070 + set_transient('bitforms_v1_form_contents', $v1FormContents);
3071 + update_option('bitforms_migrating_to_v2', true);
2455 3072 }
2456 3073 }