class-wp-ms-users-list-table.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <?php
  2. /**
  3. * List Table API: WP_MS_Users_List_Table class
  4. *
  5. * @package WordPress
  6. * @subpackage Administration
  7. * @since 3.1.0
  8. */
  9. /**
  10. * Core class used to implement displaying users in a list table for the network admin.
  11. *
  12. * @since 3.1.0
  13. *
  14. * @see WP_List_Table
  15. */
  16. class WP_MS_Users_List_Table extends WP_List_Table {
  17. /**
  18. * @return bool
  19. */
  20. public function ajax_user_can() {
  21. return current_user_can( 'manage_network_users' );
  22. }
  23. /**
  24. * @global string $mode List table view mode.
  25. * @global string $usersearch
  26. * @global string $role
  27. */
  28. public function prepare_items() {
  29. global $mode, $usersearch, $role;
  30. if ( ! empty( $_REQUEST['mode'] ) ) {
  31. $mode = 'excerpt' === $_REQUEST['mode'] ? 'excerpt' : 'list';
  32. set_user_setting( 'network_users_list_mode', $mode );
  33. } else {
  34. $mode = get_user_setting( 'network_users_list_mode', 'list' );
  35. }
  36. $usersearch = isset( $_REQUEST['s'] ) ? wp_unslash( trim( $_REQUEST['s'] ) ) : '';
  37. $users_per_page = $this->get_items_per_page( 'users_network_per_page' );
  38. $role = isset( $_REQUEST['role'] ) ? $_REQUEST['role'] : '';
  39. $paged = $this->get_pagenum();
  40. $args = array(
  41. 'number' => $users_per_page,
  42. 'offset' => ( $paged - 1 ) * $users_per_page,
  43. 'search' => $usersearch,
  44. 'blog_id' => 0,
  45. 'fields' => 'all_with_meta',
  46. );
  47. if ( wp_is_large_network( 'users' ) ) {
  48. $args['search'] = ltrim( $args['search'], '*' );
  49. } elseif ( '' !== $args['search'] ) {
  50. $args['search'] = trim( $args['search'], '*' );
  51. $args['search'] = '*' . $args['search'] . '*';
  52. }
  53. if ( 'super' === $role ) {
  54. $args['login__in'] = get_super_admins();
  55. }
  56. /*
  57. * If the network is large and a search is not being performed,
  58. * show only the latest users with no paging in order to avoid
  59. * expensive count queries.
  60. */
  61. if ( ! $usersearch && wp_is_large_network( 'users' ) ) {
  62. if ( ! isset( $_REQUEST['orderby'] ) ) {
  63. $_GET['orderby'] = 'id';
  64. $_REQUEST['orderby'] = 'id';
  65. }
  66. if ( ! isset( $_REQUEST['order'] ) ) {
  67. $_GET['order'] = 'DESC';
  68. $_REQUEST['order'] = 'DESC';
  69. }
  70. $args['count_total'] = false;
  71. }
  72. if ( isset( $_REQUEST['orderby'] ) ) {
  73. $args['orderby'] = $_REQUEST['orderby'];
  74. }
  75. if ( isset( $_REQUEST['order'] ) ) {
  76. $args['order'] = $_REQUEST['order'];
  77. }
  78. /** This filter is documented in wp-admin/includes/class-wp-users-list-table.php */
  79. $args = apply_filters( 'users_list_table_query_args', $args );
  80. // Query the user IDs for this page.
  81. $wp_user_search = new WP_User_Query( $args );
  82. $this->items = $wp_user_search->get_results();
  83. $this->set_pagination_args(
  84. array(
  85. 'total_items' => $wp_user_search->get_total(),
  86. 'per_page' => $users_per_page,
  87. )
  88. );
  89. }
  90. /**
  91. * @return array
  92. */
  93. protected function get_bulk_actions() {
  94. $actions = array();
  95. if ( current_user_can( 'delete_users' ) ) {
  96. $actions['delete'] = __( 'Delete' );
  97. }
  98. $actions['spam'] = _x( 'Mark as spam', 'user' );
  99. $actions['notspam'] = _x( 'Not spam', 'user' );
  100. return $actions;
  101. }
  102. /**
  103. */
  104. public function no_items() {
  105. _e( 'No users found.' );
  106. }
  107. /**
  108. * @global string $role
  109. * @return array
  110. */
  111. protected function get_views() {
  112. global $role;
  113. $total_users = get_user_count();
  114. $super_admins = get_super_admins();
  115. $total_admins = count( $super_admins );
  116. $role_links = array();
  117. $role_links['all'] = array(
  118. 'url' => network_admin_url( 'users.php' ),
  119. 'label' => sprintf(
  120. /* translators: Number of users. */
  121. _nx(
  122. 'All <span class="count">(%s)</span>',
  123. 'All <span class="count">(%s)</span>',
  124. $total_users,
  125. 'users'
  126. ),
  127. number_format_i18n( $total_users )
  128. ),
  129. 'current' => 'super' !== $role,
  130. );
  131. $role_links['super'] = array(
  132. 'url' => network_admin_url( 'users.php?role=super' ),
  133. 'label' => sprintf(
  134. /* translators: Number of users. */
  135. _n(
  136. 'Super Admin <span class="count">(%s)</span>',
  137. 'Super Admins <span class="count">(%s)</span>',
  138. $total_admins
  139. ),
  140. number_format_i18n( $total_admins )
  141. ),
  142. 'current' => 'super' === $role,
  143. );
  144. return $this->get_views_links( $role_links );
  145. }
  146. /**
  147. * @global string $mode List table view mode.
  148. *
  149. * @param string $which
  150. */
  151. protected function pagination( $which ) {
  152. global $mode;
  153. parent::pagination( $which );
  154. if ( 'top' === $which ) {
  155. $this->view_switcher( $mode );
  156. }
  157. }
  158. /**
  159. * @return array
  160. */
  161. public function get_columns() {
  162. $users_columns = array(
  163. 'cb' => '<input type="checkbox" />',
  164. 'username' => __( 'Username' ),
  165. 'name' => __( 'Name' ),
  166. 'email' => __( 'Email' ),
  167. 'registered' => _x( 'Registered', 'user' ),
  168. 'blogs' => __( 'Sites' ),
  169. );
  170. /**
  171. * Filters the columns displayed in the Network Admin Users list table.
  172. *
  173. * @since MU (3.0.0)
  174. *
  175. * @param string[] $users_columns An array of user columns. Default 'cb', 'username',
  176. * 'name', 'email', 'registered', 'blogs'.
  177. */
  178. return apply_filters( 'wpmu_users_columns', $users_columns );
  179. }
  180. /**
  181. * @return array
  182. */
  183. protected function get_sortable_columns() {
  184. return array(
  185. 'username' => 'login',
  186. 'name' => 'name',
  187. 'email' => 'email',
  188. 'registered' => 'id',
  189. );
  190. }
  191. /**
  192. * Handles the checkbox column output.
  193. *
  194. * @since 4.3.0
  195. * @since 5.9.0 Renamed `$user` to `$item` to match parent class for PHP 8 named parameter support.
  196. *
  197. * @param WP_User $item The current WP_User object.
  198. */
  199. public function column_cb( $item ) {
  200. // Restores the more descriptive, specific name for use within this method.
  201. $user = $item;
  202. if ( is_super_admin( $user->ID ) ) {
  203. return;
  204. }
  205. ?>
  206. <label class="screen-reader-text" for="blog_<?php echo $user->ID; ?>">
  207. <?php
  208. /* translators: %s: User login. */
  209. printf( __( 'Select %s' ), $user->user_login );
  210. ?>
  211. </label>
  212. <input type="checkbox" id="blog_<?php echo $user->ID; ?>" name="allusers[]" value="<?php echo esc_attr( $user->ID ); ?>" />
  213. <?php
  214. }
  215. /**
  216. * Handles the ID column output.
  217. *
  218. * @since 4.4.0
  219. *
  220. * @param WP_User $user The current WP_User object.
  221. */
  222. public function column_id( $user ) {
  223. echo $user->ID;
  224. }
  225. /**
  226. * Handles the username column output.
  227. *
  228. * @since 4.3.0
  229. *
  230. * @param WP_User $user The current WP_User object.
  231. */
  232. public function column_username( $user ) {
  233. $super_admins = get_super_admins();
  234. $avatar = get_avatar( $user->user_email, 32 );
  235. echo $avatar;
  236. if ( current_user_can( 'edit_user', $user->ID ) ) {
  237. $edit_link = esc_url( add_query_arg( 'wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), get_edit_user_link( $user->ID ) ) );
  238. $edit = "<a href=\"{$edit_link}\">{$user->user_login}</a>";
  239. } else {
  240. $edit = $user->user_login;
  241. }
  242. ?>
  243. <strong>
  244. <?php
  245. echo $edit;
  246. if ( in_array( $user->user_login, $super_admins, true ) ) {
  247. echo ' &mdash; ' . __( 'Super Admin' );
  248. }
  249. ?>
  250. </strong>
  251. <?php
  252. }
  253. /**
  254. * Handles the name column output.
  255. *
  256. * @since 4.3.0
  257. *
  258. * @param WP_User $user The current WP_User object.
  259. */
  260. public function column_name( $user ) {
  261. if ( $user->first_name && $user->last_name ) {
  262. printf(
  263. /* translators: 1: User's first name, 2: Last name. */
  264. _x( '%1$s %2$s', 'Display name based on first name and last name' ),
  265. $user->first_name,
  266. $user->last_name
  267. );
  268. } elseif ( $user->first_name ) {
  269. echo $user->first_name;
  270. } elseif ( $user->last_name ) {
  271. echo $user->last_name;
  272. } else {
  273. echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . _x( 'Unknown', 'name' ) . '</span>';
  274. }
  275. }
  276. /**
  277. * Handles the email column output.
  278. *
  279. * @since 4.3.0
  280. *
  281. * @param WP_User $user The current WP_User object.
  282. */
  283. public function column_email( $user ) {
  284. echo "<a href='" . esc_url( "mailto:$user->user_email" ) . "'>$user->user_email</a>";
  285. }
  286. /**
  287. * Handles the registered date column output.
  288. *
  289. * @since 4.3.0
  290. *
  291. * @global string $mode List table view mode.
  292. *
  293. * @param WP_User $user The current WP_User object.
  294. */
  295. public function column_registered( $user ) {
  296. global $mode;
  297. if ( 'list' === $mode ) {
  298. $date = __( 'Y/m/d' );
  299. } else {
  300. $date = __( 'Y/m/d g:i:s a' );
  301. }
  302. echo mysql2date( $date, $user->user_registered );
  303. }
  304. /**
  305. * @since 4.3.0
  306. *
  307. * @param WP_User $user
  308. * @param string $classes
  309. * @param string $data
  310. * @param string $primary
  311. */
  312. protected function _column_blogs( $user, $classes, $data, $primary ) {
  313. echo '<td class="', $classes, ' has-row-actions" ', $data, '>';
  314. echo $this->column_blogs( $user );
  315. echo $this->handle_row_actions( $user, 'blogs', $primary );
  316. echo '</td>';
  317. }
  318. /**
  319. * Handles the sites column output.
  320. *
  321. * @since 4.3.0
  322. *
  323. * @param WP_User $user The current WP_User object.
  324. */
  325. public function column_blogs( $user ) {
  326. $blogs = get_blogs_of_user( $user->ID, true );
  327. if ( ! is_array( $blogs ) ) {
  328. return;
  329. }
  330. foreach ( $blogs as $site ) {
  331. if ( ! can_edit_network( $site->site_id ) ) {
  332. continue;
  333. }
  334. $path = ( '/' === $site->path ) ? '' : $site->path;
  335. $site_classes = array( 'site-' . $site->site_id );
  336. /**
  337. * Filters the span class for a site listing on the mulisite user list table.
  338. *
  339. * @since 5.2.0
  340. *
  341. * @param string[] $site_classes Array of class names used within the span tag. Default "site-#" with the site's network ID.
  342. * @param int $site_id Site ID.
  343. * @param int $network_id Network ID.
  344. * @param WP_User $user WP_User object.
  345. */
  346. $site_classes = apply_filters( 'ms_user_list_site_class', $site_classes, $site->userblog_id, $site->site_id, $user );
  347. if ( is_array( $site_classes ) && ! empty( $site_classes ) ) {
  348. $site_classes = array_map( 'sanitize_html_class', array_unique( $site_classes ) );
  349. echo '<span class="' . esc_attr( implode( ' ', $site_classes ) ) . '">';
  350. } else {
  351. echo '<span>';
  352. }
  353. echo '<a href="' . esc_url( network_admin_url( 'site-info.php?id=' . $site->userblog_id ) ) . '">' . str_replace( '.' . get_network()->domain, '', $site->domain . $path ) . '</a>';
  354. echo ' <small class="row-actions">';
  355. $actions = array();
  356. $actions['edit'] = '<a href="' . esc_url( network_admin_url( 'site-info.php?id=' . $site->userblog_id ) ) . '">' . __( 'Edit' ) . '</a>';
  357. $class = '';
  358. if ( 1 === (int) $site->spam ) {
  359. $class .= 'site-spammed ';
  360. }
  361. if ( 1 === (int) $site->mature ) {
  362. $class .= 'site-mature ';
  363. }
  364. if ( 1 === (int) $site->deleted ) {
  365. $class .= 'site-deleted ';
  366. }
  367. if ( 1 === (int) $site->archived ) {
  368. $class .= 'site-archived ';
  369. }
  370. $actions['view'] = '<a class="' . $class . '" href="' . esc_url( get_home_url( $site->userblog_id ) ) . '">' . __( 'View' ) . '</a>';
  371. /**
  372. * Filters the action links displayed next the sites a user belongs to
  373. * in the Network Admin Users list table.
  374. *
  375. * @since 3.1.0
  376. *
  377. * @param string[] $actions An array of action links to be displayed. Default 'Edit', 'View'.
  378. * @param int $userblog_id The site ID.
  379. */
  380. $actions = apply_filters( 'ms_user_list_site_actions', $actions, $site->userblog_id );
  381. $action_count = count( $actions );
  382. $i = 0;
  383. foreach ( $actions as $action => $link ) {
  384. ++$i;
  385. $separator = ( $i < $action_count ) ? ' | ' : '';
  386. echo "<span class='$action'>{$link}{$separator}</span>";
  387. }
  388. echo '</small></span><br />';
  389. }
  390. }
  391. /**
  392. * Handles the default column output.
  393. *
  394. * @since 4.3.0
  395. * @since 5.9.0 Renamed `$user` to `$item` to match parent class for PHP 8 named parameter support.
  396. *
  397. * @param WP_User $item The current WP_User object.
  398. * @param string $column_name The current column name.
  399. */
  400. public function column_default( $item, $column_name ) {
  401. /** This filter is documented in wp-admin/includes/class-wp-users-list-table.php */
  402. echo apply_filters(
  403. 'manage_users_custom_column',
  404. '', // Custom column output. Default empty.
  405. $column_name,
  406. $item->ID // User ID.
  407. );
  408. }
  409. public function display_rows() {
  410. foreach ( $this->items as $user ) {
  411. $class = '';
  412. $status_list = array(
  413. 'spam' => 'site-spammed',
  414. 'deleted' => 'site-deleted',
  415. );
  416. foreach ( $status_list as $status => $col ) {
  417. if ( $user->$status ) {
  418. $class .= " $col";
  419. }
  420. }
  421. ?>
  422. <tr class="<?php echo trim( $class ); ?>">
  423. <?php $this->single_row_columns( $user ); ?>
  424. </tr>
  425. <?php
  426. }
  427. }
  428. /**
  429. * Gets the name of the default primary column.
  430. *
  431. * @since 4.3.0
  432. *
  433. * @return string Name of the default primary column, in this case, 'username'.
  434. */
  435. protected function get_default_primary_column_name() {
  436. return 'username';
  437. }
  438. /**
  439. * Generates and displays row action links.
  440. *
  441. * @since 4.3.0
  442. * @since 5.9.0 Renamed `$user` to `$item` to match parent class for PHP 8 named parameter support.
  443. *
  444. * @param WP_User $item User being acted upon.
  445. * @param string $column_name Current column name.
  446. * @param string $primary Primary column name.
  447. * @return string Row actions output for users in Multisite, or an empty string
  448. * if the current column is not the primary column.
  449. */
  450. protected function handle_row_actions( $item, $column_name, $primary ) {
  451. if ( $primary !== $column_name ) {
  452. return '';
  453. }
  454. // Restores the more descriptive, specific name for use within this method.
  455. $user = $item;
  456. $super_admins = get_super_admins();
  457. $actions = array();
  458. if ( current_user_can( 'edit_user', $user->ID ) ) {
  459. $edit_link = esc_url( add_query_arg( 'wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), get_edit_user_link( $user->ID ) ) );
  460. $actions['edit'] = '<a href="' . $edit_link . '">' . __( 'Edit' ) . '</a>';
  461. }
  462. if ( current_user_can( 'delete_user', $user->ID ) && ! in_array( $user->user_login, $super_admins, true ) ) {
  463. $actions['delete'] = '<a href="' . esc_url( network_admin_url( add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), wp_nonce_url( 'users.php', 'deleteuser' ) . '&amp;action=deleteuser&amp;id=' . $user->ID ) ) ) . '" class="delete">' . __( 'Delete' ) . '</a>';
  464. }
  465. /**
  466. * Filters the action links displayed under each user in the Network Admin Users list table.
  467. *
  468. * @since 3.2.0
  469. *
  470. * @param string[] $actions An array of action links to be displayed. Default 'Edit', 'Delete'.
  471. * @param WP_User $user WP_User object.
  472. */
  473. $actions = apply_filters( 'ms_user_row_actions', $actions, $user );
  474. return $this->row_actions( $actions );
  475. }
  476. }