From 8ba63e9e4412e55ae37243dc36e4b1a5c817316d Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Wed, 13 Aug 2025 09:49:28 +0200 Subject: [PATCH 1/8] Adding initial developer documentation This is AI generated, so the next step will be a human review --- docs/1.intro.md | 66 +++++++ docs/2.getting-started.md | 56 ++++++ docs/3.registering-abilities.md | 272 +++++++++++++++++++++++++++++ docs/4.using-abilities.md | 297 ++++++++++++++++++++++++++++++++ 4 files changed, 691 insertions(+) create mode 100644 docs/1.intro.md create mode 100644 docs/2.getting-started.md create mode 100644 docs/3.registering-abilities.md create mode 100644 docs/4.using-abilities.md diff --git a/docs/1.intro.md b/docs/1.intro.md new file mode 100644 index 00000000..e20edabd --- /dev/null +++ b/docs/1.intro.md @@ -0,0 +1,66 @@ +# 1. Introduction & Overview + +**What is the Abilities API?** + +The WordPress Abilities API provides a standardized way to register and discover distinct units of functionality within a WordPress site. These units, called "Abilities," represent specific actions or capabilities that components can perform, with clearly defined inputs, outputs, and permissions. + +It acts as a central registry, making it easier for different parts of WordPress, third-party plugins, themes, and external systems (like AI agents) to understand and interact with the capabilities available on a specific site. + +**Core Concepts** + +- **Ability:** A distinct piece of functionality with a unique ID following the `namespace/ability-name` pattern. Each ability has a human-readable name and description, input/output definitions (using JSON Schema), optional permissions, and an associated callback function for execution. +- **Registry:** A central, singleton object (`WP_Abilities_Registry`) that holds all registered abilities. It provides methods for registering, unregistering, finding, and querying abilities. +- **Callback:** The PHP function or method executed when an ability is called via `wp_execute_ability()`. +- **Schema:** JSON Schema definitions for an ability's expected input (`input_schema`) and its returned output (`output_schema`). This allows for validation and helps agents understand how to use the ability. +- **Permission Callback:** An optional function that determines if the current user can execute a specific ability. Falls back to input validation if not provided. +- **Namespace:** The first part of an ability ID (before the slash), typically matching the plugin or component name that registers the ability. + +**Goals and Benefits** + +- **Standardization:** Provides a single, consistent way to expose site capabilities. +- **Discoverability:** Makes functionality easily discoverable by AI systems and automation tools. +- **Validation:** Built-in input/output validation using JSON Schema ensures data integrity. +- **Security:** Permission callbacks provide fine-grained access control. +- **Extensibility:** Simple registration pattern allows any plugin or theme to expose their capabilities. +- **AI-Friendly:** Machine-readable format enables intelligent automation and AI agent interactions. + +**Use Cases** + +- **AI Integration:** Allow AI agents to discover and interact with site capabilities +- **Plugin Interoperability:** Enable plugins to discover and use each other's functionality +- **Automation Tools:** Provide programmatic access to site features +- **API Documentation:** Self-documenting capabilities with schema validation +- **Developer Tools:** Standardized way to expose plugin functionality + +**Registration Example** + +```php +add_action( 'abilities_api_init', function() { + wp_register_ability( 'my-plugin/create-post', [ + 'label' => 'Create Post', + 'description' => 'Creates a new blog post with the provided content', + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'title' => [ 'type' => 'string' ], + 'content' => [ 'type' => 'string' ], + 'status' => [ 'type' => 'string', 'enum' => [ 'draft', 'publish' ] ] + ], + 'required' => [ 'title', 'content' ] + ], + 'output_schema' => [ + 'type' => 'object', + 'properties' => [ + 'post_id' => [ 'type' => 'integer' ], + 'url' => [ 'type' => 'string' ] + ] + ], + 'execute_callback' => 'my_plugin_create_post', + 'permission_callback' => function( $input ) { + return current_user_can( 'publish_posts' ); + } + ]); +}); +``` + +This creates a machine-readable capability that AI systems and automation tools can discover, understand, and execute safely within the bounds of WordPress permissions and validation rules. diff --git a/docs/2.getting-started.md b/docs/2.getting-started.md new file mode 100644 index 00000000..1f706dc3 --- /dev/null +++ b/docs/2.getting-started.md @@ -0,0 +1,56 @@ +# 2. Getting Started + +## Installation Options + +(To be determined) + +## Basic Usage Example + +The below example is for a plugin implementation, but it could also be adapted for a theme's functions.php + +```php + __( 'Get Site Title', 'my-plugin' ), + 'description' => __( 'Retrieves the title of the current WordPress site.', 'my-plugin' ), + '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', + ), + ) ); +} + +// 3. Later, you can retrieve and execute the ability +add_action( 'admin_init', 'my_plugin_use_ability' ); + +function my_plugin_use_ability() { + $ability = wp_get_ability( 'my-plugin/get-site-title' ); + + if ( $ability && $ability->has_permission() ) { + $site_title = $ability->execute(); + // $site_title now holds the result of get_bloginfo('name') + // error_log( 'Site Title: ' . $site_title ); + } +} +``` diff --git a/docs/3.registering-abilities.md b/docs/3.registering-abilities.md new file mode 100644 index 00000000..54723282 --- /dev/null +++ b/docs/3.registering-abilities.md @@ -0,0 +1,272 @@ +### 3. 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** + +```php +wp_register_ability( string $id, array $args ): bool +``` + +- `$id` (`string`): A unique identifier for the ability. +- `$args` (`array`): An array of arguments defining the ability configuration. +- **Return:** (`bool`) `true` if the ability was successfully registered, `false` on failure (e.g., invalid arguments, duplicate ID). + +**Parameters Explained** + +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. +- `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. + - The callback receives one argument: an associative array of validated input parameters. + - The callback should return the result of the ability's operation or throw an exception on failure. +- `permission_callback` (`callable`|`null`, **Optional**): A callback function to check if the current user has permission to execute this ability. + - Should return `true` if the user has permission, `false` or a `WP_Error` object otherwise. + - If not provided, the ability will only validate input parameters before execution. + - Use `'__return_true'` for publicly accessible abilities. +- `meta` (`array`, **Optional**): An associative array for storing arbitrary additional metadata about the ability. + +**Ability ID Convention** + +The `$id` parameter must follow the pattern `namespace/ability-name`: +- **Format:** Must contain only lowercase alphanumeric characters (`a-z`, `0-9`), hyphens (`-`), and one forward slash (`/`) for namespacing. +- **Convention:** Use your plugin slug as the namespace, like `my-plugin/ability-name` or `woocommerce/get-product`. +- **Examples:** `my-plugin/update-settings`, `contact-form/send-message`, `analytics/track-event` + +**Code Examples** + +**1. Registering a Simple Data Retrieval Ability** + +```php +add_action( 'abilities_api_init', function() { + wp_register_ability( 'my-plugin/get-site-info', [ + 'label' => __( 'Get Site Information', 'my-plugin' ), + 'description' => __( 'Retrieves basic information about the WordPress site including name, description, and URL.', 'my-plugin' ), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [], + 'additionalProperties' => false + ], + 'output_schema' => [ + 'type' => 'object', + 'properties' => [ + 'name' => [ + 'type' => 'string', + 'description' => 'Site name' + ], + 'description' => [ + 'type' => 'string', + 'description' => 'Site tagline' + ], + 'url' => [ + 'type' => 'string', + 'format' => 'uri', + 'description' => 'Site URL' + ] + ] + ], + 'execute_callback' => function( $input ) { + return [ + 'name' => get_bloginfo( 'name' ), + 'description' => get_bloginfo( 'description' ), + 'url' => home_url() + ]; + }, + 'permission_callback' => '__return_true' + ]); +}); +``` + +**2. Registering an Ability with Input Parameters** + +```php +add_action( 'abilities_api_init', function() { + wp_register_ability( 'my-plugin/update-option', [ + 'label' => __( 'Update WordPress Option', 'my-plugin' ), + 'description' => __( 'Updates the value of a WordPress option in the database. Requires manage_options capability.', 'my-plugin' ), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'option_name' => [ + 'type' => 'string', + 'description' => 'The name of the option to update', + 'minLength' => 1 + ], + 'option_value' => [ + 'description' => 'The new value for the option' + ] + ], + 'required' => [ 'option_name', 'option_value' ], + 'additionalProperties' => false + ], + 'output_schema' => [ + 'type' => 'object', + 'properties' => [ + 'success' => [ + 'type' => 'boolean', + 'description' => 'Whether the option was successfully updated' + ], + 'previous_value' => [ + 'description' => 'The previous value of the option' + ] + ] + ], + 'execute_callback' => function( $input ) { + $option_name = $input['option_name']; + $new_value = $input['option_value']; + + $previous_value = get_option( $option_name ); + $success = update_option( $option_name, $new_value ); + + return [ + 'success' => $success, + 'previous_value' => $previous_value + ]; + }, + 'permission_callback' => function() { + return current_user_can( 'manage_options' ); + } + ]); +}); +``` + +**3. Registering an Ability with Plugin Dependencies** + +```php +add_action( 'abilities_api_init', function() { + // Only register if WooCommerce is active + if ( ! class_exists( 'WooCommerce' ) ) { + return; + } + + wp_register_ability( 'my-plugin/get-woo-stats', [ + 'label' => __( 'Get WooCommerce Statistics', 'my-plugin' ), + 'description' => __( 'Retrieves basic WooCommerce store statistics including total orders and revenue.', 'my-plugin' ), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'period' => [ + 'type' => 'string', + 'enum' => [ 'today', 'week', 'month', 'year' ], + 'default' => 'month', + 'description' => 'Time period for statistics' + ] + ], + 'additionalProperties' => false + ], + 'output_schema' => [ + 'type' => 'object', + 'properties' => [ + 'total_orders' => [ + 'type' => 'integer', + 'description' => 'Number of orders in period' + ], + 'total_revenue' => [ + 'type' => 'number', + 'description' => 'Total revenue in period' + ] + ] + ], + 'execute_callback' => function( $input ) { + $period = $input['period'] ?? 'month'; + + // Implementation would calculate stats based on period + return [ + 'total_orders' => 42, + 'total_revenue' => 1250.50 + ]; + }, + 'permission_callback' => function() { + return current_user_can( 'manage_woocommerce' ); + }, + 'meta' => [ + 'requires_plugin' => 'woocommerce', + 'category' => 'ecommerce' + ] + ]); +}); +``` + +**4. Registering an Ability That May Fail** + +```php +add_action( 'abilities_api_init', function() { + wp_register_ability( 'my-plugin/send-email', [ + 'label' => __( 'Send Email', 'my-plugin' ), + 'description' => __( 'Sends an email to the specified recipient using WordPress mail functions.', 'my-plugin' ), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'to' => [ + 'type' => 'string', + 'format' => 'email', + 'description' => 'Recipient email address' + ], + 'subject' => [ + 'type' => 'string', + 'minLength' => 1, + 'description' => 'Email subject' + ], + 'message' => [ + 'type' => 'string', + 'minLength' => 1, + 'description' => 'Email message body' + ] + ], + 'required' => [ 'to', 'subject', 'message' ], + 'additionalProperties' => false + ], + 'output_schema' => [ + 'type' => 'object', + 'properties' => [ + 'sent' => [ + 'type' => 'boolean', + 'description' => 'Whether the email was successfully sent' + ] + ] + ], + 'execute_callback' => function( $input ) { + $sent = wp_mail( + $input['to'], + $input['subject'], + $input['message'] + ); + + if ( ! $sent ) { + throw new Exception( 'Failed to send email' ); + } + + return [ 'sent' => true ]; + }, + 'permission_callback' => function() { + return current_user_can( 'publish_posts' ); + } + ]); +}); +``` + +**Usage Examples** + +```php +// Get a registered ability +$ability = wp_get_ability( 'my-plugin/get-site-info' ); + +if ( $ability ) { + // Execute the ability + try { + $result = $ability->execute( [] ); + // Handle success: $result contains the output + } catch ( Exception $e ) { + // Handle error: $e->getMessage() contains error details + } +} + +// List all registered abilities +$abilities = wp_get_abilities(); +foreach ( $abilities as $id => $ability ) { + echo $ability->get_label() . ': ' . $ability->get_description(); +} +``` \ No newline at end of file diff --git a/docs/4.using-abilities.md b/docs/4.using-abilities.md new file mode 100644 index 00000000..17771e75 --- /dev/null +++ b/docs/4.using-abilities.md @@ -0,0 +1,297 @@ +### 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 + try { + $result = $site_info_ability->execute(); + // Handle successful result + } catch ( Exception $e ) { + // Handle execution error + } +} 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 array $input Optional. The input data for the ability. + * @return mixed|WP_Error The result of the ability execution, or WP_Error on failure. + */ +// public function execute( array $input = array() ) + +// Example 1: Ability with no input parameters +$ability = wp_get_ability( 'my-plugin/get-site-info' ); +if ( $ability ) { + try { + $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']; + } + } catch ( Exception $e ) { + // Handle execution exceptions + echo 'Exception: ' . $e->getMessage(); + } +} + +// 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', + ); + + try { + $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']; + } + } + } catch ( Exception $e ) { + // Handle execution exceptions or permission errors + echo 'Exception: ' . $e->getMessage(); + } +} + +// 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.', + ); + + try { + $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.'; + } + } catch ( Exception $e ) { + // Handle execution exceptions + echo 'Exception: ' . $e->getMessage(); + } +} +``` + +**Checking Permissions (`$ability->has_permission()`)** + +Before executing an ability, you can check if the current user has permission: + +```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 + if ( $ability->has_permission( $input ) ) { + try { + $result = $ability->execute( $input ); + // Handle result + } catch ( Exception $e ) { + // Handle execution error + } + } else { + echo 'You do not have permission 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; +} + +try { + $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 + +} catch ( Exception $e ) { + // Handle exceptions thrown by the execute callback + echo 'Exception during execution: ' . $e->getMessage(); +} +``` + +**Practical Usage Examples** + +**Example 1: Building an Admin Dashboard Widget** + +```php +function my_plugin_dashboard_widget() { + $abilities = wp_get_abilities(); + + echo '

Available Site Abilities

'; + echo ''; +} +``` + +**Example 2: API Endpoint Integration** + +```php +add_action( 'rest_api_init', function() { + register_rest_route( 'my-plugin/v1', '/execute/(?P[a-zA-Z0-9-/]+)', array( + 'methods' => 'POST', + 'callback' => 'my_plugin_execute_ability_endpoint', + 'permission_callback' => function( $request ) { + $ability_name = $request['ability']; + $ability = wp_get_ability( $ability_name ); + + if ( ! $ability ) { + return false; + } + + return $ability->has_permission( $request->get_json_params() ?: array() ); + }, + )); +}); + +function my_plugin_execute_ability_endpoint( $request ) { + $ability_name = $request['ability']; + $input = $request->get_json_params() ?: array(); + + $ability = wp_get_ability( $ability_name ); + if ( ! $ability ) { + return new WP_Error( 'ability_not_found', 'Ability not found', array( 'status' => 404 ) ); + } + + try { + $result = $ability->execute( $input ); + + if ( is_wp_error( $result ) ) { + return $result; + } + + return rest_ensure_response( $result ); + + } catch ( Exception $e ) { + return new WP_Error( 'execution_error', $e->getMessage(), array( 'status' => 500 ) ); + } +} +``` \ No newline at end of file From 475c54e7136744b021882e3c2629b25b12b22219 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Mon, 18 Aug 2025 16:45:37 +0200 Subject: [PATCH 2/8] Manual review updates --- docs/1.intro.md | 6 +++--- docs/2.getting-started.md | 7 ++++++- docs/3.registering-abilities.md | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/1.intro.md b/docs/1.intro.md index e20edabd..344c25cb 100644 --- a/docs/1.intro.md +++ b/docs/1.intro.md @@ -8,12 +8,12 @@ It acts as a central registry, making it easier for different parts of WordPress **Core Concepts** -- **Ability:** A distinct piece of functionality with a unique ID following the `namespace/ability-name` pattern. Each ability has a human-readable name and description, input/output definitions (using JSON Schema), optional permissions, and an associated callback function for execution. +- **Ability:** A distinct piece of functionality with a unique name following the `namespace/ability-name` pattern. Each ability has a human-readable name and description, input/output definitions (using JSON Schema), optional permissions, and an associated callback function for execution. Each registered Ability is an instance of the `WP_Ability` class. - **Registry:** A central, singleton object (`WP_Abilities_Registry`) that holds all registered abilities. It provides methods for registering, unregistering, finding, and querying abilities. -- **Callback:** The PHP function or method executed when an ability is called via `wp_execute_ability()`. +- **Callback:** he PHP function or method executed when an ability is called via `WP_Ability::do_execute()`. - **Schema:** JSON Schema definitions for an ability's expected input (`input_schema`) and its returned output (`output_schema`). This allows for validation and helps agents understand how to use the ability. - **Permission Callback:** An optional function that determines if the current user can execute a specific ability. Falls back to input validation if not provided. -- **Namespace:** The first part of an ability ID (before the slash), typically matching the plugin or component name that registers the ability. +- **Namespace:** The first part of an ability name (before the slash), typically matching the plugin or component name that registers the ability. **Goals and Benefits** diff --git a/docs/2.getting-started.md b/docs/2.getting-started.md index 1f706dc3..c8e3c291 100644 --- a/docs/2.getting-started.md +++ b/docs/2.getting-started.md @@ -2,7 +2,12 @@ ## Installation Options -(To be determined) +Currently, the Abilities API is available as a WordPress plugin. You can install it by cloning the repository into your `wp-content/plugins` directory. + +```bash +cd wp-content/plugins +git clone https://github.com/WordPress/abilities-api +``` ## Basic Usage Example diff --git a/docs/3.registering-abilities.md b/docs/3.registering-abilities.md index 54723282..082796cb 100644 --- a/docs/3.registering-abilities.md +++ b/docs/3.registering-abilities.md @@ -5,12 +5,12 @@ The primary way to add functionality to the Abilities API is by using the `wp_re **Function Signature** ```php -wp_register_ability( string $id, array $args ): bool +wp_register_ability( string $id, array $args ): ?\WP_Ability ``` - `$id` (`string`): A unique identifier for the ability. - `$args` (`array`): An array of arguments defining the ability configuration. -- **Return:** (`bool`) `true` if the ability was successfully registered, `false` on failure (e.g., invalid arguments, duplicate ID). +- **Return:** (`?\WP_Ability`) An instance of the registered ability if the it was successfully registered, `null` on failure (e.g., invalid arguments, duplicate ID). **Parameters Explained** @@ -269,4 +269,4 @@ $abilities = wp_get_abilities(); foreach ( $abilities as $id => $ability ) { echo $ability->get_label() . ': ' . $ability->get_description(); } -``` \ No newline at end of file +``` From 1fe2b768e3d52a8a18c3cc7be09345bc05d7898e Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Mon, 18 Aug 2025 17:03:02 +0200 Subject: [PATCH 3/8] Manual review updates --- docs/1.intro.md | 2 +- docs/4.using-abilities.md | 69 --------------------------------------- 2 files changed, 1 insertion(+), 70 deletions(-) diff --git a/docs/1.intro.md b/docs/1.intro.md index 344c25cb..712c2b65 100644 --- a/docs/1.intro.md +++ b/docs/1.intro.md @@ -10,7 +10,7 @@ It acts as a central registry, making it easier for different parts of WordPress - **Ability:** A distinct piece of functionality with a unique name following the `namespace/ability-name` pattern. Each ability has a human-readable name and description, input/output definitions (using JSON Schema), optional permissions, and an associated callback function for execution. Each registered Ability is an instance of the `WP_Ability` class. - **Registry:** A central, singleton object (`WP_Abilities_Registry`) that holds all registered abilities. It provides methods for registering, unregistering, finding, and querying abilities. -- **Callback:** he PHP function or method executed when an ability is called via `WP_Ability::do_execute()`. +- **Callback:** he PHP function or method executed when an ability is called via `WP_Ability::execute()`. - **Schema:** JSON Schema definitions for an ability's expected input (`input_schema`) and its returned output (`output_schema`). This allows for validation and helps agents understand how to use the ability. - **Permission Callback:** An optional function that determines if the current user can execute a specific ability. Falls back to input validation if not provided. - **Namespace:** The first part of an ability name (before the slash), typically matching the plugin or component name that registers the ability. diff --git a/docs/4.using-abilities.md b/docs/4.using-abilities.md index 17771e75..b86adcae 100644 --- a/docs/4.using-abilities.md +++ b/docs/4.using-abilities.md @@ -226,72 +226,3 @@ try { echo 'Exception during execution: ' . $e->getMessage(); } ``` - -**Practical Usage Examples** - -**Example 1: Building an Admin Dashboard Widget** - -```php -function my_plugin_dashboard_widget() { - $abilities = wp_get_abilities(); - - echo '

Available Site Abilities

'; - echo '
    '; - - foreach ( $abilities as $name => $ability ) { - // Only show abilities from our plugin - if ( strpos( $name, 'my-plugin/' ) === 0 ) { - echo '
  • '; - echo '' . esc_html( $ability->get_label() ) . '
    '; - echo esc_html( $ability->get_description() ); - echo '
  • '; - } - } - - echo '
'; -} -``` - -**Example 2: API Endpoint Integration** - -```php -add_action( 'rest_api_init', function() { - register_rest_route( 'my-plugin/v1', '/execute/(?P[a-zA-Z0-9-/]+)', array( - 'methods' => 'POST', - 'callback' => 'my_plugin_execute_ability_endpoint', - 'permission_callback' => function( $request ) { - $ability_name = $request['ability']; - $ability = wp_get_ability( $ability_name ); - - if ( ! $ability ) { - return false; - } - - return $ability->has_permission( $request->get_json_params() ?: array() ); - }, - )); -}); - -function my_plugin_execute_ability_endpoint( $request ) { - $ability_name = $request['ability']; - $input = $request->get_json_params() ?: array(); - - $ability = wp_get_ability( $ability_name ); - if ( ! $ability ) { - return new WP_Error( 'ability_not_found', 'Ability not found', array( 'status' => 404 ) ); - } - - try { - $result = $ability->execute( $input ); - - if ( is_wp_error( $result ) ) { - return $result; - } - - return rest_ensure_response( $result ); - - } catch ( Exception $e ) { - return new WP_Error( 'execution_error', $e->getMessage(), array( 'status' => 500 ) ); - } -} -``` \ No newline at end of file From 54ab112c260ed3ad27f72c86b0f92ecf8eecb314 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Mon, 18 Aug 2025 17:22:30 +0200 Subject: [PATCH 4/8] Updating array syntax to follow WPCS --- docs/1.intro.md | 32 +++---- docs/3.registering-abilities.md | 160 ++++++++++++++++---------------- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/docs/1.intro.md b/docs/1.intro.md index 712c2b65..1d9f9d2b 100644 --- a/docs/1.intro.md +++ b/docs/1.intro.md @@ -36,30 +36,30 @@ It acts as a central registry, making it easier for different parts of WordPress ```php add_action( 'abilities_api_init', function() { - wp_register_ability( 'my-plugin/create-post', [ + wp_register_ability( 'my-plugin/create-post', array( 'label' => 'Create Post', 'description' => 'Creates a new blog post with the provided content', - 'input_schema' => [ + 'input_schema' => array( 'type' => 'object', - 'properties' => [ - 'title' => [ 'type' => 'string' ], - 'content' => [ 'type' => 'string' ], - 'status' => [ 'type' => 'string', 'enum' => [ 'draft', 'publish' ] ] - ], - 'required' => [ 'title', 'content' ] - ], - 'output_schema' => [ + 'properties' => array( + 'title' => array( 'type' => 'string' ), + 'content' => array( 'type' => 'string' ), + 'status' => array( 'type' => 'string', 'enum' => array( 'draft', 'publish' ) ) + ), + 'required' => array( 'title', 'content' ) + ), + 'output_schema' => array( 'type' => 'object', - 'properties' => [ - 'post_id' => [ 'type' => 'integer' ], - 'url' => [ 'type' => 'string' ] - ] - ], + 'properties' => array( + 'post_id' => array( 'type' => 'integer' ), + 'url' => array( 'type' => 'string' ) + ) + ), 'execute_callback' => 'my_plugin_create_post', 'permission_callback' => function( $input ) { return current_user_can( 'publish_posts' ); } - ]); + )); }); ``` diff --git a/docs/3.registering-abilities.md b/docs/3.registering-abilities.md index 082796cb..b940b3ea 100644 --- a/docs/3.registering-abilities.md +++ b/docs/3.registering-abilities.md @@ -42,41 +42,41 @@ The `$id` parameter must follow the pattern `namespace/ability-name`: ```php add_action( 'abilities_api_init', function() { - wp_register_ability( 'my-plugin/get-site-info', [ + wp_register_ability( 'my-plugin/get-site-info', array( 'label' => __( 'Get Site Information', 'my-plugin' ), 'description' => __( 'Retrieves basic information about the WordPress site including name, description, and URL.', 'my-plugin' ), - 'input_schema' => [ + 'input_schema' => array( 'type' => 'object', - 'properties' => [], + 'properties' => array(), 'additionalProperties' => false - ], - 'output_schema' => [ + ), + 'output_schema' => array( 'type' => 'object', - 'properties' => [ - 'name' => [ + 'properties' => array( + 'name' => array( 'type' => 'string', 'description' => 'Site name' - ], - 'description' => [ + ), + 'description' => array( 'type' => 'string', 'description' => 'Site tagline' - ], - 'url' => [ + ), + 'url' => array( 'type' => 'string', 'format' => 'uri', 'description' => 'Site URL' - ] - ] - ], + ) + ) + ), 'execute_callback' => function( $input ) { - return [ + return array( 'name' => get_bloginfo( 'name' ), 'description' => get_bloginfo( 'description' ), 'url' => home_url() - ]; + ); }, 'permission_callback' => '__return_true' - ]); + )); }); ``` @@ -84,36 +84,36 @@ add_action( 'abilities_api_init', function() { ```php add_action( 'abilities_api_init', function() { - wp_register_ability( 'my-plugin/update-option', [ + wp_register_ability( 'my-plugin/update-option', array( 'label' => __( 'Update WordPress Option', 'my-plugin' ), 'description' => __( 'Updates the value of a WordPress option in the database. Requires manage_options capability.', 'my-plugin' ), - 'input_schema' => [ + 'input_schema' => array( 'type' => 'object', - 'properties' => [ - 'option_name' => [ + 'properties' => array( + 'option_name' => array( 'type' => 'string', 'description' => 'The name of the option to update', 'minLength' => 1 - ], - 'option_value' => [ + ), + 'option_value' => array( 'description' => 'The new value for the option' - ] - ], - 'required' => [ 'option_name', 'option_value' ], + ) + ), + 'required' => array( 'option_name', 'option_value' ), 'additionalProperties' => false - ], - 'output_schema' => [ + ), + 'output_schema' => array( 'type' => 'object', - 'properties' => [ - 'success' => [ + 'properties' => array( + 'success' => array( 'type' => 'boolean', 'description' => 'Whether the option was successfully updated' - ], - 'previous_value' => [ + ), + 'previous_value' => array( 'description' => 'The previous value of the option' - ] - ] - ], + ) + ) + ), 'execute_callback' => function( $input ) { $option_name = $input['option_name']; $new_value = $input['option_value']; @@ -121,15 +121,15 @@ add_action( 'abilities_api_init', function() { $previous_value = get_option( $option_name ); $success = update_option( $option_name, $new_value ); - return [ + return array( 'success' => $success, 'previous_value' => $previous_value - ]; + ); }, 'permission_callback' => function() { return current_user_can( 'manage_options' ); } - ]); + )); }); ``` @@ -142,51 +142,51 @@ add_action( 'abilities_api_init', function() { return; } - wp_register_ability( 'my-plugin/get-woo-stats', [ + wp_register_ability( 'my-plugin/get-woo-stats', array( 'label' => __( 'Get WooCommerce Statistics', 'my-plugin' ), 'description' => __( 'Retrieves basic WooCommerce store statistics including total orders and revenue.', 'my-plugin' ), - 'input_schema' => [ + 'input_schema' => array( 'type' => 'object', - 'properties' => [ - 'period' => [ + 'properties' => array( + 'period' => array( 'type' => 'string', - 'enum' => [ 'today', 'week', 'month', 'year' ], + 'enum' => array( 'today', 'week', 'month', 'year' ), 'default' => 'month', 'description' => 'Time period for statistics' - ] - ], + ) + ), 'additionalProperties' => false - ], - 'output_schema' => [ + ), + 'output_schema' => array( 'type' => 'object', - 'properties' => [ - 'total_orders' => [ + 'properties' => array( + 'total_orders' => array( 'type' => 'integer', 'description' => 'Number of orders in period' - ], - 'total_revenue' => [ + ), + 'total_revenue' => array( 'type' => 'number', 'description' => 'Total revenue in period' - ] - ] - ], + ) + ) + ), 'execute_callback' => function( $input ) { $period = $input['period'] ?? 'month'; // Implementation would calculate stats based on period - return [ + return array( 'total_orders' => 42, 'total_revenue' => 1250.50 - ]; + ); }, 'permission_callback' => function() { return current_user_can( 'manage_woocommerce' ); }, - 'meta' => [ + 'meta' => array( 'requires_plugin' => 'woocommerce', 'category' => 'ecommerce' - ] - ]); + ) + )); }); ``` @@ -194,40 +194,40 @@ add_action( 'abilities_api_init', function() { ```php add_action( 'abilities_api_init', function() { - wp_register_ability( 'my-plugin/send-email', [ + wp_register_ability( 'my-plugin/send-email', array( 'label' => __( 'Send Email', 'my-plugin' ), 'description' => __( 'Sends an email to the specified recipient using WordPress mail functions.', 'my-plugin' ), - 'input_schema' => [ + 'input_schema' => array( 'type' => 'object', - 'properties' => [ - 'to' => [ + 'properties' => array( + 'to' => array( 'type' => 'string', 'format' => 'email', 'description' => 'Recipient email address' - ], - 'subject' => [ + ), + 'subject' => array( 'type' => 'string', 'minLength' => 1, 'description' => 'Email subject' - ], - 'message' => [ + ), + 'message' => array( 'type' => 'string', 'minLength' => 1, 'description' => 'Email message body' - ] - ], - 'required' => [ 'to', 'subject', 'message' ], + ) + ), + 'required' => array( 'to', 'subject', 'message' ), 'additionalProperties' => false - ], - 'output_schema' => [ + ), + 'output_schema' => array( 'type' => 'object', - 'properties' => [ - 'sent' => [ + 'properties' => array( + 'sent' => array( 'type' => 'boolean', 'description' => 'Whether the email was successfully sent' - ] - ] - ], + ) + ) + ), 'execute_callback' => function( $input ) { $sent = wp_mail( $input['to'], @@ -239,12 +239,12 @@ add_action( 'abilities_api_init', function() { throw new Exception( 'Failed to send email' ); } - return [ 'sent' => true ]; + return array( 'sent' => true ); }, 'permission_callback' => function() { return current_user_can( 'publish_posts' ); } - ]); + )); }); ``` @@ -257,7 +257,7 @@ $ability = wp_get_ability( 'my-plugin/get-site-info' ); if ( $ability ) { // Execute the ability try { - $result = $ability->execute( [] ); + $result = $ability->execute( array() ); // Handle success: $result contains the output } catch ( Exception $e ) { // Handle error: $e->getMessage() contains error details From 0d2d71236b25341879f693d38416db896dde043d Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Mon, 18 Aug 2025 17:31:04 +0200 Subject: [PATCH 5/8] Updating example code to use named callback functions --- docs/1.intro.md | 5 +++-- docs/3.registering-abilities.md | 20 ++++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/1.intro.md b/docs/1.intro.md index 1d9f9d2b..638b1c9e 100644 --- a/docs/1.intro.md +++ b/docs/1.intro.md @@ -35,7 +35,8 @@ It acts as a central registry, making it easier for different parts of WordPress **Registration Example** ```php -add_action( 'abilities_api_init', function() { +add_action( 'abilities_api_init', `my_plugin_register_ability`); +function my_plugin_register_ability(){ wp_register_ability( 'my-plugin/create-post', array( 'label' => 'Create Post', 'description' => 'Creates a new blog post with the provided content', @@ -60,7 +61,7 @@ add_action( 'abilities_api_init', function() { return current_user_can( 'publish_posts' ); } )); -}); +} ``` This creates a machine-readable capability that AI systems and automation tools can discover, understand, and execute safely within the bounds of WordPress permissions and validation rules. diff --git a/docs/3.registering-abilities.md b/docs/3.registering-abilities.md index b940b3ea..60ea8852 100644 --- a/docs/3.registering-abilities.md +++ b/docs/3.registering-abilities.md @@ -41,7 +41,8 @@ The `$id` parameter must follow the pattern `namespace/ability-name`: **1. Registering a Simple Data Retrieval Ability** ```php -add_action( 'abilities_api_init', function() { +add_action( 'abilities_api_init', 'my_plugin_register_site_info_ability' ); +function my_plugin_register_site_info_ability() { wp_register_ability( 'my-plugin/get-site-info', array( 'label' => __( 'Get Site Information', 'my-plugin' ), 'description' => __( 'Retrieves basic information about the WordPress site including name, description, and URL.', 'my-plugin' ), @@ -77,13 +78,14 @@ add_action( 'abilities_api_init', function() { }, 'permission_callback' => '__return_true' )); -}); +} ``` **2. Registering an Ability with Input Parameters** ```php -add_action( 'abilities_api_init', function() { +add_action( 'abilities_api_init', 'my_plugin_register_update_option_ability' ); +function my_plugin_register_update_option_ability() { wp_register_ability( 'my-plugin/update-option', array( 'label' => __( 'Update WordPress Option', 'my-plugin' ), 'description' => __( 'Updates the value of a WordPress option in the database. Requires manage_options capability.', 'my-plugin' ), @@ -130,13 +132,14 @@ add_action( 'abilities_api_init', function() { return current_user_can( 'manage_options' ); } )); -}); +} ``` **3. Registering an Ability with Plugin Dependencies** ```php -add_action( 'abilities_api_init', function() { +add_action( 'abilities_api_init', 'my_plugin_register_woo_stats_ability' ); +function my_plugin_register_woo_stats_ability() { // Only register if WooCommerce is active if ( ! class_exists( 'WooCommerce' ) ) { return; @@ -187,13 +190,14 @@ add_action( 'abilities_api_init', function() { 'category' => 'ecommerce' ) )); -}); +} ``` **4. Registering an Ability That May Fail** ```php -add_action( 'abilities_api_init', function() { +add_action( 'abilities_api_init', 'my_plugin_register_send_email_ability' ); +function my_plugin_register_send_email_ability() { wp_register_ability( 'my-plugin/send-email', array( 'label' => __( 'Send Email', 'my-plugin' ), 'description' => __( 'Sends an email to the specified recipient using WordPress mail functions.', 'my-plugin' ), @@ -245,7 +249,7 @@ add_action( 'abilities_api_init', function() { return current_user_can( 'publish_posts' ); } )); -}); +} ``` **Usage Examples** From c7fa8db6b48ffa209ad4e8c10bee9bb364b55cfb Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 19 Aug 2025 10:09:56 +0200 Subject: [PATCH 6/8] Review updates to intro --- docs/1.intro.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/1.intro.md b/docs/1.intro.md index 638b1c9e..c8eff099 100644 --- a/docs/1.intro.md +++ b/docs/1.intro.md @@ -2,7 +2,7 @@ **What is the Abilities API?** -The WordPress Abilities API provides a standardized way to register and discover distinct units of functionality within a WordPress site. These units, called "Abilities," represent specific actions or capabilities that components can perform, with clearly defined inputs, outputs, and permissions. +The WordPress Abilities API provides a standardized way to register and discover distinct units of functionality within a WordPress site. These units, called "Abilities", represent specific actions or capabilities that components can perform, with clearly defined inputs, outputs, and permissions. It acts as a central registry, making it easier for different parts of WordPress, third-party plugins, themes, and external systems (like AI agents) to understand and interact with the capabilities available on a specific site. @@ -10,9 +10,9 @@ It acts as a central registry, making it easier for different parts of WordPress - **Ability:** A distinct piece of functionality with a unique name following the `namespace/ability-name` pattern. Each ability has a human-readable name and description, input/output definitions (using JSON Schema), optional permissions, and an associated callback function for execution. Each registered Ability is an instance of the `WP_Ability` class. - **Registry:** A central, singleton object (`WP_Abilities_Registry`) that holds all registered abilities. It provides methods for registering, unregistering, finding, and querying abilities. -- **Callback:** he PHP function or method executed when an ability is called via `WP_Ability::execute()`. +- **Callback:** The PHP function or method executed when an ability is called via `WP_Ability::execute()`. - **Schema:** JSON Schema definitions for an ability's expected input (`input_schema`) and its returned output (`output_schema`). This allows for validation and helps agents understand how to use the ability. -- **Permission Callback:** An optional function that determines if the current user can execute a specific ability. Falls back to input validation if not provided. +- **Permission Callback:** An optional function that determines if the current user can execute a specific ability. - **Namespace:** The first part of an ability name (before the slash), typically matching the plugin or component name that registers the ability. **Goals and Benefits** @@ -26,11 +26,11 @@ It acts as a central registry, making it easier for different parts of WordPress **Use Cases** -- **AI Integration:** Allow AI agents to discover and interact with site capabilities -- **Plugin Interoperability:** Enable plugins to discover and use each other's functionality -- **Automation Tools:** Provide programmatic access to site features -- **API Documentation:** Self-documenting capabilities with schema validation -- **Developer Tools:** Standardized way to expose plugin functionality +- **AI Integration:** Allow AI agents to discover and interact with site capabilities. +- **Plugin Interoperability:** Enable plugins to discover and use each other's functionality. +- **Automation Tools:** Provide programmatic access to site features. +- **API Documentation:** Self-documenting capabilities with schema validation. +- **Developer Tools:** Standardized way to expose plugin functionality. **Registration Example** @@ -38,8 +38,8 @@ It acts as a central registry, making it easier for different parts of WordPress add_action( 'abilities_api_init', `my_plugin_register_ability`); function my_plugin_register_ability(){ wp_register_ability( 'my-plugin/create-post', array( - 'label' => 'Create Post', - 'description' => 'Creates a new blog post with the provided content', + 'label' => __( 'Create Post', 'my-plugin' ), + 'description' => __( 'Creates a new blog post with the provided content', 'my-plugin' ), 'input_schema' => array( 'type' => 'object', 'properties' => array( From a168ff2a6d8c64962b1537210c036e838dc968c1 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 19 Aug 2025 10:24:09 +0200 Subject: [PATCH 7/8] Review updates --- docs/3.registering-abilities.md | 38 ++++++++------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/docs/3.registering-abilities.md b/docs/3.registering-abilities.md index 60ea8852..d01c1af2 100644 --- a/docs/3.registering-abilities.md +++ b/docs/3.registering-abilities.md @@ -22,19 +22,19 @@ The `$args` array accepts the following keys: - `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 argument: an associative array of validated input parameters. - - The callback should return the result of the ability's operation or throw an exception on failure. + - The callback should return the result of the ability's operation or return a `WP_Error` object on failure. - `permission_callback` (`callable`|`null`, **Optional**): A callback function to check if the current user has permission to execute this ability. - - Should return `true` if the user has permission, `false` or a `WP_Error` object otherwise. + - Should return `true` if the user has permission, or a `WP_Error` object otherwise. - If not provided, the ability will only validate input parameters before execution. - - Use `'__return_true'` for publicly accessible abilities. + - This defaults to `true` if not set. - `meta` (`array`, **Optional**): An associative array for storing arbitrary additional metadata about the ability. **Ability ID Convention** The `$id` parameter must follow the pattern `namespace/ability-name`: - **Format:** Must contain only lowercase alphanumeric characters (`a-z`, `0-9`), hyphens (`-`), and one forward slash (`/`) for namespacing. -- **Convention:** Use your plugin slug as the namespace, like `my-plugin/ability-name` or `woocommerce/get-product`. -- **Examples:** `my-plugin/update-settings`, `contact-form/send-message`, `analytics/track-event` +- **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** @@ -240,7 +240,10 @@ function my_plugin_register_send_email_ability() { ); if ( ! $sent ) { - throw new Exception( 'Failed to send email' ); + return new \WP_Error( + 'email_send_failed', + sprintf( __( 'Failed to send email' ), 'my-plugin' ) + ); } return array( 'sent' => true ); @@ -251,26 +254,3 @@ function my_plugin_register_send_email_ability() { )); } ``` - -**Usage Examples** - -```php -// Get a registered ability -$ability = wp_get_ability( 'my-plugin/get-site-info' ); - -if ( $ability ) { - // Execute the ability - try { - $result = $ability->execute( array() ); - // Handle success: $result contains the output - } catch ( Exception $e ) { - // Handle error: $e->getMessage() contains error details - } -} - -// List all registered abilities -$abilities = wp_get_abilities(); -foreach ( $abilities as $id => $ability ) { - echo $ability->get_label() . ': ' . $ability->get_description(); -} -``` From e5b250297fbf01e1cb2897ad65339430e8df4dc9 Mon Sep 17 00:00:00 2001 From: Jonathan Bossenger Date: Tue, 19 Aug 2025 10:31:54 +0200 Subject: [PATCH 8/8] Update code examples to use WP_Error object on failure --- docs/4.using-abilities.md | 136 +++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 75 deletions(-) diff --git a/docs/4.using-abilities.md b/docs/4.using-abilities.md index b86adcae..4e94e8ef 100644 --- a/docs/4.using-abilities.md +++ b/docs/4.using-abilities.md @@ -19,15 +19,17 @@ function wp_get_ability( string $name ): ?WP_Ability $site_info_ability = wp_get_ability( 'my-plugin/get-site-info' ); if ( $site_info_ability ) { - // Ability exists and is registered - try { - $result = $site_info_ability->execute(); - // Handle successful result - } catch ( Exception $e ) { - // Handle execution error - } + // Ability exists and is registered + $result = $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 + // Ability not found or not registered } ``` @@ -70,18 +72,13 @@ Once you have a `WP_Ability` object (usually from `wp_get_ability`), you execute // Example 1: Ability with no input parameters $ability = wp_get_ability( 'my-plugin/get-site-info' ); if ( $ability ) { - try { - $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']; - } - } catch ( Exception $e ) { - // Handle execution exceptions - echo 'Exception: ' . $e->getMessage(); + $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']; } } @@ -92,22 +89,17 @@ if ( $ability ) { 'option_name' => 'blogname', 'option_value' => 'My Updated Site Name', ); - - try { - $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']; - } + + $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']; } - } catch ( Exception $e ) { - // Handle execution exceptions or permission errors - echo 'Exception: ' . $e->getMessage(); } } @@ -119,20 +111,15 @@ if ( $ability ) { 'subject' => 'Hello from WordPress', 'message' => 'This is a test message from the Abilities API.', ); - - try { - $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.'; - } - } catch ( Exception $e ) { - // Handle execution exceptions - echo 'Exception: ' . $e->getMessage(); + + $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.'; } } ``` @@ -151,11 +138,16 @@ if ( $ability ) { // Check permission before execution if ( $ability->has_permission( $input ) ) { - try { - $result = $ability->execute( $input ); - // Handle result - } catch ( Exception $e ) { - // Handle execution error + $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']; + } } } else { echo 'You do not have permission to execute this ability.'; @@ -203,26 +195,20 @@ if ( ! $ability ) { return; } -try { - $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 - -} catch ( Exception $e ) { - // Handle exceptions thrown by the execute callback - echo 'Exception during execution: ' . $e->getMessage(); +$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 ```