<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[52064] trunk: Users: Introduce `wp_list_users()` function.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/52064">52064</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/52064","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>hellofromTonya</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2021-11-09 00:22:34 +0000 (Tue, 09 Nov 2021)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Users: Introduce `wp_list_users()` function.

Introduces a new function called `wp_list_users()`. Similar to other list functions such as `wp_list_authors()`, it lists all the users of the site. Options are available to configure the HTML output.

Following the same pattern of the other list functions, the list's HTML output is rendered by default. Setting the `echo` argument to `false`, returns the list's HTML output.

A new test class is included.

Props afercia, audrasjb, chriscct7, costdev, desrosj, greenshady, hellofromTonya, mte90, nacin, rohan013, sergeybiryukov.
Fixes <a href="https://core.trac.wordpress.org/ticket/15145">#15145</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesuserphp">trunk/src/wp-includes/user.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestsuserwpListUsersphp">trunk/tests/phpunit/tests/user/wpListUsers.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesuserphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/user.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/user.php    2021-11-08 23:18:35 UTC (rev 52063)
+++ trunk/src/wp-includes/user.php      2021-11-09 00:22:34 UTC (rev 52064)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -766,6 +766,126 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * List all the users of the site, with several options available.
+ *
+ * @since 5.9.0
+ *
+ * @param string|array $args {
+ *     Optional. Array or string of default arguments.
+ *
+ *     @type string $orderby       How to sort the users. Accepts 'nicename', 'email', 'url', 'registered',
+ *                                 'user_nicename', 'user_email', 'user_url', 'user_registered', 'name',
+ *                                 'display_name', 'post_count', 'ID', 'meta_value', 'user_login'. Default 'name'.
+ *     @type string $order         Sorting direction for $orderby. Accepts 'ASC', 'DESC'. Default 'ASC'.
+ *     @type int    $number        Maximum users to return or display. Default empty (all users).
+ *     @type bool   $exclude_admin Whether to exclude the 'admin' account, if it exists. Default false.
+ *     @type bool   $show_fullname Whether to show the user's full name. Default false.
+ *     @type string $feed          If not empty, show a link to the user's feed and use this text as the alt
+ *                                 parameter of the link. Default empty.
+ *     @type string $feed_image    If not empty, show a link to the user's feed and use this image URL as
+ *                                 clickable anchor. Default empty.
+ *     @type string $feed_type     The feed type to link to, such as 'rss2'. Defaults to default feed type.
+ *     @type bool   $echo          Whether to output the result or instead return it. Default true.
+ *     @type string $style         If 'list', each user is wrapped in an `<li>` element, otherwise the users
+ *                                 will be separated by commas.
+ *     @type bool   $html          Whether to list the items in HTML form or plaintext. Default true.
+ *     @type string $exclude       An array, comma-, or space-separated list of user IDs to exclude. Default empty.
+ *     @type string $include       An array, comma-, or space-separated list of user IDs to include. Default empty.
+ * }
+ * @return string|null The output if echo is false. Otherwise null.
+ */
+function wp_list_users( $args = array() ) {
+       $defaults = array(
+               'orderby'       => 'name',
+               'order'         => 'ASC',
+               'number'        => '',
+               'exclude_admin' => true,
+               'show_fullname' => false,
+               'feed'          => '',
+               'feed_image'    => '',
+               'feed_type'     => '',
+               'echo'          => true,
+               'style'         => 'list',
+               'html'          => true,
+               'exclude'       => '',
+               'include'       => '',
+       );
+
+       $args = wp_parse_args( $args, $defaults );
+
+       $return = '';
+
+       $query_args           = wp_array_slice_assoc( $args, array( 'orderby', 'order', 'number', 'exclude', 'include' ) );
+       $query_args['fields'] = 'ids';
+       $users                = get_users( $query_args );
+
+       foreach ( $users as $user_id ) {
+               $user = get_userdata( $user_id );
+
+               if ( $args['exclude_admin'] && 'admin' === $user->display_name ) {
+                       continue;
+               }
+
+               if ( $args['show_fullname'] && '' !== $user->first_name && '' !== $user->last_name ) {
+                       $name = "$user->first_name $user->last_name";
+               } else {
+                       $name = $user->display_name;
+               }
+
+               if ( ! $args['html'] ) {
+                       $return .= $name . ', ';
+
+                       continue; // No need to go further to process HTML.
+               }
+
+               if ( 'list' === $args['style'] ) {
+                       $return .= '<li>';
+               }
+
+               $row = $name;
+
+               if ( ! empty( $args['feed_image'] ) || ! empty( $args['feed'] ) ) {
+                       $row .= ' ';
+                       if ( empty( $args['feed_image'] ) ) {
+                               $row .= '(';
+                       }
+
+                       $row .= '<a href="' . get_author_feed_link( $user->ID, $args['feed_type'] ) . '"';
+
+                       $alt = '';
+                       if ( ! empty( $args['feed'] ) ) {
+                               $alt  = ' alt="' . esc_attr( $args['feed'] ) . '"';
+                               $name = $args['feed'];
+                       }
+
+                       $row .= '>';
+
+                       if ( ! empty( $args['feed_image'] ) ) {
+                               $row .= '<img src="' . esc_url( $args['feed_image'] ) . '" style="border: none;"' . $alt . ' />';
+                       } else {
+                               $row .= $name;
+                       }
+
+                       $row .= '</a>';
+
+                       if ( empty( $args['feed_image'] ) ) {
+                               $row .= ')';
+                       }
+               }
+
+               $return .= $row;
+               $return .= ( 'list' === $args['style'] ) ? '</li>' : ', ';
+       }
+
+       $return = rtrim( $return, ', ' );
+
+       if ( ! $args['echo'] ) {
+               return $return;
+       }
+       echo $return;
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Get the sites a user belongs to.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 3.0.0
</span></span></pre></div>
<a id="trunktestsphpunittestsuserwpListUsersphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/user/wpListUsers.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/user/wpListUsers.php                            (rev 0)
+++ trunk/tests/phpunit/tests/user/wpListUsers.php      2021-11-09 00:22:34 UTC (rev 52064)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,202 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * @group user
+ *
+ * @covers ::wp_list_users
+ */
+class Tests_Functions_wpListUsers extends WP_UnitTestCase {
+       private static $user_ids = array();
+
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$user_ids[] = $factory->user->create(
+                       array(
+                               'user_login'   => 'zack',
+                               'display_name' => 'zack',
+                               'role'         => 'subscriber',
+                               'first_name'   => 'zack',
+                               'last_name'    => 'moon',
+                               'user_email'   => 'm.zack@example.com',
+                               'user_url'     => 'http://moonzack.fake',
+                       )
+               );
+
+               self::$user_ids[] = $factory->user->create(
+                       array(
+                               'user_login'   => 'jane',
+                               'display_name' => 'jane',
+                               'role'         => 'contributor',
+                               'first_name'   => 'jane',
+                               'last_name'    => 'reno',
+                               'user_email'   => 'r.jane@example.com',
+                               'user_url'     => 'http://janereno.fake',
+                       )
+               );
+
+               self::$user_ids[] = $factory->user->create(
+                       array(
+                               'user_login'   => 'michelle',
+                               'display_name' => 'michelle',
+                               'role'         => 'subscriber',
+                               'first_name'   => 'michelle',
+                               'last_name'    => 'jones',
+                               'user_email'   => 'j.michelle@example.com',
+                               'user_url'     => 'http://lemichellejones.fake',
+                       )
+               );
+
+               self::$user_ids[] = $factory->user->create(
+                       array(
+                               'user_login'   => 'paul',
+                               'display_name' => 'paul',
+                               'role'         => 'subscriber',
+                               'first_name'   => 'paul',
+                               'last_name'    => 'norris',
+                               'user_email'   => 'n.paul@example.com',
+                               'user_url'     => 'http://awildpaulappeared.fake',
+                       )
+               );
+
+               foreach ( self::$user_ids as $user ) {
+                       $factory->post->create(
+                               array(
+                                       'post_type'   => 'post',
+                                       'post_author' => $user,
+                               )
+                       );
+               }
+       }
+
+       /**
+        * Test that wp_list_users() creates the expected list of users.
+        *
+        * @dataProvider data_should_create_a_user_list
+        *
+        * @ticket 15145
+        *
+        * @param array|string $args     The arguments to create a list of users.
+        * @param string       $expected The expected result.
+        */
+       public function test_should_create_a_user_list( $args, $expected ) {
+               $actual = wp_list_users( $args );
+
+               $expected = str_replace(
+                       array( 'AUTHOR_ID_zack', 'AUTHOR_ID_jane', 'AUTHOR_ID_michelle', 'AUTHOR_ID_paul' ),
+                       array( self::$user_ids[0], self::$user_ids[1], self::$user_ids[2], self::$user_ids[3] ),
+                       $expected
+               );
+
+               if ( null === $actual ) {
+                       $this->expectOutputString( $expected );
+               } else {
+                       $this->assertSame( $expected, $actual );
+               }
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array
+        */
+       public function data_should_create_a_user_list() {
+               return array(
+                       'defaults when no args are supplied' => array(
+                               'args'     => '',
+                               'expected' => '<li>jane</li><li>michelle</li><li>paul</li><li>zack</li>',
+                       ),
+                       'the admin account included'         => array(
+                               'args'     => array(
+                                       'exclude_admin' => false,
+                               ),
+                               'expected' => '<li>admin</li><li>jane</li><li>michelle</li><li>paul</li><li>zack</li>',
+                       ),
+                       'the full name of each user'         => array(
+                               'args'     => array(
+                                       'show_fullname' => true,
+                               ),
+                               'expected' => '<li>jane reno</li><li>michelle jones</li><li>paul norris</li><li>zack moon</li>',
+                       ),
+                       'the feed of each user'              => array(
+                               'args'     => array(
+                                       'feed' => 'User feed',
+                               ),
+                               'expected' => '<li>jane (<a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_jane">User feed</a>)</li>' .
+                                               '<li>michelle (<a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_michelle">User feed</a>)</li>' .
+                                               '<li>paul (<a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_paul">User feed</a>)</li>' .
+                                               '<li>zack (<a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_zack">User feed</a>)</li>',
+                       ),
+                       'the feed of each user and an image' => array(
+                               'args'     => array(
+                                       'feed'       => 'User feed with image',
+                                       'feed_image' => 'http://example.org/image.jpg',
+                               ),
+                               'expected' => '<li>jane <a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_jane"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>' .
+                                               '<li>michelle <a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_michelle"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>' .
+                                               '<li>paul <a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_paul"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>' .
+                                               '<li>zack <a href="http://example.org/?feed=rss2&amp;author=AUTHOR_ID_zack"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>',
+                       ),
+                       'a feed of the specified type'       => array(
+                               'args'     => array(
+                                       'feed'      => 'User feed as atom',
+                                       'feed_type' => 'atom',
+                               ),
+                               'expected' => '<li>jane (<a href="http://example.org/?feed=atom&amp;author=AUTHOR_ID_jane">User feed as atom</a>)</li>' .
+                                               '<li>michelle (<a href="http://example.org/?feed=atom&amp;author=AUTHOR_ID_michelle">User feed as atom</a>)</li>' .
+                                               '<li>paul (<a href="http://example.org/?feed=atom&amp;author=AUTHOR_ID_paul">User feed as atom</a>)</li>' .
+                                               '<li>zack (<a href="http://example.org/?feed=atom&amp;author=AUTHOR_ID_zack">User feed as atom</a>)</li>',
+                       ),
+                       'no output via echo'                 => array(
+                               'args'     => array(
+                                       'echo' => false,
+                               ),
+                               'expected' => '<li>jane</li><li>michelle</li><li>paul</li><li>zack</li>',
+                       ),
+                       'commas separating each user'        => array(
+                               'args'     => array(
+                                       'style' => '',
+                               ),
+                               'expected' => 'jane, michelle, paul, zack',
+                       ),
+                       'plain text format'                  => array(
+                               'args'     => array(
+                                       'html' => false,
+                               ),
+                               'expected' => 'jane, michelle, paul, zack',
+                       ),
+               );
+       }
+
+       /**
+        * Tests that wp_list_users() does not create a user list.
+        *
+        * @dataProvider data_should_not_create_a_user_list
+        *
+        * @ticket 15145
+        *
+        * @param array|string $args The arguments to create a list of users.
+        */
+       public function test_should_not_create_a_user_list( $args ) {
+               $actual = wp_list_users( $args );
+
+               if ( null === $actual ) {
+                       $this->expectOutputString( '', 'wp_list_users() did not output an empty string.' );
+               } else {
+                       $this->assertSame( $actual, 'wp_list_users() did not return an empty string.' );
+               }
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array
+        */
+       public function data_should_not_create_a_user_list() {
+               return array(
+                       'an empty user query result' => array(
+                               'args'     => array(
+                                       'include' => array( 9999 ),
+                               ),
+                               'expected' => '',
+                       ),
+               );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/user/wpListUsers.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span></div>

</body>
</html>