diff --git a/includes/abilities-api.php b/includes/abilities-api.php index f898ac00..7d409a30 100644 --- a/includes/abilities-api.php +++ b/includes/abilities-api.php @@ -1,15 +1,103 @@ $ability ) { + $pass = true; + foreach ( $args as $key => $expected ) { + $negate = false; + $require_key = false; + $property = $key; + + if ( is_string( $key ) ) { + if ( $key[0] === '!' ) { + $negate = true; + $property = substr( $key, 1 ); + } elseif ( $key[0] === '?' ) { + $require_key = true; + $property = substr( $key, 1 ); + } + } + + // Namespace filter (special case) + if ( $property === 'namespace' ) { + $ability_ns = explode( '/', $ability->get_name() )[0] ?? ''; + $expected_ns = (array) $expected; + $match = in_array( $ability_ns, $expected_ns, true ); + $pass = $negate ? !$match : $match; + if ( ! $pass ) break; + continue; + } + + // Check meta + if ( $ability->get_meta() && array_key_exists( $property, $ability->get_meta() ) ) { + $actual = $ability->get_meta()[ $property ]; + } elseif ( method_exists( $ability, 'get_' . $property ) ) { + $actual = $ability->{'get_' . $property}(); + } else { + $actual = null; + } + + if ( $require_key ) { + $has_key = $actual !== null; + $pass = (bool) $has_key === (bool) $expected; + } else { + if ( is_array( $expected ) ) { + $actArr = is_array( $actual ) ? $actual : ( null !== $actual ? [ $actual ] : [] ); + $pass = ! empty( array_intersect( $expected, $actArr ) ); + } else { + $pass = ( $actual === $expected ); + } + } + if ( $negate ) { + $pass = ! $pass; + } + if ( ! $pass ) break; + } + if ( $pass ) { + $filtered[ $name ] = $ability; + } + } + + /** + * Filter the list of abilities returned by wp_query_abilities. + * + * @since 0.2.0 + * + * @param WP_Ability[] $filtered Array of filtered abilities. + * @param array $args Query arguments. + */ + return apply_filters( 'wp_query_abilities', $filtered, $args ); +} + -declare( strict_types = 1 ); /** * Registers a new ability using Abilities API. diff --git a/tests/unit/abilities-api/wpQueryAbilities.php b/tests/unit/abilities-api/wpQueryAbilities.php new file mode 100644 index 00000000..4bd1a219 --- /dev/null +++ b/tests/unit/abilities-api/wpQueryAbilities.php @@ -0,0 +1,90 @@ + array( + 'label' => 'Foo One', + 'description' => 'Ability one', + 'input_schema' => array(), + 'output_schema' => array(), + 'execute_callback' => '__return_null', + 'permission_callback' => '__return_true', + 'meta' => array('type' => 'resource', 'tags' => array('a','b'), 'audience' => array('internal')), + ), + 'foo/two' => array( + 'label' => 'Foo Two', + 'description' => 'Ability two', + 'input_schema' => array(), + 'output_schema' => array(), + 'execute_callback' => '__return_null', + 'permission_callback' => '__return_true', + 'meta' => array('type' => 'tool', 'tags' => array('b','c')), + ), + 'bar/three' => array( + 'label' => 'Bar Three', + 'description' => 'Ability three', + 'input_schema' => array(), + 'output_schema' => array(), + 'execute_callback' => '__return_null', + 'permission_callback' => '__return_true', + 'meta' => array('type' => 'resource', 'audience' => array('external')), + ), + ); + foreach ( self::$test_abilities as $name => $args ) { + wp_register_ability( $name, $args ); + } + } + + public function tear_down(): void { + foreach ( array_keys( self::$test_abilities ) as $name ) { + wp_unregister_ability( $name ); + } + parent::tear_down(); + } + + public function test_query_by_namespace() { + $result = wp_query_abilities( array( 'namespace' => 'foo' ) ); + $this->assertCount( 2, $result ); + foreach ( $result as $ability ) { + $this->assertStringStartsWith( 'foo/', $ability->get_name() ); + } + } + + public function test_query_by_meta_type() { + $result = wp_query_abilities( array( 'type' => 'resource' ) ); + $this->assertCount( 2, $result ); + } + + public function test_query_by_meta_tags_includes() { + $result = wp_query_abilities( array( 'tags' => array('b') ) ); + $this->assertCount( 2, $result ); + } + + public function test_query_by_audience_internal() { + $result = wp_query_abilities( array( 'audience' => array('internal') ) ); + $this->assertCount( 1, $result ); + $ability = reset( $result ); + $this->assertEquals( 'foo/one', $ability->get_name() ); + } + + public function test_query_by_meta_key_presence() { + $result = wp_query_abilities( array( '?tags' => true ) ); + $this->assertCount( 2, $result ); + } + + public function test_query_by_negation() { + $result = wp_query_abilities( array( '!type' => 'tool' ) ); + $this->assertCount( 2, $result ); + } +}