-
Notifications
You must be signed in to change notification settings - Fork 33
Adds Abilities Explorer #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
karmatosed
wants to merge
5
commits into
WordPress:develop
Choose a base branch
from
karmatosed:TRY/abilitiesexplorer
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
0491146
Adds Abilities Explorer
karmatosed cd33025
Merge branch 'trunk' into TRY/abilitiesexplorer
JasonTheAdams 7948747
Update fixes as suggested in review
karmatosed 6322322
Update as review
karmatosed 544903e
Remove additional checks
karmatosed File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
includes/Features/Abilities_Explorer/Abilities_Explorer.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| <?php | ||
| /** | ||
| * Abilities Explorer Feature | ||
| * | ||
| * Discover, inspect, test, and document all abilities registered via the WordPress Abilities API. | ||
| * | ||
| * @package WordPress\AI\Features\Abilities_Explorer | ||
| * @since 0.1.0 | ||
| */ | ||
|
|
||
| namespace WordPress\AI\Features\Abilities_Explorer; | ||
|
|
||
| use WordPress\AI\Abstracts\Abstract_Feature; | ||
|
|
||
| /** | ||
| * Abilities Explorer Feature Class | ||
| * | ||
| * Provides a comprehensive interface for exploring the WordPress Abilities API. | ||
| * | ||
| * @since 0.1.0 | ||
| */ | ||
| class Abilities_Explorer extends Abstract_Feature { | ||
|
|
||
| /** | ||
| * Feature version. | ||
| * | ||
| * @since 0.1.0 | ||
| * @var string | ||
| */ | ||
| private const VERSION = '1.0.0'; | ||
|
|
||
| /** | ||
| * Load feature metadata. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @return array{id: string, label: string, description: string} Feature metadata. | ||
| */ | ||
| protected function load_feature_metadata(): array { | ||
| return array( | ||
| 'id' => 'abilities-explorer', | ||
| 'label' => __( 'Abilities Explorer', 'ai' ), | ||
| 'description' => __( 'Discover, inspect, test, and document all abilities registered via the WordPress Abilities API.', 'ai' ), | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Register the feature. | ||
| * | ||
| * Sets up hooks and initializes the feature functionality. | ||
| * | ||
| * @since 0.1.0 | ||
| */ | ||
| public function register(): void { | ||
| // Initialize admin interface | ||
| if ( is_admin() ) { | ||
| $this->init_admin(); | ||
| } | ||
| } | ||
| /** | ||
| * Initialize admin functionality. | ||
| * | ||
| * @since 0.1.0 | ||
| */ | ||
| private function init_admin(): void { | ||
| require_once __DIR__ . '/Admin_Page.php'; | ||
|
|
||
| $admin_page = new Admin_Page(); | ||
| $admin_page->init(); | ||
| } | ||
| } | ||
313 changes: 313 additions & 0 deletions
313
includes/Features/Abilities_Explorer/Ability_Handler.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,313 @@ | ||
| <?php | ||
| /** | ||
| * Ability Handler Class | ||
| * | ||
| * Handles fetching and processing abilities from the WordPress Abilities API. | ||
| * | ||
| * @package WordPress\AI\Features\Abilities_Explorer | ||
| * @since 0.1.0 | ||
| */ | ||
|
|
||
| namespace WordPress\AI\Features\Abilities_Explorer; | ||
|
|
||
| if ( ! defined( 'ABSPATH' ) ) { | ||
| exit; | ||
| } | ||
|
|
||
| /** | ||
| * Ability Handler Class | ||
| * | ||
| * Provides methods for retrieving, formatting, and invoking WordPress abilities. | ||
| * | ||
| * @since 0.1.0 | ||
| */ | ||
| class Ability_Handler { | ||
|
|
||
| /** | ||
| * Get all registered abilities. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @return array Array of abilities. | ||
| */ | ||
| public static function get_all_abilities(): array { | ||
| if ( ! function_exists( 'wp_get_abilities' ) ) { | ||
| return array(); | ||
| } | ||
|
|
||
| $all_abilities = wp_get_abilities(); | ||
|
|
||
| return self::format_abilities( $all_abilities ); | ||
| } | ||
|
|
||
| /** | ||
| * Get a single ability by slug. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @param string $slug Ability slug (name). | ||
| * @return array|null Ability data or null if not found. | ||
| */ | ||
| public static function get_ability( $slug ): ?array { | ||
| if ( ! function_exists( 'wp_get_ability' ) ) { | ||
| return null; | ||
| } | ||
|
|
||
| $ability = wp_get_ability( $slug ); | ||
|
|
||
| if ( ! $ability ) { | ||
| return null; | ||
| } | ||
|
|
||
| return self::format_single_ability( $ability ); | ||
| } | ||
|
|
||
| /** | ||
| * Format abilities array. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @param array $abilities Raw abilities array (WP_Ability objects). | ||
| * @return array Formatted abilities. | ||
| */ | ||
| private static function format_abilities( $abilities ): array { | ||
| if ( empty( $abilities ) || ! is_array( $abilities ) ) { | ||
| return array(); | ||
| } | ||
|
|
||
| $formatted = array(); | ||
|
|
||
| foreach ( $abilities as $ability ) { | ||
| $formatted[] = self::format_single_ability( $ability ); | ||
| } | ||
|
|
||
| return $formatted; | ||
| } | ||
|
|
||
| /** | ||
| * Format a single ability. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @param \WP_Ability $ability Ability object. | ||
| * @return array Formatted ability data. | ||
| */ | ||
| private static function format_single_ability( $ability ) : array { | ||
| if ( ! is_object( $ability ) || ! method_exists( $ability, 'get_name' ) ) { | ||
| return array(); | ||
| } | ||
|
|
||
| $name = $ability->get_name(); | ||
| $meta = method_exists( $ability, 'get_meta' ) ? $ability->get_meta() : array(); | ||
|
|
||
| return array( | ||
| 'slug' => $name, | ||
| 'name' => $ability->get_label(), | ||
| 'description' => $ability->get_description(), | ||
| 'provider' => self::detect_provider( $name, $meta ), | ||
| 'input_schema' => $ability->get_input_schema(), | ||
| 'output_schema' => $ability->get_output_schema(), | ||
| 'raw_data' => array( | ||
| 'name' => $name, | ||
| 'label' => $ability->get_label(), | ||
| 'description' => $ability->get_description(), | ||
| 'input_schema' => $ability->get_input_schema(), | ||
| 'output_schema' => $ability->get_output_schema(), | ||
| 'meta' => $meta, | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Detect ability provider (Core, Plugin, or Theme). | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @param string $name Ability name (slug). | ||
| * @param array $meta Ability metadata. | ||
| * @return string Provider type. | ||
| */ | ||
| private static function detect_provider( $name, $meta ): string { | ||
| // Check if provider is explicitly set in meta | ||
| if ( isset( $meta['provider'] ) ) { | ||
| return $meta['provider']; | ||
| } | ||
|
|
||
| // Detect based on name prefix (namespace/ability format) | ||
| $parts = explode( '/', $name ); | ||
| if ( count( $parts ) === 2 ) { | ||
| $namespace = $parts[0]; | ||
|
|
||
| // WordPress core abilities | ||
| if ( in_array( $namespace, array( 'wordpress', 'wp', 'core' ), true ) ) { | ||
| return 'Core'; | ||
| } | ||
|
|
||
| // Check if namespace matches active theme | ||
| if ( $namespace === get_stylesheet() || $namespace === get_template() ) { | ||
| return 'Theme'; | ||
| } | ||
| } | ||
|
|
||
| // Default to Plugin | ||
| return 'Plugin'; | ||
| } | ||
|
|
||
| /** | ||
| * Invoke an ability. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @param string $slug Ability name. | ||
| * @param array $input Input data. | ||
| * @return array Result with success status and data/error. | ||
| */ | ||
| public static function invoke_ability( $slug, $input = array() ): string { | ||
| if ( ! function_exists( 'wp_get_ability' ) ) { | ||
| return array( | ||
| 'success' => false, | ||
| 'error' => 'Abilities API not available', | ||
| ); | ||
| } | ||
|
|
||
| $ability = wp_get_ability( $slug ); | ||
|
|
||
| if ( ! $ability ) { | ||
| return array( | ||
| 'success' => false, | ||
| 'error' => sprintf( 'Ability "%s" not found', $slug ), | ||
| ); | ||
| } | ||
|
|
||
| // If ability has no input schema, invoke without input | ||
| $input_schema = $ability->get_input_schema(); | ||
| if ( empty( $input_schema ) ) { | ||
| $result = $ability->execute(); | ||
| } else { | ||
| $result = $ability->execute( $input ); | ||
| } | ||
|
|
||
| // Check if result is WP_Error | ||
| if ( is_wp_error( $result ) ) { | ||
| return array( | ||
| 'success' => false, | ||
| 'error' => $result->get_error_message(), | ||
| 'code' => $result->get_error_code(), | ||
| 'data' => $result->get_error_data(), | ||
| ); | ||
| } | ||
|
|
||
| return array( | ||
| 'success' => true, | ||
| 'data' => $result, | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Validate input against input schema. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @param array $schema Input schema. | ||
| * @param array $input Input data to validate. | ||
| * @return array Validation result. | ||
| */ | ||
| public static function validate_input( $schema, $input ): array { | ||
| $errors = array(); | ||
|
|
||
| if ( empty( $schema ) ) { | ||
| return array( | ||
| 'valid' => true, | ||
| 'errors' => array(), | ||
| ); | ||
| } | ||
|
|
||
| // Basic JSON Schema validation | ||
| if ( isset( $schema['required'] ) && is_array( $schema['required'] ) ) { | ||
| foreach ( $schema['required'] as $required_field ) { | ||
| if ( ! isset( $input[ $required_field ] ) ) { | ||
| $errors[] = sprintf( 'Required field "%s" is missing', $required_field ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Type validation for properties | ||
| if ( isset( $schema['properties'] ) && is_array( $schema['properties'] ) ) { | ||
| foreach ( $schema['properties'] as $prop_name => $prop_schema ) { | ||
| if ( isset( $input[ $prop_name ] ) && isset( $prop_schema['type'] ) ) { | ||
| $valid = self::validate_type( $input[ $prop_name ], $prop_schema['type'] ); | ||
| if ( ! $valid ) { | ||
| $errors[] = sprintf( | ||
| 'Field "%s" should be of type "%s"', | ||
| $prop_name, | ||
| $prop_schema['type'] | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return array( | ||
| 'valid' => empty( $errors ), | ||
| 'errors' => $errors, | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Validate value type. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @param mixed $value Value to validate. | ||
| * @param string $expected_type Expected type. | ||
| * @return bool Whether the value matches the expected type. | ||
| */ | ||
| private static function validate_type( $value, $expected_type ): bool { | ||
| switch ( $expected_type ) { | ||
| case 'string': | ||
| return is_string( $value ); | ||
| case 'number': | ||
| case 'integer': | ||
| return is_numeric( $value ); | ||
| case 'boolean': | ||
| return is_bool( $value ); | ||
| case 'array': | ||
| return is_array( $value ); | ||
| case 'object': | ||
| return is_object( $value ) || is_array( $value ); | ||
| default: | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Get ability statistics. | ||
| * | ||
| * @since 0.1.0 | ||
| * | ||
| * @return array Statistics about registered abilities. | ||
| */ | ||
| public static function get_statistics(): array { | ||
| $abilities = self::get_all_abilities(); | ||
|
|
||
| $stats = array( | ||
| 'total' => count( $abilities ), | ||
| 'by_provider' => array( | ||
| 'Core' => 0, | ||
| 'Plugin' => 0, | ||
| 'Theme' => 0, | ||
| ), | ||
| ); | ||
|
|
||
| foreach ( $abilities as $ability ) { | ||
| // Count by provider | ||
| if ( isset( $ability['provider'] ) ) { | ||
| if ( isset( $stats['by_provider'][ $ability['provider'] ] ) ) { | ||
| ++$stats['by_provider'][ $ability['provider'] ]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return $stats; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.