Index: author-template.php
===================================================================
--- author-template.php	(revision 13630)
+++ author-template.php	(working copy)
@@ -247,6 +247,13 @@
  * or as a string.</li>
  * <li>html (bool) (true): Whether to list the items in html for or plaintext.
  * </li>
+ * <li>orderby (string) ('name'): The sort order of the list.</li>
+ * <li>order (string) ('ASC'): Sort order for authors (either ascending or
+ * descending).</li>
+ * <li>number (int) (NULL): Sets the number of authors to display. This
+ * causes the SQL LIMIT value to be defined. Default to no LIMIT.</li>
+ * <li>min_count (int) (NULL): Sets a minimum post count threshold. Defaults to
+ * no minimum.</li>
  * </ul>
  *
  * @link http://codex.wordpress.org/Template_Tags/wp_list_authors
@@ -255,46 +262,129 @@
  * @return null|string The output, if echo is set to false.
  */
 function wp_list_authors($args = '') {
-	global $wpdb;
+	global $wpdb, $blog_id;
 
 	$defaults = array(
 		'optioncount' => false, 'exclude_admin' => true,
 		'show_fullname' => false, 'hide_empty' => true,
 		'feed' => '', 'feed_image' => '', 'feed_type' => '', 'echo' => true,
-		'style' => 'list', 'html' => true
+		'style' => 'list', 'html' => true, 'orderby' => 'name',
+		'order' => 'ASC', 'number' => NULL, 'min_count' => NULL
 	);
 
 	$r = wp_parse_args( $args, $defaults );
 	extract($r, EXTR_SKIP);
 	$return = '';
 
-	/** @todo Move select to get_authors(). */
-	$users = get_users_of_blog();
-	$author_ids = array();
-	foreach ( (array) $users as $user )
-		$author_ids[] = $user->user_id;
-	if ( count($author_ids) > 0  ) {
-		$author_ids = implode(',', $author_ids );
-		$authors = $wpdb->get_results( "SELECT ID, user_nicename from $wpdb->users WHERE ID IN($author_ids) " . ($exclude_admin ? "AND user_login <> 'admin' " : '') . "ORDER BY display_name" );
+	if ( empty($id) )
+		$id = (int) $blog_id;
+	$blog_prefix = $wpdb->get_blog_prefix($id);
+
+	// Order ASC or DESC based on the order and orderby arguments
+	if ( $show_fullname ) {
+        // Full names will be looked up via user meta-data and sorted later
+        // order by NULL avoids the implicit sorting of the GROUP BY
+        $author_sort = 'ORDER BY NULL';
+    } else {
+		if ( $orderby == 'count' ) {
+			$author_sort = "ORDER BY author_count $order, author_name";
+		} else {
+			$author_sort = "ORDER BY author_name $order";
+		}
+	}
+
+	// Limit the results based on the min_count argument
+	$min_count = absint( $min_count );
+	if ( $hide_empty )
+		$min_count = max(1, $min_count);
+	if ( $min_count ) {
+		$author_having = "HAVING post_count >= $min_count";
 	} else {
-		$authors = array();
+		$author_having = '';
 	}
 
-	$author_count = array();
-	foreach ( (array) $wpdb->get_results("SELECT DISTINCT post_author, COUNT(ID) AS count FROM $wpdb->posts WHERE post_type = 'post' AND " . get_private_posts_cap_sql( 'post' ) . " GROUP BY post_author") as $row )
-		$author_count[$row->post_author] = $row->count;
+	// Limit the results based on the number argument
+	$number = intval( $number );
+	if ( $number > 0 ) {
+		$author_limit = "LIMIT $number";
+	} else {
+		$author_limit = '';
+	}
 
+	// Join to exclude authors from other blogs. Only needed if hide_empty is
+	// false or and no min_count is set (the INNER JOIN to $wpdb->posts would
+	// already exclude non-authors in both cases)
+	if ( !$hide_empty && !$min_count ) {
+		$author_cap_join = "JOIN $wpdb->usermeta AS meta_capabilities ON meta_capabilities.user_id = $wpdb->users.ID AND meta_capabilities.meta_key = '{$blog_prefix}capabilities'";
+	} else {
+		$author_cap_join = '';
+	}
+	
+	$author_select = "$wpdb->users.ID, $wpdb->users.user_nicename, $wpdb->users.display_name AS author_name";
+	
+	// join on posts only when necessary
+	if ( $hide_empty || $min_count || $optioncount || $orderby == 'count' ) {
+		$author_select .= ", COUNT($wpdb->posts.ID) as post_count";
+		$author_posts_join = "JOIN $wpdb->posts ON $wpdb->posts.post_author = $wpdb->users.ID";
+		$author_where = "($wpdb->posts.post_type = 'post' OR $wpdb->posts.post_type IS NULL) AND ($wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status IS NULL)";
+		$author_group_by = "GROUP BY $wpdb->users.ID";
+		// a left join is needed if we're only interested in fetching numbers of posts
+		if ( !$min_count && !$hide_empty )
+			$author_posts_join = 'LEFT ' . $author_posts_join;
+	} else {
+		$author_posts_join = '';
+		$author_where = '';
+		$author_group_by = '';
+	}
+	
+	if ( $exclude_admin )
+		$author_where .= ( $author_where ? ' AND ' : '' ) . "$wpdb->users.user_login <> 'admin'";
+	
+	$author_where = $author_where ? ( 'WHERE ' . $author_where ) : '';
+	
+	// Query the list of users
+	$sql = "
+		SELECT $author_select
+		FROM $wpdb->users
+		$author_posts_join
+		$author_name_join
+		$author_cap_join
+		$author_where
+		$author_group_by
+		$author_having
+		$author_sort
+		$author_limit
+		";
+
+	$authors = $wpdb->get_results($sql);
+
+	if ( $show_fullname ) {
+        // Lookup first and last name via cached user meta-data
+        foreach ( (array) $authors as $author ) {
+            $userdata = get_userdata( $author->ID );
+            if ( !empty( $userdata->first_name ) || !empty( $userdata->last_name ) ) {
+				$author->author_name = $userdata->first_name . ' ' . $userdata->last_name;
+				trim ( $author->author_name );
+            }
+        }
+		// Sort the objects
+		if ( $orderby == 'name' ) {
+			usort( $authors, '_wp_list_authors_usort_callback_name' );
+		} else {
+			usort( $authors, '_wp_list_authors_usort_callback_count' );
+		}
+		if ( $order == 'DESC' ) {
+			$authors = array_reverse( $authors );
+		}
+    }
+
 	foreach ( (array) $authors as $author ) {
 
 		$link = '';
 
-		$author = get_userdata( $author->ID );
-		$posts = (isset($author_count[$author->ID])) ? $author_count[$author->ID] : 0;
-		$name = $author->display_name;
+		$posts = $author->post_count;
+		$name = $author->author_name;
 
-		if ( $show_fullname && ($author->first_name != '' && $author->last_name != '') )
-			$name = "$author->first_name $author->last_name";
-
 		if( !$html ) {
 			if ( $posts == 0 ) {
 				if ( ! $hide_empty )
@@ -308,11 +398,11 @@
 
 		if ( !($posts == 0 && $hide_empty) && 'list' == $style )
 			$return .= '<li>';
-		if ( $posts == 0 ) {
+		if ( $optioncount && $posts == 0 ) {
 			if ( ! $hide_empty )
 				$link = $name;
 		} else {
-			$link = '<a href="' . get_author_posts_url($author->ID, $author->user_nicename) . '" title="' . esc_attr( sprintf(__("Posts by %s"), $author->display_name) ) . '">' . $name . '</a>';
+			$link = '<a href="' . get_author_posts_url($author->ID, $author->user_nicename) . '" title="' . esc_attr( sprintf(__("Posts by %s"), $author->author_name) ) . '">' . $name . '</a>';
 
 			if ( (! empty($feed_image)) || (! empty($feed)) ) {
 				$link .= ' ';
@@ -358,4 +448,12 @@
 	echo $return;
 }
 
+function _wp_list_authors_usort_callback_name($a, $b) {
+    return strcasecmp( $a->author_name, $b->author_name );
+}
+
+function _wp_list_authors_usort_callback_count($a, $b) {
+	return $a->post_count - $b->post_count;
+}
+
 ?>
