diff --git a/classes/controllers/FrmAbilitiesEntriesController.php b/classes/controllers/FrmAbilitiesEntriesController.php index 21f9383ad3..1722c18cfb 100644 --- a/classes/controllers/FrmAbilitiesEntriesController.php +++ b/classes/controllers/FrmAbilitiesEntriesController.php @@ -41,6 +41,7 @@ private static function register_list_entries_ability() { 'description' => __( 'Retrieve a list of Formidable entries (form submissions). Use list-forms to find the form_id.', 'formidable' ), 'category' => FrmAbilitiesController::CATEGORY, 'input_schema' => array( + 'default' => array(), 'type' => 'object', 'properties' => array( 'form_id' => array( diff --git a/classes/controllers/FrmAbilitiesFormsController.php b/classes/controllers/FrmAbilitiesFormsController.php index 31f094fc45..4aee2aee0c 100644 --- a/classes/controllers/FrmAbilitiesFormsController.php +++ b/classes/controllers/FrmAbilitiesFormsController.php @@ -43,6 +43,7 @@ private static function register_list_forms_ability() { ), 'category' => FrmAbilitiesController::CATEGORY, 'input_schema' => array( + 'default' => array(), 'type' => 'object', 'properties' => array( 'page' => array( diff --git a/classes/controllers/FrmAbilitiesPaymentsController.php b/classes/controllers/FrmAbilitiesPaymentsController.php index 75faf39c8f..ac9a0cd032 100644 --- a/classes/controllers/FrmAbilitiesPaymentsController.php +++ b/classes/controllers/FrmAbilitiesPaymentsController.php @@ -45,6 +45,7 @@ private static function register_list_payments_ability() { ), 'category' => FrmAbilitiesController::CATEGORY, 'input_schema' => array( + 'default' => array(), 'type' => 'object', 'properties' => array( 'form_id' => array( diff --git a/classes/controllers/FrmAbilitiesStylesController.php b/classes/controllers/FrmAbilitiesStylesController.php index a986ec1336..a4122d2b01 100644 --- a/classes/controllers/FrmAbilitiesStylesController.php +++ b/classes/controllers/FrmAbilitiesStylesController.php @@ -39,6 +39,7 @@ private static function register_list_styles_ability() { ), 'category' => FrmAbilitiesController::CATEGORY, 'input_schema' => array( + 'default' => array(), 'type' => 'object', 'properties' => array( 'page' => array( diff --git a/classes/controllers/FrmAbilitiesSubscriptionsController.php b/classes/controllers/FrmAbilitiesSubscriptionsController.php index 56b4835c53..544d9fd8c0 100644 --- a/classes/controllers/FrmAbilitiesSubscriptionsController.php +++ b/classes/controllers/FrmAbilitiesSubscriptionsController.php @@ -45,6 +45,7 @@ private static function register_list_subscriptions_ability() { ), 'category' => FrmAbilitiesController::CATEGORY, 'input_schema' => array( + 'default' => array(), 'type' => 'object', 'properties' => array( 'form_id' => array( diff --git a/classes/controllers/FrmMcpSettingsController.php b/classes/controllers/FrmMcpSettingsController.php index 1a708477a5..fa1ae8ad73 100644 --- a/classes/controllers/FrmMcpSettingsController.php +++ b/classes/controllers/FrmMcpSettingsController.php @@ -328,9 +328,7 @@ private static function build_connection_prompt( $install_step ) { home_url() ), $install_step . ' ' . __( 'If the skill only loads after a restart, tell me.', 'formidable' ), - // The config directory, not the skill's scripts directory: a plugin - // install keeps scripts in a versioned cache that every update replaces. - __( '2. Find the newest frm-mcp*.env file in my Downloads folder and move it to ~/.config/formidable-mcp/frm-mcp.env.', 'formidable' ) + __( '2. Move the newest frm-mcp*.env file from my Downloads folder to frm-mcp.env beside the installed skill\'s scripts/frm-mcp helper.', 'formidable' ) . ' ' . __( 'Create the folder if needed, and replace any older file there.', 'formidable' ) . ' ' . __( 'Do not open, print, or paste the file or its credentials into chat.', 'formidable' ), __( '3. Run the skill\'s scripts/frm-mcp-setup script to confirm the connection, then use the scripts/frm-mcp helper for my Formidable requests.', 'formidable' ) diff --git a/classes/models/FrmHcaptchaSettings.php b/classes/models/FrmHcaptchaSettings.php index 75b60ce97f..462d99582a 100644 --- a/classes/models/FrmHcaptchaSettings.php +++ b/classes/models/FrmHcaptchaSettings.php @@ -79,6 +79,7 @@ public function get_error_code_messages() { array( 'invalid-or-already-seen-response' => __( 'The CAPTCHA response expired or was already used.', 'formidable' ), 'not-using-dummy-passcode' => __( 'A test site key is paired with a live secret key. Use a matching pair in the global CAPTCHA settings.', 'formidable' ), + 'not-using-dummy-secret' => __( 'A test site key is paired with a live secret key. Use a matching pair in the global CAPTCHA settings.', 'formidable' ), 'sitekey-secret-mismatch' => __( 'The site key and secret key in the global CAPTCHA settings belong to different hCaptcha accounts.', 'formidable' ), 'invalid-sitekey' => __( 'The site key in the global CAPTCHA settings is not valid.', 'formidable' ), 'invalid-remoteip' => __( 'The visitor IP address sent for verification was not valid.', 'formidable' ), @@ -96,6 +97,7 @@ public function get_configuration_error_codes() { parent::get_configuration_error_codes(), array( 'not-using-dummy-passcode', + 'not-using-dummy-secret', 'sitekey-secret-mismatch', 'invalid-sitekey', ) diff --git a/classes/models/FrmMcpCompat.php b/classes/models/FrmMcpCompat.php index a51956a3f1..a3560635ff 100644 --- a/classes/models/FrmMcpCompat.php +++ b/classes/models/FrmMcpCompat.php @@ -249,7 +249,7 @@ private static function detect_unsupported_reason() { } if ( array() !== self::missing_ability_functions() ) { - return __( 'The WordPress Abilities API the MCP server is built on is not available on this site. It is part of WordPress 7.0 and newer.', 'formidable' ); + return __( 'The WordPress Abilities API the MCP server is built on is not available on this site. It is part of WordPress 6.9 and newer.', 'formidable' ); } if ( ! class_exists( self::ADAPTER_CLASS ) ) { diff --git a/classes/views/frm-fields/back-end/settings.php b/classes/views/frm-fields/back-end/settings.php index 6671c2d18b..d815923dc9 100644 --- a/classes/views/frm-fields/back-end/settings.php +++ b/classes/views/frm-fields/back-end/settings.php @@ -375,7 +375,7 @@
diff --git a/tests/phpunit/abilities/test_FrmAbilitiesContract.php b/tests/phpunit/abilities/test_FrmAbilitiesContract.php index 2402a219a0..4611e06e4c 100644 --- a/tests/phpunit/abilities/test_FrmAbilitiesContract.php +++ b/tests/phpunit/abilities/test_FrmAbilitiesContract.php @@ -29,8 +29,34 @@ class test_FrmAbilitiesContract extends FrmUnitTest { 'formidable-forms/list-forms', 'formidable-forms/list-entries', 'formidable-forms/list-styles', + 'formidable-forms/list-payments', + 'formidable-forms/list-subscriptions', ); + /** + * @return void + */ + public function test_optional_input_abilities_accept_omitted_input() { + foreach ( self::$no_required_input_abilities as $name ) { + $this->assertNotWPError( wp_get_ability( $name )->execute(), $name . ' should accept omitted input.' ); + } + } + + /** + * @return void + */ + public function test_required_input_abilities_reject_omitted_input() { + foreach ( $this->get_formidable_abilities() as $name => $ability ) { + $schema = $ability->get_input_schema(); + + if ( empty( $schema['required'] ) ) { + continue; + } + + $this->assertWPError( $ability->execute(), $name . ' should require its input.' ); + } + } + /** * @return void */ diff --git a/tests/phpunit/fields/test_FrmCaptchaSpam.php b/tests/phpunit/fields/test_FrmCaptchaSpam.php index c2cff927f5..672ef742c8 100644 --- a/tests/phpunit/fields/test_FrmCaptchaSpam.php +++ b/tests/phpunit/fields/test_FrmCaptchaSpam.php @@ -229,6 +229,36 @@ public function test_configuration_error_can_be_saved_with_actual_reason() { $this->assertStringContainsString( 'invalid-input-secret', $reason, 'The configuration error code should be stored.' ); } + /** + * @return void + */ + public function test_hcaptcha_test_key_mismatch_keeps_configuration_message() { + $settings = FrmAppHelper::get_settings(); + $settings->active_captcha = 'hcaptcha'; + $settings->hcaptcha_pubkey = 'test-site-key'; + $settings->hcaptcha_privkey = 'test-secret-key'; + $_POST = array( 'h-captcha-response' => 'test-token' ); + + foreach ( array( 'not-using-dummy-passcode', 'not-using-dummy-secret' ) as $code ) { + FrmSpamEntriesHelper::reset_flags(); + $settings->spam_handling['captcha'] = FrmSpamEntriesHelper::BLOCK; + $this->response = array( + 'body' => wp_json_encode( + array( + 'success' => false, + 'error-codes' => array( $code ), + ) + ), + ); + $errors = $this->validate_captcha(); + $this->assertStringContainsString( 'not set up correctly', $errors[ 'field' . $this->captcha->id ], $code ); + $settings->spam_handling['captcha'] = FrmSpamEntriesHelper::SAVE; + $this->assertSame( array(), $this->validate_captcha() ); + $reason = FrmSpamEntriesHelper::get_flagged_reason( array( 'form_id' => $this->captcha->form_id ) ); + $this->assertStringContainsString( 'matching pair', $reason, $code ); + } + } + public function test_unknown_service_error_code_is_preserved() { FrmAppHelper::get_settings()->spam_handling['captcha'] = FrmSpamEntriesHelper::SAVE; $this->response = array(