Make WordPress Core

Changeset 51943


Ignore:
Timestamp:
10/27/2021 06:42:13 PM (3 months ago)
Author:
swissspidy
Message:

Role/Capability: Add support for capability queries in WP_User_Query.

Similar to the existing role/role__in/role__not_in query arguments, this adds support for three new query arguments in WP_User_Query:

  • capability
  • capability__in
  • capability__not_in

These can be used to fetch users with (or without) a specific set of capabilities, for example to get all users
with the capability to edit a certain post type.

Under the hood, this will check all existing roles on the site and perform a LIKE query against the capabilities user meta field to find:

  • all users with a role that has this capability
  • all users with the capability being assigned directly

Note: In WordPress, not all capabilities are stored in the database. Capabilities can also be modified using filters like map_meta_cap. These new query arguments do NOT work for such capabilities.

The prime use case for capability queries is to get all "authors", i.e. users with the capability to edit a certain post type.

Until now, 'who' => 'authors' was used for this, which relies on user levels. However, user levels were deprecated a long time ago and thus never added to custom roles. This led to constant frustration due to users with custom roles missing from places like author dropdowns.

This updates any usage of 'who' => 'authors' in core to use capability queries instead.

Subsequently, 'who' => 'authors' queries are being deprecated in favor of these new query arguments.

Also adds a new capabilities parameter (mapping to capability__in in WP_User_Query) to the REST API users controller.

Also updates twentyfourteen_list_authors() in Twenty Fourteen to make use of this new functionality, adding a new twentyfourteen_list_authors_query_args filter to make it easier to override this behavior.

Props scribu, lgladdly, boonebgorges, spacedmonkey, peterwilsoncc, SergeyBiryukov, swissspidy.
Fixes #16841.

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-posts-list-table.php

    r51737 r51943  
    16611661                        $users_opt = array(
    16621662                            'hide_if_only_one_author' => false,
    1663                             'who'                     => 'authors',
     1663                            'capability'              => array( $post_type_object->cap->edit_posts ),
    16641664                            'name'                    => 'post_author',
    16651665                            'class'                   => 'authors',
  • trunk/src/wp-admin/includes/meta-boxes.php

    r51837 r51943  
    904904function post_author_meta_box( $post ) {
    905905    global $user_ID;
     906
     907    $post_type_object = get_post_type_object( $post->post_type );
    906908    ?>
    907909<label class="screen-reader-text" for="post_author_override"><?php _e( 'Author' ); ?></label>
     
    909911    wp_dropdown_users(
    910912        array(
    911             'who'              => 'authors',
     913            'capability'       => array( $post_type_object->cap->edit_posts ),
    912914            'name'             => 'post_author_override',
    913915            'selected'         => empty( $post->ID ) ? $user_ID : $post->post_author,
  • trunk/src/wp-content/themes/twentyfourteen/functions.php

    r51045 r51943  
    492492     */
    493493    function twentyfourteen_list_authors() {
    494         $contributor_ids = get_users(
    495             array(
    496                 'fields'  => 'ID',
    497                 'orderby' => 'post_count',
    498                 'order'   => 'DESC',
    499                 'who'     => 'authors',
    500             )
    501         );
     494        $args = array(
     495            'fields'     => 'ID',
     496            'orderby'    => 'post_count',
     497            'order'      => 'DESC',
     498            'capability' => array( 'edit_posts' ),
     499        );
     500
     501        /**
     502         * Filters query arguments for listing authors.
     503         *
     504         * @since 3.3
     505         *
     506         * @param array $args Query arguments.
     507         */
     508        $args = apply_filters( 'twentyfourteen_list_authors_query_args', $args );
     509
     510        $contributor_ids = get_users( $args );
    502511
    503512        foreach ( $contributor_ids as $contributor_id ) :
  • trunk/src/wp-includes/class-wp-user-query.php

    r49946 r51943  
    9494            'role__in'            => array(),
    9595            'role__not_in'        => array(),
     96            'capability'          => '',
     97            'capability__in'      => array(),
     98            'capability__not_in'  => array(),
    9699            'meta_key'            => '',
    97100            'meta_value'          => '',
     
    134137     * @since 4.7.0 Added 'nicename', 'nicename__in', 'nicename__not_in', 'login', 'login__in',
    135138     *              and 'login__not_in' parameters.
     139     * @since 5.9.0 Added 'capability', 'capability__in', and 'capability__not_in' parameters.
    136140     *
    137141     * @global wpdb $wpdb WordPress database abstraction object.
     
    149153     *     @type string[]     $role__not_in        An array of role names to exclude. Users matching one or more of these
    150154     *                                             roles will not be included in results. Default empty array.
     155     *     @type string       $capability          An array or a comma-separated list of capability names that users must match
     156     *                                             to be included in results. Note that this is an inclusive list: users
     157     *                                             must match *each* capability.
     158     *                                             Does NOT work for capabilities not in the database or filtered via {@see 'map_meta_cap'}.
     159     *                                             Default empty.
     160     *     @type string[]     $capability__in      An array of capability names. Matched users must have at least one of these
     161     *                                             capabilities.
     162     *                                             Does NOT work for capabilities not in the database or filtered via {@see 'map_meta_cap'}.
     163     *                                             Default empty array.
     164     *     @type string[]     $capability__not_in  An array of capability names to exclude. Users matching one or more of these
     165     *                                             capabilities will not be included in results.
     166     *                                             Does NOT work for capabilities not in the database or filtered via {@see 'map_meta_cap'}.
     167     *                                             Default empty array.
    151168     *     @type string       $meta_key            User meta key. Default empty.
    152169     *     @type string       $meta_value          User meta value. Default empty.
     
    321338
    322339        if ( isset( $qv['who'] ) && 'authors' === $qv['who'] && $blog_id ) {
     340            _deprecated_argument(
     341                'WP_User_Query',
     342                '5.9.0',
     343                sprintf(
     344                    /* translators: 1: who, 2: capability */
     345                    __( '%1$s is deprecated. Use %2$s instead.' ),
     346                    '<code>who</code>',
     347                    '<code>capability</code>'
     348                )
     349            );
     350
    323351            $who_query = array(
    324352                'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'user_level',
     
    344372        }
    345373
     374        // Roles.
    346375        $roles = array();
    347376        if ( isset( $qv['role'] ) ) {
     
    361390        if ( isset( $qv['role__not_in'] ) ) {
    362391            $role__not_in = (array) $qv['role__not_in'];
     392        }
     393
     394        // Capabilities.
     395        $available_roles = array();
     396
     397        if ( ! empty( $qv['capability'] ) || ! empty( $qv['capability__in'] ) || ! empty( $qv['capability__not_in'] ) ) {
     398            global $wp_roles;
     399
     400            $wp_roles->for_site( $blog_id );
     401            $available_roles = $wp_roles->roles;
     402        }
     403
     404        $capabilities = array();
     405        if ( ! empty( $qv['capability'] ) ) {
     406            if ( is_array( $qv['capability'] ) ) {
     407                $capabilities = $qv['capability'];
     408            } elseif ( is_string( $qv['capability'] ) ) {
     409                $capabilities = array_map( 'trim', explode( ',', $qv['capability'] ) );
     410            }
     411        }
     412
     413        $capability__in = array();
     414        if ( ! empty( $qv['capability__in'] ) ) {
     415            $capability__in = (array) $qv['capability__in'];
     416        }
     417
     418        $capability__not_in = array();
     419        if ( ! empty( $qv['capability__not_in'] ) ) {
     420            $capability__not_in = (array) $qv['capability__not_in'];
     421        }
     422
     423        // Keep track of all capabilities and the roles they're added on.
     424        $caps_with_roles = array();
     425
     426        foreach ( $available_roles as $role => $role_data ) {
     427            $role_caps = array_keys( array_filter( $role_data['capabilities'] ) );
     428
     429            foreach ( $capabilities as $cap ) {
     430                if ( in_array( $cap, $role_caps, true ) ) {
     431                    $caps_with_roles[ $cap ][] = $role;
     432                    break;
     433                }
     434            }
     435
     436            foreach ( $capability__in as $cap ) {
     437                if ( in_array( $cap, $role_caps, true ) ) {
     438                    $role__in[] = $role;
     439                    break;
     440                }
     441            }
     442
     443            foreach ( $capability__not_in as $cap ) {
     444                if ( in_array( $cap, $role_caps, true ) ) {
     445                    $role__not_in[] = $role;
     446                    break;
     447                }
     448            }
     449        }
     450
     451        $role__in     = array_merge( $role__in, $capability__in );
     452        $role__not_in = array_merge( $role__not_in, $capability__not_in );
     453
     454        $roles        = array_unique( $roles );
     455        $role__in     = array_unique( $role__in );
     456        $role__not_in = array_unique( $role__not_in );
     457
     458        // Support querying by capabilities added directly to users.
     459        if ( $blog_id && ! empty( $capabilities ) ) {
     460            $capabilities_clauses = array( 'relation' => 'AND' );
     461
     462            foreach ( $capabilities as $cap ) {
     463                $clause = array( 'relation' => 'OR' );
     464
     465                $clause[] = array(
     466                    'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
     467                    'value'   => '"' . $cap . '"',
     468                    'compare' => 'LIKE',
     469                );
     470
     471                if ( ! empty( $caps_with_roles[ $cap ] ) ) {
     472                    foreach ( $caps_with_roles[ $cap ] as $role ) {
     473                        $clause[] = array(
     474                            'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
     475                            'value'   => '"' . $role . '"',
     476                            'compare' => 'LIKE',
     477                        );
     478                    }
     479                }
     480
     481                $capabilities_clauses[] = $clause;
     482            }
     483
     484            $role_queries[] = $capabilities_clauses;
     485
     486            if ( empty( $this->meta_query->queries ) ) {
     487                $this->meta_query->queries[] = $capabilities_clauses;
     488            } else {
     489                // Append the cap query to the original queries and reparse the query.
     490                $this->meta_query->queries = array(
     491                    'relation' => 'AND',
     492                    array( $this->meta_query->queries, array( $capabilities_clauses ) ),
     493                );
     494            }
     495
     496            $this->meta_query->parse_query_vars( $this->meta_query->queries );
    363497        }
    364498
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php

    r51786 r51943  
    199199        }
    200200
     201        // Check if capabilities is specified in GET request and if user can list users.
     202        if ( ! empty( $request['capabilities'] ) && ! current_user_can( 'list_users' ) ) {
     203            return new WP_Error(
     204                'rest_user_cannot_view',
     205                __( 'Sorry, you are not allowed to filter users by capability.' ),
     206                array( 'status' => rest_authorization_required_code() )
     207            );
     208        }
     209
    201210        if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
    202211            return new WP_Error(
     
    255264         */
    256265        $parameter_mappings = array(
    257             'exclude'  => 'exclude',
    258             'include'  => 'include',
    259             'order'    => 'order',
    260             'per_page' => 'number',
    261             'search'   => 'search',
    262             'roles'    => 'role__in',
    263             'slug'     => 'nicename__in',
     266            'exclude'      => 'exclude',
     267            'include'      => 'include',
     268            'order'        => 'order',
     269            'per_page'     => 'number',
     270            'search'       => 'search',
     271            'roles'        => 'role__in',
     272            'capabilities' => 'capability__in',
     273            'slug'         => 'nicename__in',
    264274        );
    265275
     
    15551565        );
    15561566
     1567        $query_params['capabilities'] = array(
     1568            'description' => __( 'Limit result set to users matching at least one specific capability provided. Accepts csv list or single capability.' ),
     1569            'type'        => 'array',
     1570            'items'       => array(
     1571                'type' => 'string',
     1572            ),
     1573        );
     1574
    15571575        $query_params['who'] = array(
    15581576            'description' => __( 'Limit result set to users who are considered authors.' ),
  • trunk/src/wp-includes/user.php

    r51738 r51943  
    13211321        'role__in'                => array(),
    13221322        'role__not_in'            => array(),
     1323        'capability'              => '',
     1324        'capability__in'          => array(),
     1325        'capability__not_in'      => array(),
    13231326    );
    13241327
     
    13271330    $parsed_args = wp_parse_args( $args, $defaults );
    13281331
    1329     $query_args = wp_array_slice_assoc( $parsed_args, array( 'blog_id', 'include', 'exclude', 'orderby', 'order', 'who', 'role', 'role__in', 'role__not_in' ) );
     1332    $query_args = wp_array_slice_assoc(
     1333        $parsed_args,
     1334        array(
     1335            'blog_id',
     1336            'include',
     1337            'exclude',
     1338            'orderby',
     1339            'order',
     1340            'who',
     1341            'role',
     1342            'role__in',
     1343            'role__not_in',
     1344            'capability',
     1345            'capability__in',
     1346            'capability__not_in',
     1347        )
     1348    );
    13301349
    13311350    $fields = array( 'ID', 'user_login' );
  • trunk/tests/phpunit/tests/rest-api/rest-users-controller.php

    r51568 r51943  
    1616    protected static $draft_editor;
    1717    protected static $subscriber;
     18    protected static $author;
    1819
    1920    protected static $authors     = array();
     
    5455                'display_name' => 'subscriber',
    5556                'user_email'   => 'subscriber@example.com',
     57            )
     58        );
     59        self::$author       = $factory->user->create(
     60            array(
     61                'display_name' => 'author',
     62                'role'         => 'author',
     63                'user_email'   => 'author@example.com',
    5664            )
    5765        );
     
    108116
    109117        // Set up users for pagination tests.
    110         for ( $i = 0; $i < self::$total_users - 10; $i++ ) {
     118        for ( $i = 0; $i < self::$total_users - 11; $i++ ) {
    111119            self::$user_ids[] = $factory->user->create(
    112120                array(
     
    122130        self::delete_user( self::$editor );
    123131        self::delete_user( self::$draft_editor );
     132        self::delete_user( self::$author );
    124133
    125134        foreach ( self::$posts as $post ) {
     
    184193        $data     = $response->get_data();
    185194        $keys     = array_keys( $data['endpoints'][0]['args'] );
    186         sort( $keys );
    187         $this->assertSame(
     195        $this->assertEqualSets(
    188196            array(
    189197                'context',
     
    196204                'per_page',
    197205                'roles',
     206                'capabilities',
    198207                'search',
    199208                'slug',
     
    796805        wp_set_current_user( self::$user );
    797806
    798         $tango = $this->factory->user->create(
    799             array(
    800                 'display_name' => 'tango',
    801                 'role'         => 'subscriber',
    802             )
    803         );
    804         $yolo  = $this->factory->user->create(
    805             array(
    806                 'display_name' => 'yolo',
    807                 'role'         => 'author',
    808             )
    809         );
    810 
    811807        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
    812808        $request->set_param( 'roles', 'author,subscriber' );
    813809        $response = rest_get_server()->dispatch( $request );
    814810        $data     = $response->get_data();
    815         $this->assertCount( 3, $data );
    816         $this->assertSame( $tango, $data[1]['id'] );
    817         $this->assertSame( $yolo, $data[2]['id'] );
     811        $this->assertCount( 2, $data );
     812        $this->assertSame( self::$author, $data[0]['id'] );
     813        $this->assertSame( self::$subscriber, $data[1]['id'] );
    818814
    819815        $request->set_param( 'roles', 'author' );
     
    821817        $data     = $response->get_data();
    822818        $this->assertCount( 1, $data );
    823         $this->assertSame( $yolo, $data[0]['id'] );
     819        $this->assertSame( self::$author, $data[0]['id'] );
    824820
    825821        wp_set_current_user( 0 );
     
    839835        wp_set_current_user( self::$user );
    840836
    841         $lolz = $this->factory->user->create(
    842             array(
    843                 'display_name' => 'lolz',
    844                 'role'         => 'author',
    845             )
    846         );
    847 
    848837        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
    849838        $request->set_param( 'roles', 'ilovesteak,author' );
     
    851840        $data     = $response->get_data();
    852841        $this->assertCount( 1, $data );
    853         $this->assertSame( $lolz, $data[0]['id'] );
     842        $this->assertSame( self::$author, $data[0]['id'] );
    854843
    855844        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     
    857846        $response = rest_get_server()->dispatch( $request );
    858847        $data     = $response->get_data();
    859         $this->assertCount( 0, $data );
    860         $this->assertSame( array(), $data );
    861     }
    862 
     848        $this->assertIsArray( $data );
     849        $this->assertEmpty( $data );
     850    }
     851
     852    /**
     853     * @ticket 16841
     854     */
     855    public function test_get_items_capabilities() {
     856        wp_set_current_user( self::$user );
     857
     858        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     859        $request->set_param( 'capabilities', 'edit_posts' );
     860        $response = rest_get_server()->dispatch( $request );
     861        $data     = $response->get_data();
     862
     863        $this->assertNotEmpty( $data );
     864        foreach ( $data as $user ) {
     865            $this->assertTrue( user_can( $user['id'], 'edit_posts' ) );
     866        }
     867    }
     868
     869    /**
     870     * @ticket 16841
     871     */
     872    public function test_get_items_capabilities_no_permission_no_user() {
     873        wp_set_current_user( 0 );
     874
     875        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     876        $request->set_param( 'capabilities', 'edit_posts' );
     877        $response = rest_get_server()->dispatch( $request );
     878        $this->assertErrorResponse( 'rest_user_cannot_view', $response, 401 );
     879    }
     880
     881    /**
     882     * @ticket 16841
     883     */
     884    public function test_get_items_capabilities_no_permission_editor() {
     885        wp_set_current_user( self::$editor );
     886
     887        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     888        $request->set_param( 'capabilities', 'edit_posts' );
     889        $response = rest_get_server()->dispatch( $request );
     890        $this->assertErrorResponse( 'rest_user_cannot_view', $response, 403 );
     891    }
     892
     893    /**
     894     * @ticket 16841
     895     */
     896    public function test_get_items_invalid_capabilities() {
     897        wp_set_current_user( self::$user );
     898
     899        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     900        $request->set_param( 'roles', 'ilovesteak,author' );
     901        $response = rest_get_server()->dispatch( $request );
     902        $data     = $response->get_data();
     903        $this->assertCount( 1, $data );
     904        $this->assertSame( self::$author, $data[0]['id'] );
     905
     906        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     907        $request->set_param( 'capabilities', 'steakisgood' );
     908        $response = rest_get_server()->dispatch( $request );
     909        $data     = $response->get_data();
     910        $this->assertIsArray( $data );
     911        $this->assertEmpty( $data );
     912    }
     913
     914    /**
     915     * @expectedDeprecated WP_User_Query
     916     */
    863917    public function test_get_items_who_author_query() {
    864918        wp_set_current_user( self::$superadmin );
  • trunk/tests/phpunit/tests/user/query.php

    r51462 r51943  
    731731     * @ticket 32019
    732732     * @group ms-required
     733     * @expectedDeprecated WP_User_Query
    733734     */
    734735    public function test_who_authors() {
     
    756757     * @ticket 32019
    757758     * @group ms-required
     759     * @expectedDeprecated WP_User_Query
    758760     */
    759761    public function test_who_authors_should_work_alongside_meta_query() {
     
    790792     * @ticket 36724
    791793     * @group ms-required
     794     * @expectedDeprecated WP_User_Query
    792795     */
    793796    public function test_who_authors_should_work_alongside_meta_params() {
     
    17261729        return array( 555 );
    17271730    }
     1731
     1732    /**
     1733     * @ticket 16841
     1734     * @group ms-excluded
     1735     */
     1736    public function test_get_single_capability_by_string() {
     1737        $wp_user_search = new WP_User_Query( array( 'capability' => 'install_plugins' ) );
     1738        $users          = $wp_user_search->get_results();
     1739
     1740        $this->assertNotEmpty( $users );
     1741        foreach ( $users as $user ) {
     1742            // User has the capability, but on Multisite they would also need to be a super admin.
     1743            // Hence using get_role_caps() instead of has_cap().
     1744            $role_caps = $user->get_role_caps();
     1745            $this->assertArrayHasKey( 'install_plugins', $role_caps );
     1746            $this->assertTrue( $role_caps['install_plugins'] );
     1747        }
     1748    }
     1749
     1750    /**
     1751     * @ticket 16841
     1752     * @group ms-required
     1753     */
     1754    public function test_get_single_capability_by_string_multisite() {
     1755        $wp_user_search = new WP_User_Query( array( 'capability' => array( 'install_plugins' ) ) );
     1756        $users          = $wp_user_search->get_results();
     1757
     1758        $this->assertNotEmpty( $users );
     1759        foreach ( $users as $user ) {
     1760            $role_caps = $user->get_role_caps();
     1761            $this->assertArrayHasKey( 'install_plugins', $role_caps );
     1762            $this->assertTrue( $role_caps['install_plugins'] );
     1763            // While the user can have the capability, on Multisite they also need to be a super admin.
     1764            if ( is_super_admin( $user->ID ) ) {
     1765                $this->assertTrue( $user->has_cap( 'install_plugins' ) );
     1766            } else {
     1767                $this->assertFalse( $user->has_cap( 'install_plugins' ) );
     1768            }
     1769        }
     1770    }
     1771
     1772    /**
     1773     * @ticket 16841
     1774     */
     1775    public function test_get_single_capability_invalid() {
     1776        $wp_user_search = new WP_User_Query( array( 'capability' => 'foo_bar' ) );
     1777        $users          = $wp_user_search->get_results();
     1778
     1779        $this->assertEmpty( $users );
     1780    }
     1781
     1782    /**
     1783     * @ticket 16841
     1784     */
     1785    public function test_get_single_capability_by_array() {
     1786        $wp_user_search = new WP_User_Query( array( 'capability' => array( 'install_plugins' ) ) );
     1787        $users          = $wp_user_search->get_results();
     1788
     1789        $this->assertNotEmpty( $users );
     1790        foreach ( $users as $user ) {
     1791            // User has the capability, but on Multisite they would also need to be a super admin.
     1792            // Hence using get_role_caps() instead of has_cap().
     1793            $role_caps = $user->get_role_caps();
     1794            $this->assertArrayHasKey( 'install_plugins', $role_caps );
     1795            $this->assertTrue( $role_caps['install_plugins'] );
     1796        }
     1797    }
     1798
     1799    /**
     1800     * @ticket 16841
     1801     */
     1802    public function test_get_single_capability_added_to_user() {
     1803        foreach ( self::$sub_ids as $subscriber ) {
     1804            $subscriber = get_user_by( 'ID', $subscriber );
     1805            $subscriber->add_cap( 'custom_cap' );
     1806        }
     1807
     1808        $wp_user_search = new WP_User_Query( array( 'capability' => 'custom_cap' ) );
     1809        $users          = $wp_user_search->get_results();
     1810
     1811        $this->assertCount( 2, $users );
     1812        $this->assertEqualSets( self::$sub_ids, wp_list_pluck( $users, 'ID' ) );
     1813
     1814        foreach ( $users as $user ) {
     1815            $this->assertTrue( $user->has_cap( 'custom_cap' ) );
     1816        }
     1817    }
     1818
     1819    /**
     1820     * @ticket 16841
     1821     */
     1822    public function test_get_multiple_capabilities_should_only_match_users_who_have_each_capability_test() {
     1823        wp_roles()->add_role( 'role_1', 'Role 1', array( 'role_1_cap' => true ) );
     1824        wp_roles()->add_role( 'role_2', 'Role 2', array( 'role_2_cap' => true ) );
     1825
     1826        $subscriber1 = get_user_by( 'ID', self::$sub_ids[0] );
     1827        $subscriber1->add_role( 'role_1' );
     1828
     1829        $subscriber2 = get_user_by( 'ID', self::$sub_ids[1] );
     1830        $subscriber2->add_role( 'role_1' );
     1831        $subscriber2->add_role( 'role_2' );
     1832
     1833        $wp_user_search = new WP_User_Query( array( 'capability' => array( 'role_1_cap', 'role_2_cap' ) ) );
     1834        $users          = $wp_user_search->get_results();
     1835
     1836        $this->assertCount( 1, $users );
     1837        $this->assertSame( $users[0]->ID, $subscriber2->ID );
     1838        foreach ( $users as $user ) {
     1839            $this->assertTrue( $user->has_cap( 'role_1_cap' ) );
     1840            $this->assertTrue( $user->has_cap( 'role_2_cap' ) );
     1841        }
     1842    }
     1843
     1844    /**
     1845     * @ticket 16841
     1846     */
     1847    public function test_get_multiple_capabilities_should_only_match_users_who_have_each_capability_added_to_user() {
     1848        $admin1 = get_user_by( 'ID', self::$admin_ids[0] );
     1849        $admin1->add_cap( 'custom_cap' );
     1850
     1851        $wp_user_search = new WP_User_Query( array( 'capability' => array( 'manage_options', 'custom_cap' ) ) );
     1852        $users          = $wp_user_search->get_results();
     1853
     1854        $this->assertCount( 1, $users );
     1855        $this->assertSame( $users[0]->ID, $admin1->ID );
     1856        $this->assertTrue( $users[0]->has_cap( 'custom_cap' ) );
     1857        $this->assertTrue( $users[0]->has_cap( 'manage_options' ) );
     1858    }
     1859
     1860    /**
     1861     * @ticket 16841
     1862     */
     1863    public function test_get_multiple_capabilities_or() {
     1864        $wp_user_search = new WP_User_Query( array( 'capability__in' => array( 'publish_posts', 'edit_posts' ) ) );
     1865        $users          = $wp_user_search->get_results();
     1866
     1867        $this->assertNotEmpty( $users );
     1868        foreach ( $users as $user ) {
     1869            $this->assertTrue( $user->has_cap( 'publish_posts' ) || $user->has_cap( 'edit_posts' ) );
     1870        }
     1871    }
     1872
     1873    /**
     1874     * @ticket 16841
     1875     */
     1876    public function test_get_multiple_capabilities_or_added_to_user() {
     1877        $user = self::factory()->user->create_and_get( array( 'role' => 'subscriber' ) );
     1878        $user->add_cap( 'custom_cap' );
     1879
     1880        $wp_user_search = new WP_User_Query( array( 'capability__in' => array( 'publish_posts', 'custom_cap' ) ) );
     1881        $users          = $wp_user_search->get_results();
     1882
     1883        $this->assertNotEmpty( $users );
     1884        foreach ( $users as $user ) {
     1885            $this->assertTrue( $user->has_cap( 'publish_posts' ) || $user->has_cap( 'custom_cap' ) );
     1886        }
     1887    }
     1888
     1889    /**
     1890     * @ticket 16841
     1891     */
     1892    public function test_capability_exclusion() {
     1893        $wp_user_search = new WP_User_Query( array( 'capability__not_in' => array( 'publish_posts', 'edit_posts' ) ) );
     1894        $users          = $wp_user_search->get_results();
     1895
     1896        $this->assertNotEmpty( $users );
     1897        foreach ( $users as $user ) {
     1898            $this->assertFalse( $user->has_cap( 'publish_posts' ) );
     1899            $this->assertFalse( $user->has_cap( 'edit_posts' ) );
     1900        }
     1901    }
     1902
     1903    /**
     1904     * @ticket 16841
     1905     */
     1906    public function test_capability_exclusion_added_to_user() {
     1907        $user = self::factory()->user->create_and_get( array( 'role' => 'subscriber' ) );
     1908        $user->add_cap( 'custom_cap' );
     1909
     1910        $wp_user_search = new WP_User_Query( array( 'capability__not_in' => array( 'publish_posts', 'custom_cap' ) ) );
     1911        $users          = $wp_user_search->get_results();
     1912
     1913        $this->assertNotEmpty( $users );
     1914        foreach ( $users as $user ) {
     1915            $this->assertFalse( $user->has_cap( 'publish_posts' ) );
     1916            $this->assertFalse( $user->has_cap( 'custom_cap' ) );
     1917        }
     1918    }
     1919
     1920    /**
     1921     * @ticket 16841
     1922     */
     1923    public function test_capability__in_capability__not_in_combined() {
     1924        $wp_user_search = new WP_User_Query(
     1925            array(
     1926                'capability__in'     => array( 'read' ),
     1927                'capability__not_in' => array( 'manage_options' ),
     1928            )
     1929        );
     1930        $users          = $wp_user_search->get_results();
     1931
     1932        $this->assertNotEmpty( $users );
     1933        foreach ( $users as $user ) {
     1934            $this->assertTrue( $user->has_cap( 'read' ) );
     1935            $this->assertFalse( $user->has_cap( 'manage_options' ) );
     1936        }
     1937    }
     1938
     1939    /**
     1940     * @ticket 16841
     1941     * @group ms-required
     1942     */
     1943    public function test_get_single_capability_multisite_blog_id() {
     1944        $blog_id = self::factory()->blog->create();
     1945
     1946        add_user_to_blog( $blog_id, self::$author_ids[0], 'subscriber' );
     1947        add_user_to_blog( $blog_id, self::$author_ids[1], 'author' );
     1948        add_user_to_blog( $blog_id, self::$author_ids[2], 'editor' );
     1949
     1950        $wp_user_search = new WP_User_Query(
     1951            array(
     1952                'capability' => 'publish_posts',
     1953                'blog_id'    => $blog_id,
     1954            )
     1955        );
     1956        $users          = $wp_user_search->get_results();
     1957
     1958        $found = wp_list_pluck( $wp_user_search->get_results(), 'ID' );
     1959
     1960        $this->assertNotEmpty( $users );
     1961        foreach ( $users as $user ) {
     1962            $this->assertTrue( $user->has_cap( 'publish_posts' ) );
     1963        }
     1964
     1965        $this->assertNotContains( self::$author_ids[0], $found );
     1966        $this->assertContains( self::$author_ids[1], $found );
     1967        $this->assertContains( self::$author_ids[2], $found );
     1968    }
    17281969}
  • trunk/tests/phpunit/tests/xmlrpc/wp/getUsers.php

    r51367 r51943  
    5555    }
    5656
     57    /**
     58     * @expectedDeprecated WP_User_Query
     59     */
    5760    function test_role_filter() {
    5861        $author_id        = $this->make_user_by_role( 'author' );
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r51463 r51943  
    53585358                            "required": false
    53595359                        },
     5360                        "capabilities": {
     5361                            "description": "Limit result set to users matching at least one specific capability provided. Accepts csv list or single capability.",
     5362                            "type": "array",
     5363                            "items": {
     5364                                "type": "string"
     5365                            },
     5366                            "required": false
     5367                        },
    53605368                        "who": {
    53615369                            "description": "Limit result set to users who are considered authors.",
Note: See TracChangeset for help on using the changeset viewer.