PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Admin/AdminAjax.php +164 -24 V_3.0.03.3.1 View file →
@@ -8,8 +8,9 @@
8 8
9 9 use BitCode\BitForm\Admin\Form\FrontEndScriptGenerator;
10 10 use BitCode\BitForm\Admin\Form\Helpers;
11 11 use BitCode\BitForm\Admin\Form\Template\TemplateProvider;
12 +use BitCode\BitForm\Core\Database\FormEntryLogModel;
12 13 use BitCode\BitForm\Core\Database\FormEntryModel;
13 14 use BitCode\BitForm\Core\Database\FormModel;
14 15 use BitCode\BitForm\Core\Form\FormHandler;
15 16 use BitCode\BitForm\Core\Integration\IntegrationHandler;
@@ -16,8 +17,9 @@
16 17 use BitCode\BitForm\Core\Integration\Integrations;
17 18 use BitCode\BitForm\Core\Util\IpTool;
18 19 use BitCode\BitForm\Core\Util\MailConfig;
19 20 use BitCode\BitForm\Core\Util\MetaBoxService;
21 +use BitCode\BitForm\Core\Util\Utilities;
20 22 use BitCode\BitForm\Frontend\Form\FrontendFormManager;
21 23 use BitCode\BitForm\GlobalHelper;
22 24 use WP_Error;
23 25
@@ -28,8 +30,10 @@
28 30 add_action('wp_ajax_bitforms_integrations', [$this, 'integrations']);
29 31 add_action('wp_ajax_bitforms_save_connected_integration_apps', [$this, 'saveConnectedIntegrationApps']);
30 32 add_action('wp_ajax_bitforms_get_connected_integration_apps', [$this, 'getConnectedIntegrationApps']);
31 33 add_action('wp_ajax_bitforms_delete_connected_app', [$this, 'deleteConnectedApp']);
34 + add_action('wp_ajax_bitforms_update_connected_app', [$this, 'updateConnectedApp']);
35 + add_action('wp_ajax_bitforms_integration_last_runs', [$this, 'integrationLastRuns']);
32 36 add_action('wp_ajax_bitforms_update_form', [$this, 'updateForm']);
33 37 add_action('wp_ajax_bitforms_templates', [$this, 'templates']);
34 38 add_action('wp_ajax_bitforms_create_new_form', [$this, 'createNewForm']);
35 39 add_action('wp_ajax_bitforms_save_css', [$this, 'saveCss']);
@@ -51,8 +55,9 @@
51 55 add_action('wp_ajax_bitforms_get_all_form', [$this, 'getAllForms']);
52 56 add_action('wp_ajax_bitforms_get_all_wp_pages', [$this, 'getAllWPPages']);
53 57 add_action('wp_ajax_bitforms_delete_success_messsage', [$this, 'deleteSuccessMessage']);
54 58 add_action('wp_ajax_bitforms_delete_integration', [$this, 'deleteAIntegration']);
59 + add_action('wp_ajax_bitforms_update_integration_status', [$this, 'updateIntegrationStatus']);
55 60 add_action('wp_ajax_bitforms_delete_workflow', [$this, 'deleteAWorkflow']);
56 61 add_action('wp_ajax_bitforms_delete_mailtemplate', [$this, 'deleteAMailTemplate']);
57 62 add_action('wp_ajax_bitforms_duplicate_mailtemplate', [$this, 'duplicateAMailTemplate']);
58 63 add_action('wp_ajax_bitforms_save_allForm_report_prefs', [$this, 'setAllFormsReport']);
@@ -57,9 +62,8 @@
57 62 add_action('wp_ajax_bitforms_duplicate_mailtemplate', [$this, 'duplicateAMailTemplate']);
58 63 add_action('wp_ajax_bitforms_save_allForm_report_prefs', [$this, 'setAllFormsReport']);
59 64 add_action('wp_ajax_bitforms_save_grecaptcha', [$this, 'savegReCaptcha']);
60 65 add_action('wp_ajax_bitforms_form_log_history', [$this, 'getLogHistory']);
61 - add_action('wp_ajax_bitforms_import_file_data', [$this, 'importFileData']);
62 66 add_action('wp_ajax_bitforms_filter_export_data', [$this, 'filterExportEntry']);
63 67 add_action('wp_ajax_bitforms_api_key', [$this, 'saveApiKey']);
64 68 add_action('wp_ajax_bitforms_form_helpers_state', [$this, 'builerHelperState']);
65 69 add_action('wp_ajax_bitforms_icn_save_setting', [$this, 'iconUpload']);
@@ -110,9 +114,8 @@
110 114 add_action('wp_ajax_bitforms_changelog_version', [$this, 'setChangelogVersion']);
111 115
112 116 // Notice Options
113 117 add_action('wp_ajax_bitforms_handle_notice', [$this, 'handleNotice']);
114 - add_action('wp_ajax_bitforms_dismiss_pro_notice', [$this, 'dismissProUpgradeNotice']);
115 118
116 119 // conversational
117 120 add_action('wp_ajax_bitforms_save_conversational_css', [$this, 'saveConversationalCSS']);
118 121
@@ -133,9 +136,15 @@
133 136 $this->verifyAdminPermission();
134 137 $formId = isset($_REQUEST['formID']) ? sanitize_text_field(wp_unslash($_REQUEST['formID'])) : '';
135 138
136 139 $FrontendFormManager = FrontendFormManager::getInstance($formId);
140 + if (!$FrontendFormManager->isExist()) {
141 + wp_send_json_error(__('Form is not exists.', 'bit-form'), 404);
142 + }
137 143 $formContent = $FrontendFormManager->getFormContentWithValue();
144 + if (!is_object($formContent) || !isset($formContent->fields, $formContent->layout)) {
145 + wp_send_json_error(__('Form content is unavailable.', 'bit-form'), 404);
146 + }
138 147 $fields = $formContent->fields;
139 148 $layout = $formContent->layout;
140 149 $file = count($FrontendFormManager->getUploadFields()) > 0 ? $FrontendFormManager->getUploadFields() : false;
141 150 $html = $FrontendFormManager->formView($fields, $file);
@@ -290,11 +299,14 @@
290 299 } catch (\InvalidArgumentException $e) {
291 300 wp_send_json_error($e->getMessage(), 400);
292 301 }
293 302 // Nonce verified at handler entry (wp_verify_nonce 'bitforms_save', line 273).
294 - $appId = isset($_REQUEST['appId']) && $_REQUEST['appId']
303 + $requestedAppId = isset($_REQUEST['appId'])
295 304 ? sanitize_text_field(wp_unslash($_REQUEST['appId']))
296 - : sanitize_text_field(wp_unslash((string) $input->appId));
305 + : '';
306 + $appId = $requestedAppId
307 + ? $requestedAppId
308 + : sanitize_text_field(wp_unslash((string) ($input->appId ?? '')));
297 309 $integrationHandler = Integrations::getInstance();
298 310 $status = $integrationHandler->deleteConnectedApp($appId);
299 311 if (is_wp_error($status)) {
300 312 wp_send_json_error($status->get_error_message(), 411);
@@ -311,8 +323,89 @@
311 323 );
312 324 }
313 325 }
314 326
327 + /**
328 + * Renames a connected account (category connected_integration_apps, form_id 0).
329 + * Only integration_name is writable; updateIntegration would overwrite the rest.
330 + *
331 + * @return void
332 + */
333 + public function updateConnectedApp()
334 + {
335 + if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
336 + $this->verifyAdminPermission();
337 + GlobalHelper::requirePostMethod();
338 + try {
339 + $input = GlobalHelper::formatRequestData();
340 + } catch (\InvalidArgumentException $e) {
341 + wp_send_json_error($e->getMessage(), 400);
342 + }
343 + $appId = !empty($input->appId) ? sanitize_text_field((string) $input->appId) : '';
344 + $name = isset($input->name) ? sanitize_text_field((string) $input->name) : '';
345 + if (empty($appId) || '' === trim($name)) {
346 + wp_send_json_error(__('Invalid request', 'bit-form'), 400);
347 + }
348 + $integrations = Integrations::getInstance();
349 + $status = $integrations->renameConnectedApp($appId, $name);
350 + if (is_wp_error($status)) {
351 + wp_send_json_error($status->get_error_message(), 411);
352 + } else {
353 + wp_send_json_success($status, 200);
354 + }
355 + } else {
356 + wp_send_json_error(
357 + __(
358 + 'Token expired',
359 + 'bit-form'
360 + ),
361 + 401
362 + );
363 + }
364 + }
365 +
366 + /**
367 + * Last execution per integration of a form, for the integrations list Last Run column.
368 + *
369 + * @return void
370 + */
371 + public function integrationLastRuns()
372 + {
373 + if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
374 + $this->verifyAdminPermission();
375 + GlobalHelper::requirePostMethod();
376 + try {
377 + $input = GlobalHelper::formatRequestData();
378 + } catch (\InvalidArgumentException $e) {
379 + wp_send_json_error($e->getMessage(), 400);
380 + }
381 + $formID = !empty($input->formID) ? absint($input->formID) : 0;
382 + if (empty($formID)) {
383 + wp_send_json_error(__('Invalid request', 'bit-form'), 400);
384 + }
385 + $logModel = new FormEntryLogModel();
386 + $rows = $logModel->getIntegrationLastRuns($formID);
387 + if (is_wp_error($rows)) {
388 + // result_empty means nothing has run yet, which is a success. Any other
389 + // error is a failed query — answering [] would paint every integration
390 + // as never executed. Message stays generic: no DB detail to the browser.
391 + if ('result_empty' !== $rows->get_error_code()) {
392 + wp_send_json_error(__('Could not load integration run history', 'bit-form'), 500);
393 + }
394 + $rows = [];
395 + }
396 + wp_send_json_success($rows, 200);
397 + } else {
398 + wp_send_json_error(
399 + __(
400 + 'Token expired',
401 + 'bit-form'
402 + ),
403 + 401
404 + );
405 + }
406 + }
407 +
315 408 public function templates()
316 409 {
317 410 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
318 411 $this->verifyAdminPermission();
@@ -476,10 +569,14 @@
476 569 // $input = json_decode($inputJSON);
477 570 $input = GlobalHelper::formatRequestData();
478 571 $optionName = isset($input->optionName) ? $input->optionName : '';
479 572 $optionValue = isset($input->optionValue) ? $input->optionValue : '';
480 - update_option($optionName, $optionValue);
481 - wp_send_json_success([$optionName, $optionValue], 200);
573 + $allowedNoticeKeys = ['bitforms_hide_cashback', 'bitforms_hide_announcement'];
574 + if (!in_array($optionName, $allowedNoticeKeys, true)) {
575 + wp_send_json_error(__('Invalid option', 'bit-form'), 400);
576 + }
577 + update_option($optionName, (bool) $optionValue);
578 + wp_send_json_success([$optionName, (bool) $optionValue], 200);
482 579 } catch (\Exception | \InvalidArgumentException $e) {
483 580 wp_send_json_error($e->getMessage(), 400);
484 581 }
485 582 } else {
@@ -492,15 +589,8 @@
492 589 );
493 590 }
494 591 }
495 592
496 - public function dismissProUpgradeNotice()
497 - {
498 - check_ajax_referer('bitforms_dismiss_pro_notice', 'nonce');
499 - update_user_meta(get_current_user_id(), 'bitforms_dismiss_pro_upgrade_notice', BITFORMS_REQUIRED_BITFORMPRO_VERSION);
500 - wp_die();
501 - }
502 -
503 593 private function formatFormContentForUpdate($formContents)
504 594 {
505 595 $updatedFormContents = (object) [];
506 596 $updatedPaths = [
@@ -544,9 +634,9 @@
544 634 }
545 635
546 636 public function getEmailConfig()
547 637 {
548 - \ignore_user_abort();
638 + Utilities::ignoreUserAbort();
549 639
550 640 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
551 641 $this->verifyAdminPermission();
552 642 $ipTool = new IpTool();
@@ -553,8 +643,11 @@
553 643 $user_details = $ipTool->getUserDetail();
554 644 $integrationHandler = new IntegrationHandler(0, $user_details);
555 645 $user_details = $ipTool->getUserDetail();
556 646 $formIntegrations = $integrationHandler->getAllIntegration('mail', 'smtp');
647 + if (is_wp_error($formIntegrations)) {
648 + wp_send_json_error($formIntegrations->get_error_message(), 411);
649 + }
557 650 if (isset($formIntegrations[0]->integration_details) && is_string($formIntegrations[0]->integration_details)) {
558 651 $formIntegrations[0]->integration_details = wp_unslash($formIntegrations[0]->integration_details);
559 652 }
560 653 wp_send_json_success($formIntegrations, 200);
@@ -570,9 +663,9 @@
570 663 }
571 664
572 665 public function saveEmailConfig()
573 666 {
574 - \ignore_user_abort();
667 + Utilities::ignoreUserAbort();
575 668 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
576 669 $this->verifyAdminPermission();
577 670 GlobalHelper::requirePostMethod();
578 671 $ipTool = new IpTool();
@@ -808,9 +901,9 @@
808 901 * @return void
809 902 */
810 903 public function updateForm()
811 904 {
812 - \ignore_user_abort();
905 + Utilities::ignoreUserAbort();
813 906 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
814 907 $this->verifyAdminPermission();
815 908 GlobalHelper::requirePostMethod();
816 909 try {
@@ -849,9 +942,12 @@
849 942 } catch (\InvalidArgumentException $e) {
850 943 wp_send_json_error($e->getMessage(), 400);
851 944 }
852 945
853 - $formId = $input->form_id;
946 + $formId = isset($input->form_id) ? sanitize_text_field($input->form_id) : '';
947 + if (!filter_var($formId, FILTER_VALIDATE_INT)) {
948 + wp_send_json_error(__('Invalid form id', 'bit-form'), 400);
949 + }
854 950 if (isset($input->atomicCssText)) {
855 951 $status = FrontEndScriptGenerator::saveCssFile($formId, $input->atomicCssText);
856 952 }
857 953 if (isset($input->atomicCssWithFormIdText)) {
@@ -1060,9 +1156,9 @@
1060 1156 }
1061 1157
1062 1158 public function duplicateAForm()
1063 1159 {
1064 - \ignore_user_abort();
1160 + Utilities::ignoreUserAbort();
1065 1161 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
1066 1162 $this->verifyAdminPermission();
1067 1163 GlobalHelper::requirePostMethod();
1068 1164 try {
@@ -1092,9 +1188,9 @@
1092 1188 }
1093 1189
1094 1190 public function importAForm()
1095 1191 {
1096 - \ignore_user_abort();
1192 + Utilities::ignoreUserAbort();
1097 1193 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
1098 1194 $this->verifyAdminPermission();
1099 1195 GlobalHelper::requirePostMethod();
1100 1196 try {
@@ -1373,9 +1469,9 @@
1373 1469 }
1374 1470
1375 1471 public function updateFormEntry()
1376 1472 {
1377 - \ignore_user_abort();
1473 + Utilities::ignoreUserAbort();
1378 1474 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
1379 1475 $this->verifyAdminPermission();
1380 1476 $formHandler = FormHandler::getInstance();
1381 1477 $sanitized_request = [
@@ -1505,8 +1601,36 @@
1505 1601 );
1506 1602 }
1507 1603 }
1508 1604
1605 + public function updateIntegrationStatus()
1606 + {
1607 + if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
1608 + $this->verifyAdminPermission();
1609 + GlobalHelper::requirePostMethod();
1610 + try {
1611 + $input = GlobalHelper::formatRequestData();
1612 + } catch (\InvalidArgumentException $e) {
1613 + wp_send_json_error($e->getMessage(), 400);
1614 + }
1615 + $formID = isset($input->formID) ? sanitize_text_field(wp_unslash($input->formID)) : '';
1616 + $integrationID = isset($input->id) ? absint($input->id) : 0;
1617 + $status = isset($input->status) ? absint($input->status) : 0;
1618 + if (empty($formID) || empty($integrationID)) {
1619 + wp_send_json_error(__('Invalid request', 'bit-form'), 400);
1620 + }
1621 + $integrationHandler = new IntegrationHandler($formID);
1622 + $result = $integrationHandler->updateIntegrationStatus($integrationID, $status);
1623 + if (is_wp_error($result) && 'result_empty' !== $result->get_error_code()) {
1624 + wp_send_json_error($result->get_error_message(), 411);
1625 + } else {
1626 + wp_send_json_success(['id' => $integrationID, 'status' => $status], 200);
1627 + }
1628 + } else {
1629 + wp_send_json_error(__('Token expired', 'bit-form'), 401);
1630 + }
1631 + }
1632 +
1509 1633 public function deleteAWorkflow()
1510 1634 {
1511 1635 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
1512 1636 $this->verifyAdminPermission();
@@ -1702,9 +1826,13 @@
1702 1826 update_option('bitform_secret_api_key', sanitize_text_field($input->api_key));
1703 1827 $api_key = $input->api_key;
1704 1828 }
1705 1829 if (!$api_key) {
1706 - $api_key = hash('sha1', base64_encode(12345));
1830 + // The generated key authenticates the whole bitform/v1 data API. It was
1831 + // previously a constant (sha1 of a fixed string), so every install that
1832 + // never set one explicitly shared the same secret. Existing stored keys
1833 + // are left untouched — only first-time generation changes.
1834 + $api_key = wp_generate_password(64, false, false);
1707 1835 update_option('bitform_secret_api_key', $api_key);
1708 1836 }
1709 1837 wp_send_json_success($api_key, 200);
1710 1838 } else {
@@ -1719,9 +1847,11 @@
1719 1847 }
1720 1848
1721 1849 private function checkExtensionWithURL($urlStr)
1722 1850 {
1723 - $pattern = '/^https?:\/\/.*(\.(svg|png|jpg|jpeg|gif))?$/i';
1851 + // Extension is mandatory (no optional group): the URL must end in a known image extension,
1852 + // otherwise any http(s) URL would pass and be fetched.
1853 + $pattern = '/^https?:\/\/.*\.(svg|png|jpg|jpeg|gif)$/i';
1724 1854
1725 1855 return preg_match($pattern, $urlStr);
1726 1856 }
1727 1857
@@ -1755,9 +1885,13 @@
1755 1885 if (!is_dir($icnDir)) {
1756 1886 wp_mkdir_p($icnDir);
1757 1887 }
1758 1888
1759 - $imageUrlData = file_get_contents($sanitize_url);
1889 + $response = wp_safe_remote_get($sanitize_url);
1890 + if (is_wp_error($response) || 200 !== (int) wp_remote_retrieve_response_code($response)) {
1891 + wp_send_json_error(__('Unable to fetch icon', 'bit-form'), 400);
1892 + }
1893 + $imageUrlData = wp_remote_retrieve_body($response);
1760 1894
1761 1895 $filename = sanitize_file_name($input->id . '-' . basename($sanitize_url));
1762 1896
1763 1897 $validation = wp_check_filetype($filename);
@@ -1764,8 +1898,14 @@
1764 1898 $type = $validation['type'];
1765 1899 $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
1766 1900 $is_svg = 'svg' === $ext; // Check if the file is an SVG
1767 1901 if ($type && 0 === strpos($type, 'image/') || $is_svg) {
1902 + if ($is_svg) {
1903 + $imageUrlData = (new \BitCode\BitForm\enshrined\svgSanitize\Sanitizer())->sanitize($imageUrlData);
1904 + if (false === $imageUrlData) {
1905 + wp_send_json_error(__('Invalid file type', 'bit-form'), 400);
1906 + }
1907 + }
1768 1908 $uploaded = file_put_contents($icnDir . '/' . $filename, $imageUrlData);
1769 1909
1770 1910 if ($uploaded) {
1771 1911 $uploadedFile = BITFORMS_UPLOAD_BASE_URL . '/' . 'icons' . '/' . $filename;
@@ -1956,9 +2096,9 @@
1956 2096 public function getGenerelSettings()
1957 2097 {
1958 2098 if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) {
1959 2099 $this->verifyAdminPermission();
1960 - $data = get_option('bitform_app_config', (object) ['cache_plugin' => 0, 'delete_table' => 0]);
2100 + $data = get_option('bitform_app_config', (object) ['cache_plugin' => true, 'delete_table' => 0]);
1961 2101
1962 2102 if (is_wp_error($data)) {
1963 2103 wp_send_json_error($data->get_error_message(), 411);
1964 2104 } else {