From eb16f47092842e49cec44199d5f678d10ff8319b Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Sat, 13 Sep 2025 19:16:57 +0300 Subject: [PATCH 1/3] dev: add `wp_ability_args` filter --- docs/5.hooks.md | 75 +++++++++++++++++++ .../class-wp-abilities-registry.php | 12 +++ 2 files changed, 87 insertions(+) create mode 100644 docs/5.hooks.md diff --git a/docs/5.hooks.md b/docs/5.hooks.md new file mode 100644 index 0000000..1d07c42 --- /dev/null +++ b/docs/5.hooks.md @@ -0,0 +1,75 @@ +# Using Action and Filter Hooks + +The Abilities API provides action and filter hooks that allow developers to customize and extend its functionality. Below are the available hooks: + +## Quick Links + +- [Actions](#actions) +- [Filters](#filters) + - [`wp_ability_args`](#wp_ability_args) + +## Actions + +There are currently **no Action hooks** provided by this version of the Abilities API. + +## Filters + +### `wp_ability_args` + +> [!IMPORTANT] +> This filter is prefixed with `wp_` to avoid potential naming collisions. +> Once merged into WordPress core, the prefix will likely be removed to preserve backward-compatibility. + +Allows modification of an Ability's args before they are validated and used to instantiate the Ability. + +```php +apply_filters( 'wp_ability_args', array $args, string $ability_name ); +``` + +#### Parameters + +- `$args` (`array`): The arguments used to instantiate the ability. See [wp_register_ability()](./3.registering-abilities.md#wp_register_ability) for the full list of args. +- `$ability_name` (`string`): The name of the ability, with its namespace. + +#### Example + +```php +/** + * Modify ability args before validation. + * @param array $args The arguments used to instantiate the ability. + * @param string $ability_name The name of the ability, with its namespace. + * @return array The modified ability arguments. + */ +function my_modify_ability_args( array $args, string $ability_name ): array { + // Check if the ability name matches what you're looking for. + if ( 'my-namespace/my-ability' !== $ability_name ) { + return $args; + } + + // Modify the args as needed. + $args['label'] = __('My Custom Ability Label'); + + // You can use the old args to build new ones. + $args['description] = sprintf( + /* translators: 1: Ability name 2: Previous description */ + __('This is a custom description for the ability %s. Previously the description was %s', 'text-domain'), + $ability_name, + $args['description'] ?? 'N/A' + ); + + // Even if they're callbacks. + $args['has_permissions' ] = static function ( $input = null ) use ( $args ) { + $previous_check = is_callable( $args['has_permissions'] ) ? $args['has_permissions']( $input ) : true; + + // If we already failed, no need for stricter checks. + if ( ! $previous_check || is_wp_error( $previous_check ) ) { + return $previous_check; + } + + return current_user_can( 'my_custom_ability_cap', $args['name'] ) + } + + return $args; +} +add_filter( 'wp_ability_args', 'my_modify_ability_args', 10, 2 ); +``` diff --git a/includes/abilities-api/class-wp-abilities-registry.php b/includes/abilities-api/class-wp-abilities-registry.php index dd507bd..fc99d66 100644 --- a/includes/abilities-api/class-wp-abilities-registry.php +++ b/includes/abilities-api/class-wp-abilities-registry.php @@ -64,6 +64,16 @@ final class WP_Abilities_Registry { * } $args */ public function register( string $name, array $args ): ?WP_Ability { + /** + * Filters the ability arguments before they are validated. + * + * @since n.e.x.t + * + * @param array $args The arguments used to instantiate the ability. + * @param string $name The name of the ability, with its namespace. + */ + $args = apply_filters( 'wp_ability_args', $args, $name ); + if ( ! preg_match( '/^[a-z0-9-]+\/[a-z0-9-]+$/', $name ) ) { _doing_it_wrong( __METHOD__, @@ -94,6 +104,8 @@ public function register( string $name, array $args ): ?WP_Ability { ); return null; } + + /** @var class-string<\WP_Ability> */ $ability_class = $args['ability_class'] ?? WP_Ability::class; unset( $args['ability_class'] ); From 15664a37b1df473d95a59d09346101e59247b0a9 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Sat, 13 Sep 2025 20:10:58 +0300 Subject: [PATCH 2/3] Update docs/5.hooks.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/5.hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/5.hooks.md b/docs/5.hooks.md index 1d07c42..841b66b 100644 --- a/docs/5.hooks.md +++ b/docs/5.hooks.md @@ -50,7 +50,7 @@ function my_modify_ability_args( array $args, string $ability_name ): array { $args['label'] = __('My Custom Ability Label'); // You can use the old args to build new ones. - $args['description] = sprintf( + $args['description'] = sprintf( /* translators: 1: Ability name 2: Previous description */ __('This is a custom description for the ability %s. Previously the description was %s', 'text-domain'), $ability_name, From dc10954dbf8571f508bde043d5a98caca67f6e42 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Sat, 13 Sep 2025 20:14:12 +0300 Subject: [PATCH 3/3] docs: fix `has_permissions` overload example --- docs/5.hooks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/5.hooks.md b/docs/5.hooks.md index 841b66b..dc28f90 100644 --- a/docs/5.hooks.md +++ b/docs/5.hooks.md @@ -58,7 +58,7 @@ function my_modify_ability_args( array $args, string $ability_name ): array { ); // Even if they're callbacks. - $args['has_permissions' ] = static function ( $input = null ) use ( $args ) { + $args['has_permissions' ] = static function ( $input = null ) use ( $args, $ability_name ) { $previous_check = is_callable( $args['has_permissions'] ) ? $args['has_permissions']( $input ) : true; // If we already failed, no need for stricter checks. @@ -66,7 +66,7 @@ function my_modify_ability_args( array $args, string $ability_name ): array { return $previous_check; } - return current_user_can( 'my_custom_ability_cap', $args['name'] ) + return current_user_can( 'my_custom_ability_cap', $ability_name ); } return $args;