Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 155 additions & 2 deletions features/ability.feature
Original file line number Diff line number Diff line change
Expand Up @@ -384,18 +384,171 @@ Feature: Manage abilities registered via the WordPress Abilities API.
} );
"""

When I run `wp ability list --namespace=test-plugin --show-in-rest=true --field=name`
When I run `wp ability list --namespace=test-plugin --show-in-rest --field=name`
Then STDOUT should be:
"""
test-plugin/public-ability
"""

When I run `wp ability list --namespace=test-plugin --show-in-rest=false --field=name`
When I run `wp ability list --namespace=test-plugin --no-show-in-rest --field=name`
Then STDOUT should be:
"""
test-plugin/private-ability
"""

@require-wp-7.1
Scenario: Display the public flag and its precedence over channel flags.
Given a wp-content/mu-plugins/test-ability.php file:
"""
<?php
add_action( 'wp_abilities_api_categories_init', function() {
wp_register_ability_category( 'test-category', array(
'label' => 'Test Category',
'description' => 'A test category.',
) );
} );

add_action( 'wp_abilities_api_init', function() {
wp_register_ability( 'test-plugin/public-ability', array(
'label' => 'Public Ability',
'description' => 'A public ability.',
'category' => 'test-category',
'execute_callback' => function( $input ) {
return array( 'result' => 'done' );
},
'permission_callback' => '__return_true',
'meta' => array( 'public' => true ),
) );

wp_register_ability( 'test-plugin/public-not-rest', array(
'label' => 'Public But Not REST',
'description' => 'A public ability hidden from REST.',
'category' => 'test-category',
'execute_callback' => function( $input ) {
return array( 'result' => 'done' );
},
'permission_callback' => '__return_true',
'meta' => array( 'public' => true, 'show_in_rest' => false ),
) );

wp_register_ability( 'test-plugin/plain-ability', array(
'label' => 'Plain Ability',
'description' => 'An ability without exposure metadata.',
'category' => 'test-category',
'execute_callback' => function( $input ) {
return array( 'result' => 'done' );
},
'permission_callback' => '__return_true',
) );
} );
"""

When I run `wp ability list --namespace=test-plugin --fields=name,public,show_in_rest`
Then STDOUT should be a table containing rows:
| name | public | show_in_rest |
| test-plugin/public-ability | 1 | 1 |
| test-plugin/public-not-rest | 1 | 0 |
| test-plugin/plain-ability | 0 | 0 |

When I run `wp ability get test-plugin/public-ability --format=json`
Then STDOUT should be JSON containing:
"""
{"public":"1","show_in_rest":"1"}
"""

When I run `wp ability get test-plugin/public-not-rest --format=json`
Then STDOUT should be JSON containing:
"""
{"public":"1","show_in_rest":"0"}
"""

@require-wp-7.1
Scenario: Filter abilities by public.
Given a wp-content/mu-plugins/test-ability.php file:
"""
<?php
add_action( 'wp_abilities_api_categories_init', function() {
wp_register_ability_category( 'test-category', array(
'label' => 'Test Category',
'description' => 'A test category.',
) );
} );

add_action( 'wp_abilities_api_init', function() {
wp_register_ability( 'test-plugin/public-ability', array(
'label' => 'Public Ability',
'description' => 'A public ability.',
'category' => 'test-category',
'execute_callback' => function( $input ) {
return array( 'result' => 'done' );
},
'permission_callback' => '__return_true',
'meta' => array( 'public' => true ),
) );

wp_register_ability( 'test-plugin/internal-ability', array(
'label' => 'Internal Ability',
'description' => 'An ability kept away from clients.',
'category' => 'test-category',
'execute_callback' => function( $input ) {
return array( 'result' => 'done' );
},
'permission_callback' => '__return_true',
'meta' => array( 'public' => false ),
) );
} );
"""

When I run `wp ability list --namespace=test-plugin --public --field=name`
Then STDOUT should be:
"""
test-plugin/public-ability
"""

When I run `wp ability list --namespace=test-plugin --no-public --field=name`
Then STDOUT should be:
"""
test-plugin/internal-ability
"""

@require-wp-6.9
Scenario: Inspect raw ability meta.
Given a wp-content/mu-plugins/test-ability.php file:
"""
<?php
add_action( 'wp_abilities_api_categories_init', function() {
wp_register_ability_category( 'test-category', array(
'label' => 'Test Category',
'description' => 'A test category.',
) );
} );

add_action( 'wp_abilities_api_init', function() {
wp_register_ability( 'test-plugin/channel-ability', array(
'label' => 'Channel Ability',
'description' => 'An ability with channel-specific metadata.',
'category' => 'test-category',
'execute_callback' => function( $input ) {
return array( 'result' => 'done' );
},
'permission_callback' => '__return_true',
'meta' => array( 'mcp' => array( 'public' => false ) ),
) );
} );
"""

When I run `wp ability get test-plugin/channel-ability --field=meta`
Then STDOUT should contain:
"""
"mcp":{"public":false}
"""

When I run `wp ability get test-plugin/channel-ability`
Then STDOUT should not contain:
"""
mcp
"""

@require-wp-6.9
Scenario: Display ability annotations.
Given a wp-content/mu-plugins/test-ability.php file:
Expand Down
65 changes: 58 additions & 7 deletions src/Ability_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
* +---------------------------+----------------------+----------+------------------------------------------+
*
* # Get details of a specific ability.
* $ wp ability get core/get-site-info --fields=name,label,category,readonly,show_in_rest
* $ wp ability get core/get-site-info --fields=name,label,category,readonly,public,show_in_rest
* +---------------+----------------------+
* | Field | Value |
* +---------------+----------------------+
* | name | core/get-site-info |
* | label | Get Site Information |
* | category | site |
* | readonly | 1 |
* | public | 1 |
* | show_in_rest | 1 |
* +---------------+----------------------+
*
Expand Down Expand Up @@ -92,6 +93,7 @@ class Ability_Command extends WP_CLI_Command {
'readonly',
'destructive',
'idempotent',
'public',
'show_in_rest',
];

Expand All @@ -106,8 +108,11 @@ class Ability_Command extends WP_CLI_Command {
* [--namespace=<prefix>]
* : Filter abilities by namespace prefix (e.g., 'core' for 'core/*' abilities).
*
* [--show-in-rest=<bool>]
* : Filter abilities by REST API exposure.
* [--public]
* : Only list abilities flagged for client exposure. Pass --no-public to invert.
*
* [--show-in-rest]
* : Only list abilities exposed to the REST API. Pass --no-show-in-rest to invert.
*
* [--field=<field>]
* : Prints the value of a single field for each ability.
Expand Down Expand Up @@ -142,8 +147,15 @@ class Ability_Command extends WP_CLI_Command {
* * readonly
* * destructive
* * idempotent
* * public
* * show_in_rest
*
* The `public` field reports the high-level client exposure flag as declared
* by the ability. As of WordPress 7.1 it seeds the default for channel-specific
* flags such as `show_in_rest`, which take precedence when set explicitly.
* On earlier versions the flag is stored but has no effect, so `public` and
* `show_in_rest` may disagree.
*
* ## EXAMPLES
*
* # List all abilities.
Expand All @@ -162,7 +174,13 @@ class Ability_Command extends WP_CLI_Command {
* $ wp ability list --namespace=core
*
* # List abilities exposed to REST API.
* $ wp ability list --show-in-rest=true
* $ wp ability list --show-in-rest
*
* # List abilities meant to be available to clients.
* $ wp ability list --public
*
* # Find abilities that opt out of REST despite being public.
* $ wp ability list --public --no-show-in-rest
*
* # List abilities as JSON.
* $ wp ability list --format=json
Expand All @@ -182,6 +200,7 @@ public function list_( $args, $assoc_args ): void {
$abilities = wp_get_abilities();
$category_slug = Utils\get_flag_value( $assoc_args, 'category' );
$namespace = Utils\get_flag_value( $assoc_args, 'namespace' );
$public = Utils\get_flag_value( $assoc_args, 'public' );
$show_in_rest = Utils\get_flag_value( $assoc_args, 'show-in-rest' );

$items = [];
Expand All @@ -203,11 +222,18 @@ public function list_( $args, $assoc_args ): void {
}
}

// Filter by public if specified.
if ( null !== $public ) {
$ability_public = '1' === $ability_data['public'];
if ( $public !== $ability_public ) {
continue;
}
}

// Filter by show_in_rest if specified.
if ( null !== $show_in_rest ) {
$show_in_rest_bool = filter_var( $show_in_rest, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
$ability_rest = '1' === $ability_data['show_in_rest'];
if ( null !== $show_in_rest_bool && $show_in_rest_bool !== $ability_rest ) {
$ability_rest = '1' === $ability_data['show_in_rest'];
if ( $show_in_rest !== $ability_rest ) {
continue;
}
}
Expand Down Expand Up @@ -246,6 +272,8 @@ public function list_( $args, $assoc_args ): void {
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default:
*
* * name
* * label
* * category
Expand All @@ -255,8 +283,22 @@ public function list_( $args, $assoc_args ): void {
* * readonly
* * destructive
* * idempotent
* * public
* * show_in_rest
*
* These fields are optionally available:
*
* * meta
*
* The `public` field reports the high-level client exposure flag as declared
* by the ability. As of WordPress 7.1 it seeds the default for channel-specific
* flags such as `show_in_rest`, which take precedence when set explicitly.
* On earlier versions the flag is stored but has no effect, so `public` and
* `show_in_rest` may disagree.
*
* The `meta` field renders the raw metadata as JSON. It is the only way to
* inspect channel-specific settings registered by plugins, such as `mcp`.
*
* ## EXAMPLES
*
* # Get details of a specific ability.
Expand All @@ -273,9 +315,14 @@ public function list_( $args, $assoc_args ): void {
* | readonly | 1 |
* | destructive | 0 |
* | idempotent | 1 |
* | public | 1 |
* | show_in_rest | 1 |
* +---------------+----------------------+
*
* # Inspect channel-specific settings that have no field of their own.
* $ wp ability get my-plugin/my-ability --field=meta
* {"annotations":{"readonly":true,"destructive":false,"idempotent":null},"mcp":{"public":false},"show_in_rest":true,"public":true}
*
* # Get ability as JSON.
* $ wp ability get core/get-site-info --format=json
*
Expand Down Expand Up @@ -650,6 +697,7 @@ private function format_ability_for_list( $ability ) {
'readonly' => $this->format_annotation( $annotations['readonly'] ),
'destructive' => $this->format_annotation( $annotations['destructive'] ),
'idempotent' => $this->format_annotation( $annotations['idempotent'] ),
'public' => $ability->get_meta_item( 'public', false ) ? '1' : '0',
Comment thread
swissspidy marked this conversation as resolved.
'show_in_rest' => $ability->get_meta_item( 'show_in_rest', false ) ? '1' : '0',
];
}
Expand All @@ -662,6 +710,7 @@ private function format_ability_for_list( $ability ) {
*/
private function format_ability_for_get( $ability ) {
$annotations = $this->get_annotations( $ability );
$meta = $ability->get_meta();

return [
'name' => $ability->get_name(),
Expand All @@ -673,7 +722,9 @@ private function format_ability_for_get( $ability ) {
'readonly' => $this->format_annotation( $annotations['readonly'] ),
'destructive' => $this->format_annotation( $annotations['destructive'] ),
'idempotent' => $this->format_annotation( $annotations['idempotent'] ),
'public' => $ability->get_meta_item( 'public', false ) ? '1' : '0',
'show_in_rest' => $ability->get_meta_item( 'show_in_rest', false ) ? '1' : '0',
'meta' => ! empty( $meta ) ? wp_json_encode( $meta ) : '{}',
];
}

Expand Down