From b305239841bdcf6f46873ad7203eebd7f0ac9102 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 14 Oct 2025 16:07:52 +0200 Subject: [PATCH 01/10] Docs refactor in prepartion for WordPress 6.9 Moved all PHP related docs to the php-api.md Created new README.md file with correct doc ordering Removed doc file numbering Updating main repo README.md doc links --- README.md | 13 +- docs/4.using-abilities.md | 211 ------------- docs/7.registering-categories.md | 61 ---- docs/README.md | 8 + ....getting-started.md => getting-started.md} | 0 docs/{6.hooks.md => hooks.md} | 0 docs/{1.intro.md => intro.md} | 0 ...ascript-client.md => javascript-client.md} | 0 ...{3.registering-abilities.md => php-api.md} | 296 +++++++++++++++++- docs/{5.rest-api.md => rest-api.md} | 0 10 files changed, 301 insertions(+), 288 deletions(-) delete mode 100644 docs/4.using-abilities.md delete mode 100644 docs/7.registering-categories.md create mode 100644 docs/README.md rename docs/{2.getting-started.md => getting-started.md} (100%) rename docs/{6.hooks.md => hooks.md} (100%) rename docs/{1.intro.md => intro.md} (100%) rename docs/{7.javascript-client.md => javascript-client.md} (100%) rename docs/{3.registering-abilities.md => php-api.md} (55%) rename docs/{5.rest-api.md => rest-api.md} (100%) diff --git a/README.md b/README.md index 4762b713..a5eab509 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,12 @@ ## Developer Documentation -- [Introduction](docs/1.intro.md) -- [Getting Started](docs/2.getting-started.md) -- [Registering Abilities](docs/3.registering-abilities.md) -- [Using Abilities](docs/4.using-abilities.md) -- [REST API Reference](docs/5.rest-api.md) -- [Hooks](docs/6.hooks.md) -- [JavaScript/TypeScript Client](docs/7.javascript-client.md) +- [Introduction](docs/intro.md) +- [Getting Started](docs/getting-started.md) +- [PHP API Reference](docs/php-api.md) +- [REST API Reference](docs/rest-api.md) +- [JavaScript Client](docs/javascript-client.md) +- [Hooks](docs/hooks.md) - [Contributing Guidelines](CONTRIBUTING.md) ## Inspiration diff --git a/docs/4.using-abilities.md b/docs/4.using-abilities.md deleted file mode 100644 index 0a51d637..00000000 --- a/docs/4.using-abilities.md +++ /dev/null @@ -1,211 +0,0 @@ -# 4. Using Abilities (`wp_get_ability`, `wp_get_abilities`) - -Once abilities are registered, they can be retrieved and executed using global functions from the Abilities API. - -## Getting a Specific Ability (`wp_get_ability`) - -To get a single ability object by its name (namespace/ability-name): - -```php -/** - * Retrieves a registered ability using Abilities API. - * - * @param string $name The name of the registered ability, with its namespace. - * @return ?WP_Ability The registered ability instance, or null if it is not registered. - */ -function wp_get_ability( string $name ): ?WP_Ability - -// Example: -$site_info_ability = wp_get_ability( 'my-plugin/get-site-info' ); - -if ( $site_info_ability ) { - // Ability exists and is registered - $site_info = $site_info_ability->execute(); - if ( is_wp_error( $site_info ) ) { - // Handle WP_Error - echo 'Error: ' . $site_info->get_error_message(); - } else { - // Use $site_info array - echo 'Site Name: ' . $site_info['name']; - } -} else { - // Ability not found or not registered -} -``` - -## Getting All Registered Abilities (`wp_get_abilities`) - -To get an array of all registered abilities: - -```php -/** - * Retrieves all registered abilities using Abilities API. - * - * @return WP_Ability[] The array of registered abilities. - */ -function wp_get_abilities(): array - -// Example: Get all registered abilities -$all_abilities = wp_get_abilities(); - -foreach ( $all_abilities as $name => $ability ) { - echo 'Ability Name: ' . esc_html( $ability->get_name() ) . "\n"; - echo 'Label: ' . esc_html( $ability->get_label() ) . "\n"; - echo 'Description: ' . esc_html( $ability->get_description() ) . "\n"; - echo "---\n"; -} -``` - -## Executing an Ability (`$ability->execute()`) - -Once you have a `WP_Ability` object (usually from `wp_get_ability`), you execute it using the `execute()` method. - -```php -/** - * Executes the ability after input validation and running a permission check. - * - * @param mixed $input Optional. The input data for the ability. Defaults to `null`. - * @return mixed|WP_Error The result of the ability execution, or WP_Error on failure. - */ -// public function execute( $input = null ) - -// Example 1: Ability with no input parameters -$ability = wp_get_ability( 'my-plugin/get-site-info' ); -if ( $ability ) { - $site_info = $ability->execute(); // No input required - if ( is_wp_error( $site_info ) ) { - // Handle WP_Error - echo 'Error: ' . $site_info->get_error_message(); - } else { - // Use $site_info array - echo 'Site Name: ' . $site_info['name']; - } -} - -// Example 2: Ability with input parameters -$ability = wp_get_ability( 'my-plugin/update-option' ); -if ( $ability ) { - $input = array( - 'option_name' => 'blogname', - 'option_value' => 'My Updated Site Name', - ); - - $result = $ability->execute( $input ); - if ( is_wp_error( $result ) ) { - // Handle WP_Error - echo 'Error: ' . $result->get_error_message(); - } else { - // Use $result - if ( $result['success'] ) { - echo 'Option updated successfully!'; - echo 'Previous value: ' . $result['previous_value']; - } - } -} - -// Example 3: Ability with complex input validation -$ability = wp_get_ability( 'my-plugin/send-email' ); -if ( $ability ) { - $input = array( - 'to' => 'user@example.com', - 'subject' => 'Hello from WordPress', - 'message' => 'This is a test message from the Abilities API.', - ); - - $result = $ability->execute( $input ); - if ( is_wp_error( $result ) ) { - // Handle WP_Error - echo 'Error: ' . $result->get_error_message(); - } elseif ( $result['sent'] ) { - echo 'Email sent successfully!'; - } else { - echo 'Email failed to send.'; - } -} -``` - -## Checking Permissions (`$ability->check_permissions()`) - -You can check if the current user has permissions to execute the ability, also without executing it. The `check_permissions()` method returns either `true`, `false`, or a `WP_Error` object. `true` means permission is granted, `false` means the user simply lacks permission, and a `WP_Error` return value typically indicates a failure in the permission check process (such as an internal error or misconfiguration). You must use `is_wp_error()` to handle errors properly and distinguish between permission denial and actual errors: - -```php -$ability = wp_get_ability( 'my-plugin/update-option' ); -if ( $ability ) { - $input = array( - 'option_name' => 'blogname', - 'option_value' => 'New Site Name', - ); - - // Check permission before execution - always use is_wp_error() first - $has_permissions = $ability->check_permissions( $input ); - if ( true === $has_permissions ) { - // Permissions granted – safe to execute. - echo 'You have permissions to execute this ability.'; - } else { - // Don't leak permission errors to unauthenticated users. - if ( is_wp_error( $has_permissions ) ) { - error_log( 'Permissions check failed: ' . $has_permissions->get_error_message() ); - } - - echo 'You do not have permissions to execute this ability.'; - } -} -``` - -## Inspecting Ability Properties - -The `WP_Ability` class provides several getter methods to inspect ability properties: - -```php -$ability = wp_get_ability( 'my-plugin/get-site-info' ); -if ( $ability ) { - // Basic properties - echo 'Name: ' . $ability->get_name() . "\n"; - echo 'Label: ' . $ability->get_label() . "\n"; - echo 'Description: ' . $ability->get_description() . "\n"; - - // Schema information - $input_schema = $ability->get_input_schema(); - $output_schema = $ability->get_output_schema(); - - echo 'Input Schema: ' . json_encode( $input_schema, JSON_PRETTY_PRINT ) . "\n"; - echo 'Output Schema: ' . json_encode( $output_schema, JSON_PRETTY_PRINT ) . "\n"; - - // Metadata - $meta = $ability->get_meta(); - if ( ! empty( $meta ) ) { - echo 'Metadata: ' . json_encode( $meta, JSON_PRETTY_PRINT ) . "\n"; - } -} -``` - -## Error Handling Patterns - -The Abilities API uses several error handling mechanisms: - -```php -$ability = wp_get_ability( 'my-plugin/some-ability' ); - -if ( ! $ability ) { - // Ability not registered - echo 'Ability not found'; - return; -} - -$result = $ability->execute( $input ); - -// Check for WP_Error (validation, permission, or callback errors) -if ( is_wp_error( $result ) ) { - echo 'WP_Error: ' . $result->get_error_message(); - return; -} - -// Check for null result (permission denied, invalid callback, or validation failure) -if ( is_null( $result ) ) { - echo 'Execution returned null - check permissions and callback validity'; - return; -} - -// Success - use the result -// Process $result based on the ability's output schema -``` diff --git a/docs/7.registering-categories.md b/docs/7.registering-categories.md deleted file mode 100644 index 8ddc78e9..00000000 --- a/docs/7.registering-categories.md +++ /dev/null @@ -1,61 +0,0 @@ -# 7. Registering Categories - -Before registering abilities, you must register at least one category. Categories help organize abilities and make them easier to discover and filter. - -## Function Signature - -```php -wp_register_ability_category( string $slug, array $args ): ?\WP_Ability_Category -``` - -**Parameters:** -- `$slug` (`string`): A unique identifier for the category. Must contain only lowercase alphanumeric characters and dashes (no underscores, no uppercase). -- `$args` (`array`): Category configuration with these keys: - - `label` (`string`, **Required**): Human-readable name for the category. Should be translatable. - - `description` (`string`, **Required**): Detailed description of the category's purpose. Should be translatable. - - `meta` (`array`, **Optional**): An associative array for storing arbitrary additional metadata about the category. - -**Return:** (`?\WP_Ability_Category`) An instance of the registered category if it was successfully registered, `null` on failure (e.g., invalid arguments, duplicate slug). - -**Note:** Categories must be registered during the `abilities_api_categories_init` action hook. - -## Code Example - -```php -add_action( 'abilities_api_categories_init', 'my_plugin_register_categories' ); -function my_plugin_register_categories() { - wp_register_ability_category( 'data-retrieval', array( - 'label' => __( 'Data Retrieval', 'my-plugin' ), - 'description' => __( 'Abilities that retrieve and return data from the WordPress site.', 'my-plugin' ), - )); - - wp_register_ability_category( 'data-modification', array( - 'label' => __( 'Data Modification', 'my-plugin' ), - 'description' => __( 'Abilities that modify data on the WordPress site.', 'my-plugin' ), - )); - - wp_register_ability_category( 'communication', array( - 'label' => __( 'Communication', 'my-plugin' ), - 'description' => __( 'Abilities that send messages or notifications.', 'my-plugin' ), - )); -} -``` - -## Category Slug Convention - -The `$slug` parameter must follow these rules: - -- **Format:** Must contain only lowercase alphanumeric characters (`a-z`, `0-9`) and hyphens (`-`). -- **Valid examples:** `data-retrieval`, `ecommerce`, `site-information`, `user-management`, `category-123` -- **Invalid examples:** - - Uppercase: `Data-Retrieval`, `MyCategory` - - Underscores: `data_retrieval` - - Special characters: `data.retrieval`, `data/retrieval`, `data retrieval` - - Leading/trailing dashes: `-data`, `data-` - - Double dashes: `data--retrieval` - -## Other Category Functions - -- `wp_unregister_ability_category( string $slug )` - Remove a registered category. Returns the unregistered category instance or `null` on failure. -- `wp_get_ability_category( string $slug )` - Retrieve a specific category by slug. Returns the category instance or `null` if not found. -- `wp_get_ability_categories()` - Get all registered categories as an associative array keyed by slug. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..01543d08 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,8 @@ +# Developer Documentation + +- [Introduction](intro.md) +- [Getting Started](getting-started.md) +- [PHP API Reference](php-api.md) +- [REST API Reference](rest-api.md) +- [JavaScript Client](javascript-client.md) +- [Hooks](hooks.md) diff --git a/docs/2.getting-started.md b/docs/getting-started.md similarity index 100% rename from docs/2.getting-started.md rename to docs/getting-started.md diff --git a/docs/6.hooks.md b/docs/hooks.md similarity index 100% rename from docs/6.hooks.md rename to docs/hooks.md diff --git a/docs/1.intro.md b/docs/intro.md similarity index 100% rename from docs/1.intro.md rename to docs/intro.md diff --git a/docs/7.javascript-client.md b/docs/javascript-client.md similarity index 100% rename from docs/7.javascript-client.md rename to docs/javascript-client.md diff --git a/docs/3.registering-abilities.md b/docs/php-api.md similarity index 55% rename from docs/3.registering-abilities.md rename to docs/php-api.md index 2d648a0b..589faa72 100644 --- a/docs/3.registering-abilities.md +++ b/docs/php-api.md @@ -1,8 +1,73 @@ -# 3. Registering Abilities (`wp_register_ability`) +# PHP API + +## Registering Categories + +Before registering abilities, you must register at least one category. Categories help organize abilities and make them easier to discover and filter. + +### Function Signature + +```php +wp_register_ability_category( string $slug, array $args ): ?\WP_Ability_Category +``` + +**Parameters:** +- `$slug` (`string`): A unique identifier for the category. Must contain only lowercase alphanumeric characters and dashes (no underscores, no uppercase). +- `$args` (`array`): Category configuration with these keys: + - `label` (`string`, **Required**): Human-readable name for the category. Should be translatable. + - `description` (`string`, **Required**): Detailed description of the category's purpose. Should be translatable. + - `meta` (`array`, **Optional**): An associative array for storing arbitrary additional metadata about the category. + +**Return:** (`?\WP_Ability_Category`) An instance of the registered category if it was successfully registered, `null` on failure (e.g., invalid arguments, duplicate slug). + +**Note:** Categories must be registered during the `abilities_api_categories_init` action hook. + +### Code Example + +```php +add_action( 'abilities_api_categories_init', 'my_plugin_register_categories' ); +function my_plugin_register_categories() { + wp_register_ability_category( 'data-retrieval', array( + 'label' => __( 'Data Retrieval', 'my-plugin' ), + 'description' => __( 'Abilities that retrieve and return data from the WordPress site.', 'my-plugin' ), + )); + + wp_register_ability_category( 'data-modification', array( + 'label' => __( 'Data Modification', 'my-plugin' ), + 'description' => __( 'Abilities that modify data on the WordPress site.', 'my-plugin' ), + )); + + wp_register_ability_category( 'communication', array( + 'label' => __( 'Communication', 'my-plugin' ), + 'description' => __( 'Abilities that send messages or notifications.', 'my-plugin' ), + )); +} +``` + +### Category Slug Convention + +The `$slug` parameter must follow these rules: + +- **Format:** Must contain only lowercase alphanumeric characters (`a-z`, `0-9`) and hyphens (`-`). +- **Valid examples:** `data-retrieval`, `ecommerce`, `site-information`, `user-management`, `category-123` +- **Invalid examples:** + - Uppercase: `Data-Retrieval`, `MyCategory` + - Underscores: `data_retrieval` + - Special characters: `data.retrieval`, `data/retrieval`, `data retrieval` + - Leading/trailing dashes: `-data`, `data-` + - Double dashes: `data--retrieval` + +### Other Category Functions + +- `wp_unregister_ability_category( string $slug )` - Remove a registered category. Returns the unregistered category instance or `null` on failure. +- `wp_get_ability_category( string $slug )` - Retrieve a specific category by slug. Returns the category instance or `null` if not found. +- `wp_get_ability_categories()` - Get all registered categories as an associative array keyed by slug. + + +## Registering Abilities (`wp_register_ability`) The primary way to add functionality to the Abilities API is by using the `wp_register_ability()` function, typically hooked into the `abilities_api_init` action. -## Function Signature +### Function Signature ```php wp_register_ability( string $id, array $args ): ?\WP_Ability @@ -12,7 +77,7 @@ wp_register_ability( string $id, array $args ): ?\WP_Ability - `$args` (`array`): An array of arguments defining the ability configuration. - **Return:** (`?\WP_Ability`) An instance of the registered ability if it was successfully registered, `null` on failure (e.g., invalid arguments, duplicate ID). -## Parameters Explained +### Parameters Explained The `$args` array accepts the following keys: @@ -38,7 +103,7 @@ The `$args` array accepts the following keys: - When `true`, the ability will be listed in REST API responses and can be executed via REST endpoints. - When `false`, the ability will be hidden from REST API listings and cannot be executed via REST endpoints, but remains available for internal PHP usage. -## Ability ID Convention +### Ability ID Convention The `$id` parameter must follow the pattern `namespace/ability-name`: @@ -46,9 +111,9 @@ The `$id` parameter must follow the pattern `namespace/ability-name`: - **Convention:** Use your plugin slug as the namespace, like `my-plugin/ability-name`. - **Examples:** `my-plugin/update-settings`, `woocommerce/get-product`, `contact-form/send-message`, `analytics/track-event` -## Code Examples +### Code Examples -### Registering a Simple Data Retrieval Ability +#### Registering a Simple Data Retrieval Ability ```php add_action( 'abilities_api_init', 'my_plugin_register_site_info_ability' ); @@ -98,7 +163,7 @@ function my_plugin_register_site_info_ability() { } ``` -### Registering an Ability with Input Parameters +#### Registering an Ability with Input Parameters ```php add_action( 'abilities_api_init', 'my_plugin_register_update_option_ability' ); @@ -159,7 +224,7 @@ function my_plugin_register_update_option_ability() { } ``` -### Registering an Ability with Plugin Dependencies +#### Registering an Ability with Plugin Dependencies ```php add_action( 'abilities_api_init', 'my_plugin_register_woo_stats_ability' ); @@ -218,7 +283,7 @@ function my_plugin_register_woo_stats_ability() { } ``` -### Registering an Ability That May Fail +#### Registering an Ability That May Fail ```php add_action( 'abilities_api_init', 'my_plugin_register_send_email_ability' ); @@ -280,3 +345,216 @@ function my_plugin_register_send_email_ability() { )); } ``` + +## Using Abilities (`wp_get_ability`, `wp_get_abilities`) + +Once abilities are registered, they can be retrieved and executed using global functions from the Abilities API. + +### Getting a Specific Ability (`wp_get_ability`) + +To get a single ability object by its name (namespace/ability-name): + +```php +/** + * Retrieves a registered ability using Abilities API. + * + * @param string $name The name of the registered ability, with its namespace. + * @return ?WP_Ability The registered ability instance, or null if it is not registered. + */ +function wp_get_ability( string $name ): ?WP_Ability + +// Example: +$site_info_ability = wp_get_ability( 'my-plugin/get-site-info' ); + +if ( $site_info_ability ) { + // Ability exists and is registered + $site_info = $site_info_ability->execute(); + if ( is_wp_error( $site_info ) ) { + // Handle WP_Error + echo 'Error: ' . $site_info->get_error_message(); + } else { + // Use $site_info array + echo 'Site Name: ' . $site_info['name']; + } +} else { + // Ability not found or not registered +} +``` + +### Getting All Registered Abilities (`wp_get_abilities`) + +To get an array of all registered abilities: + +```php +/** + * Retrieves all registered abilities using Abilities API. + * + * @return WP_Ability[] The array of registered abilities. + */ +function wp_get_abilities(): array + +// Example: Get all registered abilities +$all_abilities = wp_get_abilities(); + +foreach ( $all_abilities as $name => $ability ) { + echo 'Ability Name: ' . esc_html( $ability->get_name() ) . "\n"; + echo 'Label: ' . esc_html( $ability->get_label() ) . "\n"; + echo 'Description: ' . esc_html( $ability->get_description() ) . "\n"; + echo "---\n"; +} +``` + +### Executing an Ability (`$ability->execute()`) + +Once you have a `WP_Ability` object (usually from `wp_get_ability`), you execute it using the `execute()` method. + +```php +/** + * Executes the ability after input validation and running a permission check. + * + * @param mixed $input Optional. The input data for the ability. Defaults to `null`. + * @return mixed|WP_Error The result of the ability execution, or WP_Error on failure. + */ +// public function execute( $input = null ) + +// Example 1: Ability with no input parameters +$ability = wp_get_ability( 'my-plugin/get-site-info' ); +if ( $ability ) { + $site_info = $ability->execute(); // No input required + if ( is_wp_error( $site_info ) ) { + // Handle WP_Error + echo 'Error: ' . $site_info->get_error_message(); + } else { + // Use $site_info array + echo 'Site Name: ' . $site_info['name']; + } +} + +// Example 2: Ability with input parameters +$ability = wp_get_ability( 'my-plugin/update-option' ); +if ( $ability ) { + $input = array( + 'option_name' => 'blogname', + 'option_value' => 'My Updated Site Name', + ); + + $result = $ability->execute( $input ); + if ( is_wp_error( $result ) ) { + // Handle WP_Error + echo 'Error: ' . $result->get_error_message(); + } else { + // Use $result + if ( $result['success'] ) { + echo 'Option updated successfully!'; + echo 'Previous value: ' . $result['previous_value']; + } + } +} + +// Example 3: Ability with complex input validation +$ability = wp_get_ability( 'my-plugin/send-email' ); +if ( $ability ) { + $input = array( + 'to' => 'user@example.com', + 'subject' => 'Hello from WordPress', + 'message' => 'This is a test message from the Abilities API.', + ); + + $result = $ability->execute( $input ); + if ( is_wp_error( $result ) ) { + // Handle WP_Error + echo 'Error: ' . $result->get_error_message(); + } elseif ( $result['sent'] ) { + echo 'Email sent successfully!'; + } else { + echo 'Email failed to send.'; + } +} +``` + +### Checking Permissions (`$ability->check_permissions()`) + +You can check if the current user has permissions to execute the ability, also without executing it. The `check_permissions()` method returns either `true`, `false`, or a `WP_Error` object. `true` means permission is granted, `false` means the user simply lacks permission, and a `WP_Error` return value typically indicates a failure in the permission check process (such as an internal error or misconfiguration). You must use `is_wp_error()` to handle errors properly and distinguish between permission denial and actual errors: + +```php +$ability = wp_get_ability( 'my-plugin/update-option' ); +if ( $ability ) { + $input = array( + 'option_name' => 'blogname', + 'option_value' => 'New Site Name', + ); + + // Check permission before execution - always use is_wp_error() first + $has_permissions = $ability->check_permissions( $input ); + if ( true === $has_permissions ) { + // Permissions granted – safe to execute. + echo 'You have permissions to execute this ability.'; + } else { + // Don't leak permission errors to unauthenticated users. + if ( is_wp_error( $has_permissions ) ) { + error_log( 'Permissions check failed: ' . $has_permissions->get_error_message() ); + } + + echo 'You do not have permissions to execute this ability.'; + } +} +``` + +### Inspecting Ability Properties + +The `WP_Ability` class provides several getter methods to inspect ability properties: + +```php +$ability = wp_get_ability( 'my-plugin/get-site-info' ); +if ( $ability ) { + // Basic properties + echo 'Name: ' . $ability->get_name() . "\n"; + echo 'Label: ' . $ability->get_label() . "\n"; + echo 'Description: ' . $ability->get_description() . "\n"; + + // Schema information + $input_schema = $ability->get_input_schema(); + $output_schema = $ability->get_output_schema(); + + echo 'Input Schema: ' . json_encode( $input_schema, JSON_PRETTY_PRINT ) . "\n"; + echo 'Output Schema: ' . json_encode( $output_schema, JSON_PRETTY_PRINT ) . "\n"; + + // Metadata + $meta = $ability->get_meta(); + if ( ! empty( $meta ) ) { + echo 'Metadata: ' . json_encode( $meta, JSON_PRETTY_PRINT ) . "\n"; + } +} +``` + +### Error Handling Patterns + +The Abilities API uses several error handling mechanisms: + +```php +$ability = wp_get_ability( 'my-plugin/some-ability' ); + +if ( ! $ability ) { + // Ability not registered + echo 'Ability not found'; + return; +} + +$result = $ability->execute( $input ); + +// Check for WP_Error (validation, permission, or callback errors) +if ( is_wp_error( $result ) ) { + echo 'WP_Error: ' . $result->get_error_message(); + return; +} + +// Check for null result (permission denied, invalid callback, or validation failure) +if ( is_null( $result ) ) { + echo 'Execution returned null - check permissions and callback validity'; + return; +} + +// Success - use the result +// Process $result based on the ability's output schema +``` + diff --git a/docs/5.rest-api.md b/docs/rest-api.md similarity index 100% rename from docs/5.rest-api.md rename to docs/rest-api.md From d43eeabb60eb49a60cd1385b97486af88a0f9e2e Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 14 Oct 2025 16:20:07 +0200 Subject: [PATCH 02/10] Updating hooks Adding reference for abilities_api_init Moving hook registration in code examples above callback function for better readability --- docs/hooks.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/docs/hooks.md b/docs/hooks.md index 02339b6c..9aef1e2d 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -29,6 +29,7 @@ do_action( 'abilities_api_categories_init', $registry ); #### Usage Example ```php +add_action( 'abilities_api_categories_init', 'my_plugin_register_categories' ); /** * Register custom ability categories. * @@ -45,9 +46,49 @@ function my_plugin_register_categories( $registry ) { 'description' => __( 'Abilities that provide analytical data and insights.', 'my-plugin' ), )); } -add_action( 'abilities_api_categories_init', 'my_plugin_register_categories' ); ``` +### `abilities_api_init` + +Fires when the abilities registry has been initialized. This is the proper hook to use when registering abilities. + +```php +do_action( 'abilities_api_init', $registry ); +``` + +#### Parameters + +- `$registry` (`\WP_Abilities_Registry`): The abilities registry instance. + +#### Usage Example + +```php +add_action('abilities_api_init', 'my_plugin_register_abilities'); +/** + * Register custom abilities. + */ +function function my_plugin_register_abilities() { + wp_register_ability( 'my-plugin/ability', array( + 'label' => __( 'Title', 'my-plugin' ), + 'description' => __( 'Description.', 'my-plugin' ), + 'category' => 'analytics', + 'input_schema' => array( + 'type' => 'object', + 'properties' => array(), + 'additionalProperties' => false, + ), + 'output_schema' => array( + 'type' => 'string', + 'description' => 'The site title.', + ), + 'execute_callback' => 'my_plugin_get_site_title', + 'permission_callback' => '__return_true', // Everyone can access this + 'meta' => array( + 'category' => 'site-info', + 'show_in_rest' => true, // Optional: expose via REST API + ) ); +} +``` ### `before_execute_ability` Fires immediately before an ability gets executed, after permission checks have passed but before the execution callback is called. @@ -64,6 +105,7 @@ do_action( 'before_execute_ability', $ability_name, $input ); #### Usage Example ```php +add_action( 'before_execute_ability', 'log_ability_execution', 10, 2 ); /** * Log each ability execution attempt. * @param string $ability_name The name of the ability being executed. @@ -75,7 +117,6 @@ function log_ability_execution( string $ability_name, $input ) { error_log( 'Input: ' . wp_json_encode( $input ) ); } } -add_action( 'before_execute_ability', 'log_ability_execution', 10, 2 ); ``` ### `after_execute_ability` @@ -95,6 +136,7 @@ do_action( 'after_execute_ability', string $ability_name, $input, $result ); #### Usage Example ```php +add_action( 'after_execute_ability', 'log_ability_result', 10, 3 ); /** * Log the result of each ability execution. * @@ -106,7 +148,6 @@ function log_ability_result( string $ability_name, $input, $result ) { error_log( 'Completed ability: ' . $ability_name ); error_log( 'Result: ' . wp_json_encode( $result ) ); } -add_action( 'after_execute_ability', 'log_ability_result', 10, 3 ); ``` ## Filters @@ -127,6 +168,7 @@ $args = apply_filters( 'register_ability_args', array $args, string $ability_nam #### Usage Example ```php +add_filter( 'register_ability_args', 'my_modify_ability_args', 10, 2 ); /** * Modify ability args before validation. * @@ -166,7 +208,6 @@ function my_modify_ability_args( array $args, string $ability_name ): array { return $args; } -add_filter( 'register_ability_args', 'my_modify_ability_args', 10, 2 ); ``` ### `register_ability_category_args` @@ -185,6 +226,7 @@ $args = apply_filters( 'register_ability_category_args', array $args, string $sl #### Usage Example ```php +add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 ); /** * Modify category args before validation. * @@ -200,5 +242,4 @@ function my_modify_category_args( array $args, string $slug ): array { } return $args; } -add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 ); ``` From 008744476998f31d3ea7848edf73943f3b2cb57a Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 14 Oct 2025 16:36:31 +0200 Subject: [PATCH 03/10] Adding other category functions to PHP API --- docs/php-api.md | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/docs/php-api.md b/docs/php-api.md index 589faa72..384401dc 100644 --- a/docs/php-api.md +++ b/docs/php-api.md @@ -56,12 +56,47 @@ The `$slug` parameter must follow these rules: - Leading/trailing dashes: `-data`, `data-` - Double dashes: `data--retrieval` -### Other Category Functions +## Unregister a Category -- `wp_unregister_ability_category( string $slug )` - Remove a registered category. Returns the unregistered category instance or `null` on failure. -- `wp_get_ability_category( string $slug )` - Retrieve a specific category by slug. Returns the category instance or `null` if not found. -- `wp_get_ability_categories()` - Get all registered categories as an associative array keyed by slug. +Remove a registered category. +### Function Signature + +```php +wp_unregister_ability_category( string $slug ) ?\WP_Ability_Category +``` + +**Parameters:** +- `$slug` (`string`): The slug of the registered category. + +**Return:** (`?\WP_Ability_Category`) The unregistered category instance on success, `null` on failure. + +## Fetch a Category + +Retrieve a specific category by slug. + +### Function Signature + +```php +wp_get_ability_category( string $slug ) ?\WP_Ability_Category +``` + +**Parameters:** +- `$slug` (`string`): The slug of the registered category. + +**Return:** (`?\WP_Ability_Category`) The category instance on success, `null` on failure. + +## Fetch all Categories + +Get all registered categories as an associative array keyed by slug. + +### Function Signature + +```php +wp_get_ability_categories() array +``` + +**Return:** (`array`) An associative array of all registered categories, keyed by slug. Each value is an instance of `WP_Ability_Category`. ## Registering Abilities (`wp_register_ability`) From bb0ef4a40474bda93871bd44a45aeff3424bfcfa Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Wed, 15 Oct 2025 15:19:14 +0200 Subject: [PATCH 04/10] Update README.md with direct link to docs README --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a5eab509..22057d85 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,9 @@ 3. **Security‑first** - explicit permissions determine who/what may invoke an ability. 4. **Gradual adoption** - ships first as a Composer package, migrates smoothly to core. -## Developer Documentation - -- [Introduction](docs/intro.md) -- [Getting Started](docs/getting-started.md) -- [PHP API Reference](docs/php-api.md) -- [REST API Reference](docs/rest-api.md) -- [JavaScript Client](docs/javascript-client.md) -- [Hooks](docs/hooks.md) -- [Contributing Guidelines](CONTRIBUTING.md) +## Documentation + +- **[Developer docs](docs/README.md)**. ## Inspiration From fd9c91840a9cf35fd743772589dd37365df564e9 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Thu, 16 Oct 2025 09:53:20 +0200 Subject: [PATCH 05/10] Adding ability_class information and example (#122) * Adding ability_class information and example --- docs/php-api.md | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/docs/php-api.md b/docs/php-api.md index 384401dc..5c9820ae 100644 --- a/docs/php-api.md +++ b/docs/php-api.md @@ -137,6 +137,7 @@ The `$args` array accepts the following keys: - `show_in_rest` (`boolean`, **Optional**): Whether to expose this ability via the REST API. Default: `false`. - When `true`, the ability will be listed in REST API responses and can be executed via REST endpoints. - When `false`, the ability will be hidden from REST API listings and cannot be executed via REST endpoints, but remains available for internal PHP usage. +- `ability_class` (`string`, **Optional**): The fully-qualified class name of a custom ability class that extends `WP_Ability`. This allows you to customize the behavior of an ability by extending the base `WP_Ability` class and overriding its methods. The custom class must extend `WP_Ability`. Default: `WP_Ability`. ### Ability ID Convention @@ -381,6 +382,144 @@ function my_plugin_register_send_email_ability() { } ``` +#### Registering an Ability with a Custom Ability Class + +The `ability_class` parameter allows you to use a custom class that extends `WP_Ability`. This is useful when you want to extend the default behavior of the base `WP_Ability` class. + +**Example: Creating a custom ability class with additional methods** + +```php +/** + * Custom ability class that adds logging. + * + * This example shows how to extend WP_Ability to add custom behavior + * while still leveraging all the standard ability functionality. + */ +class My_Plugin_Post_Validator_Ability extends WP_Ability { + /** + * Override the do_execute method to add custom logging. + * + * This demonstrates how you can override methods from WP_Ability + * to customize behavior before or after the standard execution. + * + * @param mixed $input Optional. The input data for the ability. + * @return mixed|\WP_Error The result of the ability execution. + */ + protected function do_execute( $input = null ) { + // Log the execution for debugging purposes + error_log( sprintf( + 'Executing ability: %s with input: %s', + $this->get_name(), + json_encode( $input ) + ) ); + + // Call the parent's do_execute to run the normal execute_callback + $result = parent::do_execute( $input ); + + // Log the result + if ( is_wp_error( $result ) ) { + error_log( sprintf( + 'Ability %s failed: %s', + $this->get_name(), + $result->get_error_message() + ) ); + } else { + error_log( sprintf( + 'Ability %s completed successfully', + $this->get_name() + ) ); + } + + return $result; + } +} + +/** + * Register the ability using the custom ability class. + */ +add_action( 'abilities_api_init', 'my_plugin_register_post_validator_ability' ); +function my_plugin_register_post_validator_ability() { + wp_register_ability( 'my-plugin/validate-post', array( + 'label' => __( 'Validate Post', 'my-plugin' ), + 'description' => __( 'Validates that a post exists, is published, and returns its metadata.', 'my-plugin' ), + 'category' => 'data-retrieval', + 'input_schema' => array( + 'type' => 'object', + 'properties' => array( + 'post_id' => array( + 'type' => 'integer', + 'description' => 'The ID of the post to validate', + 'minimum' => 1 + ) + ), + 'required' => array( 'post_id' ), + 'additionalProperties' => false + ), + 'output_schema' => array( + 'type' => 'object', + 'properties' => array( + 'valid' => array( + 'type' => 'boolean', + 'description' => 'Whether the post is valid' + ), + 'post_title' => array( + 'type' => 'string', + 'description' => 'The post title' + ), + 'post_date' => array( + 'type' => 'string', + 'description' => 'The post publication date' + ) + ) + ), + 'execute_callback' => function( $input ) { + $post_id = $input['post_id']; + $post = get_post( $post_id ); + if ( ! $post ) { + return new \WP_Error( + 'invalid_post', + __( 'The specified post does not exist.', 'my-plugin' ) + ); + } + // Check if the post is published + if ( 'publish' !== $post->post_status ) { + return new \WP_Error( + 'post_not_published', + __( 'The specified post is not published.', 'my-plugin' ) + ); + } + // If validation passes, return post information + return array( + 'valid' => true, + 'post_title' => $post->post_title, + 'post_date' => $post->post_date + ); + }, + 'permission_callback' => function() { + // Any logged-in user can validate posts + return is_user_logged_in(); + }, + 'meta' => array( + 'annotations' => array( + 'readonly' => true, + 'destructive' => false + ) + ), + // Specify the custom ability class to use + 'ability_class' => 'My_Plugin_Post_Validator_Ability' + )); +} +``` + +**Important notes about custom ability classes:** + +- Your custom class **must** extend `WP_Ability` +- The custom class is only used to instantiate the ability - the `ability_class` parameter is not stored as a property of the ability +- You can override protected methods like `do_execute()`, `validate_input()`, or `validate_output()` to customize behavior +- You can add custom methods to provide additional functionality specific to your ability +- The custom class receives the same `$name` and `$args` parameters in its constructor as the base `WP_Ability` class +- If the specified class does not exist or does not extend `WP_Ability`, registration will fail with a `_doing_it_wrong()` notice + ## Using Abilities (`wp_get_ability`, `wp_get_abilities`) Once abilities are registered, they can be retrieved and executed using global functions from the Abilities API. From a05bfc276a57fca965d261bb5ae8da700afd88de Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Thu, 16 Oct 2025 09:56:03 +0200 Subject: [PATCH 06/10] Adding contributing docs to Documenation section in the readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 22057d85..791c2851 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ ## Documentation - **[Developer docs](docs/README.md)**. +- **[Contributing Guidelines](CONTRIBUTING.md)**. ## Inspiration From be3c64dbb8757222f6a84e098cf75018da543198 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 21 Oct 2025 09:17:22 +0200 Subject: [PATCH 07/10] Fixing outdated link --- docs/php-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/php-api.md b/docs/php-api.md index 5c9820ae..28d5b898 100644 --- a/docs/php-api.md +++ b/docs/php-api.md @@ -118,7 +118,7 @@ The `$args` array accepts the following keys: - `label` (`string`, **Required**): A human-readable name for the ability. Used for display purposes. Should be translatable. - `description` (`string`, **Required**): A detailed description of what the ability does, its purpose, and its parameters or return values. This is crucial for AI agents to understand how and when to use the ability. Should be translatable. -- `category` (`string`, **Required**): The slug of the category this ability belongs to. The category must be registered before registering the ability using `wp_register_ability_category()`. Categories help organize and filter abilities by their purpose. See [Registering Categories](7.registering-categories.md) for details. +- `category` (`string`, **Required**): The slug of the category this ability belongs to. The category must be registered before registering the ability using `wp_register_ability_category()`. Categories help organize and filter abilities by their purpose. See [Registering Categories](#registering-categories) for details. - `input_schema` (`array`, **Required**): A JSON Schema definition describing the expected input parameters for the ability's execute callback. Used for validation and documentation. - `output_schema` (`array`, **Required**): A JSON Schema definition describing the expected format of the data returned by the ability. Used for validation and documentation. - `execute_callback` (`callable`, **Required**): The PHP function or method to execute when this ability is called. From 57e971ba8810964021e9a14ea534ade3d202efd9 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 21 Oct 2025 09:39:20 +0200 Subject: [PATCH 08/10] PR review updates --- docs/php-api.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/php-api.md b/docs/php-api.md index 28d5b898..8d4295ef 100644 --- a/docs/php-api.md +++ b/docs/php-api.md @@ -119,15 +119,17 @@ The `$args` array accepts the following keys: - `label` (`string`, **Required**): A human-readable name for the ability. Used for display purposes. Should be translatable. - `description` (`string`, **Required**): A detailed description of what the ability does, its purpose, and its parameters or return values. This is crucial for AI agents to understand how and when to use the ability. Should be translatable. - `category` (`string`, **Required**): The slug of the category this ability belongs to. The category must be registered before registering the ability using `wp_register_ability_category()`. Categories help organize and filter abilities by their purpose. See [Registering Categories](#registering-categories) for details. -- `input_schema` (`array`, **Required**): A JSON Schema definition describing the expected input parameters for the ability's execute callback. Used for validation and documentation. +- `input_schema` (`array`, **Optional**): A JSON Schema definition describing the expected input parameters for the ability's execute callback. Only needed when creating Abilities that require inputs. Defaults to `null` only when no schema is provided. Used for validation and documentation. - `output_schema` (`array`, **Required**): A JSON Schema definition describing the expected format of the data returned by the ability. Used for validation and documentation. - `execute_callback` (`callable`, **Required**): The PHP function or method to execute when this ability is called. - - The callback receives one optional argument: it can have any type as defined in the input schema (e.g., `array`, `object`, `string`, etc.). + - The callback receives one optional argument, the input data for the ability. The argument is required when the input schema is defined. + - The input argument will have the same type as defined in the input schema (e.g., `array`, `object`, `string`, etc.). - The callback should return the result of the ability's operation or return a `WP_Error` object on failure. - `permission_callback` (`callable`, **Required**): A callback function to check if the current user has permission to execute this ability. - - The callback receives one optional argument: it can have any type as defined in the input schema (e.g., `array`, `object`, `string`, etc.). + - The callback receives one optional argument, the input data for the ability. The argument is required when the input schema is defined. + - The input argument will have the same type as defined in the input schema (e.g., `array`, `object`, `string`, etc.). - The callback should return a boolean (`true` if the user has permission, `false` otherwise), or a `WP_Error` object on failure. - - If the input does not validate against the input schema, the permission callback will not be called, and a `WP_Error` will be returned instead. + - If an input schema is set, and the input does not validate against the input schema, the permission callback will not be called, and a `WP_Error` will be returned instead. - `meta` (`array`, **Optional**): An associative array for storing arbitrary additional metadata about the ability. - `annotations` (`array`, **Optional**): An associative array of annotations providing hints about the ability's behavior characteristics. Supports the following keys: - `instructions` (`string`, **Optional**): Custom instructions or guidance for using the ability (default: `''`). @@ -149,7 +151,7 @@ The `$id` parameter must follow the pattern `namespace/ability-name`: ### Code Examples -#### Registering a Simple Data Retrieval Ability +#### Registering a simple data retrieval Ability without an input schema ```php add_action( 'abilities_api_init', 'my_plugin_register_site_info_ability' ); @@ -158,11 +160,6 @@ function my_plugin_register_site_info_ability() { 'label' => __( 'Get Site Information', 'my-plugin' ), 'description' => __( 'Retrieves basic information about the WordPress site including name, description, and URL.', 'my-plugin' ), 'category' => 'data-retrieval', - 'input_schema' => array( - 'type' => 'object', - 'properties' => array(), - 'additionalProperties' => false - ), 'output_schema' => array( 'type' => 'object', 'properties' => array( @@ -181,7 +178,7 @@ function my_plugin_register_site_info_ability() { ) ) ), - 'execute_callback' => function( $input ) { + 'execute_callback' => function() { return array( 'name' => get_bloginfo( 'name' ), 'description' => get_bloginfo( 'description' ), @@ -312,8 +309,7 @@ function my_plugin_register_woo_stats_ability() { return current_user_can( 'manage_woocommerce' ); }, 'meta' => array( - 'requires_plugin' => 'woocommerce', - 'category' => 'ecommerce' + 'requires_plugin' => 'woocommerce' ) )); } From 35f1714691c0577b4a0b377519a927f1ce17dda9 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 21 Oct 2025 09:46:48 +0200 Subject: [PATCH 09/10] Adding REST API docs updates from #120 --- docs/rest-api.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/docs/rest-api.md b/docs/rest-api.md index c2cebb4a..bbadba3b 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -107,6 +107,99 @@ curl https://example.com/wp-json/wp/v2/abilities ] ``` +## List Categories + +### Definition + +`GET /wp/v2/abilities/categories` + +### Arguments + +- `page` _(integer)_: Current page of the collection. Default: `1`. +- `per_page` _(integer)_: Maximum number of items to return per page. Default: `50`, Maximum: `100`. + +### Example Request + +```bash +curl -u 'USERNAME:APPLICATION_PASSWORD' \ + https://example.com/wp-json/wp/v2/abilities/categories +``` + +### Example Response + +```json +[ + { + "slug": "data-retrieval", + "label": "Data Retrieval", + "description": "Abilities that retrieve and return data from the WordPress site.", + "meta": {}, + "_links": { + "self": [ + { + "href": "https://example.com/wp-json/wp/v2/abilities/categories/data-retrieval" + } + ], + "collection": [ + { + "href": "https://example.com/wp-json/wp/v2/abilities/categories" + } + ], + "abilities": [ + { + "href": "https://example.com/wp-json/wp/v2/abilities?category=data-retrieval" + } + ] + } + } +] +``` + +## Retrieve a Category + +### Definition + +`GET /wp/v2/abilities/categories/{slug}` + +### Arguments + +- `slug` _(string)_: The unique slug of the category. + +### Example Request + +```bash +curl -u 'USERNAME:APPLICATION_PASSWORD' \ + https://example.com/wp-json/wp/v2/abilities/categories/data-retrieval +``` + +### Example Response + +```json +{ + "slug": "data-retrieval", + "label": "Data Retrieval", + "description": "Abilities that retrieve and return data from the WordPress site.", + "meta": {}, + "_links": { + "self": [ + { + "href": "https://example.com/wp-json/wp/v2/abilities/categories/data-retrieval" + } + ], + "collection": [ + { + "href": "https://example.com/wp-json/wp/v2/abilities/categories" + } + ], + "abilities": [ + { + "href": "https://example.com/wp-json/wp/v2/abilities?category=data-retrieval" + } + ] + } +} +``` + ## Retrieve an Ability ### Definition @@ -247,5 +340,6 @@ The API returns standard WordPress REST API error responses with these common co - `ability_invalid_output` - output validation failed according to the ability's schema. - `ability_invalid_execute_callback` - the ability's execute callback is not callable. - `rest_ability_not_found` - the requested ability is not registered. +- `rest_category_not_found` - the requested category is not registered. - `rest_ability_invalid_method` - the requested HTTP method is not allowed for executing the selected ability (e.g., using GET on a read-only ability, or POST on a regular ability). - `rest_ability_cannot_execute` - the ability cannot be executed due to insufficient permissions. From 332c7160db285760caf727b8c1f24656b30c33f5 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 21 Oct 2025 09:49:45 +0200 Subject: [PATCH 10/10] JavaScript client updates from #120 --- docs/javascript-client.md | 111 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/docs/javascript-client.md b/docs/javascript-client.md index 7de40a96..89239554 100644 --- a/docs/javascript-client.md +++ b/docs/javascript-client.md @@ -34,7 +34,7 @@ console.log(`Found ${abilities.length} abilities`); // List all abilities abilities.forEach(ability => { - console.log(`${ability.name}: ${ability.description}`); + console.log(`${ability.name}: ${ability.description}`); }); // Get abilities in a specific category @@ -64,6 +64,46 @@ if ( ability ) { } ``` +### `getAbilityCategories()` + +Returns an array of all registered ability categories. + +**Parameters:** None + +**Returns:** `Promise` - Array of category objects + +**Example:** + +```javascript +const categories = await getAbilityCategories(); +console.log( `Found ${ categories.length } categories` ); + +// List all categories +categories.forEach( ( category ) => { + console.log( `${ category.label }: ${ category.description }` ); +} ); +``` + +### `getAbilityCategory( slug )` + +Retrieves a specific category by slug. + +**Parameters:** + +- `slug` (string) - The category slug (e.g., 'data-retrieval') + +**Returns:** `Promise` - The category object or null if not found + +**Example:** + +```javascript +const category = await getAbilityCategory( 'data-retrieval' ); +if ( category ) { + console.log( 'Label:', category.label ); + console.log( 'Description:', category.description ); +} +``` + ### `executeAbility( name, input = null )` Executes an ability with the provided input data. @@ -98,7 +138,7 @@ Registers a client-side ability that runs in the browser. - `ability` (object) - The ability configuration object -**Returns:** `void` +**Returns:** `Promise` **Example:** @@ -110,7 +150,7 @@ const showNotification = ( message ) => { }; // Register a notification ability which calls the showNotification function -registerAbility( { +await registerAbility( { name: 'my-plugin/show-notification', label: 'Show Notification', description: 'Display a notification message to the user', @@ -160,6 +200,8 @@ Removes a previously registered client-side ability. - `name` (string) - The ability name to unregister +**Returns:** `void` + **Example:** ```javascript @@ -167,6 +209,69 @@ Removes a previously registered client-side ability. unregisterAbility( 'my-plugin/old-ability' ); ``` +### `registerAbilityCategory( slug, args )` + +Registers a client-side ability category. This is useful when registering client-side abilities that introduce new categories not defined by the server. + +**Parameters:** + +- `slug` (string) - The category slug (lowercase alphanumeric with dashes only) +- `args` (object) - Category configuration object + - `label` (string) - Human-readable label for the category + - `description` (string) - Detailed description of the category + - `meta` (object, optional) - Optional metadata about the category + +**Returns:** `Promise` + +**Example:** + +```javascript +// Register a new category +await registerAbilityCategory( 'block-editor', { + label: 'Block Editor', + description: 'Abilities for interacting with the WordPress block editor', +} ); + +// Register a category with metadata +await registerAbilityCategory( 'custom-category', { + label: 'Custom Category', + description: 'A category for custom abilities', + meta: { + priority: 'high', + icon: 'dashicons-admin-customizer', + }, +} ); + +// Then register abilities using the new category +await registerAbility( { + name: 'my-plugin/insert-block', + label: 'Insert Block', + description: 'Inserts a block into the editor', + category: 'block-editor', // Uses the client-registered category + callback: async ( { blockType } ) => { + // Implementation + return { success: true }; + }, +} ); +``` + +### `unregisterAbilityCategory( slug )` + +Removes a previously registered client-side category. + +**Parameters:** + +- `slug` (string) - The category slug to unregister + +**Returns:** `void` + +**Example:** + +```javascript +// Unregister a category +unregisterAbilityCategory( 'block-editor' ); +``` + ## Error Handling All functions return promises that may reject with specific error codes: