Make WordPress Core

Changeset 34959


Ignore:
Timestamp:
10/08/2015 09:27:04 PM (11 years ago)
Author:
boonebgorges
Message:

WP_User_Query role improvement redux.

It's back, and it's better than ever: an overhaul of role-related arguments
in WP_User_Query. This updated version of the previously-reverted [34875]
includes support for the use of $blog_id without specifying a $role, for
a 99.7% reduced chance of breaking wordpress.org and other large sites.

Props boonebgorges, swissspidy.
Fixes #22212.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-user-query.php

    r34956 r34959  
    4949         * @since 4.2.0
    5050         * @access public
    51          * @var object WP_Meta_Query
     51         * @var WP_Meta_Query
    5252         */
    5353        public $meta_query = false;
    … …  
    9898                        'blog_id' => $GLOBALS['blog_id'],
    9999                        'role' => '',
     100                        'role__in' => array(),
     101                        'role__not_in' => array(),
    100102                        'meta_key' => '',
    101103                        'meta_value' => '',
    … …  
    127129         *              for `$orderby` parameter.
    128130         * @since 4.3.0 Added 'has_published_posts' parameter.
    129          * @since 4.4.0 Added 'paged' parameter.
     131         * @since 4.4.0 Added 'paged', 'role__in', and 'role__not_in' parameters. 'role' parameter was updated to
     132         *              permit an array or comma-separated list of values.
    130133         * @access public
    131134         *
    … …  
    137140         *
    138141         *     @type int          $blog_id             The site ID. Default is the global blog id.
    139          *     @type string       $role                Role name. Default empty.
     142         *     @type string|array $role                An array or a comma-separated list of role names that users must match
     143         *                                             to be included in results. Note that this is an inclusive list: users
     144         *                                             must match *each* role. Default empty.
     145         *     @type array        $role__in            An array of role names. Matched users must have at least one of these
     146         *                                             roles. Default empty array.
     147         *     @type array        $role__not_in        An array of role names to exclude. Users matching one or more of these
     148         *                                             roles will not be included in results. Default empty array.
    140149         *     @type string       $meta_key            User meta key. Default empty.
    141150         *     @type string       $meta_value          User meta value. Default empty.
    … …  
    269278                $this->meta_query->parse_query_vars( $qv );
    270279
    271                 $role = '';
     280                $roles = array();
    272281                if ( isset( $qv['role'] ) ) {
    273                         $role = trim( $qv['role'] );
    274                 }
    275 
    276                 if ( $blog_id && ( $role || is_multisite() ) ) {
    277                         $cap_meta_query = array();
    278                         $cap_meta_query['key'] = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities';
    279 
    280                         if ( $role ) {
    281                                 $cap_meta_query['value'] = '"' . $role . '"';
    282                                 $cap_meta_query['compare'] = 'like';
    283                         }
     282                        if ( is_array( $qv['role'] ) ) {
     283                                $roles = $qv['role'];
     284                        } elseif ( is_string( $qv['role'] ) && ! empty( $qv['role'] ) ) {
     285                                $roles = array_map( 'trim', explode( ',', $qv['role'] ) );
     286                        }
     287                }
     288
     289                $role__in = array();
     290                if ( isset( $qv['role__in'] ) ) {
     291                        $role__in = (array) $qv['role__in'];
     292                }
     293
     294                $role__not_in = array();
     295                if ( isset( $qv['role__not_in'] ) ) {
     296                        $role__not_in = (array) $qv['role__not_in'];
     297                }
     298
     299                if ( $blog_id && ( ! empty( $roles ) || ! empty( $role__in ) || ! empty( $role__not_in ) || is_multisite() ) ) {
     300                        $role_queries  = array();
     301
     302                        $roles_clauses = array( 'relation' => 'AND' );
     303                        if ( ! empty( $roles ) ) {
     304                                foreach ( $roles as $role ) {
     305                                        $roles_clauses[] = array(
     306                                                'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
     307                                                'value'   => $role,
     308                                                'compare' => 'LIKE',
     309                                        );
     310                                }
     311
     312                                $role_queries[] = $roles_clauses;
     313                        }
     314
     315                        $role__in_clauses = array( 'relation' => 'OR' );
     316                        if ( ! empty( $role__in ) ) {
     317                                foreach ( $role__in as $role ) {
     318                                        $role__in_clauses[] = array(
     319                                                'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
     320                                                'value'   => $role,
     321                                                'compare' => 'LIKE',
     322                                        );
     323                                }
     324
     325                                $role_queries[] = $role__in_clauses;
     326                        }
     327
     328                        $role__not_in_clauses = array( 'relation' => 'AND' );
     329                        if ( ! empty( $role__not_in ) ) {
     330                                foreach ( $role__not_in as $role ) {
     331                                        $role__not_in_clauses[] = array(
     332                                                'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
     333                                                'value'   => $role,
     334                                                'compare' => 'NOT LIKE',
     335                                        );
     336                                }
     337
     338                                $role_queries[] = $role__not_in_clauses;
     339                        }
     340
     341                        // If there are no specific roles named, make sure the user is a member of the site.
     342                        if ( empty( $role_queries ) ) {
     343                                $role_queries[] = array(
     344                                        'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
     345                                        'compare' => 'EXISTS',
     346                                );
     347                        }
     348
     349                        // Specify that role queries should be joined with AND.
     350                        $role_queries['relation'] = 'AND';
    284351
    285352                        if ( empty( $this->meta_query->queries ) ) {
    286                                 $this->meta_query->queries = array( $cap_meta_query );
    287                         } elseif ( ! in_array( $cap_meta_query, $this->meta_query->queries, true ) ) {
     353                                $this->meta_query->queries = $role_queries;
     354                        } else {
    288355                                // Append the cap query to the original queries and reparse the query.
    289356                                $this->meta_query->queries = array(
    290357                                        'relation' => 'AND',
    291                                         array( $this->meta_query->queries, $cap_meta_query ),
     358                                        array( $this->meta_query->queries, $role_queries ),
    292359                                );
    293360                        }
  • trunk/tests/phpunit/tests/user/query.php

    r34880 r34959  
    897897                }
    898898        }
     899
     900        /**
     901         * @ticket 22212
     902         */
     903        public function test_get_single_role_by_user_query() {
     904                $this->factory->user->create_many( 2, array(
     905                        'role' => 'subscriber',
     906                ) );
     907
     908                $this->factory->user->create( array(
     909                        'role' => 'contributor',
     910                ) );
     911
     912                $wp_user_search = new WP_User_Query( array( 'role' => 'subscriber' ) );
     913                $users          = $wp_user_search->get_results();
     914
     915                $this->assertEquals( 2, count( $users ) );
     916        }
     917
     918        /**
     919         * @ticket 22212
     920         */
     921        public function test_get_multiple_roles_by_user_query() {
     922                $this->factory->user->create_many( 2, array(
     923                        'role' => 'subscriber',
     924                ) );
     925
     926                $this->factory->user->create_many( 3, array(
     927                        'role' => 'editor',
     928                ) );
     929
     930                $this->factory->user->create( array(
     931                        'role' => 'contributor',
     932                ) );
     933
     934                $wp_user_search = new WP_User_Query( array( 'role__in' => array( 'subscriber', 'editor' ) ) );
     935                $users          = $wp_user_search->get_results();
     936                $this->assertEquals( 5, count( $users ) );
     937        }
     938
     939        /**
     940         * @ticket 22212
     941         */
     942        public function test_get_single_role_by_string() {
     943                $this->factory->user->create_many( 2, array(
     944                        'role' => 'subscriber',
     945                ) );
     946
     947                $this->factory->user->create( array(
     948                        'role' => 'contributor',
     949                ) );
     950
     951                $users = get_users( array(
     952                        'role' => 'subscriber',
     953                ) );
     954
     955                $this->assertEquals( 2, count( $users ) );
     956        }
     957
     958        /**
     959         * @ticket 22212
     960         */
     961        public function test_get_single_role_by_array() {
     962                $this->factory->user->create_many( 2, array(
     963                        'role' => 'subscriber',
     964                ) );
     965
     966                $this->factory->user->create( array(
     967                        'role' => 'contributor',
     968                ) );
     969
     970                $users = get_users( array(
     971                        'role' => array( 'subscriber' ),
     972                ) );
     973
     974                $this->assertEquals( 2, count( $users ) );
     975        }
     976
     977        /**
     978         * @ticket 22212
     979         */
     980        public function test_get_multiple_roles_should_only_match_users_who_have_each_role() {
     981                $subscribers = $this->factory->user->create_many( 2, array(
     982                        'role' => 'subscriber',
     983                ) );
     984
     985                $this->factory->user->create_many( 3, array(
     986                        'role' => 'editor',
     987                ) );
     988
     989                $this->factory->user->create_many( 2, array(
     990                        'role' => 'administrator',
     991                ) );
     992
     993                $users = new WP_User_Query( array( 'role' => array( 'subscriber', 'editor' ) ) );
     994                $users = $users->get_results();
     995
     996                $this->assertEmpty( $users );
     997
     998                foreach ( $subscribers as $subscriber ) {
     999                        $subscriber = get_user_by( 'ID', $subscriber );
     1000                        $subscriber->add_role( 'editor' );
     1001                }
     1002
     1003                $users = new WP_User_Query( array( 'role' => array( 'subscriber', 'editor' ) ) );
     1004                $users = $users->get_results();
     1005
     1006                $this->assertEquals( 2, count( $users ) );
     1007
     1008                foreach ( $users as $user ) {
     1009                        $this->assertInstanceOf( 'WP_User', $user );
     1010                }
     1011        }
     1012
     1013        /**
     1014         * @ticket 22212
     1015         */
     1016        public function test_get_multiple_roles_or() {
     1017                $this->factory->user->create_many( 2, array(
     1018                        'role' => 'subscriber',
     1019                ) );
     1020
     1021                $this->factory->user->create_many( 3, array(
     1022                        'role' => 'editor',
     1023                ) );
     1024
     1025                $this->factory->user->create_many( 2, array(
     1026                        'role' => 'administrator',
     1027                ) );
     1028
     1029                $this->factory->user->create_many( 1, array(
     1030                        'role' => 'contributor',
     1031                ) );
     1032
     1033                $users = new WP_User_Query( array( 'role__in' => array( 'subscriber', 'editor', 'administrator' ) ) );
     1034                $users = $users->get_results();
     1035
     1036                // +1 for the default user created during installation.
     1037                $this->assertEquals( 8, count( $users ) );
     1038                foreach ( $users as $user ) {
     1039                        $this->assertInstanceOf( 'WP_User', $user );
     1040                }
     1041        }
     1042
     1043        /**
     1044         * @ticket 22212
     1045         */
     1046        public function test_get_multiple_roles_by_comma_separated_list() {
     1047                $subscribers = $this->factory->user->create_many( 2, array(
     1048                        'role' => 'subscriber',
     1049                ) );
     1050
     1051                $this->factory->user->create_many( 3, array(
     1052                        'role' => 'editor',
     1053                ) );
     1054
     1055                $users = get_users( array(
     1056                        'role' => 'subscriber, editor',
     1057                ) );
     1058
     1059                $this->assertEmpty( $users );
     1060
     1061                foreach ( $subscribers as $subscriber ) {
     1062                        $subscriber = get_user_by( 'ID', $subscriber );
     1063                        $subscriber->add_role( 'editor' );
     1064                }
     1065
     1066                $users = get_users( array(
     1067                        'role' => 'subscriber, editor',
     1068                ) );
     1069
     1070                $this->assertEquals( 2, count( $users ) );
     1071        }
     1072
     1073        /**
     1074         * @ticket 22212
     1075         */
     1076        public function test_get_multiple_roles_with_meta() {
     1077                // Create administrator user + meta
     1078                $administrator_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
     1079                update_user_meta( $administrator_id, 'mk1', 1 );
     1080                update_user_meta( $administrator_id, 'mk2', 1 );
     1081
     1082                // Create editor user + meta
     1083                $editor_id = $this->factory->user->create( array( 'role' => 'editor' ) );
     1084                update_user_meta( $editor_id, 'mk1', 1 );
     1085                update_user_meta( $editor_id, 'mk2', 2 );
     1086
     1087                // Create subscriber user + meta
     1088                $subscriber_id = $this->factory->user->create( array( 'role' => 'subscriber' ) );
     1089                update_user_meta( $subscriber_id, 'mk1', 1 );
     1090                update_user_meta( $subscriber_id, 'mk2', 1 );
     1091
     1092                // Create contributor user + meta
     1093                $contributor_id = $this->factory->user->create( array( 'role' => 'contributor' ) );
     1094                update_user_meta( $contributor_id, 'mk1', 1 );
     1095                update_user_meta( $contributor_id, 'mk2', 2 );
     1096
     1097                // Fetch users
     1098                $users = get_users( array(
     1099                        'role__in'   => array( 'administrator', 'editor', 'subscriber' ),
     1100                        'meta_query' => array(
     1101                                'relation' => 'AND',
     1102                                array(
     1103                                        'key'     => 'mk1',
     1104                                        'value'   => '1',
     1105                                        'compare' => "=",
     1106                                        'type'    => 'numeric',
     1107                                ),
     1108                                array(
     1109                                        'key'     => 'mk2',
     1110                                        'value'   => '2',
     1111                                        'compare' => "=",
     1112                                        'type'    => 'numeric',
     1113                                ),
     1114                        ),
     1115                ) );
     1116
     1117                // Check results
     1118                $this->assertEquals( 1, count( $users ) );
     1119                $this->assertSame( $editor_id, (int) $users[0]->ID );
     1120        }
     1121
     1122        /**
     1123         * @ticket 22212
     1124         */
     1125        public function test_role_exclusion() {
     1126                $this->factory->user->create_many( 2, array(
     1127                        'role' => 'subscriber',
     1128                ) );
     1129
     1130                $this->factory->user->create_many( 3, array(
     1131                        'role' => 'editor',
     1132                ) );
     1133
     1134                $users = get_users( array(
     1135                        'role__not_in' => 'subscriber',
     1136                ) );
     1137
     1138                // +1 for the default user created during installation.
     1139                $this->assertEquals( 4, count( $users ) );
     1140
     1141                $users = get_users( array(
     1142                        'role__not_in' => 'editor',
     1143                ) );
     1144
     1145                // +1 for the default user created during installation.
     1146                $this->assertEquals( 3, count( $users ) );
     1147        }
     1148
     1149        /**
     1150         * @ticket 22212
     1151         */
     1152        public function test_role__in_role__not_in_combined() {
     1153                $subscribers = $this->factory->user->create_many( 2, array(
     1154                        'role' => 'subscriber',
     1155                ) );
     1156
     1157                $this->factory->user->create_many( 3, array(
     1158                        'role' => 'editor',
     1159                ) );
     1160
     1161                foreach ( $subscribers as $subscriber ) {
     1162                        $subscriber = get_user_by( 'ID', $subscriber );
     1163                        $subscriber->add_role( 'editor' );
     1164                }
     1165
     1166                $users = get_users( array(
     1167                        'role__in'     => 'editor',
     1168                ) );
     1169
     1170                $this->assertEquals( 5, count( $users ) );
     1171
     1172                $users = get_users( array(
     1173                        'role__in'     => 'editor',
     1174                        'role__not_in' => 'subscriber',
     1175                ) );
     1176
     1177                $this->assertEquals( 3, count( $users ) );
     1178        }
     1179
     1180        /**
     1181         * @ticket 22212
     1182         */
     1183        public function test_role__not_in_role_combined() {
     1184                $subscribers = $this->factory->user->create_many( 2, array(
     1185                        'role' => 'subscriber',
     1186                ) );
     1187
     1188                $this->factory->user->create_many( 3, array(
     1189                        'role' => 'editor',
     1190                ) );
     1191
     1192                $subscriber = get_user_by( 'ID', $subscribers[0] );
     1193                $subscriber->add_role( 'editor' );
     1194
     1195                $users = get_users( array(
     1196                        'role'         => 'subscriber',
     1197                        'role__not_in' => array( 'editor' ),
     1198                ) );
     1199
     1200                $this->assertEquals( 1, count( $users ) );
     1201        }
     1202
     1203        /**
     1204         * @ticket 22212
     1205         */
     1206        public function test_role__not_in_user_without_role() {
     1207                $user_without_rule = $this->factory->user->get_object_by_id( $this->factory->user->create( array(
     1208                        'role' => 'subscriber',
     1209                ) ) );
     1210
     1211                $user_without_rule->remove_role( 'subscriber' );
     1212
     1213                $this->factory->user->create_many( 3, array(
     1214                        'role' => 'editor',
     1215                ) );
     1216
     1217                $users = get_users( array(
     1218                        'role__not_in' => 'subscriber',
     1219                ) );
     1220
     1221                // +1 for the default user created during installation.
     1222                $this->assertEquals( 5, count( $users ) );
     1223
     1224                $users = get_users( array(
     1225                        'role__not_in' => 'editor',
     1226                ) );
     1227
     1228                // +1 for the default user created during installation.
     1229                $this->assertEquals( 2, count( $users ) );
     1230        }
     1231
     1232        /**
     1233         * @ticket 22212
     1234         */
     1235        public function test_blog_id_should_restrict_by_blog_without_requiring_a_named_role() {
     1236                if ( ! is_multisite() ) {
     1237                        $this->markTestSkipped( __METHOD__ . ' requires multisite.' );
     1238                }
     1239
     1240                $sites = $this->factory->blog->create_many( 2 );
     1241                $users = $this->factory->user->create_many( 2 );
     1242
     1243                add_user_to_blog( $sites[0], $users[0], 'author' );
     1244                add_user_to_blog( $sites[1], $users[1], 'author' );
     1245
     1246                $found = get_users( array(
     1247                        'blog_id' => $sites[1],
     1248                        'fields' => 'ID',
     1249                ) );
     1250
     1251                $this->assertEqualSets( array( $users[1] ), $found );
     1252        }
     1253
     1254        /**
     1255         * @ticket 22212
     1256         * @ticket 21119
     1257         */
     1258        public function test_calling_prepare_query_a_second_time_should_not_add_another_cap_query_on_multisite() {
     1259                if ( ! is_multisite() ) {
     1260                        $this->markTestSkipped( __METHOD__ . ' requires Multisite.' );
     1261                }
     1262
     1263                $site_id = get_current_blog_id();
     1264                $u = $this->factory->user->create();
     1265                add_user_to_blog( $site_id, $u, 'author' );
     1266
     1267                $q = new WP_User_Query( array(
     1268                        'include' => $u,
     1269                ) );
     1270
     1271                $r1 = $q->request;
     1272
     1273                $q->prepare_query( array(
     1274                        'include' => $u,
     1275                ) );
     1276
     1277                $r2 = $q->request;
     1278
     1279                $q->prepare_query( array(
     1280                        'include' => $u,
     1281                ) );
     1282
     1283                $r3 = $q->request;
     1284
     1285                $this->assertSame( $r1, $r2 );
     1286                $this->assertSame( $r1, $r3 );
     1287        }
    8991288}
Note: See TracChangeset for help on using the changeset viewer.