| @@ -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']); |
| @@ -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(); |
| @@ -496,15 +589,8 @@ | ||
| 496 | 589 | ); |
| 497 | 590 | } |
| 498 | 591 | } |
| 499 | 592 | |
| 500 | - public function dismissProUpgradeNotice() | |
| 501 | - { | |
| 502 | - check_ajax_referer('bitforms_dismiss_pro_notice', 'nonce'); | |
| 503 | - update_user_meta(get_current_user_id(), 'bitforms_dismiss_pro_upgrade_notice', BITFORMS_REQUIRED_BITFORMPRO_VERSION); | |
| 504 | - wp_die(); | |
| 505 | - } | |
| 506 | - | |
| 507 | 593 | private function formatFormContentForUpdate($formContents) |
| 508 | 594 | { |
| 509 | 595 | $updatedFormContents = (object) []; |
| 510 | 596 | $updatedPaths = [ |
| @@ -548,9 +634,9 @@ | ||
| 548 | 634 | } |
| 549 | 635 | |
| 550 | 636 | public function getEmailConfig() |
| 551 | 637 | { |
| 552 | - \ignore_user_abort(); | |
| 638 | + Utilities::ignoreUserAbort(); | |
| 553 | 639 | |
| 554 | 640 | if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) { |
| 555 | 641 | $this->verifyAdminPermission(); |
| 556 | 642 | $ipTool = new IpTool(); |
| @@ -577,9 +663,9 @@ | ||
| 577 | 663 | } |
| 578 | 664 | |
| 579 | 665 | public function saveEmailConfig() |
| 580 | 666 | { |
| 581 | - \ignore_user_abort(); | |
| 667 | + Utilities::ignoreUserAbort(); | |
| 582 | 668 | if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) { |
| 583 | 669 | $this->verifyAdminPermission(); |
| 584 | 670 | GlobalHelper::requirePostMethod(); |
| 585 | 671 | $ipTool = new IpTool(); |
| @@ -815,9 +901,9 @@ | ||
| 815 | 901 | * @return void |
| 816 | 902 | */ |
| 817 | 903 | public function updateForm() |
| 818 | 904 | { |
| 819 | - \ignore_user_abort(); | |
| 905 | + Utilities::ignoreUserAbort(); | |
| 820 | 906 | if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) { |
| 821 | 907 | $this->verifyAdminPermission(); |
| 822 | 908 | GlobalHelper::requirePostMethod(); |
| 823 | 909 | try { |
| @@ -1070,9 +1156,9 @@ | ||
| 1070 | 1156 | } |
| 1071 | 1157 | |
| 1072 | 1158 | public function duplicateAForm() |
| 1073 | 1159 | { |
| 1074 | - \ignore_user_abort(); | |
| 1160 | + Utilities::ignoreUserAbort(); | |
| 1075 | 1161 | if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) { |
| 1076 | 1162 | $this->verifyAdminPermission(); |
| 1077 | 1163 | GlobalHelper::requirePostMethod(); |
| 1078 | 1164 | try { |
| @@ -1102,9 +1188,9 @@ | ||
| 1102 | 1188 | } |
| 1103 | 1189 | |
| 1104 | 1190 | public function importAForm() |
| 1105 | 1191 | { |
| 1106 | - \ignore_user_abort(); | |
| 1192 | + Utilities::ignoreUserAbort(); | |
| 1107 | 1193 | if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) { |
| 1108 | 1194 | $this->verifyAdminPermission(); |
| 1109 | 1195 | GlobalHelper::requirePostMethod(); |
| 1110 | 1196 | try { |
| @@ -1383,9 +1469,9 @@ | ||
| 1383 | 1469 | } |
| 1384 | 1470 | |
| 1385 | 1471 | public function updateFormEntry() |
| 1386 | 1472 | { |
| 1387 | - \ignore_user_abort(); | |
| 1473 | + Utilities::ignoreUserAbort(); | |
| 1388 | 1474 | if (isset($_REQUEST['_ajax_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_ajax_nonce'])), 'bitforms_save')) { |
| 1389 | 1475 | $this->verifyAdminPermission(); |
| 1390 | 1476 | $formHandler = FormHandler::getInstance(); |
| 1391 | 1477 | $sanitized_request = [ |
| @@ -1740,9 +1826,13 @@ | ||
| 1740 | 1826 | update_option('bitform_secret_api_key', sanitize_text_field($input->api_key)); |
| 1741 | 1827 | $api_key = $input->api_key; |
| 1742 | 1828 | } |
| 1743 | 1829 | if (!$api_key) { |
| 1744 | - $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); | |
| 1745 | 1835 | update_option('bitform_secret_api_key', $api_key); |
| 1746 | 1836 | } |
| 1747 | 1837 | wp_send_json_success($api_key, 200); |
| 1748 | 1838 | } else { |