Make WordPress Core

Changeset 65308


Ignore:
Timestamp:
11/06/2026 01:09:43 PM (25 hours ago)
Author:
gziolo
Message:

Abilities API: Require a registered category for abilities

Partially reverts [63343]. Abilities that omit a category are no longer assigned to the built-in uncategorized category, which is removed. Registration fails with a _doing_it_wrong() notice instead. Updates the docs to explain why categories are required and how to reuse existing ones.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/15462.

Props gziolo, jason_the_adams, jorgefilipecosta.
Fixes #65569.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/abilities-api.php

    r65306 r65308  
    5353 *     add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
    5454 *
    55  * Use a category to group related abilities and improve discoverability. The
    56  * `data-export` category must be registered first with `wp_register_ability_category()`
     55 * Every ability requires a category to group related abilities and improve discoverability.
     56 * The `data-export` category must be registered first with `wp_register_ability_category()`
    5757 * on `wp_abilities_api_categories_init`, or the ability registration fails.
    58  * Abilities that omit `category` fall back to the built-in `uncategorized` category.
    5958 *
    6059 * Once registered, abilities can be checked, retrieved, and managed:
    … …  
    149148 * ### Categories
    150149 *
    151  * Abilities can be organized into categories. If no category is provided, the ability is
    152  * assigned to the built-in `uncategorized` category. Custom categories must be registered
    153  * before the abilities that reference them:
     150 * Every ability must specify a registered category. Categories group related abilities
     151 * so clients can discover and retrieve relevant subsets. Plugins can reuse an existing
     152 * category when appropriate, including categories registered by core. Custom categories must be
     153 * registered on `wp_abilities_api_categories_init` before abilities that reference them
     154 * are registered on `wp_abilities_api_init`:
    154155 *
    155156 *     function my_plugin_register_categories(): void {
    … …  
    287288 * @since 6.9.0
    288289 * @since 7.1.0 Added the `public` meta argument.
    289  * @since 7.2.0 The `category` argument is now optional and defaults to `uncategorized`.
    290290 * @since 7.2.0 Added the `required_capabilities` argument.
    291291 *
    … …  
    305305 *     @type string               $description           Required. A detailed description of what the ability does
    306306 *                                                       and when it should be used.
    307  *     @type string               $category              Optional. The ability category slug this ability belongs to.
    308  *                                                       Defaults to `uncategorized`. Custom categories must be registered
    309  *                                                       via `wp_register_ability_category()` before registering the ability.
     307 *     @type string               $category              Required. The registered ability category slug this ability belongs to.
     308 *                                                       Custom categories must be registered via
     309 *                                                       `wp_register_ability_category()` before registering the ability.
    310310 *     @type callable             $execute_callback      Required. A callback function to execute when the ability is invoked.
    311311 *                                                       Receives optional mixed input data and must return either a result
    … …  
    708708 *
    709709 * Ability categories provide a way to organize and group related abilities for better
    710  * discoverability and management. Custom categories must be registered before abilities
    711  * that reference them. Abilities that omit a category are assigned to the built-in
    712  * `uncategorized` category, which is intended as an escape hatch for simple or transitional
    713  * registrations.
     710 * discoverability and management. Every ability requires a registered category.
     711 * Custom categories must be registered before abilities that reference them.
    714712 *
    715713 * Ability categories must be registered on the `wp_abilities_api_categories_init` action hook.
  • trunk/src/wp-includes/abilities-api/class-wp-abilities-registry.php

    r64685 r65308  
    4242         * @since 6.9.0
    4343         * @since 7.1.0 Added the `public` meta argument.
    44          * @since 7.2.0 The `category` argument is now optional and defaults to `uncategorized`.
    4544         * @since 7.2.0 Added the `required_capabilities` argument.
    4645         *
    … …  
    5554         *     @type string               $label                 The human-readable label for the ability.
    5655         *     @type string               $description           A detailed description of what the ability does.
    57          *     @type string               $category              Optional. The ability category slug this ability belongs to.
    58          *                                                       Defaults to `uncategorized`.
     56         *     @type string               $category              The registered ability category slug this ability belongs to.
    5957         *     @type callable             $execute_callback      A callback function to execute when the ability is invoked.
    6058         *                                                       Receives optional mixed input and returns mixed result or WP_Error.
    … …  
    120118                 * @since 6.9.0
    121119                 * @since 7.1.0 Added the `public` meta argument.
    122                  * @since 7.2.0 The `category` argument is now optional and defaults to `uncategorized`.
    123120                 * @since 7.2.0 Added the `required_capabilities` argument.
    124121                 *
    … …  
    128125                 *     @type string               $label                 The human-readable label for the ability.
    129126                 *     @type string               $description           A detailed description of what the ability does.
    130                  *     @type string               $category              Optional. The ability category slug this ability belongs to.
    131                  *                                                       Defaults to `uncategorized`.
     127                 *     @type string               $category              The registered ability category slug this ability belongs to.
    132128                 *     @type callable             $execute_callback      A callback function to execute when the ability is invoked.
    133129                 *                                                       Receives optional mixed input and returns mixed result or WP_Error.
    … …  
    157153                $args = apply_filters( 'wp_register_ability_args', $args, $name );
    158154
    159                 if ( ! array_key_exists( 'category', $args ) ) {
    160                         $args['category'] = 'uncategorized';
    161                 }
    162 
    163                 if ( ! is_string( $args['category'] ) ) {
    164                         _doing_it_wrong(
    165                                 __METHOD__,
    166                                 __( 'Ability category must be a string.' ),
     155                if ( ! isset( $args['category'] ) || ! is_string( $args['category'] ) ) {
     156                        _doing_it_wrong(
     157                                __METHOD__,
     158                                __( 'The ability properties must contain a `category` string.' ),
    167159                                '6.9.0'
    168160                        );
  • trunk/src/wp-includes/abilities.php

    r64729 r65308  
    1818 *
    1919 * @since 6.9.0
    20  * @since 7.2.0 Added the `content` and `uncategorized` categories.
     20 * @since 7.2.0 Added the `content` category.
    2121 */
    2222function wp_register_core_ability_categories(): void {
    … …  
    4242                        'label'       => __( 'Content' ),
    4343                        'description' => __( 'Abilities that retrieve or manage posts and other content.' ),
    44                 )
    45         );
    46 
    47         wp_register_ability_category(
    48                 'uncategorized',
    49                 array(
    50                         'label'       => __( 'Uncategorized' ),
    51                         'description' => __( 'Abilities that have not been assigned to a specific category.' ),
    5244                )
    5345        );
  • trunk/tests/phpunit/tests/abilities-api/wpAbilitiesRegistry.php

    r65044 r65308  
    246246                $this->assertNull( $result );
    247247                $this->assertStringContainsString(
    248                         'Ability category must be a string.',
     248                        'The ability properties must contain a `category` string.',
    249249                        $this->caught_doing_it_wrong['WP_Abilities_Registry::register']
    250250                );
    … …  
    266266
    267267        /**
    268          * Tests that an empty category is rejected rather than replaced by the default.
     268         * Tests that an empty category is rejected.
    269269         *
    270270         * @ticket 65569
  • trunk/tests/phpunit/tests/abilities-api/wpRegisterAbility.php

    r65110 r65308  
    101101                // Clean up registered test ability category.
    102102                wp_unregister_ability_category( 'math' );
    103                 if ( wp_has_ability_category( 'uncategorized' ) ) {
    104                         wp_unregister_ability_category( 'uncategorized' );
    105                 }
    106103
    107104                parent::tear_down();
    … …  
    227224
    228225        /**
     226         * Tests that registering an ability requires a category.
     227         *
    229228         * @ticket 65569
    230          */
    231         public function test_register_ability_without_category_uses_uncategorized_category(): void {
    232                 global $wp_current_filter;
    233 
    234                 $this->simulate_doing_wp_abilities_init_action();
    235 
    236                 $wp_current_filter[] = 'wp_abilities_api_categories_init';
    237                 wp_register_ability_category(
    238                         'uncategorized',
    239                         array(
    240                                 'label'       => 'Uncategorized',
    241                                 'description' => 'Abilities that have not been assigned to a specific category.',
    242                         )
    243                 );
    244                 array_pop( $wp_current_filter );
    245 
    246                 $ability = wp_register_ability(
    247                         'test/without-category',
    248                         array(
    249                                 'label'               => 'Test ability without category',
    250                                 'description'         => 'Test ability description.',
    251                                 'input_schema'        => array(),
    252                                 'output_schema'       => array(),
    253                                 'permission_callback' => '__return_true',
    254                                 'execute_callback'    => static function (): array {
    255                                         return array( 'success' => true );
    256                                 },
    257                         )
    258                 );
    259 
    260                 $this->assertInstanceOf( WP_Ability::class, $ability );
    261                 $this->assertSame( 'uncategorized', $ability->get_category() );
     229         * @expectedIncorrectUsage WP_Abilities_Registry::register
     230         */
     231        public function test_register_ability_without_category_fails(): void {
     232                $this->simulate_doing_wp_abilities_init_action();
     233
     234                $args = self::$test_ability_args;
     235                unset( $args['category'] );
     236
     237                $ability = wp_register_ability( self::$test_ability_name, $args );
     238
     239                $this->assertNull( $ability );
     240                $this->assertFalse( wp_has_ability( self::$test_ability_name ) );
    262241        }
    263242
  • trunk/tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php

    r65307 r65308  
    5858
    5959        /**
    60          * Tests that the uncategorized fallback category is registered by core.
     60         * Tests that core does not register an uncategorized fallback category.
    6161         *
    6262         * @ticket 65569
    6363         */
    64         public function test_uncategorized_category_is_registered(): void {
    65                 $category = wp_get_ability_category( 'uncategorized' );
    66 
    67                 $this->assertInstanceOf( WP_Ability_Category::class, $category );
    68                 $this->assertSame( 'Uncategorized', $category->get_label() );
    69                 $this->assertSame( 'Abilities that have not been assigned to a specific category.', $category->get_description() );
     64        public function test_uncategorized_category_is_not_registered(): void {
     65                $this->assertFalse( wp_has_ability_category( 'uncategorized' ) );
    7066        }
    7167
Note: See TracChangeset for help on using the changeset viewer.