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 +93 -7 3.2.0 → V3.3.2 View file →
@@ -244,8 +244,9 @@
244 244 // Guard: never persist orphan fields (in fields but in no layout) — they
245 245 // render nothing yet their validation rules block submission. Covers old
246 246 // clients and hand-edited imports (importAForm funnels through here).
247 247 $this->pruneOrphanFields($fields, $nestedLayout, $layout, null, $post);
248 + FormManager::syncCompositeChildNames($fields);
248 249 $form_content = [
249 250 'fields' => $fields,
250 251 'layout' => $layout,
251 252 'nestedLayout' => $nestedLayout,
@@ -524,8 +525,12 @@
524 525 $updated_data['form_content']['is_default'] = 1;
525 526 }
526 527 $updated_data['message'] = __('Form Saved successfully', 'bit-form');
527 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 +
528 533 return $updated_data;
529 534 }
530 535 }
531 536
@@ -584,8 +589,9 @@
584 589 }
585 590
586 591 // Guard: never persist orphan fields (see createNewForm). Fail closed.
587 592 $this->pruneOrphanFields($fields, $nestedLayout, $layout, $formID, $post);
593 + FormManager::syncCompositeChildNames($fields);
588 594
589 595 $form_content = [
590 596 'fields' => $fields,
591 597 'layout' => $layout,
@@ -664,9 +670,14 @@
664 670 $templateDetail->id = $savedID;
665 671 unset($templateDetail->clRef);
666 672 }
667 673 } else {
668 - $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 + }
669 680 }
670 681 }
671 682 }
672 683 // return $formSettings;
@@ -792,8 +803,11 @@
792 803 }
793 804 $form_content = array_merge($form_content, ['workFlowExist' => $workFlowExist]);
794 805 //wrokFlows [end]
795 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;
796 810 if (!empty($reports)) {
797 811 $reportsModel = new ReportsModel();
798 812 $fieldNames = [];
799 813 foreach ($fields as $key => $field) {
@@ -811,9 +825,13 @@
811 825 $fieldNames['__created_at'] = __('Created Time', 'bit-form');
812 826 $fieldNames['__updated_at'] = __('Modified Time', 'bit-form');
813 827
814 828 $reportIsDefault = null;
815 - 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)) {
816 834 $validDateReport = $reportsModel->validateReportFields($reports, $fieldNames);
817 835 if (isset($reports->isDefault)) {
818 836 $reportIsDefault = $reports->isDefault;
819 837 } else {
@@ -924,8 +942,12 @@
924 942 }
925 943 if (2 === $newData['reports']) {
926 944 $errorIN .= empty($errorIN) ? 'reports' : ', reports';
927 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 +
928 950 if (!empty($errorIN)) {
929 951 /* translators: %s: comma-separated list of areas where error occurred */
930 952 $updated_data['message'] = sprintf(__('Error Occured in saving %s', 'bit-form'), $errorIN);
931 953 return new WP_Error('Form update Error.', $updated_data);
@@ -933,8 +955,9 @@
933 955 if (null !== $reportIsDefault) {
934 956 $updated_data['form_content']['is_default'] = $reportIsDefault;
935 957 }
936 958 $updated_data['message'] = __('Form updated successfully.', 'bit-form');
959 +
937 960 return $updated_data;
938 961 }
939 962 }
940 963
@@ -1112,9 +1135,9 @@
1112 1135 'nestedLayout' => !empty($form_content->nestedLayout) ? $form_content->nestedLayout : (object) [],
1113 1136 'formInfo' => !empty($form_content->formInfo) ? $form_content->formInfo : (object) ['formName' => $formManager->getFormName()],
1114 1137 'fields' => $form_content->fields,
1115 1138 'form_name' => $formManager->getFormName(),
1116 - 'workFlowExist' => $form_content->workFlowExist,
1139 + 'workFlowExist' => isset($form_content->workFlowExist) ? $form_content->workFlowExist : [],
1117 1140 'report_id' => isset($form_content->report_id) ? $form_content->report_id : null
1118 1141 ];
1119 1142 $successMessageHandler
1120 1143 = new SuccessMessageHandler($formID);
@@ -1142,9 +1165,9 @@
1142 1165 'title' => $emailTemplatevalue->title,
1143 1166 'sub' => $emailTemplatevalue->sub,
1144 1167 'body' => $emailTemplatevalue->body,
1145 1168 'status' => isset($emailTemplatevalue->status) ? (int) $emailTemplatevalue->status : 1,
1146 - 'config' => !empty($emailTemplatevalue->config) ? json_decode($emailTemplatevalue->config) : (object) [],
1169 + 'config' => EmailTemplateHandler::normalizeConfig($emailTemplatevalue->config ?? null),
1147 1170 ];
1148 1171 }
1149 1172 }
1150 1173 // get all pdf template
@@ -1410,9 +1433,9 @@
1410 1433 $form_content_arr = [
1411 1434 'layout' => $form_content->layout,
1412 1435 'fields' => $form_content->fields,
1413 1436 'form_name' => $formManager->getFormName(),
1414 - 'workFlowExist' => $form_content->workFlowExist,
1437 + 'workFlowExist' => isset($form_content->workFlowExist) ? $form_content->workFlowExist : [],
1415 1438 'report_id' => isset($form_content->report_id) ? $form_content->report_id : null
1416 1439 ];
1417 1440 $successMessageHandler
1418 1441 = new SuccessMessageHandler($formID);
@@ -1439,9 +1462,9 @@
1439 1462 'title' => $emailTemplatevalue->title,
1440 1463 'sub' => $emailTemplatevalue->sub,
1441 1464 'body' => $emailTemplatevalue->body,
1442 1465 'status' => isset($emailTemplatevalue->status) ? (int) $emailTemplatevalue->status : 1,
1443 - 'config' => !empty($emailTemplatevalue->config) ? json_decode($emailTemplatevalue->config) : (object) [],
1466 + 'config' => EmailTemplateHandler::normalizeConfig($emailTemplatevalue->config ?? null),
1444 1467 ];
1445 1468 }
1446 1469 }
1447 1470 $integrationHandler = new IntegrationHandler($formID);
@@ -2019,8 +2042,65 @@
2019 2042 $response['fieldDetails'] = $labels;
2020 2043 return $response;
2021 2044 }
2022 2045
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 +
2023 2103 public function getFormEntry($Request, $post)
2024 2104 {
2025 2105 if (!empty($Request['id'])) {
2026 2106 $id = absint(wp_unslash($Request['id']));
@@ -2040,8 +2120,9 @@
2040 2120 $pageSize = isset($post->pageSize) ?
2041 2121 wp_unslash($post->pageSize) : 10;
2042 2122 $queryCondition = isset($post->queryCondition) ? wp_unslash($post->queryCondition) : ['form_id' => $id];
2043 2123 }
2124 + $queryCondition = $this->sanitizeEntryQueryCondition($queryCondition, $id);
2044 2125 if (is_null($id)) {
2045 2126 return new WP_Error('empty_form', __('Form id is empty.', 'bit-form'));
2046 2127 }
2047 2128 $formManager = new AdminFormManager($id);
@@ -2410,10 +2491,15 @@
2410 2491 $layout = $formContent->layout;
2411 2492 foreach ($form_fields as $key => $value) {
2412 2493 // $field_name = preg_replace('/[\`\~\!\@\#\$\'\.\s\?\+\-\*\&\|\/\\!]/', '_', $value->lbl);
2413 2494 if (isset($entries[$key])) {
2414 - $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];
2415 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 + }
2416 2502 }
2417 2503 }
2418 2504 $workFlowRunHelper = new WorkFlow($formID);
2419 2505 $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(