PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / V3.3.2
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder vV3.3.2
V3.3.2 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 All 139 releases
← All changes | includes/Admin/Form/AdminFormHandler.php +1398 -477 2.0 → V3.3.2 View file →
@@ -16,29 +16,36 @@
16 16 use BitCode\BitForm\Core\Database\IntegrationModel;
17 17 use BitCode\BitForm\Core\Database\ReportsModel;
18 18 use BitCode\BitForm\Core\Database\SuccessMessageModel;
19 19 use BitCode\BitForm\Core\Database\WorkFlowModel;
20 +use BitCode\BitForm\Core\Form\FormManager;
20 21 use BitCode\BitForm\Core\Integration\IntegrationHandler;
21 22 use BitCode\BitForm\Core\Messages\EmailTemplateHandler;
22 23 use BitCode\BitForm\Core\Messages\SuccessMessageHandler;
24 +use BitCode\BitForm\Core\Migration\MigrationHelper;
23 25 use BitCode\BitForm\Core\Util\FileHandler;
24 26 use BitCode\BitForm\Core\Util\FormDuplicateHelper;
25 27 use BitCode\BitForm\Core\Util\HttpHelper;
26 28 use BitCode\BitForm\Core\Util\IpTool;
29 +use BitCode\BitForm\Core\Util\Log;
30 +use BitCode\BitForm\Core\Util\Utilities;
31 +use BitCode\BitForm\Core\WorkFlow\WorkFlow;
27 32 use BitCode\BitForm\Core\WorkFlow\WorkFlowHandler;
28 -use BitCode\BitForm\Core\WorkFlow\WorkFlowRunHelper;
29 33 use WP_Error;
30 34
31 -class AdminFormHandler {
35 +class AdminFormHandler
36 +{
32 37 private static $formModel;
33 38 private static $ipTool;
34 39
35 - public function __construct() {
40 + public function __construct()
41 + {
36 42 static::$formModel = new FormModel();
37 43 static::$ipTool = new IpTool();
38 44 }
39 45
40 - public function getTemplate($Request, $post) {
46 + public function getTemplate($Request, $post)
47 + {
41 48 $templateName = null;
42 49 $newFormId = null;
43 50 if (isset($post->template)) {
44 51 $templateName = $post->template;
@@ -49,9 +56,9 @@
49 56 $form_content_raw = $templateProvider->getTemplate($templateName, $newFormId);
50 57 if (!$form_content_raw) {
51 58 return new WP_Error('template_empty', __('Template not found, by this name: ', 'bit-form') . $templateName);
52 59 } else {
53 - return \json_encode(
60 + return wp_json_encode(
54 61 [
55 62 'id' => 0,
56 63 'form_content' => $form_content_raw,
57 64 ]
@@ -58,30 +65,30 @@
58 65 );
59 66 }
60 67 }
61 68
62 - public function saveStyleSheet($styles, $sheetName) {
69 + public function saveStyleSheet($styles, $sheetName)
70 + {
63 71 $this->createFormStylesDirIfNotExists();
64 72
65 73 if (
66 74 file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
67 - && is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
75 + && wp_is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
68 76 ) {
69 - $createStyleFile = fopen(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName, 'w');
70 - fwrite($createStyleFile, $styles);
71 - fclose($createStyleFile);
72 - return true;
77 + $filePath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName;
78 + return FileHandler::writeFile($filePath, $styles);
73 79 }
74 80 return false;
75 81 }
76 82
77 - public function saveLayoutStyleSheet($layout, $sheetName, $lay_row_height = 43) {
83 + public function saveLayoutStyleSheet($layout, $sheetName, $lay_row_height = 43)
84 + {
78 85 $md_width = 600;
79 86 $sm_width = 400;
80 87 $lg_styles = '';
81 88 $md_styles = '';
82 89 $sm_styles = '';
83 - for ($i = 0; $i < count($layout->lg); $i++) {
90 + for ($i = 0; $i < count($layout->lg ?? []); $i++) {
84 91 $fld = $layout->lg[$i];
85 92
86 93 if ($fld->w < 10) {
87 94 $lay_row_height = 43;
@@ -145,19 +152,18 @@
145 152 $this->createFormStylesDirIfNotExists();
146 153
147 154 if (
148 155 file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
149 - && is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
156 + && wp_is_writable(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')
150 157 ) {
151 - $createStyleFile = fopen(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName, 'w');
152 - fwrite($createStyleFile, $formStyle);
153 - fclose($createStyleFile);
154 - return true;
158 + $filePath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . $sheetName;
159 + return FileHandler::writeFile($filePath, $formStyle);
155 160 }
156 161 return false;
157 162 }
158 163
159 - public function createFormStylesDirIfNotExists() {
164 + public function createFormStylesDirIfNotExists()
165 + {
160 166 if (!file_exists(BITFORMS_UPLOAD_DIR)) {
161 167 wp_mkdir_p(BITFORMS_UPLOAD_DIR);
162 168 }
163 169 if (!file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles')) {
@@ -164,9 +170,18 @@
164 170 wp_mkdir_p(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles');
165 171 }
166 172 }
167 173
168 - public function createNewForm($Request, $post) {
174 + private function pdfFontDownload($fontName)
175 + {
176 + if (class_exists('\BitCode\BitFormPro\Admin\AppSetting\Pdf')) {
177 + \BitCode\BitFormPro\Admin\AppSetting\Pdf::getInstance()->fontsDownload($fontName);
178 + }
179 + return false;
180 + }
181 +
182 + public function createNewForm($Request, $post)
183 + {
169 184 // wp_send_json_success($post);
170 185 // exit;
171 186 if (!isset($post->form_id) || '' === $post->form_id) {
172 187 return new WP_Error('empty_form', __('Error Occurred, Please Reload', 'bit-form'));
@@ -176,17 +191,8 @@
176 191 if ($formManager->isExist()) {
177 192 return new WP_Error('empty_form', __('Error Occurred, Please Reload', 'bit-form'));
178 193 }
179 194
180 - if (!class_exists('BitCode\\BitFormPro\\Plugin')) {
181 - if (!empty($workFlows) && count($workFlows) > 2) {
182 - return new WP_Error(
183 - 'free_limit',
184 - __('You are allowed to add maximum 2 workflows ', 'bit-form')
185 - );
186 - }
187 - }
188 -
189 195 if (isset($post->formStyle) && $post->formStyle) {
190 196 $sheetName = 'bitform-' . $post->form_id . '.css';
191 197 $this->saveStyleSheet($post->formStyle, $sheetName);
192 198 }
@@ -192,15 +198,20 @@
192 198 }
193 199
194 200 if (isset($post->layoutChanged) && $post->layoutChanged) {
195 201 $sheetName = 'bitform-layout-' . $post->form_id . '.css';
196 - $rowHeight = (int)$post->rowHeight;
202 + $rowHeight = (int) $post->rowHeight;
197 203 $this->saveLayoutStyleSheet($post->layout, $sheetName, $rowHeight);
198 204 }
199 205
200 206 $fields = wp_unslash($post->fields);
207 + $fields = Helpers::replaceFieldsDefaultErrorMsg($fields);
208 + // $fields = $post->fields;
201 209 $layout = wp_unslash($post->layout);
210 + $nestedLayout = isset($post->nestedLayouts) ? wp_unslash($post->nestedLayouts) : (object) [];
211 + $formInfo = wp_unslash($post->formInfo);
202 212 $form_name = wp_unslash($post->form_name);
213 + $form_name = strlen($form_name) > 50 ? substr($form_name, 0, 50) : $form_name;
203 214 $formSettings = (object) wp_unslash($post->formSettings);
204 215 $atomic_class_map = isset($post->atomicClassMap) ? wp_unslash($post->atomicClassMap) : [];
205 216 $breakpointSize = wp_unslash($post->breakpointSize);
206 217 $style = $post->style;
@@ -212,9 +223,11 @@
212 223 $reports = !empty($post->reports) ? $post->reports : (object) [];
213 224 $builderSettings = (object) isset($post->builderSettings) ? wp_unslash($post->builderSettings) : [];
214 225
215 226 $mailTem = $formSettings->mailTem;
227 + $pdfTem = $formSettings->pdfTem;
216 228 $integrations = $formSettings->integrations;
229 + $formPermissions = $formSettings->formPermissions;
217 230
218 231 $user_details = static::$ipTool->getUserDetail();
219 232 if (empty($fields) || empty($layout)) {
220 233 return new WP_Error(
@@ -227,11 +240,18 @@
227 240 'empty_form',
228 241 __('Form Name Should Be Within 50 Characters', 'bit-form')
229 242 );
230 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);
248 + FormManager::syncCompositeChildNames($fields);
231 249 $form_content = [
232 - 'fields' => $fields,
233 - 'layout' => $layout,
250 + 'fields' => $fields,
251 + 'layout' => $layout,
252 + 'nestedLayout' => $nestedLayout,
253 + 'formInfo' => $formInfo,
234 254 ];
235 255
236 256 /* if (!empty($formSettings->submitBtn)) {
237 257 $form_content = array_merge($form_content, array('buttons' => $formSettings->submitBtn));
@@ -239,20 +259,26 @@
239 259 if (!empty($formSettings->theme)) {
240 260 $form_content = array_merge($form_content, ['theme' => $formSettings->theme]);
241 261 }
242 262
263 + if (!empty($formSettings->formPermissions)) {
264 + $form_content = array_merge($form_content, ['formPermissions' => $formSettings->formPermissions]);
265 + }
266 +
243 267 if (!empty($additional)) {
244 268 $form_content = array_merge($form_content, ['additional' => $additional]);
245 269 }
246 270 if (!empty($workFlows)) {
247 - $workFlowsStr = is_string($workFlows) ? $workFlows : json_encode($workFlows);
271 + $workflows = is_string($workFlows) ? json_decode($workFlows) : $workFlows;
248 272 $workFlowExist = [];
249 - if (\strpos($workFlowsStr, 'onload')) {
250 - $workFlowExist['onload'] = true;
273 + foreach ($workflows as $index => $workFlow) {
274 + if (in_array($workFlow->action_type, ['always', 'onload'])) {
275 + $workFlowExist['onload'] = true;
276 + }
277 + if ('oninput' === $workFlow->action_type || ('always' === $workFlow->action_type && 'cond' === $workFlow->action_behaviour)) {
278 + $workFlowExist['oninput'] = true;
279 + }
251 280 }
252 - if (\strpos($workFlowsStr, 'oninput')) {
253 - $workFlowExist['oninput'] = true;
254 - }
255 281 $form_content = array_merge($form_content, ['workFlowExist' => $workFlowExist]);
256 282 }
257 283 $form_content = \wp_json_encode($form_content);
258 284
@@ -286,10 +312,11 @@
286 312 return $save_status;
287 313 } elseif (!$save_status) {
288 314 return false;
289 315 } else {
290 - wp_mkdir_p(BITFORMS_UPLOAD_DIR . "/$save_status");
316 + FileHandler::createIndexFile(BITFORMS_UPLOAD_DIR . "/$save_status");
291 317 $formID = $save_status;
318 + do_action('bitform_admin_form_changed');
292 319 $integartionIDForWorkflow = [];
293 320 // Confiramtion Message [start]
294 321 if (!empty($formSettings->confirmation->type->successMsg)) {
295 322 $successMessages = $formSettings->confirmation->type->successMsg;
@@ -296,10 +323,13 @@
296 323 $successMessageHandler = new SuccessMessageHandler($formID, $user_details);
297 324 foreach ($successMessages as $messageKey => $messageDetail) {
298 325 $savedID = $successMessageHandler->saveMessage($messageDetail);
299 326 if ($savedID) {
300 - $msgIdx = empty($messageDetail->id) ? $messageKey : \json_encode(['id' => $messageDetail->id]);
327 + $msgIdx = empty($messageDetail->id) ? $messageKey : wp_json_encode(['id' => $messageDetail->id]);
301 328 $integartionIDForWorkflow['successMsg'][$msgIdx] = $savedID;
329 + if (!empty($messageDetail->clRef)) {
330 + $integartionIDForWorkflow['confirmationCLRef'][$messageDetail->clRef] = $savedID;
331 + }
302 332 $messageDetail->id = $savedID;
303 333 $style = $this->updateSuccessMessageClassName($style, $messageKey, $savedID);
304 334 $atomic_class_map = $this->updateSuccessMessageClassName($atomic_class_map, $messageKey, $savedID);
305 335 }
@@ -311,11 +341,13 @@
311 341 'themeColors' => $themeColors,
312 342 'builderSettings' => $builderSettings
313 343 ];
314 344 $update_status = static::$formModel->update(
315 - ['builder_helper_state' => \wp_json_encode($builder_helper_state),
316 - 'atomic_class_map' => $atomic_class_map, ],
317 345 [
346 + 'builder_helper_state' => \wp_json_encode($builder_helper_state),
347 + 'atomic_class_map' => $atomic_class_map,
348 + ],
349 + [
318 350 'id' => $formID,
319 351 ]
320 352 );
321 353 if (is_wp_error($update_status)) {
@@ -330,15 +362,55 @@
330 362 $emailTemplateHandler
331 363 = new EmailTemplateHandler($formID, $user_details);
332 364 foreach ($mailTem as $templateKey => $templateDetail) {
333 365 $savedID = $emailTemplateHandler->saveTemplate($templateDetail);
334 - if ($savedID) {
335 - $tempIdx = empty($templateDetail->id) ? $templateKey : \json_encode(['id' => $templateDetail->id]);
366 + if ($savedID && !is_wp_error($savedID)) {
367 + $tempIdx = empty($templateDetail->id) ? $templateKey : wp_json_encode(['id' => $templateDetail->id]);
336 368 $integartionIDForWorkflow['mailNotify'][$tempIdx] = $savedID;
369 + if (!empty($templateDetail->clRef)) {
370 + $integartionIDForWorkflow['emailCLRef'][$templateDetail->clRef] = $savedID;
371 + }
372 + // return the new id (and drop the temp ref) so the frontend reconciles the template
373 + $templateDetail->id = $savedID;
374 + unset($templateDetail->clRef);
337 375 }
338 376 }
339 377 }
340 378 // Email Template [end] */
379 +
380 + // PDF Template [start]
381 + if (!empty($pdfTem) && class_exists('\BitCode\BitFormPro\Core\Messages\PdfTemplateHandler')) {
382 + $pdfTemplateHandler = new \BitCode\BitFormPro\Core\Messages\PdfTemplateHandler($formID);
383 + $workFlowString = '';
384 + if (!empty($workFlows)) {
385 + $workFlowString = wp_json_encode($workFlows);
386 + }
387 + foreach ($pdfTem as $templateDetail) {
388 + $savedID = $pdfTemplateHandler->save($templateDetail);
389 +
390 + $pdfSetting = $templateDetail->setting;
391 +
392 + if (isset($pdfSetting->font->name) && !empty($pdfSetting->font->name)) {
393 + $this->pdfFontDownload($pdfSetting->font->name);
394 + }
395 + if (!empty($workFlowString)) {
396 + $workFlowString = str_replace('"pdfId":"{\"id\":\"' . $templateDetail->id . '\"}"', '"pdfId":"{\"id\":\"' . $savedID . '\"}"', $workFlowString);
397 + // Multi-PDF: remap ids inside "pdfIds":[...] segments only — email-template ids use
398 + // the same {\"id\":N} token shape, so a global replace would corrupt them.
399 + $workFlowString = preg_replace_callback(
400 + '/"pdfIds":\[[^\]]*\]/',
401 + function ($matches) use ($templateDetail, $savedID) {
402 + return str_replace('{\"id\":\"' . $templateDetail->id . '\"}', '{\"id\":\"' . $savedID . '\"}', $matches[0]);
403 + },
404 + $workFlowString
405 + );
406 + }
407 + }
408 + if (!empty($workFlowString) && !empty($workFlows)) {
409 + $workFlows = json_decode($workFlowString);
410 + }
411 + }
412 + // PDF Template [end] */
341 413 //Integration [start] */
342 414 $integrationHandler = new IntegrationHandler($formID, $user_details);
343 415 if (!empty($formSettings->confirmation->type)) {
344 416 $allIntegrations = $formSettings->confirmation->type;
@@ -343,8 +415,14 @@
343 415 if (!empty($formSettings->confirmation->type)) {
344 416 $allIntegrations = $formSettings->confirmation->type;
345 417 foreach ($allIntegrations as $integrationType => $integrationGroup) {
346 418 foreach ($integrationGroup as $singleIntegrationKey => $singleIntegrationDetail) {
419 + $integrationStatus = isset($singleIntegrationDetail->status) ? (int) $singleIntegrationDetail->status : 1;
420 + // Inline-CL temp ref for a brand-new redirect (reconciled to the real id for workflow rows).
421 + $clRef = isset($singleIntegrationDetail->clRef) ? $singleIntegrationDetail->clRef : null;
422 + if (is_object($singleIntegrationDetail)) {
423 + unset($singleIntegrationDetail->status, $singleIntegrationDetail->clRef);
424 + }
347 425 if (empty($singleIntegrationDetail->details)) {
348 426 $integrationName = $singleIntegrationDetail->title;
349 427 unset($singleIntegrationDetail->title, $singleIntegrationDetail->id);
350 428
@@ -354,14 +432,25 @@
354 432 $integrationName = $singleIntegrationDetail->title;
355 433 $integrationDetails = !is_string($singleIntegrationDetail->details) ?
356 434 wp_json_encode($singleIntegrationDetail->details) : $singleIntegrationDetail->details;
357 435 }
358 - $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
359 - $integIdx = empty($singleIntegrationDetail->id) ? $savedID : \json_encode(['id' => $singleIntegrationDetail->id]);
436 + $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form', $integrationStatus);
437 + $integIdx = empty($singleIntegrationDetail->id) ? $savedID : wp_json_encode(['id' => $singleIntegrationDetail->id]);
360 438 $integartionIDForWorkflow[$integrationType][$integIdx] = $savedID;
439 + if (!empty($clRef) && 'redirectPage' === $integrationType) {
440 + $integartionIDForWorkflow['redirectCLRef'][$clRef] = $savedID;
441 + }
361 442 }
362 443 }
363 444 }
445 + if (isset($formSettings->formAbandonment)) {
446 + $formAbandonment = $formSettings->formAbandonment;
447 +
448 + $integrationName = 'formAbandonment';
449 + $integrationType = 'formAbandonment';
450 + $abandonmentIntegDetails = wp_json_encode($formAbandonment);
451 + $integrationHandler->saveIntegration($integrationName, $integrationType, $abandonmentIntegDetails, 'formAbandonment');
452 + }
364 453 if (!empty($integrations)) {
365 454 foreach ($integrations as $singleIntegrationKey => $singleIntegrationDetail) {
366 455 $integrationName = $singleIntegrationDetail->name;
367 456 unset($singleIntegrationDetail->name);
@@ -368,8 +457,11 @@
368 457 $integrationType = $singleIntegrationDetail->type;
369 458 unset($singleIntegrationDetail->type);
370 459 $singleIntegrationDetailID = empty($singleIntegrationDetail->id) ? null : $singleIntegrationDetail->id;
371 460 unset($singleIntegrationDetail->id);
461 + // Inline-CL temp ref for a brand-new integration (reconciled to the real id for workflow rows).
462 + $clRef = isset($singleIntegrationDetail->__bf_cl_ref) ? $singleIntegrationDetail->__bf_cl_ref : null;
463 + unset($singleIntegrationDetail->__bf_cl_ref);
372 464 if (empty($singleIntegrationDetail->details)) {
373 465 $integrationDetails = !is_string($singleIntegrationDetail) ?
374 466 wp_json_encode($singleIntegrationDetail) : $singleIntegrationDetail;
375 467 } else {
@@ -376,10 +468,13 @@
376 468 $integrationDetails = !is_string($singleIntegrationDetail->details) ?
377 469 wp_json_encode($singleIntegrationDetail->details) : $singleIntegrationDetail->details;
378 470 }
379 471 $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
380 - $integIdx = empty($singleIntegrationDetailID) ? $singleIntegrationKey : \json_encode(['id' => $singleIntegrationDetailID]);
472 + $integIdx = empty($singleIntegrationDetailID) ? $singleIntegrationKey : wp_json_encode(['id' => $singleIntegrationDetailID]);
381 473 $integartionIDForWorkflow['integ'][$integIdx] = $savedID;
474 + if (!empty($clRef) && !is_wp_error($savedID)) {
475 + $integartionIDForWorkflow['integrationCLRef'][$clRef] = $savedID;
476 + }
382 477 }
383 478 }
384 479 //Integrations [end]
385 480 //wrokFlows [start]
@@ -399,10 +494,10 @@
399 494 // }
400 495 }
401 496 //wrokFlows [end]
402 497 $details = [
403 - 'report_name' => 'All Entries',
404 - 'hiddenColumns' => [],
498 + 'report_name' => __('All Entries', 'bit-form'),
499 + 'hiddenColumns' => ['__user_id', '__user_ip', '__referer', '__user_device', '__created_at', '__updated_at'],
405 500 'pageSize' => 10,
406 501 'sortBy' => [],
407 502 'filters' => [],
408 503 'globalFilter' => '',
@@ -430,24 +525,19 @@
430 525 $updated_data['form_content']['is_default'] = 1;
431 526 }
432 527 $updated_data['message'] = __('Form Saved successfully', 'bit-form');
433 528
529 + // Form and dependent rows are persisted; multilingual providers register
530 + // the form's translatable strings here.
531 + do_action('bitform_form_saved', $save_status);
532 +
434 533 return $updated_data;
435 534 }
436 535 }
437 536
438 - public function updateForm($Request, $post) {
439 - // wp_send_json_success($post);
440 - if ($post->formStyle) {
441 - $sheetName = 'bitform-' . $post->id . '.css';
442 - $this->saveStyleSheet($post->formStyle, $sheetName);
443 - }
444 -
445 - if ($post->layoutChanged) {
446 - $sheetName = 'bitform-layout-' . $post->id . '.css';
447 - $rowHeight = (int)$post->rowHeight;
448 - $this->saveLayoutStyleSheet($post->layout, $sheetName, $rowHeight);
449 - }
537 + public function updateForm($Request, $post)
538 + {
539 + // return ['checked dta'=>$post];
450 540 $formId = $post->id;
451 541 // get the builder_helper_state from the form
452 542 $builder_helper_state = self::$formModel->get(['builder_helper_state'], ['id' => $formId]);
453 543 $builder_helper_state = (!is_wp_error($builder_helper_state) && 1 === count($builder_helper_state) && isset($builder_helper_state[0]->builder_helper_state)) ? $builder_helper_state[0]->builder_helper_state : '{}';
@@ -470,14 +560,17 @@
470 560 if (property_exists($post, 'builderSettings')) {
471 561 $builder_helper_state->builderSettings = $post->builderSettings;
472 562 }
473 563 if (property_exists($post, 'atomicClassMap')) {
474 - $atomic_class_map = \wp_json_encode((object)$post->atomicClassMap);
564 + $atomic_class_map = \wp_json_encode((object) $post->atomicClassMap);
475 565 }
476 566
477 567 if (property_exists($post, 'fields') && null !== $post->fields && property_exists($post, 'layout') && $post->layout) {
478 568 $fields = wp_unslash($post->fields);
569 + $fields = Helpers::replaceFieldsDefaultErrorMsg($fields);
479 570 $layout = wp_unslash($post->layout);
571 + $nestedLayout = wp_unslash($post->nestedLayouts);
572 + $formInfo = wp_unslash($post->formInfo);
480 573 $formID = wp_unslash($post->id);
481 574 $form_name = wp_unslash($post->form_name);
482 575 $formSettings = wp_unslash($post->formSettings);
483 576 $workFlows = wp_unslash($post->workFlows);
@@ -485,27 +578,28 @@
485 578 $reports = wp_unslash($post->currentReport);
486 579 }
487 580
488 581 $mailTem = $formSettings->mailTem;
582 + $pdfTem = isset($formSettings->pdfTem) ? $formSettings->pdfTem : [];
489 583 $integrations = $formSettings->integrations;
490 584
491 585 $user_details = static::$ipTool->getUserDetail();
586 +
492 587 if (empty($fields) || empty($layout) || is_null($formID)) {
493 588 return new WP_Error('empty_form', 'Can not update empty form.');
494 589 }
495 590
496 - if (!class_exists('BitCode\\BitFormPro\\Plugin')) {
497 - if (count($workFlows) > 2) {
498 - return new WP_Error(
499 - 'free_limit',
500 - __('You are allowed to add maximum 2 workflows ', 'bit-form')
501 - );
502 - }
503 - }
591 + // Guard: never persist orphan fields (see createNewForm). Fail closed.
592 + $this->pruneOrphanFields($fields, $nestedLayout, $layout, $formID, $post);
593 + FormManager::syncCompositeChildNames($fields);
594 +
504 595 $form_content = [
505 - 'fields' => $fields,
506 - 'layout' => $layout,
596 + 'fields' => $fields,
597 + 'layout' => $layout,
598 + 'nestedLayout' => $nestedLayout,
599 + 'formInfo' => $formInfo,
507 600 ];
601 +
508 602 if (isset($post->report_id)) {
509 603 $form_content['report_id'] = wp_unslash($post->report_id);
510 604 }
511 605
@@ -514,8 +608,12 @@
514 608 } */
515 609 if (!empty($formSettings->theme)) {
516 610 $form_content = array_merge($form_content, ['theme' => $formSettings->theme]);
517 611 }
612 +
613 + if (!empty($formSettings->formPermissions)) {
614 + $form_content = array_merge($form_content, ['formPermissions' => $formSettings->formPermissions]);
615 + }
518 616 if (!empty($additional)) {
519 617 $form_content = array_merge($form_content, ['additional' => $additional]);
520 618 }
521 619 $integartionIDForWorkflow = [];
@@ -523,8 +621,9 @@
523 621 $newData['integation'] = 0;
524 622 $newData['mailTemplate'] = 0;
525 623 $newData['workflow'] = 0;
526 624 $newData['reports'] = 0;
625 + $newData['pdfTemplate'] = 0;
527 626 // Confiramtion Message [start]
528 627 if (!empty($formSettings->confirmation->type->successMsg)) {
529 628 $successMessages = $formSettings->confirmation->type->successMsg;
530 629 $successMessageHandler
@@ -534,8 +633,11 @@
534 633 $savedID = $successMessageHandler->saveMessage($messageDetail);
535 634 if ($savedID) {
536 635 $newData['settingConfiramation'] = 1;
537 636 $integartionIDForWorkflow['successMsg'][$messageKey] = $savedID;
637 + if (!empty($messageDetail->clRef)) {
638 + $integartionIDForWorkflow['confirmationCLRef'][$messageDetail->clRef] = $savedID;
639 + }
538 640 $messageDetail->id = $savedID;
539 641 $builder_helper_state->style = $this->updateSuccessMessageClassName($builder_helper_state->style, $messageKey, $savedID);
540 642 if (isset($atomic_class_map)) {
541 643 $atomic_class_map = $this->updateSuccessMessageClassName($atomic_class_map, $messageKey, $savedID);
@@ -547,9 +649,9 @@
547 649 }
548 650 unset($formSettings->confirmation->type->successMsg);
549 651 }
550 652 // return $formSettings;
551 - // Confiramtion Message [end] */
653 + // Confirmation Message [end] */
552 654 // Email Template [start]
553 655 if (!empty($mailTem)) {
554 656 $emailTemplateHandler
555 657 = new EmailTemplateHandler($formID, $user_details);
@@ -560,16 +662,44 @@
560 662 $newData['mailTemplate'] = 2;
561 663 } else {
562 664 $newData['mailTemplate'] = 1;
563 665 $integartionIDForWorkflow['mailTem'][$templateKey] = $savedID;
666 + if (!empty($templateDetail->clRef)) {
667 + $integartionIDForWorkflow['emailCLRef'][$templateDetail->clRef] = $savedID;
668 + }
669 + // return the new id (and drop the temp ref) so the frontend reconciles the template
670 + $templateDetail->id = $savedID;
671 + unset($templateDetail->clRef);
564 672 }
565 673 } else {
566 - $emailTemplateHandler->updateTemplate($templateDetail);
674 + $updated = $emailTemplateHandler->updateTemplate($templateDetail);
675 + if (is_wp_error($updated) && 'db_error' === $updated->get_error_code()) {
676 + $newData['mailTemplate'] = 2;
677 + } elseif (0 === $newData['mailTemplate']) {
678 + $newData['mailTemplate'] = 1;
679 + }
567 680 }
568 681 }
569 682 }
570 683 // return $formSettings;
571 684 // Email Template [end] */
685 + if (!empty($pdfTem) && class_exists('\BitCode\BitFormPro\Core\Messages\PdfTemplateHandler')) {
686 + $pdfTemplateHandler = new \BitCode\BitFormPro\Core\Messages\PdfTemplateHandler($formID);
687 +
688 + foreach ($pdfTem as $templateKey => $templateDetail) {
689 + if (isset($templateDetail->setting->font->name)) {
690 + $this->pdfFontDownload($templateDetail->setting->font->name);
691 + }
692 +
693 + $savedID = $pdfTemplateHandler->saveOrUpdate($templateDetail);
694 + if (is_wp_error($savedID) && 'result_empty' === $savedID->get_error_code()) {
695 + $newData['pdfTemplate'] = 2;
696 + } else {
697 + $newData['pdfTemplate'] = 1;
698 + $integartionIDForWorkflow['pdfTem'][$templateKey] = $savedID;
699 + }
700 + }
701 + }
572 702 //Integration [start] */
573 703 $integrationHandler = new IntegrationHandler($formID, $user_details);
574 704 if (!empty($formSettings->confirmation->type)) {
575 705 $allIntegrations = $formSettings->confirmation->type;
@@ -574,8 +704,15 @@
574 704 if (!empty($formSettings->confirmation->type)) {
575 705 $allIntegrations = $formSettings->confirmation->type;
576 706 foreach ($allIntegrations as $integrationType => $integrationGroup) {
577 707 foreach ($integrationGroup as $singleIntegrationKey => $singleIntegrationDetail) {
708 + // Redirect (and webhook) enable/disable toggle persists to the integration status column.
709 + $integrationStatus = isset($singleIntegrationDetail->status) ? (int) $singleIntegrationDetail->status : 1;
710 + // Inline-CL temp ref for a brand-new redirect (reconciled to the real id for workflow rows).
711 + $clRef = isset($singleIntegrationDetail->clRef) ? $singleIntegrationDetail->clRef : null;
712 + if (is_object($singleIntegrationDetail)) {
713 + unset($singleIntegrationDetail->status, $singleIntegrationDetail->clRef);
714 + }
578 715 if (empty($singleIntegrationDetail->details)) {
579 716 $integrationName = $singleIntegrationDetail->title;
580 717 unset($singleIntegrationDetail->title);
581 718 $singleIntegrationDetailID = empty($singleIntegrationDetail->id) ? null : $singleIntegrationDetail->id;
@@ -589,13 +726,16 @@
589 726 $integrationDetails = !is_string($singleIntegrationDetail->details) ?
590 727 wp_json_encode($singleIntegrationDetail->details) : $singleIntegrationDetail->details;
591 728 }
592 729 if (empty($singleIntegrationDetailID)) {
593 - $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
730 + $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form', $integrationStatus);
594 731 $newData['settingConfiramation'] = 1;
595 732 $integartionIDForWorkflow[$integrationType][$singleIntegrationKey] = $savedID;
733 + if (!empty($clRef) && 'redirectPage' === $integrationType) {
734 + $integartionIDForWorkflow['redirectCLRef'][$clRef] = $savedID;
735 + }
596 736 } else {
597 - $integrationHandler->updateIntegration($singleIntegrationDetailID, $integrationName, $integrationType, $integrationDetails, 'form');
737 + $integrationHandler->updateIntegration($singleIntegrationDetailID, $integrationName, $integrationType, $integrationDetails, 'form', $integrationStatus);
598 738 }
599 739 }
600 740 }
601 741 }
@@ -606,8 +746,11 @@
606 746 $integrationType = $singleIntegrationDetail->type;
607 747 unset($singleIntegrationDetail->type);
608 748 $singleIntegrationDetailID = empty($singleIntegrationDetail->id) ? null : $singleIntegrationDetail->id;
609 749 unset($singleIntegrationDetail->id);
750 + // Inline-CL temp ref for a brand-new integration (reconciled to the real id for workflow rows).
751 + $clRef = isset($singleIntegrationDetail->__bf_cl_ref) ? $singleIntegrationDetail->__bf_cl_ref : null;
752 + unset($singleIntegrationDetail->__bf_cl_ref);
610 753 if (empty($singleIntegrationDetail->details)) {
611 754 $integrationDetails = !is_string($singleIntegrationDetail) ?
612 755 wp_json_encode($singleIntegrationDetail) : $singleIntegrationDetail;
613 756 } else {
@@ -616,8 +759,11 @@
616 759 }
617 760 if (empty($singleIntegrationDetailID)) {
618 761 $savedID = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'form');
619 762 $integartionIDForWorkflow[$integrationType][$singleIntegrationKey] = $savedID;
763 + if (!empty($clRef) && !is_wp_error($savedID)) {
764 + $integartionIDForWorkflow['integrationCLRef'][$clRef] = $savedID;
765 + }
620 766 if (is_wp_error($savedID) && 'result_empty' !== $savedID->get_error_code()) {
621 767 $newData['integation'] = 2;
622 768 } else {
623 769 $newData['integation'] = 1;
@@ -632,16 +778,18 @@
632 778 $workFlowExist = [];
633 779 if (!empty($workFlows)) {
634 780 $workFlowHandler = new WorkFlowHandler($formID, $user_details);
635 781 // foreach ($workFlows as $workFlowIndex => $workFlow) {
782 + $workflows = is_string($workFlows) ? json_decode($workFlows) : $workFlows;
636 783
637 - foreach ($workFlows as $index => $workFlow) {
638 - if ('onload' === $workFlow->action_type) {
784 + foreach ($workflows as $index => $workFlow) {
785 + if (in_array($workFlow->action_type, ['always', 'onload'])) {
639 786 $workFlowExist['onload'] = true;
640 787 }
641 - if ('oninput' === $workFlow->action_type) {
788 + if ('oninput' === $workFlow->action_type || ('always' === $workFlow->action_type && 'cond' === $workFlow->action_behaviour)) {
642 789 $workFlowExist['oninput'] = true;
643 790 }
791 + // $integartionIDForWorkflow = apply_filters('bitform_filter_integration_id', $workFlow, $formID, $index);
644 792 if (empty($workFlow->id)) {
645 793 $savedID = $workFlowHandler->saveworkFlow($workFlow, $integartionIDForWorkflow, $index);
646 794 if (is_wp_error($savedID) && 'result_empty' !== $savedID->get_error_code()) {
647 795 $newData['workflow'] = 2;
@@ -648,9 +796,9 @@
648 796 } else {
649 797 $newData['workflow'] = 1;
650 798 }
651 799 } else {
652 - $newData = $workFlowHandler->updateworkFlow($workFlow->id, $workFlow, $integartionIDForWorkflow, $index, $newData);
800 + $newData = $workFlowHandler->updateworkFlow($workFlow->id, $workFlow, $integartionIDForWorkflow, $index, $newData);
653 801 }
654 802 }
655 803 }
656 804 $form_content = array_merge($form_content, ['workFlowExist' => $workFlowExist]);
@@ -655,8 +803,11 @@
655 803 }
656 804 $form_content = array_merge($form_content, ['workFlowExist' => $workFlowExist]);
657 805 //wrokFlows [end]
658 806 //reports [start] */
807 + // read unconditionally after the reports block; a save without currentReport
808 + // must not trip an undefined-variable warning
809 + $reportIsDefault = null;
659 810 if (!empty($reports)) {
660 811 $reportsModel = new ReportsModel();
661 812 $fieldNames = [];
662 813 foreach ($fields as $key => $field) {
@@ -668,15 +819,19 @@
668 819
669 820 $fieldNames['__user_id'] = __('User', 'bit-form');
670 821 $fieldNames['__user_ip'] = __('IP address', 'bit-form');
671 822 // $fieldNames['__user_location'] = __('');
672 - $fieldNames['__referer'] = __('Refer URL');
673 - $fieldNames['__user_device'] = __('Device');
674 - $fieldNames['__created_at'] = __('Created Time');
675 - $fieldNames['__updated_at'] = __('Modified Time');
823 + $fieldNames['__referer'] = __('Refer URL', 'bit-form');
824 + $fieldNames['__user_device'] = __('Device', 'bit-form');
825 + $fieldNames['__created_at'] = __('Created Time', 'bit-form');
826 + $fieldNames['__updated_at'] = __('Modified Time', 'bit-form');
676 827
677 828 $reportIsDefault = null;
678 - if (isset($form_content['report_id'])) {
829 + // The client posts currentReport as {} whenever its report atom has not resolved yet. That
830 + // is an empty stdClass, which empty() above treats as non-empty, so we reach here with
831 + // nothing to save — and writing the validated result would replace the stored report's
832 + // column order, hidden columns, page size and name with an empty list. Leave the row alone.
833 + if (isset($form_content['report_id']) && ReportsModel::isValidatableReport($reports)) {
679 834 $validDateReport = $reportsModel->validateReportFields($reports, $fieldNames);
680 835 if (isset($reports->isDefault)) {
681 836 $reportIsDefault = $reports->isDefault;
682 837 } else {
@@ -701,8 +856,26 @@
701 856 );
702 857 }
703 858 }
704 859 //reports [end]
860 + //form abandonment [start]
861 + if (!empty($formSettings->formAbandonment)) {
862 + $formAbandonment = $formSettings->formAbandonment;
863 + // search for existing form abandonment in integration table
864 + $abandonmentInteg = $integrationHandler->getAllIntegration('formAbandonment', 'formAbandonment');
865 + if (!is_wp_error($abandonmentInteg) && !empty($abandonmentInteg)) {
866 + $abandonmentInteg = $abandonmentInteg[0];
867 + }
868 + if (isset($abandonmentInteg->id)) {
869 + $abandonmentIntegID = $abandonmentInteg->id;
870 + $abandonmentIntegDetails = wp_json_encode($formAbandonment);
871 + $integrationHandler->updateIntegration($abandonmentIntegID, 'formAbandonment', 'formAbandonment', $abandonmentIntegDetails, 'formAbandonment');
872 + } else {
873 + $abandonmentIntegDetails = wp_json_encode($formAbandonment);
874 + $integrationHandler->saveIntegration('formAbandonment', 'formAbandonment', $abandonmentIntegDetails, 'formAbandonment');
875 + }
876 + }
877 + //form abandonment [end]
705 878 $form_content = wp_json_encode($form_content);
706 879 $updateData = [
707 880 'form_name' => $form_name,
708 881 'form_content' => $form_content,
@@ -707,24 +880,24 @@
707 880 'form_name' => $form_name,
708 881 'form_content' => $form_content,
709 882 'user_id' => $user_details['id'],
710 883 'user_ip' => $user_details['ip'],
711 - 'builder_helper_state' => \wp_json_encode((object)$builder_helper_state),
884 + 'builder_helper_state' => \wp_json_encode((object) $builder_helper_state),
712 885 'generated_script_page_ids' => \wp_json_encode((object) []),
713 886 'user_device' => $user_details['device'],
714 - 'status' => 1,
715 887 'updated_at' => $user_details['time'],
716 888 ];
717 889 if (isset($atomic_class_map)) {
718 890 $updateData['atomic_class_map'] = $atomic_class_map;
719 891 }
720 - $formUpdateVersion = get_option('bit-form_form_update_version');
892 + $formUpdateVersion = get_option('bitform_form_update_version');
721 893 if (!$formUpdateVersion) {
722 894 $formUpdateVersion = 1;
723 895 } else {
724 896 $formUpdateVersion = (int) $formUpdateVersion + 1;
725 897 }
726 - update_option('bit-form_form_update_version', $formUpdateVersion);
898 + update_option('bitform_form_update_version', $formUpdateVersion);
899 + do_action('bitform_admin_form_changed');
727 900
728 901 $update_status = static::$formModel->update(
729 902 $updateData,
730 903 [
@@ -741,9 +914,10 @@
741 914 unset($post->deletedFldKey);
742 915 }
743 916
744 917 $updated_data = $this->getAForm(['id' => $formID], null);
745 - if (0 === $newData['settingConfiramation'] && 0 === $newData['mailTemplate'] && 0 === $newData['integation']) {
918 +
919 + if (0 === $newData['settingConfiramation'] && 0 === $newData['mailTemplate'] && 0 === $newData['integation'] && 0 === $newData['pdfTemplate']) {
746 920 unset($updated_data['formSettings']);
747 921 }
748 922 if (0 === $newData['integation']) {
749 923 unset($updated_data['integrations']);
@@ -755,8 +929,13 @@
755 929 unset($updated_data['mailTem']);
756 930 } elseif (2 === $newData['mailTemplate']) {
757 931 $errorIN .= empty($errorIN) ? 'mailTemplate' : ', mailTemplate';
758 932 }
933 + if (0 === $newData['pdfTemplate']) {
934 + unset($updated_data['pdfTem']);
935 + } elseif (2 === $newData['pdfTemplate']) {
936 + $errorIN .= empty($errorIN) ? 'pdfTemplate' : ', pdfTemplate';
937 + }
759 938 if (0 === $newData['workflow']) {
760 939 unset($updated_data['workFlows']);
761 940 } elseif (2 === $newData['workflow']) {
762 941 $errorIN .= empty($errorIN) ? 'workflow' : ', workflow';
@@ -763,9 +942,14 @@
763 942 }
764 943 if (2 === $newData['reports']) {
765 944 $errorIN .= empty($errorIN) ? 'reports' : ', reports';
766 945 }
946 + // See createNewForm(). Fired before the partial-failure return: the rows
947 + // that did save are already persisted, so their strings still need registering.
948 + do_action('bitform_form_saved', $formId);
949 +
767 950 if (!empty($errorIN)) {
951 + /* translators: %s: comma-separated list of areas where error occurred */
768 952 $updated_data['message'] = sprintf(__('Error Occured in saving %s', 'bit-form'), $errorIN);
769 953 return new WP_Error('Form update Error.', $updated_data);
770 954 }
771 955 if (null !== $reportIsDefault) {
@@ -770,113 +954,169 @@
770 954 }
771 955 if (null !== $reportIsDefault) {
772 956 $updated_data['form_content']['is_default'] = $reportIsDefault;
773 957 }
774 - $updated_data['message'] = __('Form updated successfully.', 'bitform');
958 + $updated_data['message'] = __('Form updated successfully.', 'bit-form');
959 +
775 960 return $updated_data;
776 961 }
777 962 }
778 963
779 - public function setEmptyMetaValue($fieldkeys) {
780 - $sqlEscaped = array_map(function ($fld) {
781 - return "'" . esc_sql($fld) . "'";
782 - }, $fieldkeys);
783 - $deletedFldKey = implode(',', $sqlEscaped);
964 + /**
965 + * Drop orphan fields (present in `fields` but in no layout) from form_content
966 + * before persisting, and remove now-childless nestedLayout entries.
967 + *
968 + * Entry meta is NEVER touched here — only explicit user deletes clear meta
969 + * (see setEmptyMetaValue). Fail closed: an unusable/partial layout, or a
970 + * client that flagged a failed pre-save layout sync (->skipOrphanPrune),
971 + * prunes nothing. Pruned keys are logged, never silently dropped.
972 + *
973 + * @param object|array $fields by ref; orphan keys removed
974 + * @param object|null $nestedLayout by ref; stale container entries removed
975 + * @param array|object $layout raw layout (single or multi-step)
976 + * @param int|string|null $formID for logging ('new' when absent)
977 + * @param object $post raw request; honors ->skipOrphanPrune
978 + */
979 + private function pruneOrphanFields(&$fields, &$nestedLayout, $layout, $formID, $post)
980 + {
981 + $formLabel = $formID ? $formID : 'new';
982 + if (isset($post->skipOrphanPrune) && $post->skipOrphanPrune) {
983 + Log::debug_log('Bit Form: orphan prune skipped (client reported failed layout sync). FormID=' . $formLabel);
984 + return;
985 + }
986 + $orphanFldKeys = FormManager::computeOrphanFieldKeys($layout, $nestedLayout, $fields);
987 + // null = unusable/partial layout (fail closed); [] = nothing to prune
988 + if (!is_array($orphanFldKeys) || empty($orphanFldKeys)) {
989 + return;
990 + }
991 + Log::debug_log('Bit Form: pruning orphan fields (absent from every layout). FormID=' . $formLabel . ' keys=' . implode(',', $orphanFldKeys));
992 + foreach ($orphanFldKeys as $orphanKey) {
993 + if (is_object($fields)) {
994 + unset($fields->{$orphanKey});
995 + } elseif (is_array($fields)) {
996 + unset($fields[$orphanKey]);
997 + }
998 + }
999 + // prune stale nestedLayout entries — containers that no longer exist in fields
1000 + if (is_object($nestedLayout)) {
1001 + foreach (array_keys(get_object_vars($nestedLayout)) as $nKey) {
1002 + $parentExists = is_object($fields) ? isset($fields->{$nKey}) : isset($fields[$nKey]);
1003 + if (!$parentExists) {
1004 + unset($nestedLayout->{$nKey});
1005 + }
1006 + }
1007 + }
1008 + }
1009 +
1010 + public function setEmptyMetaValue($fieldkeys)
1011 + {
784 1012 global $wpdb;
785 - $tablename = $wpdb->prefix . 'bitforms_form_entrymeta';
786 - $sql = $wpdb->prepare("UPDATE $tablename SET meta_value = %s WHERE meta_key IN ( " . $deletedFldKey . ')', '');
787 - $wpdb->query($sql);
1013 + // Placeholders and arguments built dynamically from $fieldkeys; count matches at runtime.
1014 + $placeholders = implode(',', array_fill(0, \count($fieldkeys), '%s'));
1015 + $wpdb->query($wpdb->prepare(
1016 + "UPDATE `{$wpdb->prefix}bitforms_form_entrymeta` SET meta_value = %s WHERE meta_key IN ($placeholders)",
1017 + '',
1018 + ...$fieldkeys
1019 + ));
788 1020 }
789 1021
790 - public function changeFormStatus($Request, $post) {
791 - if (isset($Request['status']) && $Request['id']) {
792 - $status = wp_unslash($Request['status']);
793 - $id = wp_unslash($Request['id']);
794 - } else {
795 - $status = wp_unslash($post->status);
796 - $id = wp_unslash($post->id);
797 - }
798 - $user_details = static::$ipTool->getUserDetail();
799 - // return $post->fields;
800 - $status = 'true' === $status || true === $status ? true : false;
801 - if (!is_bool($status) || is_null($id)) {
802 - // echo $status;
803 - return new WP_Error('status_change', __('Form status change failed.', 'bit-form'));
804 - }
805 - $update_status = static::$formModel->update(
806 - [
807 - 'user_id' => $user_details['id'],
808 - 'user_ip' => $user_details['ip'],
809 - 'user_device' => $user_details['device'],
810 - 'status' => $status ? 1 : 0,
811 - 'updated_at' => $user_details['time'],
812 - ],
813 - [
814 - 'id' => $id,
815 - ]
816 - );
817 - if (is_wp_error($update_status)) {
818 - return $update_status;
819 - } elseif (!$update_status) {
820 - return false;
821 - } else {
822 - return __('Form status changed successfully', 'bit-form');
823 - }
1022 + public function changeFormStatus($Request, $post)
1023 + {
1024 + if (isset($Request['status']) && $Request['id']) {
1025 + $status = sanitize_text_field(wp_unslash($Request['status']));
1026 + $id = absint(wp_unslash($Request['id']));
1027 + } else {
1028 + $status = wp_unslash($post->status);
1029 + $id = wp_unslash($post->id);
824 1030 }
1031 + $user_details = static::$ipTool->getUserDetail();
1032 + // return $post->fields;
1033 + $status = 'true' === $status || true === $status ? true : false;
1034 + if (!is_bool($status) || is_null($id)) {
1035 + // echo $status;
1036 + return new WP_Error('status_change', __('Form status change failed.', 'bit-form'));
1037 + }
1038 + $update_status = static::$formModel->update(
1039 + [
1040 + 'user_id' => $user_details['id'],
1041 + 'user_ip' => $user_details['ip'],
1042 + 'user_device' => $user_details['device'],
1043 + 'status' => $status ? 1 : 0,
1044 + 'updated_at' => $user_details['time'],
1045 + ],
1046 + [
1047 + 'id' => $id,
1048 + ]
1049 + );
1050 + if (is_wp_error($update_status)) {
1051 + return $update_status;
1052 + } elseif (!$update_status) {
1053 + return false;
1054 + } else {
1055 + return __('Form status changed successfully', 'bit-form');
1056 + }
1057 + }
825 1058
826 - public function changeBulkFormStatus($Request, $post) {
827 - if (isset($Request['status']) && $Request['formID']) {
828 - $status = wp_unslash($Request['status']);
829 - $formID = wp_unslash($Request['formID']);
830 - } else {
831 - $status = wp_unslash($post->status);
832 - $formID = wp_unslash($post->formID);
833 - }
834 - $user_details = static::$ipTool->getUserDetail();
835 - // return $post->fields;
836 - $status = 'true' === $status || true === $status ? true : false;
837 - if (!is_bool($status) || is_null($formID) || !is_array($formID)) {
838 - // echo $status;
839 - return new WP_Error('status_change', __('Form status change failed.', 'bit-form'));
840 - }
841 - $update_status = static::$formModel->bulkUpdate(
842 - [
843 - 'user_id' => $user_details['id'],
844 - 'user_ip' => $user_details['ip'],
845 - 'user_device' => $user_details['device'],
846 - 'status' => $status ? 1 : 0,
847 - 'updated_at' => $user_details['time'],
848 - ],
849 - [
850 - 'id' => $formID,
851 - ]
852 - );
853 - if (is_wp_error($update_status)) {
854 - return $update_status;
855 - } elseif (!$update_status) {
856 - return false;
857 - } else {
858 - return __('Form status changed successfully', 'bit-form');
859 - }
1059 + public function changeBulkFormStatus($Request, $post)
1060 + {
1061 + if (isset($Request['status']) && $Request['formID']) {
1062 + $status = sanitize_text_field(wp_unslash($Request['status']));
1063 + $formID = array_map('absint', wp_unslash($Request['formID']));
1064 + } else {
1065 + $status = wp_unslash($post->status);
1066 + $formID = wp_unslash($post->formID);
860 1067 }
1068 + $user_details = static::$ipTool->getUserDetail();
1069 + // return $post->fields;
1070 + $status = 'true' === $status || true === $status ? true : false;
1071 + if (!is_bool($status) || is_null($formID) || !is_array($formID)) {
1072 + // echo $status;
1073 + return new WP_Error('status_change', __('Form status change failed.', 'bit-form'));
1074 + }
1075 + $update_status = static::$formModel->bulkUpdate(
1076 + [
1077 + 'user_id' => $user_details['id'],
1078 + 'user_ip' => $user_details['ip'],
1079 + 'user_device' => $user_details['device'],
1080 + 'status' => $status ? 1 : 0,
1081 + 'updated_at' => $user_details['time'],
1082 + ],
1083 + [
1084 + 'id' => $formID,
1085 + ]
1086 + );
1087 + if (is_wp_error($update_status)) {
1088 + return $update_status;
1089 + } elseif (!$update_status) {
1090 + return false;
1091 + } else {
1092 + return __('Form status changed successfully', 'bit-form');
1093 + }
1094 + }
861 1095
862 - public function getAllForm() {
863 - global $wpdb;
1096 + public function getAllForm()
1097 + {
1098 + global $wpdb;
864 1099
865 - $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");
1100 + // Direct query: table name interpolation only (no user input). $wpdb->prepare() cannot parameterize table names.
1101 + $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
1102 + FROM `{$wpdb->prefix}bitforms_form` as forms
1103 + LEFT JOIN `{$wpdb->prefix}bitforms_form_entries` as entries ON forms.id = entries.form_id
1104 + GROUP BY forms.id");
866 1105
867 - if (is_wp_error($allForms)) {
868 - return $allForms;
869 - } elseif (!$allForms) {
870 - return [];
871 - } else {
872 - return $allForms;
873 - }
1106 + if (is_wp_error($allForms)) {
1107 + return $allForms;
1108 + } elseif (!$allForms) {
1109 + return [];
1110 + } else {
1111 + return $allForms;
874 1112 }
1113 + }
875 1114
876 - public function getAForm($Request, $post) {
877 - if (isset($Request['id'])) {
878 - $formID = wp_unslash($Request['id']);
1115 + public function getAForm($Request, $post)
1116 + {
1117 + if (!empty($Request['id'])) {
1118 + $formID = absint(wp_unslash($Request['id']));
879 1119 } else {
880 1120 $formID = wp_unslash($post->id);
881 1121 }
882 1122 // return $post->fields;
@@ -891,11 +1131,13 @@
891 1131 $helper_states = $formManager->getFormHelperStates();
892 1132 if ($formManager->isExist()) {
893 1133 $form_content_arr = [
894 1134 'layout' => $form_content->layout,
1135 + 'nestedLayout' => !empty($form_content->nestedLayout) ? $form_content->nestedLayout : (object) [],
1136 + 'formInfo' => !empty($form_content->formInfo) ? $form_content->formInfo : (object) ['formName' => $formManager->getFormName()],
895 1137 'fields' => $form_content->fields,
896 1138 'form_name' => $formManager->getFormName(),
897 - 'workFlowExist' => $form_content->workFlowExist,
1139 + 'workFlowExist' => isset($form_content->workFlowExist) ? $form_content->workFlowExist : [],
898 1140 'report_id' => isset($form_content->report_id) ? $form_content->report_id : null
899 1141 ];
900 1142 $successMessageHandler
901 1143 = new SuccessMessageHandler($formID);
@@ -918,15 +1160,40 @@
918 1160 if (!is_wp_error($emailTemplates)) {
919 1161 foreach ($emailTemplates as $emailTemplatekey => $emailTemplatevalue) {
920 1162 $mailTem[] =
921 1163 [
922 - 'id' => $emailTemplatevalue->id,
923 - 'title' => $emailTemplatevalue->title,
924 - 'sub' => $emailTemplatevalue->sub,
925 - 'body' => $emailTemplatevalue->body,
1164 + 'id' => $emailTemplatevalue->id,
1165 + 'title' => $emailTemplatevalue->title,
1166 + 'sub' => $emailTemplatevalue->sub,
1167 + 'body' => $emailTemplatevalue->body,
1168 + 'status' => isset($emailTemplatevalue->status) ? (int) $emailTemplatevalue->status : 1,
1169 + 'config' => EmailTemplateHandler::normalizeConfig($emailTemplatevalue->config ?? null),
926 1170 ];
927 1171 }
928 1172 }
1173 + // get all pdf template
1174 + if (class_exists('\BitCode\BitFormPro\Core\Messages\PdfTemplateHandler')) {
1175 + $pdfTemplateHandler = new \BitCode\BitFormPro\Core\Messages\PdfTemplateHandler($formID);
1176 + $pdfTemplates = $pdfTemplateHandler->getAll();
1177 +
1178 + if (!is_wp_error($pdfTemplates)) {
1179 + foreach ($pdfTemplates as $value) {
1180 + $pdfTem[] =
1181 + [
1182 + 'id' => $value->id,
1183 + 'title' => $value->title,
1184 + 'setting' => json_decode($value->setting),
1185 + 'pdf_config' => !empty($value->pdf_config) ? json_decode($value->pdf_config) : (object) [
1186 + 'status' => 1,
1187 + 'header' => '',
1188 + 'footer' => '',
1189 + ],
1190 + 'body' => $value->body,
1191 + ];
1192 + }
1193 + }
1194 + }
1195 +
929 1196 $integrationHandler = new IntegrationHandler($formID);
930 1197 $formIntegrations = $integrationHandler->getAllIntegration('form');
931 1198 if (!is_wp_error($formIntegrations)) {
932 1199 foreach ($formIntegrations as $integrationkey => $integrationValue) {
@@ -931,10 +1198,11 @@
931 1198 if (!is_wp_error($formIntegrations)) {
932 1199 foreach ($formIntegrations as $integrationkey => $integrationValue) {
933 1200 if ('redirectPage' === $integrationValue->integration_type || 'webHooks' === $integrationValue->integration_type) {
934 1201 $integrationData = [
935 - 'id' => $integrationValue->id,
936 - 'title' => $integrationValue->integration_name,
1202 + 'id' => $integrationValue->id,
1203 + 'title' => $integrationValue->integration_name,
1204 + 'status' => isset($integrationValue->status) ? (int) $integrationValue->status : 1,
937 1205 ];
938 1206 if (!empty($integrationValue->integration_details)) {
939 1207 $integration_details = (array) json_decode($integrationValue->integration_details);
940 1208 $integrationData = array_merge($integration_details, $integrationData);
@@ -954,17 +1222,32 @@
954 1222 ];
955 1223 $integrations[] = array_merge(
956 1224 $integrationData,
957 1225 is_string($integrationValue->integration_details) ?
958 - (array) json_decode($integrationValue->integration_details) :
959 - $integrationValue->integration_details
1226 + (array) json_decode($integrationValue->integration_details) :
1227 + $integrationValue->integration_details
960 1228 );
961 1229 }
962 1230 }
963 1231 }
1232 +
1233 + // get form abandonment integration if exist
1234 + $formAbandonmentInteg = $integrationHandler->getAllIntegration('formAbandonment', 'formAbandonment');
1235 + if (!is_wp_error($formAbandonmentInteg) && !empty($formAbandonmentInteg)) {
1236 + $formAbandonmentInteg = $formAbandonmentInteg[0];
1237 + if (!empty($formAbandonmentInteg->integration_details)) {
1238 + $formAbandonment = json_decode($formAbandonmentInteg->integration_details);
1239 + } else {
1240 + $formAbandonment = (object) [];
1241 + }
1242 + } else {
1243 + $formAbandonment = (object) [];
1244 + }
1245 +
964 1246 $settingsContent = [
965 - 'formName' => $formManager->getFormName(),
966 - 'theme' => isset($form_content->theme) ? $form_content->theme : 'default',
1247 + 'formName' => $formManager->getFormName(),
1248 + 'theme' => isset($form_content->theme) ? $form_content->theme : 'default',
1249 + 'formPermissions' => isset($form_content->formPermissions) ? $form_content->formPermissions : (object) [],
967 1250 // 'submitBtn' => $form_content->buttons,
968 1251 ];
969 1252
970 1253 if (!empty($allConfirmation)) {
@@ -991,8 +1274,9 @@
991 1274 'integrations' => []
992 1275 ]
993 1276 );
994 1277 }
1278 +
995 1279 if (!empty($mailTem)) {
996 1280 $settingsContent = array_merge(
997 1281 $settingsContent,
998 1282 [
@@ -1006,8 +1290,40 @@
1006 1290 'mailTem' => []
1007 1291 ]
1008 1292 );
1009 1293 }
1294 +
1295 + if (!empty($pdfTem)) {
1296 + $settingsContent = array_merge(
1297 + $settingsContent,
1298 + [
1299 + 'pdfTem' => $pdfTem,
1300 + ]
1301 + );
1302 + } else {
1303 + $settingsContent = array_merge(
1304 + $settingsContent,
1305 + [
1306 + 'pdfTem' => [],
1307 + ]
1308 + );
1309 + }
1310 +
1311 + if (!empty($formAbandonment)) {
1312 + $settingsContent = array_merge(
1313 + $settingsContent,
1314 + [
1315 + 'formAbandonment' => $formAbandonment,
1316 + ]
1317 + );
1318 + } else {
1319 + $settingsContent = array_merge(
1320 + $settingsContent,
1321 + [
1322 + 'formAbandonment' => (object) [],
1323 + ]
1324 + );
1325 + }
1010 1326 $formSettings = [
1011 1327 'formSettings' => $settingsContent,
1012 1328 ];
1013 1329 $data = [
@@ -1018,12 +1334,14 @@
1018 1334 'style' => $helper_states->style,
1019 1335 'themeVars' => $helper_states->themeVars,
1020 1336 'themeColors' => $helper_states->themeColors,
1021 1337 'builderSettings' => $helper_states->builderSettings,
1338 + 'customCode' => FrontEndScriptGenerator::getCustomCodes($formID),
1022 1339 'additional' => empty($form_content->additional) ? [
1023 1340 'enabled' => [
1024 1341 'blocked_ip' => false,
1025 1342 'restrict_form' => false,
1343 + 'onePerIp' => false,
1026 1344 ],
1027 1345 'settings' => [
1028 1346 'restrict_form' => [
1029 1347 'day' => [],
@@ -1038,9 +1356,8 @@
1038 1356 ],
1039 1357 'entry_limit' => null,
1040 1358 'blocked_ip' => [],
1041 1359 ],
1042 - 'onePerIp' => false,
1043 1360 ] : $form_content->additional,
1044 1361 'created_at' => $formManager->getFormMetaData()['created_at'],
1045 1362 'views' => $formManager->getFormMetaData()['views'],
1046 1363 'entries' => $formManager->getFormMetaData()['entries'],
@@ -1081,9 +1398,9 @@
1081 1398 } else {
1082 1399 $returnedReportData[$reportKey]->details = [];
1083 1400 }
1084 1401 if ('1' === (string) $reportData->isDefault) {
1085 - $returnedReportData[$reportKey]->details->report_name = 'All Entries';
1402 + $returnedReportData[$reportKey]->details->report_name = __('All Entries', 'bit-form');
1086 1403 }
1087 1404 }
1088 1405 $reports = ['reports' => $returnedReportData];
1089 1406 $data = array_merge($data, $reports);
@@ -1095,31 +1412,262 @@
1095 1412 return $data;
1096 1413 }
1097 1414 }
1098 1415
1099 - public function deleteAForm($Request, $post) {
1100 - if (isset($Request['id'])) {
1101 - $formID = wp_unslash($Request['id']);
1416 + public function getAFormV1($Request, $post)
1417 + {
1418 + if (!empty($Request['id'])) {
1419 + $formID = absint(wp_unslash($Request['id']));
1102 1420 } else {
1103 1421 $formID = wp_unslash($post->id);
1104 1422 }
1423 + // return $post->fields;
1105 1424 if (is_null($formID)) {
1106 1425 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1107 1426 }
1427 + $formManager = new AdminFormManager($formID);
1428 + if (!$formManager->isExist()) {
1429 + return new WP_Error('not_exists', __('Form is not exists.', 'bit-form'));
1430 + }
1431 + $form_content = $formManager->getFormContent();
1432 + if ($formManager->isExist()) {
1433 + $form_content_arr = [
1434 + 'layout' => $form_content->layout,
1435 + 'fields' => $form_content->fields,
1436 + 'form_name' => $formManager->getFormName(),
1437 + 'workFlowExist' => isset($form_content->workFlowExist) ? $form_content->workFlowExist : [],
1438 + 'report_id' => isset($form_content->report_id) ? $form_content->report_id : null
1439 + ];
1440 + $successMessageHandler
1441 + = new SuccessMessageHandler($formID);
1442 + $successMessages = $successMessageHandler->getAllMessage();
1443 + if (!is_wp_error($successMessages)) {
1444 + foreach ($successMessages as $sucessMessagekey => $sucessMessagevalue) {
1445 + $allConfirmation['type']['successMsg'][$sucessMessagekey] =
1446 + [
1447 + 'id' => $sucessMessagevalue->id,
1448 + 'title' => $sucessMessagevalue->message_title,
1449 + 'msg' => $sucessMessagevalue->message_content,
1450 + 'config' => !empty($sucessMessagevalue->message_config) ? json_decode($sucessMessagevalue->message_config) : (object) [],
1451 + ];
1452 + }
1453 + }
1454 + $emailTemplateHandler
1455 + = new EmailTemplateHandler($formID);
1456 + $emailTemplates = $emailTemplateHandler->getAllTemplate();
1457 + if (!is_wp_error($emailTemplates)) {
1458 + foreach ($emailTemplates as $emailTemplatekey => $emailTemplatevalue) {
1459 + $mailTem[] =
1460 + [
1461 + 'id' => $emailTemplatevalue->id,
1462 + 'title' => $emailTemplatevalue->title,
1463 + 'sub' => $emailTemplatevalue->sub,
1464 + 'body' => $emailTemplatevalue->body,
1465 + 'status' => isset($emailTemplatevalue->status) ? (int) $emailTemplatevalue->status : 1,
1466 + 'config' => EmailTemplateHandler::normalizeConfig($emailTemplatevalue->config ?? null),
1467 + ];
1468 + }
1469 + }
1470 + $integrationHandler = new IntegrationHandler($formID);
1471 + $formIntegrations = $integrationHandler->getAllIntegration('form');
1472 + if (!is_wp_error($formIntegrations)) {
1473 + foreach ($formIntegrations as $integrationkey => $integrationValue) {
1474 + if ('redirectPage' === $integrationValue->integration_type || 'webHooks' === $integrationValue->integration_type) {
1475 + $integrationData = [
1476 + 'id' => $integrationValue->id,
1477 + 'title' => $integrationValue->integration_name,
1478 + 'status' => isset($integrationValue->status) ? (int) $integrationValue->status : 1,
1479 + ];
1480 + if (!empty($integrationValue->integration_details)) {
1481 + // var_dump(json_decode($integrationValue->integration_details));
1482 + $integration_details = (array) json_decode($integrationValue->integration_details);
1483 + $integrationData = array_merge($integration_details, $integrationData);
1484 + } else {
1485 + $integration_details = [
1486 + 'url' => '',
1487 + ];
1488 + $integrationData = array_merge($integrationData, $integration_details);
1489 + }
1490 + $allConfirmation['type'][$integrationValue->integration_type][]
1491 + = $integrationData;
1492 + } else {
1493 + $integrationData = [
1494 + 'id' => $integrationValue->id,
1495 + 'name' => $integrationValue->integration_name,
1496 + 'type' => $integrationValue->integration_type,
1497 + ];
1498 + $integrations[] = array_merge(
1499 + $integrationData,
1500 + is_string($integrationValue->integration_details) ?
1501 + (array) json_decode($integrationValue->integration_details) :
1502 + $integrationValue->integration_details
1503 + );
1504 + }
1505 + }
1506 + }
1507 + $settingsContent = [
1508 + 'formName' => $formManager->getFormName(),
1509 + 'theme' => $form_content->theme,
1510 + // 'submitBtn' => $form_content->buttons,
1511 + ];
1512 +
1513 + if (!empty($allConfirmation)) {
1514 + $confirmation = [
1515 + 'confirmation' => $allConfirmation,
1516 + ];
1517 + } else {
1518 + $confirmation = [
1519 + 'confirmation' => ['type' => []],
1520 + ];
1521 + }
1522 + $settingsContent = array_merge($settingsContent, $confirmation);
1523 + if (!empty($integrations)) {
1524 + $settingsContent = array_merge(
1525 + $settingsContent,
1526 + [
1527 + 'integrations' => $integrations,
1528 + ]
1529 + );
1530 + } else {
1531 + $settingsContent = array_merge(
1532 + $settingsContent,
1533 + [
1534 + 'integrations' => []
1535 + ]
1536 + );
1537 + }
1538 + if (!empty($mailTem)) {
1539 + $settingsContent = array_merge(
1540 + $settingsContent,
1541 + [
1542 + 'mailTem' => $mailTem,
1543 + ]
1544 + );
1545 + } else {
1546 + $settingsContent = array_merge(
1547 + $settingsContent,
1548 + [
1549 + 'mailTem' => []
1550 + ]
1551 + );
1552 + }
1553 + $formSettings = [
1554 + 'formSettings' => $settingsContent,
1555 + ];
1556 + $data = [
1557 + 'id' => $formID,
1558 + 'form_name' => $formManager->getFormName(),
1559 + 'form_content' => $form_content_arr,
1560 + 'additional' => empty($form_content->additional) ? [
1561 + 'enabled' => [
1562 + 'blocked_ip' => false,
1563 + 'restrict_form' => false,
1564 + 'onePerIp' => false,
1565 + ],
1566 + 'settings' => [
1567 + 'restrict_form' => [
1568 + 'day' => [],
1569 + 'date' => [
1570 + 'from' => null,
1571 + 'to' => null,
1572 + ],
1573 + 'time' => [
1574 + 'from' => null,
1575 + 'to' => null,
1576 + ],
1577 + ],
1578 + 'entry_limit' => null,
1579 + 'blocked_ip' => [],
1580 + ],
1581 + ] : $form_content->additional,
1582 + 'created_at' => $formManager->getFormMetaData()['created_at'],
1583 + 'views' => $formManager->getFormMetaData()['views'],
1584 + 'entries' => $formManager->getFormMetaData()['entries'],
1585 + 'status' => $formManager->getFormMetaData()['status'],
1586 + ];
1587 + $data = array_merge($data, $formSettings);
1588 + $workFlowHandler = new WorkFlowHandler($formID);
1589 + $workFlows = ['workFlows' => $workFlowHandler->getAllworkFlowV1()];
1590 + $data = array_merge($data, $workFlows);
1591 + $reportsModel = new ReportsModel();
1592 + $returnedReportData = $reportsModel->get(
1593 + [
1594 + 'id',
1595 + 'details',
1596 + 'isDefault',
1597 + 'type',
1598 + ],
1599 + [
1600 + 'category' => 'form',
1601 + 'context' => $formID,
1602 + ]
1603 + );
1604 +
1605 + if (!is_wp_error($returnedReportData)) {
1606 + $fieldNames = [];
1607 + foreach ($formManager->getFieldLabel() as $key => $field) {
1608 + $fieldNames[$field['key']] = isset($field->lbl) ? $field->lbl : '';
1609 + }
1610 + if ($formManager->isGCLIDEnabled()) {
1611 + $fieldNames['GCLID'] = 'GCLID';
1612 + }
1613 + foreach ($returnedReportData as $reportKey => $reportData) {
1614 + $reportDetails = $reportsModel->validateReportFields($reportData, $fieldNames);
1615 + if (!empty($reportDetails) && is_string($reportDetails)) {
1616 + $returnedReportData[$reportKey]->details = json_decode($reportDetails);
1617 + } elseif (!empty($reportDetails)) {
1618 + $returnedReportData[$reportKey]->details = $reportDetails;
1619 + } else {
1620 + $returnedReportData[$reportKey]->details = [];
1621 + }
1622 + if ('1' === (string) $reportData->isDefault) {
1623 + $returnedReportData[$reportKey]->details->report_name = __('All Entries', 'bit-form');
1624 + }
1625 + }
1626 + $reports = ['reports' => $returnedReportData];
1627 + $data = array_merge($data, $reports);
1628 + }
1629 +
1630 + $formFields = $formManager->getFieldLabel();
1631 + $data = array_merge($data, ['Labels' => $formFields]);
1632 +
1633 + return $data;
1634 + }
1635 + }
1636 +
1637 + public function deleteAForm($Request, $post)
1638 + {
1639 + if (!empty($Request['id'])) {
1640 + $formID = absint(wp_unslash($Request['id']));
1641 + } else {
1642 + $formID = wp_unslash($post->id);
1643 + }
1644 + if (is_null($formID)) {
1645 + return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1646 + }
1647 +
1648 + if (!filter_var($formID, FILTER_VALIDATE_INT)) {
1649 + return new WP_Error('invalid_form', __('Form id is invalid.', 'bit-form'));
1650 + }
1651 +
1108 1652 $delete_status = static::$formModel->delete(
1109 1653 [
1110 1654 'id' => $formID,
1111 1655 ]
1112 1656 );
1657 + do_action('bitform_admin_form_changed');
1113 1658
1114 - $successMessageModel
1115 - = new SuccessMessageModel();
1659 + $successMessageModel = new SuccessMessageModel();
1116 1660 $deleteMsgStatus = $successMessageModel->delete(['form_id' => $formID]);
1117 1661
1118 - $emailTemplateModel
1119 - = new EmailTemplateModel();
1662 + $emailTemplateModel = new EmailTemplateModel();
1120 1663 $deleteTemplateStatus = $emailTemplateModel->delete(['form_id' => $formID]);
1121 1664
1665 + if (class_exists('\BitCode\BitFormPro\Core\Database\PdfTemplateModel')) {
1666 + $pdfTemplateModel = new \BitCode\BitFormPro\Core\Database\PdfTemplateModel();
1667 + $deletePdfTemplateStatus = $pdfTemplateModel->delete(['form_id' => $formID]);
1668 + }
1669 +
1122 1670 $integrationModel = new IntegrationModel();
1123 1671 $deleteIntegrationStatus = $integrationModel->delete(['form_id' => $formID]);
1124 1672
1125 1673 $workFlowModel = new WorkFlowModel();
@@ -1131,22 +1679,11 @@
1131 1679 $formEntryModel = new FormEntryModel();
1132 1680 $returnedEntries = $formEntryModel->get('id', ['form_id' => $formID]);
1133 1681 $entries = [];
1134 1682
1135 - if (!is_wp_error($returnedEntries)) {
1136 - foreach ($returnedEntries as $entryKey => $entryInfo) {
1137 - $entries[] = $entryInfo->id;
1138 - }
1139 - global $wpdb;
1140 - $prefix = $wpdb->prefix;
1141 - if (count($entries) > 0) {
1142 - $deleteEntriesStatus = $formEntryModel->bulkDelete(
1143 - [
1144 - "`{$prefix}bitforms_form_entries`.`id`" => $entries,
1145 - "`{$prefix}bitforms_form_entries`.`form_id`" => $formID,
1146 - ]
1147 - );
1148 - }
1683 + // checked namespace exist
1684 + if (class_exists('\BitCode\BitFormPro\Admin\BfTable\Table')) {
1685 + \BitCode\BitFormPro\Admin\BfTable\Table::deleteTableByFormId($formID);
1149 1686 }
1150 1687
1151 1688 $fileHandler = new FileHandler();
1152 1689 if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID)) {
@@ -1154,35 +1691,54 @@
1154 1691 }
1155 1692 if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID)) {
1156 1693 $fileHandler->rmrf(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID);
1157 1694 }
1158 - if (file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-' . $formID . '.css')) {
1159 - unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-' . $formID . '.css');
1160 - }
1161 - if (file_exists(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-layout-' . $formID . '.css')) {
1162 - unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-layout-' . $formID . '.css');
1163 - }
1695 + $formStylePath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles';
1696 +
1697 + $formCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-' . $formID . '.css';
1698 + FileHandler::deleteIsFileExists($formCssPath);
1699 +
1700 + $layoutCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-layout-' . $formID . '.css';
1701 + FileHandler::deleteIsFileExists($layoutCssPath);
1702 +
1703 + $customCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.css';
1704 + FileHandler::deleteIsFileExists($customCssPath);
1705 +
1706 + $customJSPath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-scripts' . DIRECTORY_SEPARATOR . 'bitform-custom-' . $formID . '.js';
1707 + FileHandler::deleteIsFileExists($customJSPath);
1708 +
1709 + $conversationCssPath = $formStylePath . DIRECTORY_SEPARATOR . 'bitform-conversational-' . $formID . '.css';
1710 + FileHandler::deleteIsFileExists($conversationCssPath);
1711 +
1164 1712 return is_wp_error($delete_status) ? $delete_status : __('Form deleted successfully.', 'bit-form');
1165 1713 }
1166 1714
1167 - public function deleteBlukForm($Request, $post) {
1168 - if (isset($Request['formID'])) {
1169 - $formID = wp_unslash($Request['formID']);
1715 + public function deleteBlukForm($Request, $post)
1716 + {
1717 + if (!empty($Request['formID'])) {
1718 + $formID = array_map('absint', wp_unslash($Request['formID']));
1170 1719 } else {
1171 1720 $formID = wp_unslash($post->formID);
1172 1721 }
1722 +
1173 1723 if (is_null($formID) || !is_array($formID)) {
1174 1724 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1175 1725 }
1726 +
1727 + if (!Helpers::checkIsIntArr($formID)) {
1728 + return new WP_Error('invalid_form', __('Form id is invalid', 'bit-form'));
1729 + }
1730 +
1176 1731 $cssPath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR;
1177 1732 foreach ($formID as $id) {
1178 - unlink($cssPath . 'bitform-' . $id . '.css');
1179 - if (file_exists($cssPath . 'bitform-layout-' . $id . '.css')) {
1180 - unlink($cssPath . 'bitform-layout-' . $id . '.css');
1181 - }
1182 - if (file_exists($cssPath . 'bitform-' . $id . '-formid' . '.css')) {
1183 - unlink($cssPath . 'bitform-' . $id . '-formid' . '.css');
1184 - }
1733 + $formCssPath = $cssPath . 'bitform-' . $id . '.css';
1734 + FileHandler::deleteIsFileExists($formCssPath);
1735 +
1736 + $layoutPath = $cssPath . 'bitform-layout-' . $id . '.css';
1737 + FileHandler::deleteIsFileExists($layoutPath);
1738 +
1739 + $bitformCustomPath = $cssPath . 'bitform-' . $id . '-formid' . '.css';
1740 + FileHandler::deleteIsFileExists($bitformCustomPath);
1185 1741 }
1186 1742 $delete_status = static::$formModel->bulkDelete(
1187 1743 [
1188 1744 'id' => $formID,
@@ -1187,16 +1743,26 @@
1187 1743 [
1188 1744 'id' => $formID,
1189 1745 ]
1190 1746 );
1747 + do_action('bitform_admin_form_changed');
1748 +
1749 + if (is_wp_error($delete_status)) {
1750 + wp_send_json_error($delete_status->get_error_message(), 400);
1751 + }
1752 +
1191 1753 $successMessageModel
1192 1754 = new SuccessMessageModel();
1193 1755 $deleteMsgStatus = $successMessageModel->bulkDelete(['form_id' => $formID]);
1194 1756
1195 - $emailTemplateModel
1196 - = new EmailTemplateModel();
1757 + $emailTemplateModel = new EmailTemplateModel();
1197 1758 $deleteTemplateStatus = $emailTemplateModel->bulkDelete(['form_id' => $formID]);
1198 1759
1760 + if (class_exists('\BitCode\BitFormPro\Core\Database\PdfTemplateModel')) {
1761 + $pdfTemplateModel = new \BitCode\BitFormPro\Core\Database\PdfTemplateModel();
1762 + $deletePdfTemplateStatus = $pdfTemplateModel->bulkDelete(['form_id' => $formID]);
1763 + }
1764 +
1199 1765 $integrationModel = new IntegrationModel();
1200 1766 $deleteIntegrationStatus = $integrationModel->bulkDelete(['form_id' => $formID]);
1201 1767
1202 1768 $workFlowModel = new WorkFlowModel();
@@ -1206,11 +1772,18 @@
1206 1772 $deleteReportStatus = $reportsModel->bulkDelete(['context' => $formID]);
1207 1773
1208 1774 $formEntryModel = new FormEntryModel();
1209 1775 $returnedEntries = $formEntryModel->get('id', ['form_id' => $formID]);
1776 +
1777 + if (class_exists('\BitCode\BitFormPro\Admin\BfTable\Table')) {
1778 + \BitCode\BitFormPro\Admin\BfTable\Table::deleteTableByBulkFormId($formID);
1779 + }
1780 +
1210 1781 $entries = [];
1211 1782 foreach ($returnedEntries as $entryKey => $entryInfo) {
1212 - $entries[] = $entryInfo->id;
1783 + if (!empty($entryInfo->id)) {
1784 + $entries[] = $entryInfo->id;
1785 + }
1213 1786 }
1214 1787 global $wpdb;
1215 1788 $prefix = $wpdb->prefix;
1216 1789 if (count($entries) > 0) {
@@ -1221,22 +1794,26 @@
1221 1794 ]
1222 1795 );
1223 1796 }
1224 1797
1798 + // delete entry log and details; table name interpolation only — $wpdb->prepare() parameterizes the form_id value.
1799 + $wpdb->query($wpdb->prepare(
1800 + "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",
1801 + $formID
1802 + ));
1803 +
1225 1804 $fileHandler = new FileHandler();
1226 1805 foreach ($formID as $fId) {
1227 1806 if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $fId)) {
1228 1807 $fileHandler->rmrf(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $fId);
1229 1808 }
1230 -
1231 - // unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-' . $fId . '.css');
1232 - // unlink(BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . 'bitform-layout-' . $fId . '.css');
1233 1809 }
1234 1810
1235 1811 return is_wp_error($delete_status) ? $delete_status : __('Forms deleted successfully.', 'bit-form');
1236 1812 }
1237 1813
1238 - public function genarateNewLayoutNField($layout, $fields, $oldId, $newId) {
1814 + public function genarateNewLayoutNField($layout, $fields, $oldId, $newId)
1815 + {
1239 1816 $newField = (object) [];
1240 1817 foreach ($layout->lg as $ind => $itm) {
1241 1818 $fld_tmp = $fields->{$layout->lg[$ind]->i};
1242 1819 $layout->lg[$ind]->i = str_replace("bf$oldId-", "bf$newId-", $layout->lg[$ind]->i);
@@ -1246,9 +1823,10 @@
1246 1823 }
1247 1824 return ['layout' => $layout, 'fields' => $newField];
1248 1825 }
1249 1826
1250 - public function duplicateAForm($Request, $post) {
1827 + public function duplicateAForm($Request, $post)
1828 + {
1251 1829 $oldFormId = intval($post->id);
1252 1830 $newFormId = intval($post->newFormId);
1253 1831 if ($oldFormId < 1) {
1254 1832 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
@@ -1270,21 +1848,15 @@
1270 1848 if (!is_wp_error($duplicateResponse) || (is_wp_error($duplicateResponse) && 'result_empty' === $duplicateResponse->get_error_code())) {
1271 1849 // duplicate stylesheet
1272 1850 $style_dir = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR;
1273 1851 // layout style copy
1274 - $mainFormLayStyle = fopen($style_dir . 'bitform-layout-' . $oldFormId . '.css', 'r');
1275 - $styleStr = fread($mainFormLayStyle, filesize($style_dir . 'bitform-layout-' . $oldFormId . '.css'));
1852 + $styleStr = FileHandler::readFile($style_dir . 'bitform-layout-' . $oldFormId . '.css');
1276 1853 $newStyle = str_replace(".bf$oldFormId-", ".bf$newFormId-", $styleStr);
1277 - $createStyleFile = fopen($style_dir . "bitform-layout-$newFormId.css", 'w');
1278 - fwrite($createStyleFile, $newStyle);
1279 - fclose($createStyleFile);
1854 + FileHandler::writeFile($style_dir . "bitform-layout-$newFormId.css", $newStyle);
1280 1855 // style copy
1281 - $mainFormStyle = fopen($style_dir . 'bitform-' . $oldFormId . '.css', 'r');
1282 - $styleStr = fread($mainFormStyle, filesize($style_dir . 'bitform-' . $oldFormId . '.css'));
1856 + $styleStr = FileHandler::readFile($style_dir . 'bitform-' . $oldFormId . '.css');
1283 1857 $newStyle = str_replace("-$oldFormId", "-$newFormId", $styleStr);
1284 - $createStyleFile = fopen($style_dir . "bitform-$newFormId.css", 'w');
1285 - fwrite($createStyleFile, $newStyle);
1286 - fclose($createStyleFile);
1858 + FileHandler::writeFile($style_dir . "bitform-$newFormId.css", $newStyle);
1287 1859 $duplicatedForm = $this->getAForm(null, (object) ['id' => $newFormId]);
1288 1860 $duplicatedForm['message'] = __('Form duplicated successfully', 'bit-form');
1289 1861 return $duplicatedForm;
1290 1862 }
@@ -1290,19 +1862,22 @@
1290 1862 }
1291 1863 return $duplicateResponse;
1292 1864 }
1293 1865
1294 - public function importAForm($post) {
1866 + public function importAForm($post)
1867 + {
1295 1868 $oldFormId = !empty($post->formDetail->form_id) ? $post->formDetail->form_id : null;
1296 1869 $newFormId = intval($post->newFormId);
1297 1870 if (!$oldFormId || empty($post->formDetail->fields) || empty($post->formDetail->layout)) {
1298 1871 return new WP_Error('invalid_form', __('Please import a valid json.', 'bit-form'));
1299 1872 }
1300 - $importtedForm = (array)$post->formDetail;
1873 + $importtedForm = (array) $post->formDetail;
1301 1874 $importtedForm['form_content']['fields'] = $importtedForm['fields'];
1302 1875 $importtedForm['form_content']['layout'] = $importtedForm['layout'];
1303 - unset($importtedForm['fields'], $importtedForm['layout']);
1304 - $formData = FormDuplicateHelper::createReplica((array)$importtedForm, $oldFormId, $newFormId);
1876 + $importtedForm['form_content']['nestedLayout'] = $importtedForm['nestedLayouts'];
1877 + $importtedForm['form_content']['formInfo'] = $importtedForm['formInfo'];
1878 + unset($importtedForm['fields'], $importtedForm['layout'], $importtedForm['nestedLayout'], $importtedForm['formInfo']);
1879 + $formData = FormDuplicateHelper::createReplica((array) $importtedForm, $oldFormId, $newFormId);
1305 1880
1306 1881 if (!empty($importtedForm['rowHeight'])) {
1307 1882 $formData->rowHeight = $importtedForm['rowHeight'];
1308 1883 }
@@ -1312,10 +1887,29 @@
1312 1887 $formData->layoutChanged = '-';
1313 1888 }
1314 1889 $importResponse = $this->createNewForm(null, $formData);
1315 1890
1891 + $customCode = [];
1892 + // add custom js
1893 + if (!empty($formData->customCode->customJs)) {
1894 + $customJsPath = 'form-scripts';
1895 + $fileName = 'bitform-custom-' . $newFormId . '.js';
1896 + $customCode['JavaScript'] = $formData->customCode->customJs;
1897 + Helpers::saveFile($customJsPath, $fileName, $formData->customCode->customJs, 'w');
1898 + }
1899 + // add custom css
1900 + if (!empty($formData->customCode->customCss)) {
1901 + $customCssPath = 'form-styles';
1902 + $fileName = 'bitform-custom-' . $newFormId . '.css';
1903 + $customCode['CSS'] = $formData->customCode->customCss;
1904 + Helpers::saveFile($customCssPath, $fileName, $formData->customCode->customCss, 'w');
1905 + }
1906 + if (!is_wp_error($importResponse)) {
1907 + $importResponse['customCode'] = $customCode;
1908 + }
1316 1909 if (!is_wp_error($importResponse) || (is_wp_error($importResponse) && 'result_empty' === $importResponse->get_error_code())) {
1317 1910 $responseData = $this->getAForm(null, (object) ['id' => $newFormId]);
1911 + $responseData['customCode'] = $customCode;
1318 1912 $responseData['message'] = __('Form imported successfully.', 'bit-form');
1319 1913 return $responseData;
1320 1914 }
1321 1915 return $importResponse;
@@ -1342,9 +1936,9 @@
1342 1936 // $formData->formStyle = str_replace("-$oldFormId", "-$newFormId", $styleStr);
1343 1937 // $formData->layoutChanged = '-';
1344 1938 // $formData->rowHeight = FormDuplicateHelper::calcRowHeight($styleStr, $oldFormId);
1345 1939
1346 - // $formJson = json_encode($formData);
1940 + // $formJson = wp_json_encode($formData);
1347 1941 // header('Content-Type: application/force-download');
1348 1942 // header('Content-Type: application/octet-stream');
1349 1943 // header('Content-Type: application/download');
1350 1944 // header('Content-Disposition: attachment; filename="bitform_export_' . $oldFormId . '.json"');
@@ -1358,9 +1952,10 @@
1358 1952 // echo $formJson;
1359 1953 // die();
1360 1954 // }
1361 1955
1362 - public function exportAForm($Request, $post) {
1956 + public function exportAForm($Request, $post)
1957 + {
1363 1958 $oldFormId = intval($post->id);
1364 1959 if ($oldFormId < 1) {
1365 1960 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1366 1961 }
@@ -1369,9 +1964,9 @@
1369 1964 return new WP_Error('empty_form', __('Form does not exists.', 'bit-form'));
1370 1965 }
1371 1966
1372 1967 $newFormId = $oldFormId;
1373 - $duplicatedForm = $this->getAForm($Request, (object)['id' => $oldFormId]);
1968 + $duplicatedForm = $this->getAForm($Request, (object) ['id' => $oldFormId]);
1374 1969 $formData = FormDuplicateHelper::createReplica($duplicatedForm, $oldFormId, $newFormId);
1375 1970 // for custom css
1376 1971 $customCssPath = 'form-styles/bitform-custom-' . $newFormId . '.css';
1377 1972 $cssPath = Helpers::generatePathDirOrFile($customCssPath);
@@ -1389,11 +1984,12 @@
1389 1984 }
1390 1985 return $formData;
1391 1986 }
1392 1987
1393 - public function getFormEntryLabelAndCount($Request, $post) {
1394 - if (isset($Request['id'])) {
1395 - $id = wp_unslash($Request['id']);
1988 + public function getFormEntryLabelAndCount($Request, $post)
1989 + {
1990 + if (!empty($Request['id'])) {
1991 + $id = absint(wp_unslash($Request['id']));
1396 1992 } else {
1397 1993 $id = wp_unslash($post->id);
1398 1994 }
1399 1995 if (is_null($id)) {
@@ -1446,15 +2042,74 @@
1446 2042 $response['fieldDetails'] = $labels;
1447 2043 return $response;
1448 2044 }
1449 2045
1450 - public function getFormEntry($Request, $post) {
1451 - if (isset($Request['id'])) {
1452 - $id = wp_unslash($Request['id']);
2046 + /**
2047 + * Sanitize a request-supplied entry query condition before it reaches the Model
2048 + * layer. Only known entry-table columns are allowed as condition keys, operators
2049 + * are restricted to a safe allow-list ['form_id' => $id] when nothing valid remains.
2050 + *
2051 + * @param mixed $queryCondition
2052 + * @param int $id
2053 + * @return array
2054 + */
2055 + private function sanitizeEntryQueryCondition($queryCondition, $id)
2056 + {
2057 + $allowedColumns = [
2058 + 'id', 'form_id', 'status', 'created_at', 'updated_at',
2059 + 'user_id', 'user_ip', 'user_device', 'user_location', 'referer',
2060 + ];
2061 + $allowedOperators = ['=', '!=', '<>', '<', '>', '<=', '>=', 'LIKE', 'IN'];
2062 +
2063 + if (!is_array($queryCondition)) {
2064 + return ['form_id' => $id];
2065 + }
2066 +
2067 + $safe = [];
2068 + foreach ($queryCondition as $key => $value) {
2069 + if (!in_array($key, $allowedColumns, true)) {
2070 + continue;
2071 + }
2072 +
2073 + if (is_array($value)) {
2074 + // Never honour a request-supplied raw SQL fragment.
2075 + unset($value['raw']);
2076 +
2077 + if (array_key_exists('operator', $value) || array_key_exists('value', $value)) {
2078 + $operator = isset($value['operator']) && is_string($value['operator'])
2079 + ? strtoupper(trim($value['operator']))
2080 + : '=';
2081 + if (!in_array($operator, $allowedOperators, true)) {
2082 + $operator = '=';
2083 + }
2084 + $condValue = isset($value['value']) ? $value['value'] : '';
2085 + if (is_scalar($condValue)) {
2086 + $safe[$key] = ['operator' => $operator, 'value' => $condValue];
2087 + }
2088 + } else {
2089 + // IN-style list: keep scalar members only (bound by the Model).
2090 + $list = array_values(array_filter($value, 'is_scalar'));
2091 + if (!empty($list)) {
2092 + $safe[$key] = $list;
2093 + }
2094 + }
2095 + } elseif (is_scalar($value)) {
2096 + $safe[$key] = $value;
2097 + }
2098 + }
2099 +
2100 + return empty($safe) ? ['form_id' => $id] : $safe;
2101 + }
2102 +
2103 + public function getFormEntry($Request, $post)
2104 + {
2105 + if (!empty($Request['id'])) {
2106 + $id = absint(wp_unslash($Request['id']));
1453 2107 $offset = isset($Request['offset']) ?
1454 2108 wp_unslash($Request['offset']) : 0;
1455 2109 $pageSize = isset($Request['pageSize']) ?
1456 2110 wp_unslash($Request['pageSize']) : 10;
2111 + $queryCondition = isset($Request['queryCondition']) ? wp_unslash($Request['queryCondition']) : ['form_id' => $id];
1457 2112 } else {
1458 2113 $id = wp_unslash($post->id);
1459 2114 $conditions = isset($post->conditions) ? wp_unslash($post->conditions) : [];
1460 2115 $dateBetweenFilter = isset($post->entriesFilterByDate) ? wp_unslash($post->entriesFilterByDate) : [];
@@ -1463,9 +2118,11 @@
1463 2118 $filters = isset($post->filters) ? wp_unslash($post->filters) : null;
1464 2119 $globalFilter = isset($post->globalFilter) ? wp_unslash($post->globalFilter) : null;
1465 2120 $pageSize = isset($post->pageSize) ?
1466 2121 wp_unslash($post->pageSize) : 10;
2122 + $queryCondition = isset($post->queryCondition) ? wp_unslash($post->queryCondition) : ['form_id' => $id];
1467 2123 }
2124 + $queryCondition = $this->sanitizeEntryQueryCondition($queryCondition, $id);
1468 2125 if (is_null($id)) {
1469 2126 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
1470 2127 }
1471 2128 $formManager = new AdminFormManager($id);
@@ -1471,9 +2128,90 @@
1471 2128 $formManager = new AdminFormManager($id);
1472 2129 if (!$formManager->isExist()) {
1473 2130 return new WP_Error('empty_form', __('Form does not exists', 'bit-form'));
1474 2131 }
2132 + $formEntry = new FormEntryModel();
2133 + $entries = $formEntry->get(
2134 + 'id',
2135 + $queryCondition,
2136 + null,
2137 + null,
2138 + 'created_at',
2139 + 'DESC'
2140 + );
2141 + if (is_wp_error($entries)) {
2142 + if ('result_empty' === $entries->get_error_code()) {
2143 + return [
2144 + 'count' => 0,
2145 + 'entries' => [],
2146 + ];
2147 + }
2148 + return $entries;
2149 + }
2150 + $filter['field'] = $filters;
2151 + $filter['global'] = $globalFilter;
1475 2152
2153 + $formFields = $formManager->getFieldLabel(true);
2154 + $fieldDetails = $formManager->getFields();
2155 +
2156 + $entryMeta = new FormEntryMetaModel();
2157 + $formEntries = $entryMeta->getEntryMeta($formFields, $entries, $pageSize, $offset, $filter, $sortBy, $conditions, $dateBetweenFilter);
2158 + $customFieldHandler = new CustomFieldHandler();
2159 + $formEntries = $customFieldHandler->updatedEntries($formEntries, $fieldDetails);
2160 + return $formEntries;
2161 + }
2162 +
2163 + public function getSingleEntry($formId, $entryId)
2164 + {
2165 + $formManager = new AdminFormManager($formId);
2166 + if (!$formManager->isExist()) {
2167 + return new WP_Error('empty_form', __('Form does not exists', 'bit-form'));
2168 + }
2169 +
2170 + $formFields = $formManager->getFieldLabel(true);
2171 + $fieldDetails = $formManager->getFields();
2172 + $entryMeta = new FormEntryMetaModel();
2173 + // $formEntry = new FormEntryModel();
2174 +
2175 + $entries = $entryMeta->getSingleEntryMeta($formFields, $entryId);
2176 +
2177 + if (!is_wp_error($entries)) {
2178 + return $entries[0];
2179 + }
2180 + return new WP_Error('entry_not_fonud', __('Entry Not Found!', 'bit-form'));
2181 +
2182 + // $customFieldHandler = new CustomFieldHandler();
2183 + // $formEntries = $customFieldHandler->updatedEntries($formEntries, $fieldDetails);
2184 + // return $formEntries;
2185 + }
2186 +
2187 + public function getEntriesForReport($Request, $post)
2188 + {
2189 + if (!empty($Request['id'])) {
2190 + $id = absint(wp_unslash($Request['id']));
2191 + $offset = isset($Request['offset']) ?
2192 + wp_unslash($Request['offset']) : null;
2193 + $pageSize = isset($Request['pageSize']) ?
2194 + wp_unslash($Request['pageSize']) : null;
2195 + } else {
2196 + $id = wp_unslash($post->id);
2197 + $conditions = isset($post->conditions) ? wp_unslash($post->conditions) : [];
2198 + $dateBetweenFilter = isset($post->entriesFilterByDate) ? wp_unslash($post->entriesFilterByDate) : [];
2199 + $offset = isset($post->offset) ? wp_unslash($post->offset) : null;
2200 + $sortBy = isset($post->sortBy) ? wp_unslash($post->sortBy) : null;
2201 + $filters = isset($post->filters) ? wp_unslash($post->filters) : null;
2202 + $globalFilter = isset($post->globalFilter) ? wp_unslash($post->globalFilter) : null;
2203 + $pageSize = isset($post->pageSize) ?
2204 + wp_unslash($post->pageSize) : null;
2205 + }
2206 + if (is_null($id)) {
2207 + return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
2208 + }
2209 + $formManager = new AdminFormManager($id);
2210 + if (!$formManager->isExist()) {
2211 + return new WP_Error('empty_form', __('Form does not exists', 'bit-form'));
2212 + }
2213 +
1476 2214 $formEntry = new FormEntryModel();
1477 2215 $entries = $formEntry->get(
1478 2216 'id',
1479 2217 [
@@ -1502,188 +2240,213 @@
1502 2240 $entryMeta = new FormEntryMetaModel();
1503 2241 $formEntries = $entryMeta->getEntryMeta($formFields, $entries, $pageSize, $offset, $filter, $sortBy, $conditions, $dateBetweenFilter);
1504 2242 $customFieldHandler = new CustomFieldHandler();
1505 2243 $formEntries = $customFieldHandler->updatedEntries($formEntries, $fieldDetails);
2244 + $formEntries['entries'] = Helpers::filterNullEntries($formEntries['entries']);
1506 2245 return $formEntries;
1507 2246 }
1508 2247
1509 - public function getExportEntry($post) {
1510 - $formId = wp_unslash($post->formId);
1511 - $fileFormate = isset($post->fileFormate) ? wp_unslash($post->fileFormate) : null;
1512 - $limit = isset($post->limit) ? wp_unslash($post->limit) : null;
1513 - $sortBy = isset($post->sort) ? wp_unslash($post->sort) : 'ASC';
1514 - $sortByField = isset($post->sortField) ? wp_unslash($post->sortField) : 'bitforms_form_entry_id';
1515 - $formFields = json_decode($post->selectedField);
1516 - if (is_null($formId)) {
1517 - return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
2248 + public function getExportEntry($post)
2249 + {
2250 + $formId = wp_unslash($post->formId);
2251 + $fileFormate = isset($post->fileFormate) ? wp_unslash($post->fileFormate) : null;
2252 + $limit = isset($post->limit) ? wp_unslash($post->limit) : null;
2253 + $entryCondition = isset($post->filter) ? wp_unslash($post->filter) : null;
2254 + $sortBy = isset($post->sort) ? wp_unslash($post->sort) : 'ASC';
2255 + $sortByField = isset($post->sortField) ? wp_unslash($post->sortField) : 'bitforms_form_entry_id';
2256 + $formFields = json_decode($post->selectedField);
2257 + if (is_null($formId)) {
2258 + return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
2259 + }
2260 + $formManager = new AdminFormManager($formId);
2261 + $fieldLabels = $formManager->getFieldLabel(true);
2262 + if (!$formManager->isExist()) {
2263 + return new WP_Error('empty_form', __('Form does not exists', 'bit-form'));
2264 + }
2265 +
2266 + $formEntry = new FormEntryModel();
2267 + $entries = $formEntry->get(
2268 + 'id',
2269 + [
2270 + 'form_id' => $formId,
2271 + ],
2272 + null,
2273 + null,
2274 + 'created_at',
2275 + 'DESC'
2276 + );
2277 + if (is_wp_error($entries)) {
2278 + if ('result_empty' === $entries->get_error_code()) {
2279 + return [
2280 + 'count' => 0,
2281 + 'entries' => [],
2282 + ];
1518 2283 }
1519 - $formManager = new AdminFormManager($formId);
1520 - $fieldLabels = $formManager->getFieldLabel(true);
1521 - if (!$formManager->isExist()) {
1522 - return new WP_Error('empty_form', __('Form does not exists', 'bit-form'));
1523 - }
2284 + return $entries;
2285 + }
2286 + $entryMeta = new FormEntryMetaModel();
2287 + $filter = [];
2288 + $formEntries = $entryMeta->getExportEntry($formFields, $entries, $formId, $fieldLabels, $limit, $sortBy, $sortByField, $offset, $entryCondition);
2289 + return $formEntries;
2290 + }
1524 2291
1525 - $formEntry = new FormEntryModel();
1526 - $entries = $formEntry->get(
1527 - 'id',
1528 - [
1529 - 'form_id' => $formId,
1530 - ],
1531 - null,
1532 - null,
1533 - 'created_at',
1534 - 'DESC'
1535 - );
1536 - if (is_wp_error($entries)) {
1537 - if ('result_empty' === $entries->get_error_code()) {
1538 - return [
1539 - 'count' => 0,
1540 - 'entries' => [],
1541 - ];
1542 - }
1543 - return $entries;
1544 - }
1545 - $entryMeta = new FormEntryMetaModel();
1546 - $filter = [];
1547 - $formEntries = $entryMeta->getExportEntry($formFields, $entries, $formId, $fieldLabels, $limit, $sortBy, $sortByField);
1548 - return $formEntries;
2292 + public function deleteBlukFormEntries($Request, $post)
2293 + {
2294 + if (!empty($Request['formID'])) {
2295 + $formID = absint(wp_unslash($Request['formID']));
2296 + $entries = array_map('absint', wp_unslash($Request['entries']));
2297 + } else {
2298 + $formID = wp_unslash($post->formID);
2299 + $entries = wp_unslash($post->entries);
1549 2300 }
2301 + if (is_null($formID) || !is_array($entries) || 0 === count($entries)) {
2302 + return new WP_Error('empty_form', __('Invalid Form ID or Entries ID.', 'bit-form'));
2303 + }
1550 2304
1551 - public function deleteBlukFormEntries($Request, $post) {
1552 - if (isset($Request['formID'])) {
1553 - $formID = wp_unslash($Request['formID']);
1554 - $entries = wp_unslash($Request['entries']);
2305 + if (!filter_var($formID, FILTER_VALIDATE_INT)) {
2306 + return new WP_Error('invalid_form', __('Form id is invalid.', 'bit-form'));
2307 + }
2308 +
2309 + if (!Helpers::checkIsIntArr($entries)) {
2310 + return new WP_Error('invalid_entries', __('Entries id is invalid', 'bit-form'));
2311 + }
2312 +
2313 + $formManager = new AdminFormManager($formID);
2314 + if (!$formManager->isExist()) {
2315 + return new WP_Error('empty_form', __('Form does not exist.', 'bit-form'));
2316 + }
2317 + $workFlowRunHelper = new WorkFlow($formID);
2318 +
2319 + $workFlowreturnedOnDelete = $workFlowRunHelper->executeOnDelete(
2320 + $formManager,
2321 + $formID,
2322 + $entries
2323 + );
2324 +
2325 + if (isset($workFlowreturnedOnDelete['entries'])) {
2326 + if (0 === count($workFlowreturnedOnDelete['entries'])) {
2327 + return ['message' => __('Entry Deletetion prevented by workflow', 'bit-form')];
2328 + } elseif (count($workFlowreturnedOnDelete['entries']) === count($entries)) {
2329 + $message = __('Entry Deleted successfully', 'bit-form');
1555 2330 } else {
1556 - $formID = wp_unslash($post->formID);
1557 - $entries = wp_unslash($post->entries);
2331 + $result['prevented'] = array_diff($entries, $workFlowreturnedOnDelete['entries']);
2332 + $entries = $workFlowreturnedOnDelete['entries'];
2333 + $message = __('Entry Deleted successfully, Some prevented by workflow', 'bit-form');
1558 2334 }
1559 - if (is_null($formID) || !is_array($entries) || 0 === count($entries)) {
1560 - return new WP_Error('empty_form', __('Invalid Form ID or Entries ID.', 'bit-form'));
1561 - }
1562 - $formManager = new AdminFormManager($formID);
1563 - if (!$formManager->isExist()) {
1564 - return new WP_Error('empty_form', __('Form does not exist.', 'bit-form'));
1565 - }
1566 - $workFlowRunHelper = new WorkFlowRunHelper($formID);
1567 - $workFlowreturnedOnDelete = $workFlowRunHelper->executeOnDelete(
1568 - $formManager,
1569 - $formID,
1570 - $entries
1571 - );
1572 - if (isset($workFlowreturnedOnDelete['entries'])) {
1573 - if (0 === count($workFlowreturnedOnDelete['entries'])) {
1574 - return ['message' => __('Entry Deletetion prevented by workflow', 'bit-form')];
1575 - } elseif (count($workFlowreturnedOnDelete['entries']) === count($entries)) {
1576 - $message = __('Entry Deleted successfully', 'bit-form');
1577 - } else {
1578 - $result['prevented'] = array_diff($entries, $workFlowreturnedOnDelete['entries']);
1579 - $entries = $workFlowreturnedOnDelete['entries'];
1580 - $message = __('Entry Deleted successfully, Some prevented by workflow', 'bit-form');
2335 + } else {
2336 + $message = __('Entry Deleted successfully', 'bit-form');
2337 + }
2338 + global $wpdb;
2339 + $prefix = $wpdb->prefix;
2340 + $formEntryModel = new FormEntryModel();
2341 + $delete_status = $formEntryModel->bulkDelete(
2342 + [
2343 + "`{$prefix}bitforms_form_entries`.`id`" => $entries,
2344 + "`{$prefix}bitforms_form_entries`.`form_id`" => $formID,
2345 + ]
2346 + );
2347 + if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID)) {
2348 + $fileHandler = new FileHandler();
2349 + foreach ($entries as $enrtyKey => $entryID) {
2350 + $fileEntries = $fileHandler->getEntriesFileUploadDir($formID, $entryID);
2351 + if (file_exists($fileEntries)) {
2352 + $fileHandler->rmrf($fileEntries);
1581 2353 }
1582 - } else {
1583 - $message = __('Entry Deleted successfully', 'bit-form');
1584 2354 }
1585 - global $wpdb;
1586 - $prefix = $wpdb->prefix;
1587 - $formEntryModel = new FormEntryModel();
1588 - $delete_status = $formEntryModel->bulkDelete(
2355 + }
2356 + $count = $formEntryModel->count(
2357 + [
2358 + 'form_id' => $formID,
2359 + ]
2360 + );
2361 + $formManager->resetSubmissionCount(intval($count[0]->count));
2362 + if (is_wp_error($delete_status)) {
2363 + return new WP_Error('entry_not_exists', __('Form entry deletion failed.', 'bit-form'));
2364 + }
2365 + $result['message'] = $message;
2366 + return $result;
2367 + }
2368 +
2369 + public function duplicateFormEntry($Request, $post)
2370 + {
2371 + if (!empty($Request['formID'])) {
2372 + $formID = absint(wp_unslash($Request['formID']));
2373 + $entries = array_map('absint', wp_unslash($Request['entries']));
2374 + } else {
2375 + $formID = wp_unslash($post->formID);
2376 + $entries = wp_unslash($post->entries);
2377 + }
2378 + if (is_null($formID) || !is_array($entries) || empty($entries)) {
2379 + return new WP_Error('empty_form', __('Form id or entries id is invalid', 'bit-form'));
2380 + }
2381 +
2382 + if (!filter_var($formID, FILTER_VALIDATE_INT)) {
2383 + return new WP_Error('invalid_form', __('Form id is invalid', 'bit-form'));
2384 + }
2385 +
2386 + if (!Helpers::checkIsIntArr($entries)) {
2387 + return new WP_Error('invalid_entries', __('Entries id is invalid', 'bit-form'));
2388 + }
2389 +
2390 + $formManager = new AdminFormManager($formID);
2391 + if (!$formManager->isExist()) {
2392 + return new WP_Error('empty_form', __('Form does not exist', 'bit-form'));
2393 + }
2394 + $formEntryModel = new FormEntryModel();
2395 + $entryMeta = new FormEntryMetaModel();
2396 + $user_details = static::$ipTool->getUserDetail();
2397 + $total_entries = count($entries);
2398 + $duplicate_count = 0;
2399 + $test = '';
2400 + $fileHandler = new FileHandler();
2401 + $result = [];
2402 + foreach ($entries as $entryIndex => $entryID) {
2403 + $duplicatedEntryId = $formEntryModel->insert(
1589 2404 [
1590 - "`{$prefix}bitforms_form_entries`.`id`" => $entries,
1591 - "`{$prefix}bitforms_form_entries`.`form_id`" => $formID,
2405 + 'form_id' => $formID,
2406 + 'user_id' => $user_details['id'],
2407 + 'user_ip' => $user_details['ip'],
2408 + 'user_device' => $user_details['device'],
2409 + 'referer' => 'duplicate of #' . $entryID,
2410 + 'status' => 1,
2411 + 'created_at' => $user_details['time'],
1592 2412 ]
1593 2413 );
1594 - if (file_exists(BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID)) {
1595 - $fileHandler = new FileHandler();
1596 - foreach ($entries as $enrtyKey => $entryID) {
1597 - $fileEntries = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID . DIRECTORY_SEPARATOR . $entryID;
1598 - if (file_exists($fileEntries)) {
1599 - $fileHandler->rmrf($fileEntries);
1600 - }
1601 - }
1602 - }
1603 - $count = $formEntryModel->count(
1604 - [
1605 - 'form_id' => $formID,
1606 - ]
1607 - );
1608 - $formManager->resetSubmissionCount(intval($count[0]->count));
1609 - if (is_wp_error($delete_status)) {
1610 - return new WP_Error('entry_not_exists', __('Form entry deletion failed.', 'bit-form'));
1611 - }
1612 - $result['message'] = $message;
1613 - return $result;
1614 - }
1615 -
1616 - public function duplicateFormEntry($Request, $post) {
1617 - if (isset($Request['formID'])) {
1618 - $formID = wp_unslash($Request['formID']);
1619 - $entries = wp_unslash($Request['entries']);
1620 - } else {
1621 - $formID = wp_unslash($post->formID);
1622 - $entries = wp_unslash($post->entries);
1623 - }
1624 - if (is_null($formID) || !is_array($entries) || empty($entries)) {
1625 - return new WP_Error('empty_form', __('Form id or entries id is invalid', 'bit-form'));
1626 - }
1627 -
1628 - $formManager = new AdminFormManager($formID);
1629 - if (!$formManager->isExist()) {
1630 - return new WP_Error('empty_form', __('Form does not exist', 'bit-form'));
1631 - }
1632 - $formEntryModel = new FormEntryModel();
1633 - $entryMeta = new FormEntryMetaModel();
1634 - $user_details = static::$ipTool->getUserDetail();
1635 - $total_entries = count($entries);
1636 - $duplicate_count = 0;
1637 - $test = '';
1638 - $fileHandler = new FileHandler();
1639 - $result = [];
1640 - foreach ($entries as $entryIndex => $entryID) {
1641 - $duplicatedEntryId = $formEntryModel->insert(
2414 + if ($duplicatedEntryId) {
2415 + $duplicate_status = $entryMeta->duplicateEntryMeta(
1642 2416 [
1643 - 'form_id' => $formID,
1644 - 'user_id' => $user_details['id'],
1645 - 'user_ip' => $user_details['ip'],
1646 - 'user_device' => $user_details['device'],
1647 - 'referer' => 'duplicate of #' . $entryID,
1648 - 'status' => 1,
1649 - 'created_at' => $user_details['time'],
2417 + 'duplicateID' => $duplicatedEntryId,
2418 + 'entryID' => $entryID,
1650 2419 ]
1651 2420 );
1652 - if ($duplicatedEntryId) {
1653 - $duplicate_status = $entryMeta->duplicateEntryMeta(
1654 - [
1655 - 'duplicateID' => $duplicatedEntryId,
1656 - 'entryID' => $entryID,
1657 - ]
1658 - );
1659 - if ($duplicate_status) {
1660 - $result['details'][$entryID] = $duplicatedEntryId;
1661 - $duplicate_count = $duplicate_count + 1;
1662 - if (file_exists(BITFORMS_UPLOAD_DIR . "/$formID/$entryID")) {
1663 - $fileHandler->cpyr(
1664 - BITFORMS_UPLOAD_DIR . "/$formID/$entryID",
1665 - BITFORMS_UPLOAD_DIR . "/$formID/$duplicatedEntryId"
1666 - );
1667 - }
2421 + if ($duplicate_status) {
2422 + $result['details'][$entryID] = $duplicatedEntryId;
2423 + $duplicate_count = $duplicate_count + 1;
2424 + if (file_exists(FileHandler::getEntriesFileUploadDir($formID, $entryID))) {
2425 + $fileHandler->cpyr(
2426 + FileHandler::getEntriesFileUploadDir($formID, $entryID),
2427 + FileHandler::getEntriesFileUploadDir($formID, $duplicatedEntryId)
2428 + );
1668 2429 }
1669 2430 }
1670 2431 }
2432 + }
1671 2433
1672 - $count = $formEntryModel->count(
1673 - [
1674 - 'form_id' => $formID,
1675 - ]
1676 - );
1677 - $formManager->resetSubmissionCount(intval($count[0]->count));
1678 - $result['message'] = 1 === count($entries) ? __('Entry Duplicated successfully', 'bit-form') : __('Entries Duplicated successfully', 'bit-form');
1679 - return ($total_entries === $duplicate_count) ? $result : false;
1680 - }
2434 + $count = $formEntryModel->count(
2435 + [
2436 + 'form_id' => $formID,
2437 + ]
2438 + );
2439 + $formManager->resetSubmissionCount(intval($count[0]->count));
2440 + $result['message'] = 1 === count($entries) ? __('Entry Duplicated successfully', 'bit-form') : __('Entries Duplicated successfully', 'bit-form');
2441 + return ($total_entries === $duplicate_count) ? $result : false;
2442 + }
1681 2443
1682 - public function editFormEntry($Request, $post) {
1683 - if (isset($Request['formID'])) {
1684 - $formID = wp_unslash($Request['formID']);
1685 - $entryID = wp_unslash($Request['entryID']);
2444 + public function editFormEntry($Request, $post)
2445 + {
2446 + if (!empty($Request['formID'])) {
2447 + $formID = absint(wp_unslash($Request['formID']));
2448 + $entryID = absint(wp_unslash($Request['entryID']));
1686 2449 } else {
1687 2450 $formID = wp_unslash($post->formID);
1688 2451 $entryID = wp_unslash($post->entryID);
1689 2452 }
@@ -1728,13 +2491,18 @@
1728 2491 $layout = $formContent->layout;
1729 2492 foreach ($form_fields as $key => $value) {
1730 2493 // $field_name = preg_replace('/[\`\~\!\@\#\$\'\.\s\?\+\-\*\&\|\/\\!]/', '_', $value->lbl);
1731 2494 if (isset($entries[$key])) {
1732 - $form_fields->{$key}->val = $entries[$key];
2495 + $isPassword = isset($value->typ) && 'password' === $value->typ;
2496 + // Never hand a stored password back to the browser; blank keeps it on update.
2497 + $form_fields->{$key}->val = $isPassword ? '' : $entries[$key];
1733 2498 $form_fields->{$key}->name = $key;
2499 + if ($isPassword && '' !== (string) $entries[$key] && isset($value->valid) && is_object($value->valid)) {
2500 + $form_fields->{$key}->valid->req = false;
2501 + }
1734 2502 }
1735 2503 }
1736 - $workFlowRunHelper = new WorkFlowRunHelper($formID);
2504 + $workFlowRunHelper = new WorkFlow($formID);
1737 2505 $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(
1738 2506 'edit',
1739 2507 $form_fields
1740 2508 );
@@ -1755,16 +2523,19 @@
1755 2523 ];
1756 2524 return $formData;
1757 2525 }
1758 2526
1759 - public function updateFormEntry($Request, $post) {
2527 + public function updateFormEntry($Request, $post)
2528 + {
1760 2529 if (isset($Request['formID'], $Request['entryID'])) {
1761 - $formID = wp_unslash($Request['formID']);
1762 - $entryID = wp_unslash($Request['entryID']);
2530 + $formID = absint(wp_unslash($Request['formID']));
2531 + $entryID = absint(wp_unslash($Request['entryID']));
1763 2532 unset($Request['action'], $Request['formID'], $Request['entryID']);
1764 -
1765 - $updatedValue = wp_unslash($post);
1766 2533 }
2534 + $formManager = new AdminFormManager($formID);
2535 + $formManager->fieldNameReplaceOfPost();
2536 + // Called from AdminAjax handler; nonce already verified at AJAX entry before dispatch.
2537 + $updatedValue = is_array($post) ? $post : wp_unslash($_POST);
1767 2538
1768 2539 if (is_null($updatedValue) || !is_array($updatedValue)) {
1769 2540 return new WP_Error('empty_data', __('Failed to update, Data is empty.', 'bit-form'));
1770 2541 }
@@ -1772,9 +2543,8 @@
1772 2543 if (is_null($formID) || is_null($entryID)) {
1773 2544 return new WP_Error('empty_data', __('Invalid Form ID or entries ID.', 'bit-form'));
1774 2545 }
1775 2546
1776 - $formManager = new AdminFormManager($formID);
1777 2547 if (!$formManager->isExist()) {
1778 2548 return new WP_Error('empty_data', __('Form does not exist.', 'bit-form'));
1779 2549 }
1780 2550 return $formManager->updateFormEntry($updatedValue, $formID, $entryID);
@@ -1779,12 +2549,13 @@
1779 2549 }
1780 2550 return $formManager->updateFormEntry($updatedValue, $formID, $entryID);
1781 2551 }
1782 2552
1783 - public function getLogHistory($Request, $post) {
1784 - if (isset($Request['formID'])) {
1785 - $formID = wp_unslash($Request['formID']);
1786 - $entryID = wp_unslash($Request['entryID']);
2553 + public function getLogHistory($Request, $post)
2554 + {
2555 + if (!empty($Request['formID'])) {
2556 + $formID = absint(wp_unslash($Request['formID']));
2557 + $entryID = absint(wp_unslash($Request['entryID']));
1787 2558 } else {
1788 2559 $formID = wp_unslash($post->formID);
1789 2560 $entryID = wp_unslash($post->entryID);
1790 2561 }
@@ -1802,11 +2573,12 @@
1802 2573 $log_history = $formLogModel->geLogHistory($formID, $entryID);
1803 2574 return $log_history;
1804 2575 }
1805 2576
1806 - public function importDataStore($Request, $post) {
1807 - if (isset($Request['formID'])) {
1808 - $formID = wp_unslash($Request['formID']);
2577 + public function importDataStore($Request, $post)
2578 + {
2579 + if (!empty($Request['formID'])) {
2580 + $formID = absint(wp_unslash($Request['formID']));
1809 2581 } else {
1810 2582 $formID = wp_unslash($post->formID);
1811 2583 }
1812 2584 if (is_null($formID)) {
@@ -1813,22 +2585,28 @@
1813 2585 return new WP_Error('empty_form', __('Invalid Form id or entries id.', 'bit-form'));
1814 2586 }
1815 2587 }
1816 2588
1817 - public function getAllWPPages($Request, $post) {
2589 + public function getAllWPPages($Request, $post)
2590 + {
1818 2591 $pages = get_pages(['post_status' => 'publish', 'sort_column' => 'post_date', 'sort_order' => 'desc']);
1819 2592 $allPages = [];
1820 2593 foreach ($pages as $pageKey => $pageDetails) {
1821 - $allPages[$pageKey]['title'] = $pageDetails->post_title;
1822 - $allPages[$pageKey]['url'] = get_page_link($pageDetails->ID);
2594 + // $allPages[$pageKey]['title'] = $pageDetails->post_title;
2595 + // $allPages[$pageKey]['url'] = get_page_link($pageDetails->ID);
2596 + $allPages[] = (object) [
2597 + 'title' => $pageDetails->post_title,
2598 + 'url' => get_page_link($pageDetails->ID)
2599 + ];
1823 2600 }
1824 2601 return $allPages;
1825 2602 }
1826 2603
1827 - public function deleteAIntegration($Request, $post) {
1828 - if (isset($Request['formID']) && $Request['id']) {
1829 - $formID = json_decode(wp_unslash($Request['formID']));
1830 - $id = wp_unslash($Request['id']);
2604 + public function deleteAIntegration($Request, $post)
2605 + {
2606 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2607 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2608 + $id = absint(wp_unslash($Request['id']));
1831 2609 } else {
1832 2610 $formID = wp_unslash($post->formID);
1833 2611 $id = wp_unslash($post->id);
1834 2612 }
@@ -1846,12 +2624,13 @@
1846 2624 'message' => __('Integration deleted', 'bit-form'),
1847 2625 ];
1848 2626 }
1849 2627
1850 - public function deleteSuccessMessage($Request, $post) {
1851 - if (isset($Request['formID']) && $Request['id']) {
1852 - $formID = json_decode(wp_unslash($Request['formID']));
1853 - $id = wp_unslash($Request['id']);
2628 + public function deleteSuccessMessage($Request, $post)
2629 + {
2630 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2631 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2632 + $id = absint(wp_unslash($Request['id']));
1854 2633 } else {
1855 2634 $formID = wp_unslash($post->formID);
1856 2635 $id = wp_unslash($post->id);
1857 2636 }
@@ -1868,12 +2647,13 @@
1868 2647 'message' => __('Message deleted', 'bit-form'),
1869 2648 ];
1870 2649 }
1871 2650
1872 - public function deleteAWorkflow($Request, $post) {
1873 - if (isset($Request['formID']) && $Request['id']) {
1874 - $formID = json_decode(wp_unslash($Request['formID']));
1875 - $id = wp_unslash($Request['id']);
2651 + public function deleteAWorkflow($Request, $post)
2652 + {
2653 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2654 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2655 + $id = absint(wp_unslash($Request['id']));
1876 2656 } else {
1877 2657 $formID = wp_unslash($post->formID);
1878 2658 $id = wp_unslash($post->id);
1879 2659 }
@@ -1889,12 +2669,13 @@
1889 2669 'message' => __('workflow deleted', 'bit-form'),
1890 2670 ];
1891 2671 }
1892 2672
1893 - public function deleteAMailTemplate($Request, $post) {
1894 - if (isset($Request['formID']) && $Request['id']) {
1895 - $formID = json_decode(wp_unslash($Request['formID']));
1896 - $id = wp_unslash($Request['id']);
2673 + public function deleteAMailTemplate($Request, $post)
2674 + {
2675 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2676 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2677 + $id = absint(wp_unslash($Request['id']));
1897 2678 } else {
1898 2679 $formID = wp_unslash($post->formID);
1899 2680 $id = wp_unslash($post->id);
1900 2681 }
@@ -1910,12 +2691,13 @@
1910 2691 'message' => __('Email Template deleted', 'bit-form'),
1911 2692 ];
1912 2693 }
1913 2694
1914 - public function duplicateAMailTemplate($Request, $post) {
1915 - if (isset($Request['formID']) && $Request['id']) {
1916 - $formID = json_decode(wp_unslash($Request['formID']));
1917 - $id = wp_unslash($Request['id']);
2695 + public function duplicateAMailTemplate($Request, $post)
2696 + {
2697 + if (!empty($Request['formID']) && !empty($Request['id'])) {
2698 + $formID = absint(json_decode(wp_unslash($Request['formID'])));
2699 + $id = absint(wp_unslash($Request['id']));
1918 2700 } else {
1919 2701 $formID = wp_unslash($post->formID);
1920 2702 $id = wp_unslash($post->id);
1921 2703 }
@@ -1921,8 +2703,17 @@
1921 2703 }
1922 2704 if (empty($formID) || empty($id)) {
1923 2705 return new WP_Error('empty_form', 'Invalid Form ID or Email Template ID.');
1924 2706 }
2707 +
2708 + if (false === filter_var($id, FILTER_VALIDATE_INT)) {
2709 + return new WP_Error('empty_form', 'Invalid Email Template ID.');
2710 + }
2711 +
2712 + if (false === filter_var($formID, FILTER_VALIDATE_INT)) {
2713 + return new WP_Error('empty_form', 'Invalid Form ID.');
2714 + }
2715 +
1925 2716 $emailTemplateHandler = new EmailTemplateHandler($formID);
1926 2717 $duplicate_status = $emailTemplateHandler->duplicateTemplate($id);
1927 2718 if (is_wp_error($duplicate_status)) {
1928 2719 return $duplicate_status;
@@ -1931,9 +2722,10 @@
1931 2722 'message' => __('Email Template duplicated', 'bit-form'),
1932 2723 ];
1933 2724 }
1934 2725
1935 - public function setAllFormsReport($Request, $post) {
2726 + public function setAllFormsReport($Request, $post)
2727 + {
1936 2728 $reports = empty($post->reports) ? null : wp_unslash($post->reports);
1937 2729 if (empty($reports)) {
1938 2730 return new WP_Error('empty_report_prefs', 'Report data is Empty, nothing to save or update.');
1939 2731 }
@@ -2009,9 +2801,10 @@
2009 2801 'reports' => empty($reports) ? [] : $reports,
2010 2802 ];
2011 2803 }
2012 2804
2013 - public function savegReCaptcha($Request, $post) {
2805 + public function savegReCaptcha($Request, $post)
2806 + {
2014 2807 $reCaptcha = $post->reCaptcha;
2015 2808
2016 2809 if (is_null($reCaptcha)) {
2017 2810 return new WP_Error('empty_gcaptchdetails', __('g-ReCAPTCHA details is empty', 'bit-form'));
@@ -2023,11 +2816,16 @@
2023 2816 } else {
2024 2817 $integrationID = $reCaptcha->id;
2025 2818 unset($reCaptcha->id);
2026 2819 }
2027 - $reCaptcha = json_encode($reCaptcha);
2028 - $integrationName = 'google reCaptcha';
2029 - $integrationType = 'v2' === $post->version ? 'gReCaptcha' : 'gReCaptchaV3';
2820 + $reCaptcha = wp_json_encode($reCaptcha);
2821 +
2822 + // $integrationName = 'google reCaptcha';
2823 + $integrationName = $post->integrationName;
2824 +
2825 + // $integrationType = 'v2' === $post->version ? 'gReCaptcha' : 'gReCaptchaV3';
2826 + $integrationType = $post->integrationType;
2827 +
2030 2828 $integrationDetails = $reCaptcha;
2031 2829 $user_details = static::$ipTool->getUserDetail();
2032 2830 $integrationHandler = new IntegrationHandler(0, $user_details);
2033 2831 $response = [];
@@ -2033,12 +2831,12 @@
2033 2831 $response = [];
2034 2832 if (empty($integrationID)) {
2035 2833 $captchaSaveStatus = $integrationHandler->saveIntegration($integrationName, $integrationType, $integrationDetails, 'app');
2036 2834 $response['id'] = $captchaSaveStatus;
2037 - $response['message'] = __('g-reCAPTCHA saved successfully', 'bit-form');
2835 + $response['message'] = __('reCAPTCHA saved successfully', 'bit-form');
2038 2836 } else {
2039 2837 $captchaSaveStatus = $integrationHandler->updateIntegration($integrationID, $integrationName, $integrationType, $integrationDetails, 'app');
2040 - $response['message'] = __('g-reCAPTCHA updated successfully', 'bit-form');
2838 + $response['message'] = __('reCAPTCHA updated successfully', 'bit-form');
2041 2839 }
2042 2840
2043 2841 if (is_wp_error($captchaSaveStatus)) {
2044 2842 return $captchaSaveStatus;
@@ -2045,9 +2843,10 @@
2045 2843 }
2046 2844 return $response;
2047 2845 }
2048 2846
2049 - public function savePaymentSetting($Request, $post) {
2847 + public function savePaymentSetting($Request, $post)
2848 + {
2050 2849 if (isset($post->paySetting)) {
2051 2850 $paySetting = $post->paySetting;
2052 2851 }
2053 2852 if (is_null($paySetting)) {
@@ -2055,21 +2854,37 @@
2055 2854 }
2056 2855 if (is_string($paySetting)) {
2057 2856 $paySetting = json_decode($paySetting);
2058 2857 }
2059 - if ('PayPal' === $paySetting->type) {
2060 - $canSave = $this->addWebhookToPaypal($paySetting);
2061 - } elseif ('Razorpay' === $paySetting->type) {
2062 - $canSave = true;
2858 +
2859 + $canSave = null;
2860 + switch ($paySetting->type) {
2861 + case 'PayPal':
2862 + $canSave = $this->addWebhookToPaypal($paySetting);
2863 + break;
2864 + case 'Razorpay':
2865 + $canSave = true;
2866 + break;
2867 + case 'Stripe':
2868 + $canSave = $this->addWebhookToStripe($paySetting);
2869 + break;
2870 + case 'Mollie':
2871 + $canSave = true;
2872 + break;
2873 + default:
2874 + $canSave = null;
2063 2875 }
2064 2876 if (is_null($canSave)) {
2065 - return ['message' => __('Please re-check your Client ID & Secret', 'bit-form')];
2877 + return new WP_Error('stripe', __('Please re-check your Client ID & Secret', 'bit-form'));
2066 2878 }
2067 - $integrationID = $paySetting->id;
2068 - unset($paySetting->id);
2879 + $integrationID = null;
2880 + if (isset($paySetting->id)) {
2881 + $integrationID = $paySetting->id;
2882 + unset($paySetting->id);
2883 + }
2069 2884 $integrationName = $paySetting->name;
2070 2885 $integrationType = 'payments';
2071 - $paySetting = json_encode($paySetting);
2886 + $paySetting = wp_json_encode($paySetting);
2072 2887 $integrationDetails = $paySetting;
2073 2888 $user_details = static::$ipTool->getUserDetail();
2074 2889 $integrationHandler = new IntegrationHandler(0, $user_details);
2075 2890 $response = [];
@@ -2087,9 +2902,10 @@
2087 2902 }
2088 2903 return $response;
2089 2904 }
2090 2905
2091 - private function addWebhookToPaypal($paySetting) {
2906 + private function addWebhookToPaypal($paySetting)
2907 + {
2092 2908 $transaction_mode = $paySetting->mode;
2093 2909 $clientId = $paySetting->clientID;
2094 2910 $clientSecret = $paySetting->clientSecret;
2095 2911 $base_url = 'sandbox' === $transaction_mode ? 'https://api-m.sandbox.paypal.com' : 'https://api.paypal.com';
@@ -2115,9 +2931,52 @@
2115 2931 }
2116 2932 return true;
2117 2933 }
2118 2934
2119 - public function builerHelperState($formId) {
2935 + private function addWebhookToStripe($paySetting)
2936 + {
2937 + $clientSecret = $paySetting->clientSecret;
2938 +
2939 + $create_webhook_url = 'https://api.stripe.com/v1/webhook_endpoints';
2940 + $headers = [
2941 + 'Content-Type' => 'application/x-www-form-urlencoded',
2942 + 'Authorization' => "Bearer $clientSecret",
2943 + ];
2944 +
2945 + $webhook_url = get_rest_url() . 'bitform/v1/payments/stripe';
2946 +
2947 + $apiResponse = HttpHelper::get($create_webhook_url, '', $headers);
2948 +
2949 + if (isset($apiResponse->error)) {
2950 + return null;
2951 + }
2952 + $webhookList = $apiResponse->data;
2953 + $webhook_already_exist = false;
2954 +
2955 + foreach ($webhookList as $webhook) {
2956 + if ($webhook->url === $webhook_url) {
2957 + $webhook_already_exist = true;
2958 + break;
2959 + }
2960 + }
2961 +
2962 + $data = [
2963 + 'enabled_events[]' => 'payment_intent.succeeded',
2964 + 'url' => $webhook_url,
2965 + ];
2966 +
2967 + if (!$webhook_already_exist) {
2968 + $webhook_response = HttpHelper::post($create_webhook_url, $data, $headers);
2969 + if (is_wp_error($webhook_response) || (isset($webhook_response->error) && $webhook_response->error)) {
2970 + Log::debug_log(['Stripe webhook creation failed' => $webhook_response]);
2971 + return null;
2972 + }
2973 + }
2974 + return true;
2975 + }
2976 +
2977 + public function builerHelperState($formId)
2978 + {
2120 2979 $data = static::$formModel->get(
2121 2980 ['id', 'builder_helper_state'],
2122 2981 ['id' => $formId]
2123 2982 );
@@ -2132,9 +2991,10 @@
2132 2991
2133 2992 return $data;
2134 2993 }
2135 2994
2136 - public function updateGeneratedScriptPageIds() {
2995 + public function updateGeneratedScriptPageIds()
2996 + {
2137 2997 $updateData = [
2138 2998 'generated_script_page_ids' => \wp_json_encode((object) []),
2139 2999 ];
2140 3000 $update_status = static::$formModel->update(
@@ -2151,9 +3011,70 @@
2151 3011 return true;
2152 3012 }
2153 3013 }
2154 3014
2155 - public function updateSuccessMessageClassName($str, $msgIdx, $msgId) {
3015 + public function replaceAllFormsErrorMessagesByGlobalMessages()
3016 + {
3017 + try {
3018 + $formModel = new FormModel();
3019 + $allForms = $formModel->get(['id', 'form_content']);
3020 +
3021 + foreach ($allForms as $form) {
3022 + // Decode form_content JSON to stdClass
3023 + $formContent = \json_decode($form->form_content);
3024 + $fields = $formContent->fields ?? null;
3025 + // If decode fails, skip to next
3026 + if (!$fields) {
3027 + Log::debug_log("Form ID {$form->id}: invalid JSON in form_content");
3028 + continue;
3029 + }
3030 +
3031 + // Replace default error messages using your helper
3032 + $updatedFields = Helpers::replaceFieldsDefaultErrorMsg($fields);
3033 +
3034 + $formContent->fields = $updatedFields;
3035 + // Encode back to JSON
3036 + $updatedContent = \json_encode($formContent);
3037 +
3038 + if (false === $updatedContent) {
3039 + Log::debug_log("Form ID {$form->id}: JSON encoding failed");
3040 + continue;
3041 + }
3042 + // Update form content in DB
3043 + $formModel->update(
3044 + ['form_content' => $updatedContent], // data to update
3045 + ['id' => $form->id] // condition for update
3046 + );
3047 + }
3048 + } catch (\Exception $e) {
3049 + Log::debug_log([
3050 + 'message' => 'Error in updateAllFormsErrorMessagesWithGlobalMessages',
3051 + 'error' => $e->getMessage(),
3052 + ]);
3053 + }
3054 + }
3055 +
3056 + public function updateSuccessMessageClassName($str, $msgIdx, $msgId)
3057 + {
2156 3058 $TEMP_CONF_ID = '_tmp_' . $msgIdx . '_conf_id';
2157 3059 return str_replace($TEMP_CONF_ID, $msgId, $str);
3060 + }
3061 +
3062 + public function startMigrationProcess()
3063 + {
3064 + Utilities::duplicateDbTable('form', 'form_v1');
3065 + Utilities::duplicateDbTable('workflows', 'workflows_v1');
3066 + Utilities::duplicateDbTable('success_messages', 'success_messages_v1');
3067 + MigrationHelper::duplicateV1StyleFiles();
3068 + $all_forms = $this->getAllForm();
3069 + $v1FormContents = [];
3070 + $defaultStyleClass = MigrationHelper::getBitformDefaultStyleClass();
3071 + foreach ($all_forms as $form) {
3072 + $formContents = $this->getAFormV1(['id' => $form->id], ['id' => $form->id]);
3073 + $v1FormContents[] = $formContents;
3074 + FrontEndScriptGenerator::saveCssFile("{$form->id}", $defaultStyleClass);
3075 + FrontEndScriptGenerator::saveCssFile("{$form->id}-formid", $defaultStyleClass);
3076 + }
3077 + set_transient('bitforms_v1_form_contents', $v1FormContents);
3078 + update_option('bitforms_migrating_to_v2', true);
2158 3079 }
2159 3080 }