| 1 | Index: wp-admin/includes/schema.php |
|---|
| 2 | =================================================================== |
|---|
| 3 | --- wp-admin/includes/schema.php (revision 13173) |
|---|
| 4 | +++ wp-admin/includes/schema.php (working copy) |
|---|
| 5 | @@ -145,7 +145,8 @@ |
|---|
| 6 | PRIMARY KEY (ID), |
|---|
| 7 | KEY post_name (post_name), |
|---|
| 8 | KEY type_status_date (post_type,post_status,post_date,ID), |
|---|
| 9 | - KEY post_parent (post_parent) |
|---|
| 10 | + KEY post_parent (post_parent), |
|---|
| 11 | + KEY post_author (post_author) |
|---|
| 12 | ) $charset_collate; |
|---|
| 13 | CREATE TABLE $wpdb->users ( |
|---|
| 14 | ID bigint(20) unsigned NOT NULL auto_increment, |
|---|
| 15 | Index: wp-admin/includes/template.php |
|---|
| 16 | =================================================================== |
|---|
| 17 | --- wp-admin/includes/template.php (revision 13173) |
|---|
| 18 | +++ wp-admin/includes/template.php (working copy) |
|---|
| 19 | @@ -1789,16 +1789,17 @@ |
|---|
| 20 | } |
|---|
| 21 | |
|---|
| 22 | /** |
|---|
| 23 | - * {@internal Missing Short Description}} |
|---|
| 24 | + * Generate HTML for a single row on the users.php admin panel. |
|---|
| 25 | * |
|---|
| 26 | * @since unknown |
|---|
| 27 | * |
|---|
| 28 | - * @param unknown_type $user_object |
|---|
| 29 | - * @param unknown_type $style |
|---|
| 30 | - * @param unknown_type $role |
|---|
| 31 | - * @return unknown |
|---|
| 32 | + * @param object $user_object |
|---|
| 33 | + * @param string $style Optional. Attributes added to the TR element. Must be sanitized. |
|---|
| 34 | + * @param string $role Key for the $wp_roles array. |
|---|
| 35 | + * @param int $numposts Optional. Post count to display for this user. Defaults to zero, as in, a new user has made zero posts. |
|---|
| 36 | + * @return string |
|---|
| 37 | */ |
|---|
| 38 | -function user_row( $user_object, $style = '', $role = '' ) { |
|---|
| 39 | +function user_row( $user_object, $style = '', $role = '', $numposts = 0 ) { |
|---|
| 40 | global $wp_roles; |
|---|
| 41 | |
|---|
| 42 | $current_user = wp_get_current_user(); |
|---|
| 43 | @@ -1814,7 +1815,6 @@ |
|---|
| 44 | $short_url = substr( $short_url, 0, -1 ); |
|---|
| 45 | if ( strlen( $short_url ) > 35 ) |
|---|
| 46 | $short_url = substr( $short_url, 0, 32 ).'...'; |
|---|
| 47 | - $numposts = get_usernumposts( $user_object->ID ); |
|---|
| 48 | $checkbox = ''; |
|---|
| 49 | // Check if the user for this row is editable |
|---|
| 50 | if ( current_user_can( 'edit_user', $user_object->ID ) ) { |
|---|
| 51 | Index: wp-admin/users.php |
|---|
| 52 | =================================================================== |
|---|
| 53 | --- wp-admin/users.php (revision 13173) |
|---|
| 54 | +++ wp-admin/users.php (working copy) |
|---|
| 55 | @@ -208,9 +208,15 @@ |
|---|
| 56 | $userspage = isset($_GET['userspage']) ? $_GET['userspage'] : null; |
|---|
| 57 | $role = isset($_GET['role']) ? $_GET['role'] : null; |
|---|
| 58 | |
|---|
| 59 | - // Query the users |
|---|
| 60 | + // Query the user IDs for this page |
|---|
| 61 | $wp_user_search = new WP_User_Search($usersearch, $userspage, $role); |
|---|
| 62 | |
|---|
| 63 | + // Query the post counts for this page |
|---|
| 64 | + $post_counts = get_users_numposts($wp_user_search->get_results()); |
|---|
| 65 | + |
|---|
| 66 | + // Query the users for this page |
|---|
| 67 | + cache_users($wp_user_search->get_results()); |
|---|
| 68 | + |
|---|
| 69 | $messages = array(); |
|---|
| 70 | if ( isset($_GET['update']) ) : |
|---|
| 71 | switch($_GET['update']) { |
|---|
| 72 | @@ -265,16 +271,10 @@ |
|---|
| 73 | <?php |
|---|
| 74 | $role_links = array(); |
|---|
| 75 | $avail_roles = array(); |
|---|
| 76 | -$users_of_blog = get_users_of_blog(); |
|---|
| 77 | -$total_users = count( $users_of_blog ); |
|---|
| 78 | -foreach ( (array) $users_of_blog as $b_user ) { |
|---|
| 79 | - $b_roles = unserialize($b_user->meta_value); |
|---|
| 80 | - foreach ( (array) $b_roles as $b_role => $val ) { |
|---|
| 81 | - if ( !isset($avail_roles[$b_role]) ) |
|---|
| 82 | - $avail_roles[$b_role] = 0; |
|---|
| 83 | - $avail_roles[$b_role]++; |
|---|
| 84 | - } |
|---|
| 85 | -} |
|---|
| 86 | + |
|---|
| 87 | +$users_of_blog = count_blog_users_by_role_intime(); |
|---|
| 88 | +$total_users = $users_of_blog['total_users']; |
|---|
| 89 | +$avail_roles =& $users_of_blog['avail_roles']; |
|---|
| 90 | unset($users_of_blog); |
|---|
| 91 | |
|---|
| 92 | $current_role = false; |
|---|
| 93 | @@ -372,7 +372,7 @@ |
|---|
| 94 | $role = array_shift($roles); |
|---|
| 95 | |
|---|
| 96 | $style = ( ' class="alternate"' == $style ) ? '' : ' class="alternate"'; |
|---|
| 97 | - echo "\n\t" . user_row($user_object, $style, $role); |
|---|
| 98 | + echo "\n\t", user_row($user_object, $style, $role, $post_counts[(string)$userid]); |
|---|
| 99 | } |
|---|
| 100 | ?> |
|---|
| 101 | </tbody> |
|---|
| 102 | Index: wp-includes/pluggable.php |
|---|
| 103 | =================================================================== |
|---|
| 104 | --- wp-includes/pluggable.php (revision 13173) |
|---|
| 105 | +++ wp-includes/pluggable.php (working copy) |
|---|
| 106 | @@ -139,6 +139,40 @@ |
|---|
| 107 | } |
|---|
| 108 | endif; |
|---|
| 109 | |
|---|
| 110 | +if ( !function_exists('cache_users') ) : |
|---|
| 111 | +/** |
|---|
| 112 | + * Retrieve info for user lists to prevent multiple queries by get_userdata() |
|---|
| 113 | + * |
|---|
| 114 | + * @since 3.0.0 |
|---|
| 115 | + * |
|---|
| 116 | + * @param array $users User ID numbers list |
|---|
| 117 | + */ |
|---|
| 118 | +function cache_users( $users ) { |
|---|
| 119 | + global $wpdb; |
|---|
| 120 | + |
|---|
| 121 | + $clean = array(); |
|---|
| 122 | + foreach($users as $id) { |
|---|
| 123 | + $id = (int) $id; |
|---|
| 124 | + if (wp_cache_get($id, 'users')) { |
|---|
| 125 | + // seems to be cached already |
|---|
| 126 | + } else { |
|---|
| 127 | + $clean[] = $id; |
|---|
| 128 | + } |
|---|
| 129 | + } |
|---|
| 130 | + |
|---|
| 131 | + if ( 0 == count($clean) ) |
|---|
| 132 | + return; |
|---|
| 133 | + |
|---|
| 134 | + $list = implode(',', $clean); |
|---|
| 135 | + |
|---|
| 136 | + $result = $wpdb->get_results("SELECT * FROM $wpdb->users WHERE ID IN ($list)", OBJECT_K); |
|---|
| 137 | + |
|---|
| 138 | + foreach($result as $user_object) { |
|---|
| 139 | + wp_cache_add($user_object->ID, $user_object, 'users'); |
|---|
| 140 | + } |
|---|
| 141 | +} |
|---|
| 142 | +endif; |
|---|
| 143 | + |
|---|
| 144 | if ( !function_exists('get_user_by') ) : |
|---|
| 145 | /** |
|---|
| 146 | * Retrieve user info by a given field |
|---|
| 147 | Index: wp-includes/post.php |
|---|
| 148 | =================================================================== |
|---|
| 149 | --- wp-includes/post.php (revision 13173) |
|---|
| 150 | +++ wp-includes/post.php (working copy) |
|---|
| 151 | @@ -3623,9 +3623,23 @@ |
|---|
| 152 | * @return string SQL code that can be added to a where clause. |
|---|
| 153 | */ |
|---|
| 154 | function get_private_posts_cap_sql($post_type) { |
|---|
| 155 | - global $user_ID; |
|---|
| 156 | - $cap = ''; |
|---|
| 157 | + return get_posts_by_author_sql($post_type, FALSE); |
|---|
| 158 | +} |
|---|
| 159 | |
|---|
| 160 | +/** |
|---|
| 161 | + * Retrieve the post SQL based on capability, author, and type. |
|---|
| 162 | + * |
|---|
| 163 | + * See above for full description. |
|---|
| 164 | + * |
|---|
| 165 | + * @since 3.0.0 |
|---|
| 166 | + * @param string $post_type currently only supports 'post' or 'page'. |
|---|
| 167 | + * @param bool $full Optional. Returns a full WHERE statement instead of just an 'andalso' term. |
|---|
| 168 | + * @param int $post_author Optional. Query posts having a single author ID. |
|---|
| 169 | + * @return string SQL WHERE code that can be added to a query. |
|---|
| 170 | + */ |
|---|
| 171 | +function get_posts_by_author_sql($post_type, $full = TRUE, $post_author = NULL) { |
|---|
| 172 | + global $user_ID, $wpdb; |
|---|
| 173 | + |
|---|
| 174 | // Private posts |
|---|
| 175 | if ($post_type == 'post') { |
|---|
| 176 | $cap = 'read_private_posts'; |
|---|
| 177 | @@ -3634,24 +3648,40 @@ |
|---|
| 178 | $cap = 'read_private_pages'; |
|---|
| 179 | // Dunno what it is, maybe plugins have their own post type? |
|---|
| 180 | } else { |
|---|
| 181 | + $cap = ''; |
|---|
| 182 | $cap = apply_filters('pub_priv_sql_capability', $cap); |
|---|
| 183 | |
|---|
| 184 | if (empty($cap)) { |
|---|
| 185 | // We don't know what it is, filters don't change anything, |
|---|
| 186 | // so set the SQL up to return nothing. |
|---|
| 187 | - return '1 = 0'; |
|---|
| 188 | + return ' 1 = 0 '; |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | - $sql = '(post_status = \'publish\''; |
|---|
| 193 | + if ($full) { |
|---|
| 194 | + if (is_null($post_author)) { |
|---|
| 195 | + $sql = $wpdb->prepare('WHERE post_type = %s AND ', $post_type); |
|---|
| 196 | + } else { |
|---|
| 197 | + $sql = $wpdb->prepare('WHERE post_author = %d AND post_type = %s AND ', $post_author, $post_type); |
|---|
| 198 | + } |
|---|
| 199 | + } else { |
|---|
| 200 | + $sql = ''; |
|---|
| 201 | + } |
|---|
| 202 | |
|---|
| 203 | + $sql .= "(post_status = 'publish'"; |
|---|
| 204 | + |
|---|
| 205 | if (current_user_can($cap)) { |
|---|
| 206 | // Does the user have the capability to view private posts? Guess so. |
|---|
| 207 | - $sql .= ' OR post_status = \'private\''; |
|---|
| 208 | + $sql .= " OR post_status = 'private'"; |
|---|
| 209 | } elseif (is_user_logged_in()) { |
|---|
| 210 | // Users can view their own private posts. |
|---|
| 211 | - $sql .= ' OR post_status = \'private\' AND post_author = \'' . $user_ID . '\''; |
|---|
| 212 | - } |
|---|
| 213 | + $id = (int) $user_ID; |
|---|
| 214 | + if (is_null($post_author)) { |
|---|
| 215 | + $sql .= " OR post_status = 'private' AND post_author = $id"; |
|---|
| 216 | + } elseif ($id == (int)$post_author) { |
|---|
| 217 | + $sql .= " OR post_status = 'private'"; |
|---|
| 218 | + } // else none |
|---|
| 219 | + } // else none |
|---|
| 220 | |
|---|
| 221 | $sql .= ')'; |
|---|
| 222 | |
|---|
| 223 | @@ -4443,4 +4473,4 @@ |
|---|
| 224 | |
|---|
| 225 | add_filter('the_preview', '_set_preview'); |
|---|
| 226 | } |
|---|
| 227 | -} |
|---|
| 228 | \ No newline at end of file |
|---|
| 229 | +} |
|---|
| 230 | Index: wp-includes/user.php |
|---|
| 231 | =================================================================== |
|---|
| 232 | --- wp-includes/user.php (revision 13173) |
|---|
| 233 | +++ wp-includes/user.php (working copy) |
|---|
| 234 | @@ -178,12 +178,47 @@ |
|---|
| 235 | */ |
|---|
| 236 | function get_usernumposts($userid) { |
|---|
| 237 | global $wpdb; |
|---|
| 238 | - $userid = (int) $userid; |
|---|
| 239 | - $count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE post_author = %d AND post_type = 'post' AND ", $userid) . get_private_posts_cap_sql('post')); |
|---|
| 240 | + |
|---|
| 241 | + $where = get_posts_by_author_sql('post', TRUE, $userid); |
|---|
| 242 | + |
|---|
| 243 | + $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" ); |
|---|
| 244 | + |
|---|
| 245 | return apply_filters('get_usernumposts', $count, $userid); |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | /** |
|---|
| 249 | + * Number of posts written by a list of users. |
|---|
| 250 | + * |
|---|
| 251 | + * @since 3.0.0 |
|---|
| 252 | + * @param array $userid User ID number list. |
|---|
| 253 | + * @return array Amount of posts each user has written. |
|---|
| 254 | + */ |
|---|
| 255 | +function get_users_numposts($users) { |
|---|
| 256 | + global $wpdb; |
|---|
| 257 | + |
|---|
| 258 | + if (0 == count($users)) |
|---|
| 259 | + return array(); |
|---|
| 260 | + |
|---|
| 261 | + $userlist = implode(',', $users); |
|---|
| 262 | + $where = get_posts_by_author_sql('post'); |
|---|
| 263 | + |
|---|
| 264 | + $result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N ); |
|---|
| 265 | + |
|---|
| 266 | + $count = array(); |
|---|
| 267 | + foreach($result as $row) { |
|---|
| 268 | + $count[$row[0]] = $row[1]; |
|---|
| 269 | + } |
|---|
| 270 | + |
|---|
| 271 | + foreach($users as $id) { |
|---|
| 272 | + $id = (string) $id; |
|---|
| 273 | + if (!isset($count[$id])) |
|---|
| 274 | + $count[$id] = 0; |
|---|
| 275 | + } |
|---|
| 276 | + |
|---|
| 277 | + return $count; |
|---|
| 278 | +} |
|---|
| 279 | + |
|---|
| 280 | +/** |
|---|
| 281 | * Check that the user login name and password is correct. |
|---|
| 282 | * |
|---|
| 283 | * @since 0.71 |
|---|
| 284 | @@ -440,6 +475,93 @@ |
|---|
| 285 | return true; |
|---|
| 286 | } |
|---|
| 287 | |
|---|
| 288 | +/** |
|---|
| 289 | + * Count number of users who have each of the user roles. |
|---|
| 290 | + * |
|---|
| 291 | + * Assumes there are neither duplicated nor orphaned capabilities meta_values. |
|---|
| 292 | + * Assumes the serialized array values are meaningless, only array keys are used. |
|---|
| 293 | + * Intended scale for this version is 10^5 users. It is not quite capable of that yet due to WP Bug #12257 |
|---|
| 294 | + * Memory exhaustion is expected at higher orders. |
|---|
| 295 | + * |
|---|
| 296 | + * @since 3.0.0 |
|---|
| 297 | + * @return array Includes a grand total and an array of counts indexed by role strings. |
|---|
| 298 | + */ |
|---|
| 299 | +function count_blog_users_by_role_inmem() { |
|---|
| 300 | + global $wpdb, $blog_id; |
|---|
| 301 | + |
|---|
| 302 | + $id = (int) $blog_id; |
|---|
| 303 | + $blog_prefix = $wpdb->get_blog_prefix($id); |
|---|
| 304 | + $avail_roles = array(); |
|---|
| 305 | + |
|---|
| 306 | + $users_of_blog = $wpdb->get_col( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'" ); |
|---|
| 307 | + |
|---|
| 308 | + foreach ( $users_of_blog as $caps_meta ) { |
|---|
| 309 | + $b_roles = unserialize($caps_meta); |
|---|
| 310 | + if ( is_array($b_roles) ) { |
|---|
| 311 | + foreach ( $b_roles as $b_role => $val ) { |
|---|
| 312 | + if ( isset($avail_roles[$b_role]) ) |
|---|
| 313 | + $avail_roles[$b_role]++; |
|---|
| 314 | + else |
|---|
| 315 | + $avail_roles[$b_role] = 1; |
|---|
| 316 | + } |
|---|
| 317 | + } |
|---|
| 318 | + } |
|---|
| 319 | + |
|---|
| 320 | + $result = array(); |
|---|
| 321 | + $result['total_users'] = count( $users_of_blog ); |
|---|
| 322 | + $result['avail_roles'] =& $avail_roles; |
|---|
| 323 | + |
|---|
| 324 | + return $result; |
|---|
| 325 | +} |
|---|
| 326 | + |
|---|
| 327 | +/** |
|---|
| 328 | + * Count number of users who have each of the user roles. |
|---|
| 329 | + * |
|---|
| 330 | + * Assumes there are neither duplicated nor orphaned capabilities meta_values. |
|---|
| 331 | + * Assumes role names are unique phrases. Same assumption made by WP_User_Search::prepare_query() |
|---|
| 332 | + * Intended scale for this version is 10^7 users. |
|---|
| 333 | + * CPU exhaustion is expected at higher orders. |
|---|
| 334 | + * |
|---|
| 335 | + * @since 3.0.0 |
|---|
| 336 | + * @return array Includes a grand total and an array of counts indexed by role strings. |
|---|
| 337 | + */ |
|---|
| 338 | +function count_blog_users_by_role_intime() { |
|---|
| 339 | + global $wpdb, $blog_id, $wp_roles; |
|---|
| 340 | + |
|---|
| 341 | + // Initialize |
|---|
| 342 | + $id = (int) $blog_id; |
|---|
| 343 | + $blog_prefix = $wpdb->get_blog_prefix($id); |
|---|
| 344 | + $avail_roles = $wp_roles->get_names(); |
|---|
| 345 | + |
|---|
| 346 | + // Build a CPU-intensive query that will return concise information. |
|---|
| 347 | + $select_count = array(); |
|---|
| 348 | + foreach ( $avail_roles as $this_role => $name ) { |
|---|
| 349 | + $select_count[] = "COUNT(NULLIF(`meta_value` LIKE '%" . like_escape($this_role) . "%', FALSE))"; |
|---|
| 350 | + } |
|---|
| 351 | + $select_count = implode(', ', $select_count); |
|---|
| 352 | + |
|---|
| 353 | + // Add the meta_value index to the selection list, then run the query. |
|---|
| 354 | + $row = $wpdb->get_row( "SELECT $select_count, COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'", ARRAY_N ); |
|---|
| 355 | + |
|---|
| 356 | + // Run the previous loop again to associate results with role names. |
|---|
| 357 | + $col = 0; |
|---|
| 358 | + $role_counts = array(); |
|---|
| 359 | + foreach ( $avail_roles as $this_role => $name ) { |
|---|
| 360 | + $count = (int) $row[$col++]; |
|---|
| 361 | + if ($count > 0) |
|---|
| 362 | + $role_counts[$this_role] = $count; |
|---|
| 363 | + } |
|---|
| 364 | + |
|---|
| 365 | + // Get the meta_value index from the end of the result set. |
|---|
| 366 | + $total_users = (int) $row[$col]; |
|---|
| 367 | + |
|---|
| 368 | + $result = array(); |
|---|
| 369 | + $result['total_users'] = $total_users; |
|---|
| 370 | + $result['avail_roles'] =& $role_counts; |
|---|
| 371 | + |
|---|
| 372 | + return $result; |
|---|
| 373 | +} |
|---|
| 374 | + |
|---|
| 375 | // |
|---|
| 376 | // Private helper functions |
|---|
| 377 | // |
|---|